From 98234f46ab5f3b8e60b292321705e2bb92fbd0ce Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Thu, 18 Jan 2018 15:21:03 +0100 Subject: [PATCH] Retire python-cratonclient Remove everything, add a README with explanation. Change-Id: I9530985e91423a451f2f7d9f5a37d06de2b8a473 --- .coveragerc | 7 - .gitignore | 55 - .gitreview | 4 - .mailmap | 3 - .testr.conf | 7 - .travis.yml | 14 - CONTRIBUTING.rst | 17 - HACKING.rst | 4 - LICENSE | 176 - MANIFEST.in | 6 - README.rst | 25 +- babel.cfg | 2 - cratonclient/__init__.py | 20 - cratonclient/auth.py | 198 - cratonclient/common/__init__.py | 1 - cratonclient/common/cliutils.py | 176 - cratonclient/crud.py | 263 - cratonclient/exceptions.py | 292 - cratonclient/formatters/__init__.py | 12 - cratonclient/formatters/base.py | 107 - cratonclient/formatters/json_format.py | 72 - cratonclient/formatters/table.py | 181 - cratonclient/session.py | 288 - cratonclient/shell/__init__.py | 1 - cratonclient/shell/main.py | 207 - cratonclient/shell/v1/__init__.py | 1 - cratonclient/shell/v1/cells_shell.py | 283 - cratonclient/shell/v1/clouds_shell.py | 215 - cratonclient/shell/v1/devices_shell.py | 150 - cratonclient/shell/v1/hosts_shell.py | 341 -- cratonclient/shell/v1/projects_shell.py | 190 - cratonclient/shell/v1/regions_shell.py | 243 - cratonclient/shell/v1/shell.py | 29 - cratonclient/tests/__init__.py | 12 - cratonclient/tests/base.py | 23 - cratonclient/tests/cassettes/README.rst | 19 - ...ells-test_autopagination_when_listing.yaml | 4832 ---------------- .../cassettes/TestCells-test_create.yaml | 200 - .../cassettes/TestCells-test_delete.yaml | 227 - .../TestCells-test_manual_pagination.yaml | 4774 ---------------- .../TestCells-test_update_existing_cell.yaml | 234 - ...ouds-test_autopagination_when_listing.yaml | 4446 --------------- .../cassettes/TestClouds-test_create.yaml | 68 - .../cassettes/TestClouds-test_delete.yaml | 95 - .../TestClouds-test_manual_pagination.yaml | 4413 --------------- ...TestClouds-test_update_existing_cloud.yaml | 101 - .../cassettes/TestHosts-test_create.yaml | 201 - .../cassettes/TestHosts-test_delete.yaml | 231 - .../TestHosts-test_list_autopaginates.yaml | 4997 ----------------- ...s-test_list_only_retrieves_first_page.yaml | 2594 --------- .../cassettes/TestHosts-test_update.yaml | 239 - .../cassettes/TestRegions-test_create.yaml | 163 - .../cassettes/TestRegions-test_delete.yaml | 161 - .../TestRegions-test_list_autopaginates.yaml | 4586 --------------- .../TestRegions-test_manual_pagination.yaml | 4542 --------------- .../cassettes/TestRegions-test_update.yaml | 167 - cratonclient/tests/integration/__init__.py | 1 - cratonclient/tests/integration/base.py | 137 - .../tests/integration/shell/__init__.py | 1 - cratonclient/tests/integration/shell/base.py | 143 - .../tests/integration/shell/v1/__init__.py | 12 - .../integration/shell/v1/test_cells_shell.py | 338 -- .../integration/shell/v1/test_clouds_shell.py | 171 - .../shell/v1/test_devices_shell.py | 182 - .../integration/shell/v1/test_hosts_shell.py | 399 -- .../integration/shell/v1/test_main_shell.py | 117 - .../shell/v1/test_projects_shell.py | 200 - .../shell/v1/test_regions_shell.py | 182 - cratonclient/tests/integration/test_auth.py | 59 - cratonclient/tests/integration/test_crud.py | 175 - cratonclient/tests/integration/v1/__init__.py | 1 - .../tests/integration/v1/test_cells.py | 121 - .../tests/integration/v1/test_clouds.py | 103 - .../tests/integration/v1/test_devices.py | 79 - .../tests/integration/v1/test_hosts.py | 107 - .../tests/integration/v1/test_regions.py | 106 - cratonclient/tests/unit/__init__.py | 12 - cratonclient/tests/unit/common/__init__.py | 1 - .../tests/unit/common/test_cliutils.py | 134 - .../tests/unit/formatters/__init__.py | 12 - cratonclient/tests/unit/formatters/base.py | 74 - .../unit/formatters/test_base_formatter.py | 63 - .../unit/formatters/test_json_formatter.py | 69 - .../unit/formatters/test_table_formatter.py | 229 - cratonclient/tests/unit/shell/__init__.py | 1 - cratonclient/tests/unit/shell/base.py | 56 - cratonclient/tests/unit/shell/test_main.py | 171 - cratonclient/tests/unit/shell/v1/__init__.py | 1 - .../tests/unit/shell/v1/test_cells_shell.py | 393 -- .../tests/unit/shell/v1/test_clouds_shell.py | 271 - .../tests/unit/shell/v1/test_devices_shell.py | 235 - .../tests/unit/shell/v1/test_hosts_shell.py | 565 -- .../unit/shell/v1/test_projects_shell.py | 265 - .../tests/unit/shell/v1/test_regions_shell.py | 314 -- cratonclient/tests/unit/test_auth.py | 197 - cratonclient/tests/unit/test_crud.py | 326 -- cratonclient/tests/unit/test_exceptions.py | 67 - cratonclient/tests/unit/test_session.py | 221 - cratonclient/tests/unit/v1/__init__.py | 1 - cratonclient/tests/unit/v1/test_clouds.py | 39 - cratonclient/tests/unit/v1/test_inventory.py | 48 - cratonclient/tests/unit/v1/test_regions.py | 39 - cratonclient/tests/unit/v1/test_variables.py | 67 - cratonclient/v1/__init__.py | 14 - cratonclient/v1/cells.py | 32 - cratonclient/v1/client.py | 81 - cratonclient/v1/clouds.py | 32 - cratonclient/v1/devices.py | 33 - cratonclient/v1/hosts.py | 32 - cratonclient/v1/inventory.py | 36 - cratonclient/v1/projects.py | 32 - cratonclient/v1/regions.py | 33 - cratonclient/v1/variables.py | 116 - doc/source/api-ref/index.rst | 95 - doc/source/conf.py | 86 - doc/source/contributing.rst | 5 - doc/source/index.rst | 34 - doc/source/installation.rst | 21 - doc/source/readme.rst | 1 - doc/source/shell/index.rst | 204 - doc/source/specs/approved/template.rst | 1 - doc/source/specs/approved/testing-plan.rst | 141 - doc/source/specs/implemented/.placeholder | 0 doc/source/specs/index.rst | 25 - doc/source/specs/template.rst | 212 - doc/source/usage/authentication.rst | 72 - doc/source/usage/cells.rst | 136 - doc/source/usage/clouds.rst | 134 - doc/source/usage/devices.rst | 32 - doc/source/usage/hosts.rst | 148 - doc/source/usage/index.rst | 30 - doc/source/usage/projects.rst | 135 - doc/source/usage/regions.rst | 135 - doc/source/usage/supported-apis.rst | 50 - requirements.txt | 11 - setup.cfg | 53 - setup.py | 29 - test-requirements.txt | 17 - tools/tox_install.sh | 30 - tox.ini | 54 - 140 files changed, 8 insertions(+), 49744 deletions(-) delete mode 100644 .coveragerc delete mode 100644 .gitignore delete mode 100644 .gitreview delete mode 100644 .mailmap delete mode 100644 .testr.conf delete mode 100644 .travis.yml delete mode 100644 CONTRIBUTING.rst delete mode 100644 HACKING.rst delete mode 100644 LICENSE delete mode 100644 MANIFEST.in delete mode 100644 babel.cfg delete mode 100644 cratonclient/__init__.py delete mode 100644 cratonclient/auth.py delete mode 100644 cratonclient/common/__init__.py delete mode 100644 cratonclient/common/cliutils.py delete mode 100644 cratonclient/crud.py delete mode 100644 cratonclient/exceptions.py delete mode 100644 cratonclient/formatters/__init__.py delete mode 100644 cratonclient/formatters/base.py delete mode 100644 cratonclient/formatters/json_format.py delete mode 100644 cratonclient/formatters/table.py delete mode 100644 cratonclient/session.py delete mode 100644 cratonclient/shell/__init__.py delete mode 100644 cratonclient/shell/main.py delete mode 100644 cratonclient/shell/v1/__init__.py delete mode 100644 cratonclient/shell/v1/cells_shell.py delete mode 100644 cratonclient/shell/v1/clouds_shell.py delete mode 100644 cratonclient/shell/v1/devices_shell.py delete mode 100644 cratonclient/shell/v1/hosts_shell.py delete mode 100644 cratonclient/shell/v1/projects_shell.py delete mode 100644 cratonclient/shell/v1/regions_shell.py delete mode 100644 cratonclient/shell/v1/shell.py delete mode 100644 cratonclient/tests/__init__.py delete mode 100644 cratonclient/tests/base.py delete mode 100644 cratonclient/tests/cassettes/README.rst delete mode 100644 cratonclient/tests/cassettes/TestCells-test_autopagination_when_listing.yaml delete mode 100644 cratonclient/tests/cassettes/TestCells-test_create.yaml delete mode 100644 cratonclient/tests/cassettes/TestCells-test_delete.yaml delete mode 100644 cratonclient/tests/cassettes/TestCells-test_manual_pagination.yaml delete mode 100644 cratonclient/tests/cassettes/TestCells-test_update_existing_cell.yaml delete mode 100644 cratonclient/tests/cassettes/TestClouds-test_autopagination_when_listing.yaml delete mode 100644 cratonclient/tests/cassettes/TestClouds-test_create.yaml delete mode 100644 cratonclient/tests/cassettes/TestClouds-test_delete.yaml delete mode 100644 cratonclient/tests/cassettes/TestClouds-test_manual_pagination.yaml delete mode 100644 cratonclient/tests/cassettes/TestClouds-test_update_existing_cloud.yaml delete mode 100644 cratonclient/tests/cassettes/TestHosts-test_create.yaml delete mode 100644 cratonclient/tests/cassettes/TestHosts-test_delete.yaml delete mode 100644 cratonclient/tests/cassettes/TestHosts-test_list_autopaginates.yaml delete mode 100644 cratonclient/tests/cassettes/TestHosts-test_list_only_retrieves_first_page.yaml delete mode 100644 cratonclient/tests/cassettes/TestHosts-test_update.yaml delete mode 100644 cratonclient/tests/cassettes/TestRegions-test_create.yaml delete mode 100644 cratonclient/tests/cassettes/TestRegions-test_delete.yaml delete mode 100644 cratonclient/tests/cassettes/TestRegions-test_list_autopaginates.yaml delete mode 100644 cratonclient/tests/cassettes/TestRegions-test_manual_pagination.yaml delete mode 100644 cratonclient/tests/cassettes/TestRegions-test_update.yaml delete mode 100644 cratonclient/tests/integration/__init__.py delete mode 100644 cratonclient/tests/integration/base.py delete mode 100644 cratonclient/tests/integration/shell/__init__.py delete mode 100644 cratonclient/tests/integration/shell/base.py delete mode 100644 cratonclient/tests/integration/shell/v1/__init__.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_cells_shell.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_clouds_shell.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_devices_shell.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_hosts_shell.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_main_shell.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_projects_shell.py delete mode 100644 cratonclient/tests/integration/shell/v1/test_regions_shell.py delete mode 100644 cratonclient/tests/integration/test_auth.py delete mode 100644 cratonclient/tests/integration/test_crud.py delete mode 100644 cratonclient/tests/integration/v1/__init__.py delete mode 100644 cratonclient/tests/integration/v1/test_cells.py delete mode 100644 cratonclient/tests/integration/v1/test_clouds.py delete mode 100644 cratonclient/tests/integration/v1/test_devices.py delete mode 100644 cratonclient/tests/integration/v1/test_hosts.py delete mode 100644 cratonclient/tests/integration/v1/test_regions.py delete mode 100644 cratonclient/tests/unit/__init__.py delete mode 100644 cratonclient/tests/unit/common/__init__.py delete mode 100644 cratonclient/tests/unit/common/test_cliutils.py delete mode 100644 cratonclient/tests/unit/formatters/__init__.py delete mode 100644 cratonclient/tests/unit/formatters/base.py delete mode 100644 cratonclient/tests/unit/formatters/test_base_formatter.py delete mode 100644 cratonclient/tests/unit/formatters/test_json_formatter.py delete mode 100644 cratonclient/tests/unit/formatters/test_table_formatter.py delete mode 100644 cratonclient/tests/unit/shell/__init__.py delete mode 100644 cratonclient/tests/unit/shell/base.py delete mode 100644 cratonclient/tests/unit/shell/test_main.py delete mode 100644 cratonclient/tests/unit/shell/v1/__init__.py delete mode 100644 cratonclient/tests/unit/shell/v1/test_cells_shell.py delete mode 100644 cratonclient/tests/unit/shell/v1/test_clouds_shell.py delete mode 100644 cratonclient/tests/unit/shell/v1/test_devices_shell.py delete mode 100644 cratonclient/tests/unit/shell/v1/test_hosts_shell.py delete mode 100644 cratonclient/tests/unit/shell/v1/test_projects_shell.py delete mode 100644 cratonclient/tests/unit/shell/v1/test_regions_shell.py delete mode 100644 cratonclient/tests/unit/test_auth.py delete mode 100644 cratonclient/tests/unit/test_crud.py delete mode 100644 cratonclient/tests/unit/test_exceptions.py delete mode 100644 cratonclient/tests/unit/test_session.py delete mode 100644 cratonclient/tests/unit/v1/__init__.py delete mode 100644 cratonclient/tests/unit/v1/test_clouds.py delete mode 100644 cratonclient/tests/unit/v1/test_inventory.py delete mode 100644 cratonclient/tests/unit/v1/test_regions.py delete mode 100644 cratonclient/tests/unit/v1/test_variables.py delete mode 100644 cratonclient/v1/__init__.py delete mode 100644 cratonclient/v1/cells.py delete mode 100644 cratonclient/v1/client.py delete mode 100644 cratonclient/v1/clouds.py delete mode 100644 cratonclient/v1/devices.py delete mode 100644 cratonclient/v1/hosts.py delete mode 100644 cratonclient/v1/inventory.py delete mode 100644 cratonclient/v1/projects.py delete mode 100644 cratonclient/v1/regions.py delete mode 100644 cratonclient/v1/variables.py delete mode 100644 doc/source/api-ref/index.rst delete mode 100755 doc/source/conf.py delete mode 100644 doc/source/contributing.rst delete mode 100644 doc/source/index.rst delete mode 100644 doc/source/installation.rst delete mode 100644 doc/source/readme.rst delete mode 100644 doc/source/shell/index.rst delete mode 120000 doc/source/specs/approved/template.rst delete mode 100644 doc/source/specs/approved/testing-plan.rst delete mode 100644 doc/source/specs/implemented/.placeholder delete mode 100644 doc/source/specs/index.rst delete mode 100644 doc/source/specs/template.rst delete mode 100644 doc/source/usage/authentication.rst delete mode 100644 doc/source/usage/cells.rst delete mode 100644 doc/source/usage/clouds.rst delete mode 100644 doc/source/usage/devices.rst delete mode 100644 doc/source/usage/hosts.rst delete mode 100644 doc/source/usage/index.rst delete mode 100644 doc/source/usage/projects.rst delete mode 100644 doc/source/usage/regions.rst delete mode 100644 doc/source/usage/supported-apis.rst delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test-requirements.txt delete mode 100755 tools/tox_install.sh delete mode 100644 tox.ini diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 68e77de..0000000 --- a/.coveragerc +++ /dev/null @@ -1,7 +0,0 @@ -[run] -branch = True -source = cratonclient -omit = cratonclient/openstack/* - -[report] -ignore_errors = True diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e52bb44..0000000 --- a/.gitignore +++ /dev/null @@ -1,55 +0,0 @@ -*.py[cod] - -# C extensions -*.so - -# Packages -*.egg* -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -cover/ -.coverage* -!.coveragerc -.tox -nosetests.xml -.testrepository -.venv - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# Complexity -output/*.html -output/*/index.html - -# Sphinx -doc/build - -# pbr generates these -AUTHORS -ChangeLog - -# Editors -*~ -.*.swp -.*sw? diff --git a/.gitreview b/.gitreview deleted file mode 100644 index 0d5b716..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/python-cratonclient.git diff --git a/.mailmap b/.mailmap deleted file mode 100644 index 516ae6f..0000000 --- a/.mailmap +++ /dev/null @@ -1,3 +0,0 @@ -# Format is: -# -# diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 6d83b3c..0000000 --- a/.testr.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ - OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ - OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ - ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 76a69f1..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -sudo: false -language: python - -matrix: - include: - - python: 2.7 - env: TOXENV=py27 - - python: 3.5 - env: TOXENV=py35 - - env: TOXENV=pep8 - -install: pip install tox-travis - -script: tox diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index e124784..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,17 +0,0 @@ -If you would like to contribute to the development of OpenStack, you must -follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -If you already have a good understanding of how the system works and your -OpenStack accounts are set up, you can skip to the development workflow -section of this documentation to learn how changes to OpenStack should be -submitted for review via the Gerrit tool: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/python-cratonclient diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index 742db14..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,4 +0,0 @@ -python-cratonclient Style Commandments -=============================================== - -Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 68c771a..0000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c978a52..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include AUTHORS -include ChangeLog -exclude .gitignore -exclude .gitreview - -global-exclude *.pyc diff --git a/README.rst b/README.rst index 21bf133..d98af48 100644 --- a/README.rst +++ b/README.rst @@ -1,19 +1,10 @@ -=============================== -python-cratonclient -=============================== +This project is no longer maintained. -Craton API Client and Command-line Utility +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". -Please fill here a long description which must be at least 3 lines wrapped on -80 cols, so that distribution package maintainers can use it in their packages. -Note that this is a hard requirement. - -* Free software: Apache license -* Documentation: https://python-cratonclient.readthedocs.io -* Source: http://git.openstack.org/cgit/openstack/python-cratonclient -* Bugs: http://bugs.launchpad.net/python-cratonclient - -Features --------- - -* TODO +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb..0000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/cratonclient/__init__.py b/cratonclient/__init__.py deleted file mode 100644 index 1c491e2..0000000 --- a/cratonclient/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Craton API Client Library and Command-Line Application.""" - -import pbr.version - - -__version__ = pbr.version.VersionInfo( - 'cratonclient').version_string() diff --git a/cratonclient/auth.py b/cratonclient/auth.py deleted file mode 100644 index 1f7b5b6..0000000 --- a/cratonclient/auth.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright (c) 2016 Rackspace -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Module that simplifies and unifies authentication for Craton.""" -from keystoneauth1.identity.v3 import password as ksa_password -from keystoneauth1 import plugin -from keystoneauth1 import session as ksa_session - -from cratonclient import exceptions as exc - - -def craton_auth(username, token, project_id, verify=True): - """Configure a cratonclient Session to authenticate to Craton. - - This will create, configure, and return a Session object that will use - Craton's built-in authentication method. - - :param str username: - The username with which to authentiate against the API. - :param str token: - The token with which to authenticate against the API. - :param str project_id: - The project ID that the user belongs to. - :param bool verify: - (Optional) Whether or not to verify HTTPS certificates provided by the - server. Default: True - :returns: - Configured cratonclient session. - :rtype: - cratonclient.session.Session - - Example: - - .. code-block:: python - - from cratonclient import auth - from cratonclient.v1 import client - - craton = client.Client(session=auth.craton_auth( - username='demo', - token='demo', - project_id='b9f10eca66ac4c279c139d01e65f96b4', - )) - - """ - auth_plugin = CratonAuth( - username=username, - token=token, - project_id=project_id, - ) - return create_session_with(auth_plugin, verify) - - -def keystone_auth(auth_url, username, password, verify=True, - project_name=None, project_id=None, - project_domain_name=None, project_domain_id=None, - user_domain_name=None, user_domain_id=None, - **auth_parameters): - r"""Configure a cratonclient Session to authenticate with Keystone. - - This will create, configure, and return a Session using thet appropriate - Keystone authentication plugin to be able to communicate and authenticate - to Craton. - - .. note:: - - Presently, this function supports only V3 Password based - authentication to Keystone. We also do not validate that you specify - required attributes. For example, Keystone will require you provide - ``project_name`` or ``project_id`` but we will not enforce whether or - not you've specified one. - - :param str auth_url: - The URL of the Keystone instance to authenticate to. - :param str username: - The username with which we will authenticate to Keystone. - :param str password: - The password used to authenticate to Keystone. - :param str project_name: - (Optional) The name of the project the user belongs to. - :param str project_id: - (Optional) The ID of the project the user belongs to. - :param str project_domain_name: - (Optional) The name of the project's domain. - :param str project_domain_id: - (Optional) The ID of the project's domain. - :param str user_domain_name: - (Optional) The name of the user's domain. - :param str user_domain_id: - (Optional) The ID of the user's domain. - :param bool verify: - (Optional) Whether or not to verify HTTPS certificates provided by the - server. Default: True - :param \*\*auth_parameters: - Any extra authentication parameters used to authenticate to Keystone. - See the Keystone documentation for usage of: - - ``trust_id`` - - ``domain_id`` - - ``domain_name`` - - ``reauthenticate`` - :returns: - Configured cratonclient session. - :rtype: - cratonclient.session.Session - - Example: - - .. code-block:: python - - from cratonclient import auth - from cratonclient.v1 import client - - craton = client.Client(session=auth.keystone_auth( - auth_url='https://keystone.cloud.org/v3', - username='admin', - password='s3cr373p@55w0rd', - project_name='admin', - project_domain_name='Default', - user_domain_name='Default', - )) - """ - password_auth = ksa_password.Password( - auth_url=auth_url, - username=username, - password=password, - project_id=project_id, - project_name=project_name, - project_domain_id=project_domain_id, - project_domain_name=project_domain_name, - user_domain_id=user_domain_id, - user_domain_name=user_domain_name, - **auth_parameters - ) - return create_session_with(password_auth, verify) - - -def create_session_with(auth_plugin, verify): - """Create a cratonclient Session with the specified auth and verify values. - - :param auth_plugin: - The authentication plugin to use with the keystoneauth1 Session - object. - :type auth_plugin: - keystoneauth1.plugin.BaseAuthPlugin - :param bool verify: - Whether or not to verify HTTPS certificates provided by the server. - :returns: - Configured cratonclient session. - :rtype: - cratonclient.session.Session - """ - from cratonclient import session - return session.Session(session=ksa_session.Session( - auth=auth_plugin, - verify=verify, - )) - - -class CratonAuth(plugin.BaseAuthPlugin): - """Custom authentication plugin for keystoneauth1. - - This is specifically for the case where we're not using Keystone for - authentication. - """ - - def __init__(self, username, project_id, token): - """Initialize our craton authentication class.""" - self.username = username - self.project_id = project_id - self.token = token - - def get_token(self, session, **kwargs): - """Return our token.""" - return self.token - - def get_headers(self, session, **kwargs): - """Return the craton authentication headers.""" - headers = super(CratonAuth, self).get_headers(session, **kwargs) - if headers is None: - # NOTE(sigmavirus24): This means that the token must be None. We - # should not allow this to go further. We're using built-in Craton - # authentication (not authenticating against Keystone) so we will - # be unable to authenticate. - raise exc.UnableToAuthenticate() - - headers['X-Auth-User'] = self.username - headers['X-Auth-Project'] = '{}'.format(self.project_id) - return headers diff --git a/cratonclient/common/__init__.py b/cratonclient/common/__init__.py deleted file mode 100644 index bc91c3c..0000000 --- a/cratonclient/common/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Common Craton common classes and functions.""" diff --git a/cratonclient/common/cliutils.py b/cratonclient/common/cliutils.py deleted file mode 100644 index 5c17101..0000000 --- a/cratonclient/common/cliutils.py +++ /dev/null @@ -1,176 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Craton CLI helper classes and functions.""" -import functools -import json -import os -import sys - -from oslo_utils import encodeutils -from oslo_utils import strutils - -from cratonclient import exceptions as exc - - -def arg(*args, **kwargs): - """Decorator for CLI args. - - Example: - - >>> @arg("name", help="Name of the new entity.") - ... def entity_create(args): - ... pass - """ - def _decorator(func): - """Decorator definition.""" - add_arg(func, *args, **kwargs) - return func - - return _decorator - - -def add_arg(func, *args, **kwargs): - """Bind CLI arguments to a shell.py `do_foo` function.""" - if not hasattr(func, 'arguments'): - func.arguments = [] - - # NOTE(sirp): avoid dups that can occur when the module is shared across - # tests. - if (args, kwargs) not in func.arguments: - # Because of the semantics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.arguments.insert(0, (args, kwargs)) - - -def field_labels_from(attributes): - """Generate a list of slightly more human readable field names. - - This takes the list of fields/attributes on the object and makes them - easier to read. - - :param list attributes: - The attribute names to convert. For example, ``["parent_id"]``. - :returns: - List of field names. For example ``["Parent Id"]`` - :rtype: - list - - Example: - - >>> field_labels_from(["id", "name", "cloud_id"]) - ["Id", "Name", "Cloud Id"] - """ - return [field.replace('_', ' ').title() for field in attributes] - - -def handle_shell_exception(function): - """Generic error handler for shell methods.""" - @functools.wraps(function) - def wrapper(cc, args): - prop_map = { - "vars": "variables" - } - try: - function(cc, args) - except exc.ClientException as client_exc: - # NOTE(thomasem): All shell methods follow a similar pattern, - # so we can parse this name to get intended parts for - # messaging what went wrong to the end-user. - # The pattern is "do__(_)", like - # do_project_show or do_project_vars_get, where is - # not guaranteed to be there, but will afford support for - # actions on some property of the resource. - parsed = function.__name__.split('_') - resource = parsed[1] - verb = parsed[-1] - prop = parsed[2] if len(parsed) > 3 else None - msg = 'Failed to {}'.format(verb) - if prop: - # NOTE(thomasem): Prop would be something like "vars" in - # "do_project_vars_get". - msg = '{} {}'.format(msg, prop_map.get(prop)) - # NOTE(thomasem): Append resource and ClientException details - # to error message. - msg = '{} for {} {} due to "{}: {}"'.format( - msg, resource, args.id, client_exc.__class__, - encodeutils.exception_to_unicode(client_exc) - ) - raise exc.CommandError(msg) - - return wrapper - - -def env(*args, **kwargs): - """Return the first environment variable set. - - If all are empty, defaults to '' or keyword arg `default`. - """ - for arg in args: - value = os.environ.get(arg) - if value: - return value - return kwargs.get('default', '') - - -def convert_arg_value(v): - """Convert different user inputs to normalized type.""" - # NOTE(thomasem): Handle case where one wants to escape this value - # conversion using the format key='"value"' - if v.startswith('"'): - return v.strip('"') - - lower_v = v.lower() - if strutils.is_int_like(v): - return int(v) - if strutils.is_valid_boolstr(lower_v): - return strutils.bool_from_string(lower_v) - if lower_v == 'null' or lower_v == 'none': - return None - try: - return float(v) - except ValueError: - pass - return v - - -def variable_updates(variables): - """Derive list of expected variables for a resource and set them.""" - update_variables = {} - delete_variables = set() - for variable in variables: - k, v = variable.split('=', 1) - if v: - update_variables[k] = convert_arg_value(v) - else: - delete_variables.add(k) - if not sys.stdin.isatty(): - if update_variables or delete_variables: - raise exc.CommandError("Cannot use variable settings from both " - "stdin and command line arguments. Please " - "choose one or the other.") - update_variables = json.load(sys.stdin) - return (update_variables, list(delete_variables)) - - -def variable_deletes(variables): - """Delete a list of variables (by key) from a resource.""" - if not sys.stdin.isatty(): - if variables: - raise exc.CommandError("Cannot use variable settings from both " - "stdin and command line arguments. Please " - "choose one or the other.") - delete_variables = json.load(sys.stdin) - else: - delete_variables = variables - return delete_variables diff --git a/cratonclient/crud.py b/cratonclient/crud.py deleted file mode 100644 index 4bcf1bd..0000000 --- a/cratonclient/crud.py +++ /dev/null @@ -1,263 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Client for CRUD operations.""" -import copy - -from oslo_utils import strutils - - -class CRUDClient(object): - """Class that handles the basic create, read, upload, delete workflow.""" - - key = "" - base_path = None - resource_class = None - - def __init__(self, session, url, **extra_request_kwargs): - """Initialize our Client with a session and base url.""" - self.session = session - self.url = url.rstrip('/') - self.extra_request_kwargs = extra_request_kwargs - - def build_url(self, path_arguments=None): - """Build a complete URL from the url, base_path, and arguments. - - A CRUDClient is constructed with the base URL, e.g. - - .. code-block:: python - - RegionManager(url='https://10.1.1.0:8080/v1', ...) - - The child class of the CRUDClient may set the ``base_path``, e.g., - - .. code-block:: python - - base_path = '/regions' - - And its ``key``, e.g., - - .. code-block:: python - - key = 'region' - - And based on the ``path_arguments`` parameter we will construct a - complete URL. For example, if someone calls: - - .. code-block:: python - - self.build_url(path_arguments={'region_id': 1}) - - with the hypothetical values above, we would return - - https://10.1.1.0:8080/v1/regions/1 - - Users can also override ``base_path`` in ``path_arguments``. - """ - if path_arguments is None: - path_arguments = {} - - base_path = path_arguments.pop('base_path', None) or self.base_path - item_id = path_arguments.pop('{0}_id'.format(self.key), None) - - url = self.url + base_path - - if item_id is not None: - url += '/{0}'.format(item_id) - - return url - - def merge_request_arguments(self, request_kwargs, skip_merge): - """Merge the extra request arguments into the per-request args.""" - if skip_merge: - return - - keys = set(self.extra_request_kwargs.keys()) - missing_keys = keys.difference(request_kwargs.keys()) - for key in missing_keys: - request_kwargs[key] = self.extra_request_kwargs[key] - - def create(self, skip_merge=False, **kwargs): - """Create a new item based on the keyword arguments provided.""" - self.merge_request_arguments(kwargs, skip_merge) - url = self.build_url(path_arguments=kwargs) - response = self.session.post(url, json=kwargs) - return self.resource_class(self, response.json(), loaded=True) - - def get(self, item_id=None, skip_merge=True, **kwargs): - """Retrieve the item based on the keyword arguments provided.""" - self.merge_request_arguments(kwargs, skip_merge) - kwargs.setdefault(self.key + '_id', item_id) - url = self.build_url(path_arguments=kwargs) - response = self.session.get(url) - return self.resource_class(self, response.json(), loaded=True) - - def list(self, skip_merge=False, **kwargs): - """Generate the items from this endpoint.""" - autopaginate = kwargs.pop('autopaginate', True) - nested = kwargs.pop('nested', False) - self.merge_request_arguments(kwargs, skip_merge) - url = self.build_url(path_arguments=kwargs) - - response_generator = self.session.paginate( - url, - autopaginate=autopaginate, - items_key=(self.key + 's'), - nested=nested, - params=kwargs, - ) - for response, items in response_generator: - for item in items: - yield self.resource_class(self, item, loaded=True) - - def update(self, item_id=None, skip_merge=True, **kwargs): - """Update the item based on the keyword arguments provided.""" - self.merge_request_arguments(kwargs, skip_merge) - kwargs.setdefault(self.key + '_id', item_id) - url = self.build_url(path_arguments=kwargs) - response = self.session.put(url, json=kwargs) - return self.resource_class(self, response.json(), loaded=True) - - def delete(self, item_id=None, skip_merge=True, json=None, **kwargs): - """Delete the item based on the keyword arguments provided.""" - self.merge_request_arguments(kwargs, skip_merge) - kwargs.setdefault(self.key + '_id', item_id) - url = self.build_url(path_arguments=kwargs) - response = self.session.delete(url, params=kwargs, json=json) - if 200 <= response.status_code < 300: - return True - return False - - def __repr__(self): - """Return string representation of a Variable.""" - return '%(class)s(%(session)s, %(url)s, %(extra_request_kwargs)s)' % \ - { - "class": self.__class__.__name__, - "session": self.session, - "url": self.url, - "extra_request_kwargs": self.extra_request_kwargs, - } - - -# NOTE(sigmavirus24): Credit for this Resource object goes to the -# keystoneclient developers and contributors. -class Resource(object): - """Base class for OpenStack resources (tenant, user, etc.). - - This is pretty much just a bag for attributes. - """ - - HUMAN_ID = False - NAME_ATTR = 'name' - subresource_managers = {} - - def __init__(self, manager, info, loaded=False): - """Populate and bind to a manager. - - :param manager: BaseManager object - :param info: dictionary representing resource attributes - :param loaded: prevent lazy-loading if set to True - """ - self.manager = manager - self._info = info - self._add_details(info) - self._loaded = loaded - - session = self.manager.session - subresource_base_url = self.manager.build_url( - {"{0}_id".format(self.manager.key): self.id} - ) - for attribute, cls in self.subresource_managers.items(): - setattr(self, attribute, - cls(session, subresource_base_url, - **self.manager.extra_request_kwargs)) - - def __repr__(self): - """Return string representation of resource attributes.""" - reprkeys = sorted(k - for k in self.__dict__.keys() - if k[0] != '_' and k != 'manager') - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) - - @property - def human_id(self): - """Human-readable ID which can be used for bash completion.""" - if self.HUMAN_ID: - name = getattr(self, self.NAME_ATTR, None) - if name is not None: - return strutils.to_slug(name) - return None - - def _add_details(self, info): - for (k, v) in info.items(): - try: - setattr(self, k, v) - self._info[k] = v - except AttributeError: # nosec(cjschaef): we already defined the - # attribute on the class - pass - - def __getattr__(self, k): - """Checking attrbiute existence.""" - if k not in self.__dict__: - # NOTE(bcwaldon): disallow lazy-loading if already loaded once - if not self.is_loaded(): - self.get() - return self.__getattr__(k) - - raise AttributeError(k) - else: - return self.__dict__[k] - - def get(self): - """Support for lazy loading details. - - Some clients, such as novaclient have the option to lazy load the - details, details which can be loaded with this function. - """ - # set_loaded() first ... so if we have to bail, we know we tried. - self.set_loaded(True) - if not hasattr(self.manager, 'get'): - return - - new = self.manager.get(self.id) - if new: - self._add_details(new._info) - self._add_details( - {'x_request_id': self.manager.client.last_request_id}) - - def __eq__(self, other): - """Define equality for resources.""" - if not isinstance(other, Resource): - return NotImplemented - # two resources of different types are not equal - if not isinstance(other, self.__class__): - return False - return self._info == other._info - - def is_loaded(self): - """Check if the resource has been loaded.""" - return self._loaded - - def set_loaded(self, val): - """Set whether the resource has been loaded or not.""" - self._loaded = val - - def to_dict(self): - """Return the resource as a dictionary.""" - return copy.deepcopy(self._info) - - def delete(self): - """Delete the resource from the service.""" - return self.manager.delete(self.id) diff --git a/cratonclient/exceptions.py b/cratonclient/exceptions.py deleted file mode 100644 index fde1bf6..0000000 --- a/cratonclient/exceptions.py +++ /dev/null @@ -1,292 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Exception classes and logic for cratonclient.""" - - -class ClientException(Exception): - """Base exception class for all exceptions in cratonclient.""" - - message = None - - def __init__(self, message=None): - """Initialize our exception instance with our class level message.""" - if message is None: - if self.message is None: - message = self.__class__.__name__ - else: - message = self.message - super(ClientException, self).__init__(message) - - -class UnableToAuthenticate(ClientException): - """There are insufficient parameters for authentication.""" - - message = "Some of the parameters required to authenticate were missing.""" - - -class Timeout(ClientException): - """Catch-all class for connect and read timeouts from requests.""" - - message = 'Request timed out' - - def __init__(self, message=None, **kwargs): - """Initialize our Timeout exception. - - This takes an optional keyword-only argument of - ``original_exception``. - """ - self.original_exception = kwargs.pop('exception', None) - super(Timeout, self).__init__(message) - - -class HTTPError(ClientException): - """Base exception class for all HTTP related exceptions in.""" - - message = "An error occurred while talking to the remote server." - status_code = None - - def __init__(self, message=None, **kwargs): - """Initialize our HTTPError instance. - - Optional keyword-only arguments include: - - - response: for the response generating the error - - original_exception: in the event that this is a requests exception - that we are re-raising. - """ - self.response = kwargs.pop('response', None) - self.original_exception = kwargs.pop('exception', None) - self.status_code = (self.status_code - or getattr(self.response, 'status_code', None)) - super(HTTPError, self).__init__(message) - - @property - def status_code(self): - """Shim to provide a similar API to other OpenStack clients.""" - return self.status_code - - @status_code.setter - def status_code(self, code): - self.status_code = code - - -class CommandError(ClientException): - """Client command was invalid or failed.""" - - message = "The command used was invalid or caused an error.""" - - -class ConnectionFailed(HTTPError): - """Connecting to the server failed.""" - - message = "An error occurred while connecting to the server.""" - - -class HTTPClientError(HTTPError): - """Base exception for client side errors (4xx status codes).""" - - message = "Something went wrong with the request." - - -class BadRequest(HTTPClientError): - """Client sent a malformed request.""" - - status_code = 400 - message = "The request sent to the server was invalid." - - -class Unauthorized(HTTPClientError): - """Client is unauthorized to access the resource in question.""" - - status_code = 401 - message = ("The user has either provided insufficient parameters for " - "authentication or is not authorized to access this resource.") - - -class Forbidden(HTTPClientError): - """Client is forbidden to access the resource.""" - - status_code = 403 - message = ("The user was unable to access the resource because they are " - "forbidden.") - - -class NotFound(HTTPClientError): - """Resource could not be found.""" - - status_code = 404 - message = "The requested resource was not found.""" - - -class MethodNotAllowed(HTTPClientError): - """The request method is not supported.""" - - status_code = 405 - message = "The method used in the request is not supported." - - -class NotAcceptable(HTTPClientError): - """The requested resource can not respond with acceptable content. - - Based on the Accept headers specified by the client, the resource can not - generate content that is an acceptable content-type. - """ - - status_code = 406 - message = "The resource can not return acceptable content." - - -class ProxyAuthenticationRequired(HTTPClientError): - """The client must first authenticate itself with the proxy.""" - - status_code = 407 - message = "The client must first authenticate itself with a proxy." - - -class Conflict(HTTPClientError): - """The request presents a conflict.""" - - status_code = 409 - message = "The request could not be processed due to a conflict." - - -class Gone(HTTPClientError): - """The requested resource is no longer available. - - The resource requested is no longer available and will not be available - again. - """ - - status_code = 410 - message = ("The resource requested is no longer available and will not be" - " available again.") - - -class LengthRequired(HTTPClientError): - """The request did not specify a Content-Length header.""" - - status_code = 411 - message = ("The request did not contain a Content-Length header but one" - " was required by the resource.") - - -class PreconditionFailed(HTTPClientError): - """The server failed to meet one of the preconditions in the request.""" - - status_code = 412 - message = ("The server failed to meet one of the preconditions in the " - "request.") - - -class RequestEntityTooLarge(HTTPClientError): - """The request is larger than the server is willing or able to process.""" - - status_code = 413 - message = ("The request is larger than the server is willing or able to " - "process.") - - -class RequestUriTooLong(HTTPClientError): - """The URI provided was too long for the server to process.""" - - status_code = 414 - message = "The URI provided was too long for the server to process." - - -class UnsupportedMediaType(HTTPClientError): - """The request entity has a media type which is unsupported.""" - - status_code = 415 - message = ("The request entity has a media type which is unsupported by " - "the server or resource.") - - -class RequestedRangeNotSatisfiable(HTTPClientError): - """The requestor wanted a range but the server was unable to provide it.""" - - status_code = 416 - message = ("The requestor wanted a range but the server was unable to " - "provide it.") - - -class UnprocessableEntity(HTTPClientError): - """There were semantic errors in the request.""" - - status_code = 422 - message = ("The request is of a valid content-type and structure but " - "semantically invalid.") - - -_4xx_classes = [ - BadRequest, - Unauthorized, - Forbidden, - NotFound, - MethodNotAllowed, - NotAcceptable, - ProxyAuthenticationRequired, - Conflict, - Gone, - LengthRequired, - PreconditionFailed, - RequestEntityTooLarge, - RequestUriTooLong, - UnsupportedMediaType, - RequestedRangeNotSatisfiable, - UnprocessableEntity, -] -_4xx_codes = {cls.status_code: cls for cls in _4xx_classes} - - -class HTTPServerError(HTTPError): - """The server encountered an error it could not recover from.""" - - message = "HTTP Server-side Error" - - -class InternalServerError(HTTPServerError): - """The server encountered an error it could not recover from.""" - - status_code = 500 - message = ("There was an internal server error that could not be recovered" - " from.") - - -_5xx_classes = [ - InternalServerError, - # NOTE(sigmavirus24): Allow for future expansion -] -_5xx_codes = {cls.status_code: cls for cls in _5xx_classes} - - -def _error_class_from(status_code): - if 400 <= status_code < 500: - cls = _4xx_codes.get(status_code, HTTPClientError) - elif 500 <= status_code < 600: - cls = _5xx_codes.get(status_code, HTTPServerError) - else: - cls = HTTPError - return cls - - -def error_from(response): - """Find an error code that matches a response status_code.""" - cls = _error_class_from(response.status_code) - return cls(response=response) - - -def raise_from(exception): - """Raise an exception from the keystoneauth1 exception.""" - cls = _error_class_from(exception.http_status) - return cls(response=exception.response, exception=exception) diff --git a/cratonclient/formatters/__init__.py b/cratonclient/formatters/__init__.py deleted file mode 100644 index 51a1b17..0000000 --- a/cratonclient/formatters/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Module containing built-in formatters for cratonclient CLI.""" diff --git a/cratonclient/formatters/base.py b/cratonclient/formatters/base.py deleted file mode 100644 index acc702d..0000000 --- a/cratonclient/formatters/base.py +++ /dev/null @@ -1,107 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Base class implementation for formatting plugins.""" - - -class Formatter(object): - """Class that defines the formatter interface. - - Instead of having to override and call up to this class's ``__init__`` - method, we also provide an ``after_init`` method that can be implemented - to extend what happens on initialization. - - .. attribute:: args - - Parsed command-line arguments stored as an instance of - :class:`argparse.Namespace`. - - """ - - def __init__(self, parsed_args): - """Instantiate our formatter with the parsed CLI arguments. - - :param parsed_args: - The CLI arguments parsed by :mod:`argparse`. - :type parsed_args: - argparse.Namespace - """ - self.args = parsed_args - self.after_init() - - def after_init(self): - """Initialize the object further after ``__init__``.""" - pass - - def configure(self, *args, **kwargs): - """Optional configuration of the plugin after instantiation.""" - return self - - def handle(self, item_to_format): - """Handle a returned item from the cratonclient API. - - cratonclient's API produces both single Resource objects as well as - generators of those objects. This method should be capable of handling - both. - - Based on the type, this will either call ``handle_generator`` or - ``handle_instance``. Subclasses must implement both of those methods. - - :returns: - None - :rtype: - None - :raises ValueError: - If the item provided is not a subclass of - :class:`~cratonclient.crud.Resource` or an iterable class then - we will not know how to handle it. In that case, we will raise a - ValueError. - """ - to_dict = getattr(item_to_format, 'to_dict', None) - if to_dict is not None: - self.handle_instance(item_to_format) - return - - try: - self.handle_generator(item_to_format) - except TypeError as err: - raise ValueError( - "Expected an iterable object but instead received something " - "of type: %s. Received a TypeError: %s" % ( - type(item_to_format), - err - ) - ) - - def handle_instance(self, instance): - """Format and print the instance provided. - - :param instance: - The instance retrieved from the API that needs to be formatted. - :type instance: - cratonclient.crud.Resource - """ - raise NotImplementedError( - "A formatter plugin subclassed Formatter but did not implement" - " the handle_instance method." - ) - - def handle_generator(self, generator): - """Format and print the instance provided. - - :param generator: - The generator retrieved from the API whose items need to be - formatted. - """ - raise NotImplementedError( - "A formatter plugin subclassed Formatter but did not implement" - " the handle_generator method." - ) diff --git a/cratonclient/formatters/json_format.py b/cratonclient/formatters/json_format.py deleted file mode 100644 index f9bbdfe..0000000 --- a/cratonclient/formatters/json_format.py +++ /dev/null @@ -1,72 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""JSON formatter implementation for the craton CLI.""" -from __future__ import print_function - -import json - -from cratonclient.formatters import base - - -class Formatter(base.Formatter): - """JSON output formatter for the CLI.""" - - def after_init(self): - """Set-up our defaults. - - At some point in the future, we may allow people to configure this via - the CLI. - """ - self.indent = 4 - self.sort_keys = True - - def format(self, dictionary): - """Return the dictionary as a JSON string.""" - return json.dumps( - dictionary, - sort_keys=self.sort_keys, - indent=self.indent, - ) - - def handle_instance(self, instance): - """Print the JSON representation of a single instance.""" - print(self.format(instance.to_dict())) - - def handle_generator(self, generator): - """Print the JSON representation of a collection.""" - # NOTE(sigmavirus24): This is tricky logic that is caused by the JSON - # specification's intolerance for trailing commas. - try: - instance = next(generator) - except StopIteration: - # If there is nothing in the generator, we should just print an - # empty Array and then exit immediately. - print('[]') - return - - # Otherwise, let's print our opening bracket to start our Array - # formatting. - print('[', end='') - while True: - print(self.format(instance.to_dict()), end='') - # After printing our instance as a JSON object, we need to - # decide if we have another object to print. If we do have - # another object to print, we need to print a comma to separate - # our previous object and our next one. If we don't, we exit our - # loop to print our closing Array bracket. - try: - instance = next(generator) - except StopIteration: - break - else: - print(', ', end='') - print(']') diff --git a/cratonclient/formatters/table.py b/cratonclient/formatters/table.py deleted file mode 100644 index 6ae7b2d..0000000 --- a/cratonclient/formatters/table.py +++ /dev/null @@ -1,181 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Pretty-table formatter implementation for the craton CLI.""" -from __future__ import print_function - -import textwrap - -from oslo_utils import encodeutils -import prettytable -import six - -from cratonclient.common import cliutils -from cratonclient.formatters import base - - -class Formatter(base.Formatter): - """Implementation of the default table-style formatter.""" - - def after_init(self): - """Set-up after initialization.""" - self.fields = [] - self.formatters = {} - self.sortby_index = None - self.mixed_case_fields = set([]) - self.field_labels = [] - self.dict_property = "Property" - self.wrap = 0 - self.dict_value = "Value" - - def configure(self, fields=None, formatters=None, sortby_index=False, - mixed_case_fields=None, field_labels=None, - dict_property=None, dict_value=None, wrap=None): - """Configure some of the settings used to print the tables. - - Parameters that configure list presentation: - - :param list fields: - List of field names as strings. - :param dict formatters: - Mapping of field names to formatter functions that accept the - resource. - :param int sortby_index: - The index of the field name in :param:`fields` to sort the table - rows by. If ``None``, PrettyTable will not sort the items at all. - :param list mixed_case_fields: - List of field names also in :param:`fields` that are mixed case - and need preprocessing prior to retrieving the attribute. - :param list field_labels: - List of field labels that need to match :param:`fields`. - - Parameters that configure the plain resource representation: - - :param str dict_property: - The name of the first column. - :param str dict_value: - The name of the second column. - :param int wrap: - Length at which to wrap the second column. - - All of these may be specified, but will be ignored based on how the - formatter is executed. - """ - if fields is not None: - self.fields = fields - if field_labels is None: - self.field_labels = cliutils.field_labels_from(self.fields) - elif len(field_labels) != len(self.fields): - raise ValueError( - "Field labels list %(labels)s has different number " - "of elements than fields list %(fields)s" % - {'labels': field_labels, 'fields': fields} - ) - else: - self.field_labels = field_labels - - if formatters is not None: - self.formatters = formatters - - if sortby_index is not False: - try: - sortby_index = int(sortby_index) - except TypeError: - if sortby_index is not None: - raise ValueError( - 'sortby_index must be None or an integer' - ) - except ValueError: - raise - else: - if self.field_labels and ( - sortby_index < 0 or - sortby_index > len(self.field_labels)): - raise ValueError( - 'sortby_index must be a non-negative number less ' - 'than {}'.format(len(self.field_labels)) - ) - self.sortby_index = sortby_index - - if mixed_case_fields is not None: - self.mixed_case_fields = set(mixed_case_fields) - - if dict_property is not None: - self.dict_property = dict_property - - if dict_value is not None: - self.dict_value = dict_value - - if wrap is not None: - self.wrap = wrap - - return self - - def sortby_kwargs(self): - """Generate the sortby keyword argument for PrettyTable.""" - if self.sortby_index is None: - return {} - return {'sortby': self.field_labels[self.sortby_index]} - - def build_table(self, field_labels, alignment='l'): - """Create a PrettyTable instance based off of the labels.""" - table = prettytable.PrettyTable(field_labels) - table.align = alignment - return table - - def handle_generator(self, generator): - """Handle a generator of resources.""" - sortby_kwargs = self.sortby_kwargs() - table = self.build_table(self.field_labels) - - for resource in generator: - row = [] - for field in self.fields: - formatter = self.formatters.get(field) - if formatter is not None: - data = formatter(resource) - else: - if field in self.mixed_case_fields: - field_name = field.replace(' ', '_') - else: - field_name = field.lower().replace(' ', '_') - data = getattr(resource, field_name, '') - row.append(data) - table.add_row(row) - - output = encodeutils.safe_encode(table.get_string(**sortby_kwargs)) - if six.PY3: - output = output.decode() - print(output) - - def handle_instance(self, instance): - """Handle a single resource.""" - table = self.build_table([self.dict_property, self.dict_value]) - - for key, value in sorted(instance.to_dict().items()): - if isinstance(value, dict): - value = six.text_type(value) - if self.wrap > 0: - value = textwrap.fill(six.text_type(value), self.wrap) - - if value and isinstance(value, six.string_types) and '\n' in value: - lines = value.strip().split('\n') - column1 = key - for line in lines: - table.add_row([column1, line]) - column1 = '' - else: - table.add_row([key, value]) - - output = encodeutils.safe_encode(table.get_string()) - if six.PY3: - output = output.decode() - print(output) diff --git a/cratonclient/session.py b/cratonclient/session.py deleted file mode 100644 index fa2dbe2..0000000 --- a/cratonclient/session.py +++ /dev/null @@ -1,288 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Craton-specific session details.""" -from itertools import chain -import logging - -from keystoneauth1 import exceptions as ksa_exc -from keystoneauth1 import session as ksa_session -from requests import exceptions as requests_exc - -import cratonclient -from cratonclient import auth -from cratonclient import exceptions as exc - -LOG = logging.getLogger(__name__) - - -class Session(object): - """Management class to allow different types of sessions to be used. - - If an instance of Craton is deployed with Keystone Middleware, this allows - for a keystoneauth session to be used so authentication will happen - immediately. - """ - - def __init__(self, session=None, username=None, token=None, - project_id=None): - """Initialize our Session. - - :param session: - The session instance to use as an underlying HTTP transport. If - not provided, we will create a keystoneauth1 Session object. - :param str username: - The username of the person authenticating against the API. - :param str token: - The authentication token of the user authenticating. - :param str project_id: - The user's project id in Craton. - """ - if session is None: - _auth = auth.CratonAuth( - username=username, - project_id=project_id, - token=token, - ) - session = ksa_session.Session(auth=_auth) - self._session = session - self._session.user_agent = 'python-cratonclient/{0}'.format( - cratonclient.__version__) - - def delete(self, url, **kwargs): - """Make a DELETE request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.delete('http://example.com') - """ - return self.request('DELETE', url, **kwargs) - - def get(self, url, **kwargs): - """Make a GET request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.get('http://example.com') - """ - return self.request('GET', url, **kwargs) - - def head(self, url, **kwargs): - """Make a HEAD request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.head('http://example.com') - """ - return self.request('HEAD', url, **kwargs) - - def options(self, url, **kwargs): - """Make an OPTIONS request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.options('http://example.com') - """ - return self.request('OPTIONS', url, **kwargs) - - def post(self, url, **kwargs): - """Make a POST request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.post( - ... 'http://example.com', - ... data=b'foo', - ... headers={'Content-Type': 'text/plain'}, - ... ) - """ - return self.request('POST', url, **kwargs) - - def put(self, url, **kwargs): - """Make a PUT request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.put( - ... 'http://example.com', - ... data=b'foo', - ... headers={'Content-Type': 'text/plain'}, - ... ) - """ - return self.request('PUT', url, **kwargs) - - def patch(self, url, **kwargs): - """Make a PATCH request with url and optional parameters. - - See the :meth:`Session.request` documentation for more details. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.put( - ... 'http://example.com', - ... data=b'foo', - ... headers={'Content-Type': 'text/plain'}, - ... ) - >>> response = session.patch( - ... 'http://example.com', - ... data=b'bar', - ... headers={'Content-Type': 'text/plain'}, - ... ) - """ - return self.request('PATCH', url, **kwargs) - - def request(self, method, url, **kwargs): - """Make a request with a method, url, and optional parameters. - - See also: python-requests.org for documentation of acceptable - parameters. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@$$w0rd', - ... project_id='1', - ... ) - >>> response = session.request('GET', 'http://example.com') - """ - kwargs.setdefault('endpoint_filter', - {'service_type': 'fleet_management'}) - try: - response = self._session.request( - method=method, - url=url, - **kwargs - ) - except requests_exc.HTTPError as err: - raise exc.HTTPError(exception=err, response=err.response) - # NOTE(sigmavirus24): The ordering of Timeout before ConnectionError - # is important on requests 2.x. The ConnectTimeout exception inherits - # from both ConnectionError and Timeout. To catch both connect and - # read timeouts similarly, we need to catch this one first. - except requests_exc.Timeout as err: - raise exc.Timeout(exception=err) - except requests_exc.ConnectionError as err: - raise exc.ConnectionFailed(exception=err) - except ksa_exc.HttpError as err: - raise exc.raise_from(err) - - if response.status_code >= 400: - raise exc.error_from(response) - - return response - - def paginate(self, url, items_key, autopaginate=True, nested=False, - **kwargs): - """Make a GET request to a paginated resource. - - If :param:`autopaginate` is set to ``True``, this will automatically - handle finding and retrieving the next page of items. - - .. code-block:: python - - >>> from cratonclient import session as craton - >>> session = craton.Session( - ... username='demo', - ... token='p@##w0rd', - ... project_id='84363597-721c-4068-9731-8824692b51bb', - ... ) - >>> url = 'https://example.com/v1/hosts' - >>> for response in session.paginate(url, items_key='hosts'): - ... print("Received status {}".format(response.status_code)) - ... print("Received {} items".format(len(items))) - - :param bool autopaginate: - Determines whether or not this method continues requesting items - automatically after the first page. - """ - get_items = True - - while get_items: - response = self.get(url, **kwargs) - json_body = response.json() - if nested: - items = list(chain(*json_body[items_key].values())) - else: - items = json_body[items_key] - - yield response, items - - links = json_body['links'] - url = _find_next_link(links) - - kwargs = {} - get_items = url and autopaginate and len(items) > 0 - - -def _find_next_link(links): - for link in links: - if link['rel'] == 'next': - return link['href'] - - return None diff --git a/cratonclient/shell/__init__.py b/cratonclient/shell/__init__.py deleted file mode 100644 index 1dab1a0..0000000 --- a/cratonclient/shell/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Command-line application that interfaces with Craton API.""" diff --git a/cratonclient/shell/main.py b/cratonclient/shell/main.py deleted file mode 100644 index 981cf0e..0000000 --- a/cratonclient/shell/main.py +++ /dev/null @@ -1,207 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Main shell for parsing arguments directed toward Craton.""" - -from __future__ import print_function - -import argparse -import sys - -from oslo_utils import encodeutils -from oslo_utils import importutils -from stevedore import extension - -from cratonclient import __version__ -from cratonclient import exceptions as exc -from cratonclient import session as craton - -from cratonclient.common import cliutils -from cratonclient.v1 import client - - -FORMATTERS_NAMESPACE = 'cratonclient.formatters' - - -class CratonShell(object): - """Class used to handle shell definition and parsing.""" - - def __init__(self): - """Initialize our shell object. - - This sets up our formatters extension manager. If we add further - managers, they will be initialized here. - """ - self.extension_mgr = extension.ExtensionManager( - namespace=FORMATTERS_NAMESPACE, - invoke_on_load=False, - ) - - def get_base_parser(self): - """Configure base craton arguments and parsing.""" - parser = argparse.ArgumentParser( - prog='craton', - description=__doc__.strip(), - epilog='See "craton help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=argparse.HelpFormatter - ) - - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS, - ) - parser.add_argument('--version', - action='version', - version=__version__, - ) - parser.add_argument('--format', - default='default', - choices=list(sorted(self.extension_mgr.names())), - help='The format to use to print the information ' - 'to the console. Defaults to pretty-printing ' - 'using ASCII tables.', - ) - parser.add_argument('--craton-url', - default=cliutils.env('CRATON_URL'), - help='The base URL of the running Craton service.' - ' Defaults to env[CRATON_URL].', - ) - parser.add_argument('--craton-version', - type=int, - default=cliutils.env('CRATON_VERSION', - default=1), - help='The version of the Craton API to use. ' - 'Defaults to env[CRATON_VERSION].' - ) - parser.add_argument('--os-project-id', - default=cliutils.env('OS_PROJECT_ID'), - help='The project ID used to authenticate to ' - 'Craton. Defaults to env[OS_PROJECT_ID].', - ) - parser.add_argument('--os-username', - default=cliutils.env('OS_USERNAME'), - help='The username used to authenticate to ' - 'Craton. Defaults to env[OS_USERNAME].', - ) - parser.add_argument('--os-password', - default=cliutils.env('OS_PASSWORD'), - help='The password used to authenticate to ' - 'Craton. Defaults to env[OS_PASSWORD].', - ) - return parser - - # NOTE(cmspence): Credit for this get_subcommand_parser function - # goes to the magnumclient developers and contributors. - def get_subcommand_parser(self, api_version): - """Get subcommands by parsing COMMAND_MODULES.""" - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='', - dest='subparser_name') - shell = importutils.import_versioned_module( - 'cratonclient.shell', - api_version, - 'shell', - ) - command_modules = shell.COMMAND_MODULES - for command_module in command_modules: - self._find_subparsers(subparsers, command_module) - self._find_subparsers(subparsers, self) - return parser - - # NOTE(cmspence): Credit for this function goes to the - # magnumclient developers and contributors. - def _find_subparsers(self, subparsers, actions_module): - """Find subparsers by looking at *_shell files.""" - help_formatter = argparse.HelpFormatter - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - action_help = desc.strip() - arguments = getattr(callback, 'arguments', []) - subparser = (subparsers.add_parser(command, - help=action_help, - description=desc, - add_help=False, - formatter_class=help_formatter) - ) - subparser.add_argument('-h', '--help', - action='help', - help=argparse.SUPPRESS) - self.subcommands[command] = subparser - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - def main(self, argv): - """Main entry-point for cratonclient shell argument parsing.""" - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) - subcommand_parser = ( - self.get_subcommand_parser(options.craton_version) - ) - self.parser = subcommand_parser - - if options.help or not argv: - self.parser.print_help() - return 0 - - args = subcommand_parser.parse_args(argv) - - # Short-circuit and deal with help right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - - session = craton.Session( - username=args.os_username, - token=args.os_password, - project_id=args.os_project_id, - ) - self.cc = client.Client(session, args.craton_url) - formatter_class = self.extension_mgr[args.format].plugin - args.formatter = formatter_class(args) - args.func(self.cc, args) - - @cliutils.arg( - 'command', - metavar='', - nargs='?', - help='Display help for .') - def do_help(self, args): - """Display help about this program or one of its subcommands.""" - if args.command: - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError("'%s' is not a valid subcommand" % - args.command) - else: - self.parser.print_help() - - -def main(): - """Main entry-point for cratonclient's CLI.""" - try: - CratonShell().main([encodeutils.safe_decode(a) for a in sys.argv[1:]]) - except Exception as e: - print("ERROR: {}".format(encodeutils.exception_to_unicode(e)), - file=sys.stderr) - sys.exit(1) - return 0 - - -if __name__ == "__main__": - main() diff --git a/cratonclient/shell/v1/__init__.py b/cratonclient/shell/v1/__init__.py deleted file mode 100644 index 69318df..0000000 --- a/cratonclient/shell/v1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Shell libraries for version 1 of Craton's API.""" diff --git a/cratonclient/shell/v1/cells_shell.py b/cratonclient/shell/v1/cells_shell.py deleted file mode 100644 index 3a45132..0000000 --- a/cratonclient/shell/v1/cells_shell.py +++ /dev/null @@ -1,283 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Cells resource and resource shell wrapper.""" -from __future__ import print_function - -import argparse -import sys - -from cratonclient.common import cliutils -from cratonclient import exceptions as exc - -DEFAULT_CELL_FIELDS = [ - 'id', - 'name', - 'cloud_id', - 'region_id', - 'created_at', -] - -CELL_FIELDS = DEFAULT_CELL_FIELDS + [ - 'updated_at', - 'note', - 'variables', - 'project_id', -] - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cell.') -def do_cell_show(cc, args): - """Show detailed information about a cell.""" - cell = cc.cells.get(args.id) - args.formatter.configure(wrap=72).handle(cell) - - -@cliutils.arg('-r', '--region', - metavar='', - type=int, - help='ID of the region that the cell belongs to.') -@cliutils.arg('--cloud', - metavar='', - type=int, - help='ID of the cloud that the cell belongs to.') -@cliutils.arg('--detail', - action='store_true', - default=False, - help='Show detailed information about the cells.') -@cliutils.arg('--sort-key', - metavar='', - help='Cell field that will be used for sorting.') -@cliutils.arg('--sort-dir', - metavar='', - default='asc', - choices=('asc', 'desc'), - help='Sort direction: "asc" (default) or "desc".') -@cliutils.arg('--fields', - nargs='+', - metavar='', - default=DEFAULT_CELL_FIELDS, - help='Space-separated list of fields to display. ' - 'Only these fields will be fetched from the server. ' - 'Can not be used when "--detail" is specified') -@cliutils.arg('--all', - action='store_true', - default=False, - help='Retrieve and show all cells. This will override ' - 'the provided value for --limit and automatically ' - 'retrieve each page of results.') -@cliutils.arg('--limit', - metavar='', - type=int, - help='Maximum number of cells to return.') -@cliutils.arg('--marker', - metavar='', - default=None, - help='ID of the cell to use to resume listing cells.') -@cliutils.arg('--vars', - metavar='', - nargs='+', - action='append', - default=[], - help='Variables to use as filter in the form of ' - '--vars="key:value" --vars="key2:value2"') -def do_cell_list(cc, args): - """Print list of cells which are registered with the Craton service.""" - params = {} - if args.vars: - query_vars = ",".join([i[0] for i in args.vars]) - params['vars'] = query_vars - if args.cloud is not None: - params['cloud_id'] = args.cloud - if args.limit is not None: - if args.limit < 0: - raise exc.CommandError('Invalid limit specified. Expected ' - 'non-negative limit, got {0}' - .format(args.limit)) - params['limit'] = args.limit - if args.all is True: - params['limit'] = 100 - - if args.detail: - if args.fields and args.fields == DEFAULT_CELL_FIELDS: - args.fields = CELL_FIELDS - else: - raise exc.CommandError( - 'Cannot specify both --fields and --detail.' - ) - params['detail'] = args.detail - - fields = args.fields - for field in fields: - if field not in CELL_FIELDS: - raise exc.CommandError( - 'Invalid field "{}"'.format(field) - ) - sort_key = args.sort_key and args.sort_key.lower() - if sort_key is not None: - if sort_key not in CELL_FIELDS: - raise exc.CommandError( - ('"--sort-key" value was "{}" but should ' - 'be one of: {}').format( - args.sort_key, - ', '.join(CELL_FIELDS) - ) - ) - params['sort_key'] = sort_key - if args.region is not None: - params['region_id'] = args.region - - params['sort_dir'] = args.sort_dir - params['marker'] = args.marker - params['autopaginate'] = args.all - - listed_cells = cc.cells.list(**params) - args.formatter.configure(fields=fields).handle(listed_cells) - - -@cliutils.arg('-n', '--name', - metavar='', - required=True, - help='Name of the cell.') -@cliutils.arg('-r', '--region', - dest='region_id', - metavar='', - type=int, - required=True, - help='ID of the region that the cell belongs to.') -@cliutils.arg('--cloud', - dest='cloud_id', - metavar='', - type=int, - required=True, - help='ID of the cloud that the cell belongs to.') -@cliutils.arg('--note', - help='Note about the cell.') -def do_cell_create(cc, args): - """Register a new cell with the Craton service.""" - fields = {k: v for (k, v) in vars(args).items() - if k in CELL_FIELDS and not (v is None)} - cell = cc.cells.create(**fields) - args.formatter.configure(wrap=72).handle(cell) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cell.') -@cliutils.arg('-n', '--name', - metavar='', - help='Name of the cell.') -@cliutils.arg('-r', '--region', - dest='region_id', - metavar='', - type=int, - help='Desired ID of the region that the cell should change to.') -@cliutils.arg('--cloud', - dest='cloud_id', - metavar='', - type=int, - help='Desired ID of the cloud that the cell should change to.') -@cliutils.arg('--note', - help='Note about the cell.') -def do_cell_update(cc, args): - """Update a cell that is registered with the Craton service.""" - fields = {k: v for (k, v) in vars(args).items() - if k in CELL_FIELDS and not (v is None)} - cell_id = fields.pop('id') - if not fields: - raise exc.CommandError( - 'Nothing to update... Please specify one of --name, --region, ' - '--cloud, or --note' - ) - cell = cc.cells.update(cell_id, **fields) - args.formatter.configure(wrap=72).handle(cell) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cell.') -def do_cell_delete(cc, args): - """Delete a cell that is registered with the Craton service.""" - try: - response = cc.cells.delete(args.id) - except exc.ClientException as client_exc: - raise exc.CommandError( - 'Failed to delete cell {} due to "{}:{}"'.format( - args.id, client_exc.__class__, str(client_exc) - ) - ) - else: - print("Cell {0} was {1} deleted.". - format(args.id, 'successfully' if response else 'not')) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID or name of the cell.') -@cliutils.handle_shell_exception -def do_cell_vars_get(cc, args): - """Get variables for a cell.""" - variables = cc.cells.get(args.id).variables.get() - formatter = args.formatter.configure(dict_property="Variable", wrap=72) - formatter.handle(variables) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cell.') -@cliutils.arg('variables', nargs=argparse.REMAINDER) -@cliutils.handle_shell_exception -def do_cell_vars_set(cc, args): - """Set variables for a cell.""" - cell_id = args.id - if not args.variables and sys.stdin.isatty(): - raise exc.CommandError( - 'Nothing to update... Please specify variables to set in the ' - 'following format: "key=value". You may also specify variables to ' - 'delete by key using the format: "key="' - ) - adds, deletes = cliutils.variable_updates(args.variables) - variables = cc.cells.get(cell_id).variables - if deletes: - variables.delete(*deletes) - variables.update(**adds) - formatter = args.formatter.configure(wrap=72, dict_property="Variable") - formatter.handle(variables.get()) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cell.') -@cliutils.arg('variables', nargs=argparse.REMAINDER) -@cliutils.handle_shell_exception -def do_cell_vars_delete(cc, args): - """Delete variables for a cell by key.""" - cell_id = args.id - if not args.variables and sys.stdin.isatty(): - raise exc.CommandError( - 'Nothing to delete... Please specify variables to delete by ' - 'listing the keys you wish to delete separated by spaces.' - ) - deletes = cliutils.variable_deletes(args.variables) - variables = cc.cells.get(cell_id).variables - response = variables.delete(*deletes) - print("Variables {0} deleted.". - format('successfully' if response else 'not')) diff --git a/cratonclient/shell/v1/clouds_shell.py b/cratonclient/shell/v1/clouds_shell.py deleted file mode 100644 index 86b0a51..0000000 --- a/cratonclient/shell/v1/clouds_shell.py +++ /dev/null @@ -1,215 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Hosts resource and resource shell wrapper.""" -from __future__ import print_function - -import argparse -import sys - -from cratonclient.common import cliutils -from cratonclient import exceptions as exc - -DEFAULT_CLOUD_FIELDS = [ - 'id', - 'name', - 'created_at', -] - -CLOUD_FIELDS = DEFAULT_CLOUD_FIELDS + [ - 'updated_at', - 'note', - 'project_id', -] - - -@cliutils.arg('-n', '--name', - metavar='', - required=True, - help='Name of the host.') -@cliutils.arg('--note', - help='Note about the host.') -def do_cloud_create(cc, args): - """Register a new cloud with the Craton service.""" - fields = {k: v for (k, v) in vars(args).items() - if k in CLOUD_FIELDS and not (v is None)} - - cloud = cc.clouds.create(**fields) - args.formatter.configure(wrap=72).handle(cloud) - - -@cliutils.arg('--fields', - nargs='+', - metavar='', - default=DEFAULT_CLOUD_FIELDS, - help='Comma-separated list of fields to display. ' - 'Only these fields will be fetched from the server. ' - 'Can not be used when "--detail" is specified') -@cliutils.arg('--all', - action='store_true', - default=False, - help='Retrieve and show all clouds. This will override ' - 'the provided value for --limit and automatically ' - 'retrieve each page of results.') -@cliutils.arg('--detail', - action='store_true', - default=False, - help='Show detailed information about all clouds.') -@cliutils.arg('--limit', - metavar='', - type=int, - help='Maximum number of clouds to return.') -@cliutils.arg('--marker', - metavar='', - default=None, - help='ID of the cell to use to resume listing clouds.') -def do_cloud_list(cc, args): - """List all clouds.""" - params = {} - if args.limit is not None: - if args.limit < 0: - raise exc.CommandError('Invalid limit specified. Expected ' - 'non-negative limit, got {0}' - .format(args.limit)) - params['limit'] = args.limit - if args.all is True: - params['limit'] = 100 - - if args.detail: - if args.fields and args.fields == DEFAULT_CLOUD_FIELDS: - args.fields = CLOUD_FIELDS - else: - raise exc.CommandError( - 'Cannot specify both --fields and --detail.' - ) - params['detail'] = args.detail - - fields = args.fields - for field in args.fields: - if field not in CLOUD_FIELDS: - raise exc.CommandError( - 'Invalid field "{}"'.format(field) - ) - - params['marker'] = args.marker - params['autopaginate'] = args.all - - clouds_list = cc.clouds.list(**params) - args.formatter.configure(fields=list(fields)).handle(clouds_list) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cloud.') -def do_cloud_show(cc, args): - """Show detailed information about a cloud.""" - cloud = cc.clouds.get(args.id) - args.formatter.configure(wrap=72).handle(cloud) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cloud') -@cliutils.arg('-n', '--name', - metavar='', - help='Name of the cloud.') -@cliutils.arg('--note', - help='Note about the cloud.') -def do_cloud_update(cc, args): - """Update a cloud that is registered with the Craton service.""" - fields = {k: v for (k, v) in vars(args).items() - if k in CLOUD_FIELDS and not (v is None)} - item_id = fields.pop('id') - if not fields: - raise exc.CommandError( - 'Nothing to update... Please specify one or more of --name, or ' - '--note' - ) - cloud = cc.clouds.update(item_id, **fields) - args.formatter.configure(wrap=72).handle(cloud) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cloud.') -def do_cloud_delete(cc, args): - """Delete a cloud that is registered with the Craton service.""" - try: - response = cc.clouds.delete(args.id) - except exc.ClientException as client_exc: - raise exc.CommandError( - 'Failed to delete cloud {} due to "{}:{}"'.format( - args.id, client_exc.__class__, str(client_exc), - ) - ) - else: - print("Cloud {0} was {1} deleted.". - format(args.id, 'successfully' if response else 'not')) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID or name of the cloud.') -@cliutils.handle_shell_exception -def do_cloud_vars_get(cc, args): - """Get variables for a cloud.""" - variables = cc.clouds.get(args.id).variables.get() - formatter = args.formatter.configure(dict_property="Variable", wrap=72) - formatter.handle(variables) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cloud.') -@cliutils.arg('variables', nargs=argparse.REMAINDER) -@cliutils.handle_shell_exception -def do_cloud_vars_set(cc, args): - """Set variables for a cloud.""" - cloud_id = args.id - if not args.variables and sys.stdin.isatty(): - raise exc.CommandError( - 'Nothing to update... Please specify variables to set in the ' - 'following format: "key=value". You may also specify variables to ' - 'delete by key using the format: "key="' - ) - adds, deletes = cliutils.variable_updates(args.variables) - variables = cc.clouds.get(cloud_id).variables - if deletes: - variables.delete(*deletes) - variables.update(**adds) - formatter = args.formatter.configure(wrap=72, dict_property="Variable") - formatter.handle(variables.get()) - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the cloud.') -@cliutils.arg('variables', nargs=argparse.REMAINDER) -@cliutils.handle_shell_exception -def do_cloud_vars_delete(cc, args): - """Delete variables for a cloud by key.""" - cloud_id = args.id - if not args.variables and sys.stdin.isatty(): - raise exc.CommandError( - 'Nothing to delete... Please specify variables to delete by ' - 'listing the keys you wish to delete separated by spaces.' - ) - deletes = cliutils.variable_deletes(args.variables) - variables = cc.clouds.get(cloud_id).variables - response = variables.delete(*deletes) - print("Variables {0} deleted.". - format('successfully' if response else 'not')) diff --git a/cratonclient/shell/v1/devices_shell.py b/cratonclient/shell/v1/devices_shell.py deleted file mode 100644 index afcce9e..0000000 --- a/cratonclient/shell/v1/devices_shell.py +++ /dev/null @@ -1,150 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Hosts resource and resource shell wrapper.""" -from __future__ import print_function - -from cratonclient.common import cliutils -from cratonclient import exceptions as exc - -DEFAULT_DEVICE_FIELDS = [ - 'id', - 'name', - 'device_type', - 'ip_address', - 'cloud_id', - 'region_id', - 'cell_id', - 'parent_id', -] - - -DEVICE_FIELDS = DEFAULT_DEVICE_FIELDS + [ - 'note', - 'created_at', - 'updated_at', - 'project_id', -] - - -@cliutils.arg('--fields', - nargs='+', - metavar='', - default=DEFAULT_DEVICE_FIELDS, - help='Space-separated list of fields to display. ' - 'Only these fields will be fetched from the server. ' - 'This cannot be combined with --detail.') -@cliutils.arg('--detail', - action='store_true', - default=False, - help='Retrieve and show all detail about devices in listing.') -@cliutils.arg('--all', - action='store_true', - default=False, - help='Retrieve and show all devices. This will override ' - 'the provided value for --limit and automatically ' - 'retrieve each page of results.') -@cliutils.arg('--sort-key', - metavar='', - help='Device field that will be used for sorting.') -@cliutils.arg('--sort-dir', - metavar='', - default='asc', - choices=('asc', 'desc'), - help='Sort direction: "asc" (default) or "desc".') -@cliutils.arg('--limit', - metavar='', - type=int, - help='Maximum number of devices to return.') -@cliutils.arg('--marker', - metavar='', - default=None, - help='ID of the device to use to resume listing devices.') -@cliutils.arg('--cloud', - metavar='', - type=int, - help='ID of the cloud that the device belongs to.') -@cliutils.arg('-r', '--region', - metavar='', - type=int, - help='ID of the region that the device belongs to.') -@cliutils.arg('-c', '--cell', - metavar='', - type=int, - help='Integer ID of the cell that contains ' - 'the desired list of devices.') -@cliutils.arg('--parent', - metavar='', - type=int, - help='Parent ID of required devices.') -@cliutils.arg('--descendants', - default=False, - action='store_true', - help='When parent is also specified, include all descendants.') -@cliutils.arg('--active', - metavar='', - choices=("true", "false"), - help='Filter devices by their active state.') -def do_device_list(cc, args): - """List all devices.""" - params = {} - if args.limit is not None: - if args.limit < 0: - raise exc.CommandError('Invalid limit specified. Expected ' - 'non-negative limit, got {0}' - .format(args.limit)) - params['limit'] = args.limit - if args.all is True: - params['limit'] = 100 - - if args.detail: - if args.fields and args.fields == DEFAULT_DEVICE_FIELDS: - args.fields = DEVICE_FIELDS - else: - raise exc.CommandError( - 'Cannot specify both --fields and --detail.' - ) - params['detail'] = args.detail - - fields = args.fields - for field in fields: - if field not in DEVICE_FIELDS: - raise exc.CommandError( - 'Invalid field "{}"'.format(field) - ) - - sort_key = args.sort_key and args.sort_key.lower() - if sort_key is not None: - if sort_key not in DEVICE_FIELDS: - raise exc.CommandError( - '{0} is an invalid key for sorting, valid values for ' - '--sort-key are: {1}'.format( - args.sort_key, DEVICE_FIELDS - ) - ) - params['sort_keys'] = sort_key - params['sort_dir'] = args.sort_dir - params['marker'] = args.marker - params['autopaginate'] = args.all - if args.parent: - params['parent_id'] = args.parent - params['descendants'] = args.descendants - if args.cloud: - params['cloud_id'] = args.cloud - if args.region: - params['region_id'] = args.region - if args.cell: - params['cell_id'] = args.cell - if args.active: - params['active'] = args.active - - devices_list = cc.devices.list(**params) - args.formatter.configure(fields=fields).handle(devices_list) diff --git a/cratonclient/shell/v1/hosts_shell.py b/cratonclient/shell/v1/hosts_shell.py deleted file mode 100644 index cdbb185..0000000 --- a/cratonclient/shell/v1/hosts_shell.py +++ /dev/null @@ -1,341 +0,0 @@ -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Hosts resource and resource shell wrapper.""" -from __future__ import print_function - -import argparse -import sys - -from cratonclient.common import cliutils -from cratonclient import exceptions as exc - - -DEFAULT_HOST_FIELDS = [ - 'id', - 'name', - 'active', - 'device_type', - 'ip_address', - 'cloud_id', - 'region_id', - 'cell_id', - 'created_at', -] - -HOST_FIELDS = DEFAULT_HOST_FIELDS + [ - 'updated_at', - 'note', - 'variables', - 'labels', - 'parent_id', - 'project_id', -] - - -@cliutils.arg('id', - metavar='', - type=int, - help='ID of the host.') -def do_host_show(cc, args): - """Show detailed information about a host.""" - host = cc.hosts.get(args.id) - args.formatter.configure(wrap=72).handle(host) - - -@cliutils.arg('-r', '--region', - metavar='', - type=int, - help='ID of the region that the host belongs to.') -@cliutils.arg('--cloud', - metavar='', - type=int, - help='ID of the cloud that the host belongs to.') -@cliutils.arg('-c', '--cell', - metavar='', - type=int, - help='Integer ID of the cell that contains ' - 'the desired list of hosts.') -@cliutils.arg('--detail', - action='store_true', - default=False, - help='Show detailed information about the hosts.') -@cliutils.arg('--sort-key', - metavar='', - help='Host field that will be used for sorting.') -@cliutils.arg('--sort-dir', - metavar='', - default='asc', - choices=('asc', 'desc'), - help='Sort direction: "asc" (default) or "desc".') -@cliutils.arg('--fields', - nargs='+', - metavar='', - default=DEFAULT_HOST_FIELDS, - help='Space-separated list of fields to display. ' - 'Only these fields will be fetched from the server. ' - 'Can not be used when "--detail" is specified') -@cliutils.arg('--all', - action='store_true', - default=False, - help='Retrieve and show all hosts. This will override ' - 'the provided value for --limit and automatically ' - 'retrieve each page of results.') -@cliutils.arg('--limit', - metavar='', - type=int, - help='Maximum number of hosts to return.') -@cliutils.arg('--marker', - metavar='', - default=None, - help='ID of the cell to use to resume listing hosts.') -@cliutils.arg('--device-type', - metavar='', - default=None, - help='Device type to use as filter.') -@cliutils.arg('--vars', - metavar='', - default=None, - help='Variables to use as filter in the form of key:value.') -@cliutils.arg('--label', - metavar='