From 8011cb177c3a804a342fc356c923c0637bb6c497 Mon Sep 17 00:00:00 2001 From: Andreas Jaeger Date: Mon, 17 Apr 2017 19:25:35 +0200 Subject: [PATCH] Retire repo This repo was created by accident, use deb-python-os-client-config instead. Needed-By: I1ac1a06931c8b6dd7c2e73620a0302c29e605f03 Change-Id: I81894aea69b9d09b0977039623c26781093a397a --- .coveragerc | 7 - .gitignore | 54 - .gitreview | 4 - .mailmap | 3 - .testr.conf | 7 - CONTRIBUTING.rst | 16 - HACKING.rst | 4 - LICENSE | 175 --- README.rst | 488 ------- README.txt | 13 + doc/source/api-reference.rst | 10 - doc/source/conf.py | 76 -- doc/source/contributing.rst | 4 - doc/source/index.rst | 18 - doc/source/installation.rst | 12 - doc/source/network-config.rst | 49 - doc/source/releasenotes.rst | 5 - doc/source/vendor-support.rst | 324 ----- os_client_config/__init__.py | 96 -- os_client_config/_log.py | 28 - os_client_config/cloud_config.py | 473 ------- os_client_config/config.py | 1130 ----------------- os_client_config/constructors.json | 12 - os_client_config/constructors.py | 28 - os_client_config/defaults.json | 24 - os_client_config/defaults.py | 41 - os_client_config/exceptions.py | 17 - os_client_config/schema.json | 121 -- os_client_config/tests/__init__.py | 0 os_client_config/tests/base.py | 234 ---- os_client_config/tests/test_cloud_config.py | 604 --------- os_client_config/tests/test_config.py | 894 ------------- os_client_config/tests/test_environ.py | 159 --- os_client_config/tests/test_json.py | 62 - os_client_config/vendor-schema.json | 217 ---- os_client_config/vendors/__init__.py | 37 - os_client_config/vendors/auro.json | 10 - os_client_config/vendors/bluebox.json | 7 - os_client_config/vendors/catalyst.json | 15 - os_client_config/vendors/citycloud.json | 18 - os_client_config/vendors/conoha.json | 14 - os_client_config/vendors/datacentred.json | 11 - os_client_config/vendors/dreamcompute.json | 11 - os_client_config/vendors/dreamhost.json | 13 - os_client_config/vendors/elastx.json | 10 - os_client_config/vendors/entercloudsuite.json | 15 - os_client_config/vendors/ibmcloud.json | 13 - os_client_config/vendors/internap.json | 17 - os_client_config/vendors/osic.json | 11 - os_client_config/vendors/ovh.json | 16 - os_client_config/vendors/rackspace.json | 28 - os_client_config/vendors/switchengines.json | 15 - os_client_config/vendors/ultimum.json | 11 - os_client_config/vendors/unitedstack.json | 16 - os_client_config/vendors/vexxhost.json | 14 - os_client_config/vendors/zetta.json | 13 - ...tch-up-release-notes-e385fad34e9f3d6e.yaml | 22 - ...cloud-profile-status-e0d29b5e2f10e95c.yaml | 6 - .../notes/magic-fixes-dca4ae4dac2441a8.yaml | 6 - .../make-rest-client-dd3d365632a26fa0.yaml | 4 - .../notes/network-list-e6e9dafdd8446263.yaml | 10 - .../option-precedence-1fecab21fdfb2c33.yaml | 7 - .../notes/sdk-helper-41f8d815cfbcfb00.yaml | 4 - .../session-client-b581a6e5d18c8f04.yaml | 6 - .../notes/shade-helper-568f8cb372eef6d9.yaml | 4 - .../started-using-reno-242e2b0cd27f9480.yaml | 3 - .../vendor-updates-f11184ba56bb27cf.yaml | 4 - releasenotes/source/_static/.placeholder | 0 releasenotes/source/_templates/.placeholder | 0 releasenotes/source/conf.py | 261 ---- releasenotes/source/index.rst | 18 - releasenotes/source/mitaka.rst | 6 - releasenotes/source/unreleased.rst | 5 - requirements.txt | 7 - setup.cfg | 34 - setup.py | 22 - test-requirements.txt | 21 - tox.ini | 38 - 78 files changed, 13 insertions(+), 6199 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 CONTRIBUTING.rst delete mode 100644 HACKING.rst delete mode 100644 LICENSE delete mode 100644 README.rst create mode 100644 README.txt delete mode 100644 doc/source/api-reference.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/network-config.rst delete mode 100644 doc/source/releasenotes.rst delete mode 100644 doc/source/vendor-support.rst delete mode 100644 os_client_config/__init__.py delete mode 100644 os_client_config/_log.py delete mode 100644 os_client_config/cloud_config.py delete mode 100644 os_client_config/config.py delete mode 100644 os_client_config/constructors.json delete mode 100644 os_client_config/constructors.py delete mode 100644 os_client_config/defaults.json delete mode 100644 os_client_config/defaults.py delete mode 100644 os_client_config/exceptions.py delete mode 100644 os_client_config/schema.json delete mode 100644 os_client_config/tests/__init__.py delete mode 100644 os_client_config/tests/base.py delete mode 100644 os_client_config/tests/test_cloud_config.py delete mode 100644 os_client_config/tests/test_config.py delete mode 100644 os_client_config/tests/test_environ.py delete mode 100644 os_client_config/tests/test_json.py delete mode 100644 os_client_config/vendor-schema.json delete mode 100644 os_client_config/vendors/__init__.py delete mode 100644 os_client_config/vendors/auro.json delete mode 100644 os_client_config/vendors/bluebox.json delete mode 100644 os_client_config/vendors/catalyst.json delete mode 100644 os_client_config/vendors/citycloud.json delete mode 100644 os_client_config/vendors/conoha.json delete mode 100644 os_client_config/vendors/datacentred.json delete mode 100644 os_client_config/vendors/dreamcompute.json delete mode 100644 os_client_config/vendors/dreamhost.json delete mode 100644 os_client_config/vendors/elastx.json delete mode 100644 os_client_config/vendors/entercloudsuite.json delete mode 100644 os_client_config/vendors/ibmcloud.json delete mode 100644 os_client_config/vendors/internap.json delete mode 100644 os_client_config/vendors/osic.json delete mode 100644 os_client_config/vendors/ovh.json delete mode 100644 os_client_config/vendors/rackspace.json delete mode 100644 os_client_config/vendors/switchengines.json delete mode 100644 os_client_config/vendors/ultimum.json delete mode 100644 os_client_config/vendors/unitedstack.json delete mode 100644 os_client_config/vendors/vexxhost.json delete mode 100644 os_client_config/vendors/zetta.json delete mode 100644 releasenotes/notes/catch-up-release-notes-e385fad34e9f3d6e.yaml delete mode 100644 releasenotes/notes/cloud-profile-status-e0d29b5e2f10e95c.yaml delete mode 100644 releasenotes/notes/magic-fixes-dca4ae4dac2441a8.yaml delete mode 100644 releasenotes/notes/make-rest-client-dd3d365632a26fa0.yaml delete mode 100644 releasenotes/notes/network-list-e6e9dafdd8446263.yaml delete mode 100644 releasenotes/notes/option-precedence-1fecab21fdfb2c33.yaml delete mode 100644 releasenotes/notes/sdk-helper-41f8d815cfbcfb00.yaml delete mode 100644 releasenotes/notes/session-client-b581a6e5d18c8f04.yaml delete mode 100644 releasenotes/notes/shade-helper-568f8cb372eef6d9.yaml delete mode 100644 releasenotes/notes/started-using-reno-242e2b0cd27f9480.yaml delete mode 100644 releasenotes/notes/vendor-updates-f11184ba56bb27cf.yaml delete mode 100644 releasenotes/source/_static/.placeholder delete mode 100644 releasenotes/source/_templates/.placeholder delete mode 100644 releasenotes/source/conf.py delete mode 100644 releasenotes/source/index.rst delete mode 100644 releasenotes/source/mitaka.rst delete mode 100644 releasenotes/source/unreleased.rst delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100755 setup.py delete mode 100644 test-requirements.txt delete mode 100644 tox.ini diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 3c12922..0000000 --- a/.coveragerc +++ /dev/null @@ -1,7 +0,0 @@ -[run] -branch = True -source = os_client_config -omit = os_client_config/tests/*,os_client_config/openstack/* - -[report] -ignore_errors = True diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 218bf6a..0000000 --- a/.gitignore +++ /dev/null @@ -1,54 +0,0 @@ -*.py[cod] -.venv - -# 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 -.tox -nosetests.xml -.testrepository - -# 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 5ba7edd..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/os-client-config.git diff --git a/.mailmap b/.mailmap deleted file mode 100644 index cc92f17..0000000 --- a/.mailmap +++ /dev/null @@ -1,3 +0,0 @@ -# Format is: -# -# \ No newline at end of file diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index fb62267..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 \ No newline at end of file diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 1990ecf..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,16 +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 - -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - 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/os-client-config \ No newline at end of file diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index c995c5c..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,4 +0,0 @@ -os-client-config Style Commandments -=============================================== - -Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 67db858..0000000 --- a/LICENSE +++ /dev/null @@ -1,175 +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/README.rst b/README.rst deleted file mode 100644 index a47e98b..0000000 --- a/README.rst +++ /dev/null @@ -1,488 +0,0 @@ -================ -os-client-config -================ - -`os-client-config` is a library for collecting client configuration for -using an OpenStack cloud in a consistent and comprehensive manner. It -will find cloud config for as few as 1 cloud and as many as you want to -put in a config file. It will read environment variables and config files, -and it also contains some vendor specific default values so that you don't -have to know extra info to use OpenStack - -* If you have a config file, you will get the clouds listed in it -* If you have environment variables, you will get a cloud named `envvars` -* If you have neither, you will get a cloud named `defaults` with base defaults - -Environment Variables ---------------------- - -`os-client-config` honors all of the normal `OS_*` variables. It does not -provide backwards compatibility to service-specific variables such as -`NOVA_USERNAME`. - -If you have OpenStack environment variables set, `os-client-config` will produce -a cloud config object named `envvars` containing your values from the -environment. If you don't like the name `envvars`, that's ok, you can override -it by setting `OS_CLOUD_NAME`. - -Service specific settings, like the nova service type, are set with the -default service type as a prefix. For instance, to set a special service_type -for trove set - -.. code-block:: bash - - export OS_DATABASE_SERVICE_TYPE=rax:database - -Config Files ------------- - -`os-client-config` will look for a file called `clouds.yaml` in the following -locations: - -* Current Directory -* ~/.config/openstack -* /etc/openstack - -The first file found wins. - -You can also set the environment variable `OS_CLIENT_CONFIG_FILE` to an -absolute path of a file to look for and that location will be inserted at the -front of the file search list. - -The keys are all of the keys you'd expect from `OS_*` - except lower case -and without the OS prefix. So, region name is set with `region_name`. - -Service specific settings, like the nova service type, are set with the -default service type as a prefix. For instance, to set a special service_type -for trove (because you're using Rackspace) set: - -.. code-block:: yaml - - database_service_type: 'rax:database' - - -Site Specific File Locations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In addition to `~/.config/openstack` and `/etc/openstack` - some platforms -have other locations they like to put things. `os-client-config` will also -look in an OS specific config dir - -* `USER_CONFIG_DIR` -* `SITE_CONFIG_DIR` - -`USER_CONFIG_DIR` is different on Linux, OSX and Windows. - -* Linux: `~/.config/openstack` -* OSX: `~/Library/Application Support/openstack` -* Windows: `C:\\Users\\USERNAME\\AppData\\Local\\OpenStack\\openstack` - -`SITE_CONFIG_DIR` is different on Linux, OSX and Windows. - -* Linux: `/etc/openstack` -* OSX: `/Library/Application Support/openstack` -* Windows: `C:\\ProgramData\\OpenStack\\openstack` - -An example config file is probably helpful: - -.. code-block:: yaml - - clouds: - mtvexx: - profile: vexxhost - auth: - username: mordred@inaugust.com - password: XXXXXXXXX - project_name: mordred@inaugust.com - region_name: ca-ymq-1 - dns_api_version: 1 - mordred: - region_name: RegionOne - auth: - username: 'mordred' - password: XXXXXXX - project_name: 'shade' - auth_url: 'https://montytaylor-sjc.openstack.blueboxgrid.com:5001/v2.0' - infra: - profile: rackspace - auth: - username: openstackci - password: XXXXXXXX - project_id: 610275 - regions: - - DFW - - ORD - - IAD - -You may note a few things. First, since `auth_url` settings are silly -and embarrassingly ugly, known cloud vendor profile information is included and -may be referenced by name. One of the benefits of that is that `auth_url` -isn't the only thing the vendor defaults contain. For instance, since -Rackspace lists `rax:database` as the service type for trove, `os-client-config` -knows that so that you don't have to. In case the cloud vendor profile is not -available, you can provide one called `clouds-public.yaml`, following the same -location rules previously mentioned for the config files. - -`regions` can be a list of regions. When you call `get_all_clouds`, -you'll get a cloud config object for each cloud/region combo. - -As seen with `dns_service_type`, any setting that makes sense to be per-service, -like `service_type` or `endpoint` or `api_version` can be set by prefixing -the setting with the default service type. That might strike you funny when -setting `service_type` and it does me too - but that's just the world we live -in. - -Auth Settings -------------- - -Keystone has auth plugins - which means it's not possible to know ahead of time -which auth settings are needed. `os-client-config` sets the default plugin type -to `password`, which is what things all were before plugins came about. In -order to facilitate validation of values, all of the parameters that exist -as a result of a chosen plugin need to go into the auth dict. For password -auth, this includes `auth_url`, `username` and `password` as well as anything -related to domains, projects and trusts. - -Splitting Secrets ------------------ - -In some scenarios, such as configuration management controlled environments, -it might be easier to have secrets in one file and non-secrets in another. -This is fully supported via an optional file `secure.yaml` which follows all -the same location rules as `clouds.yaml`. It can contain anything you put -in `clouds.yaml` and will take precedence over anything in the `clouds.yaml` -file. - -.. code-block:: yaml - - # clouds.yaml - clouds: - internap: - profile: internap - auth: - username: api-55f9a00fb2619 - project_name: inap-17037 - regions: - - ams01 - - nyj01 - # secure.yaml - clouds: - internap: - auth: - password: XXXXXXXXXXXXXXXXX - -SSL Settings ------------- - -When the access to a cloud is done via a secure connection, `os-client-config` -will always verify the SSL cert by default. This can be disabled by setting -`verify` to `False`. In case the cert is signed by an unknown CA, a specific -cacert can be provided via `cacert`. **WARNING:** `verify` will always have -precedence over `cacert`, so when setting a CA cert but disabling `verify`, the -cloud cert will never be validated. - -Client certs are also configurable. `cert` will be the client cert file -location. In case the cert key is not included within the client cert file, -its file location needs to be set via `key`. - -Cache Settings --------------- - -Accessing a cloud is often expensive, so it's quite common to want to do some -client-side caching of those operations. To facilitate that, `os-client-config` -understands passing through cache settings to dogpile.cache, with the following -behaviors: - -* Listing no config settings means you get a null cache. -* `cache.expiration_time` and nothing else gets you memory cache. -* Otherwise, `cache.class` and `cache.arguments` are passed in - -Different cloud behaviors are also differently expensive to deal with. If you -want to get really crazy and tweak stuff, you can specify different expiration -times on a per-resource basis by passing values, in seconds to an expiration -mapping keyed on the singular name of the resource. A value of `-1` indicates -that the resource should never expire. - -`os-client-config` does not actually cache anything itself, but it collects -and presents the cache information so that your various applications that -are connecting to OpenStack can share a cache should you desire. - -.. code-block:: yaml - - cache: - class: dogpile.cache.pylibmc - expiration_time: 3600 - arguments: - url: - - 127.0.0.1 - expiration: - server: 5 - flavor: -1 - clouds: - mtvexx: - profile: vexxhost - auth: - username: mordred@inaugust.com - password: XXXXXXXXX - project_name: mordred@inaugust.com - region_name: ca-ymq-1 - dns_api_version: 1 - - -IPv6 ----- - -IPv6 is the future, and you should always use it if your cloud supports it and -if your local network supports it. Both of those are easily detectable and all -friendly software should do the right thing. However, sometimes you might -exist in a location where you have an IPv6 stack, but something evil has -caused it to not actually function. In that case, there is a config option -you can set to unbreak you `force_ipv4`, or `OS_FORCE_IPV4` boolean -environment variable. - -.. code-block:: yaml - - client: - force_ipv4: true - clouds: - mtvexx: - profile: vexxhost - auth: - username: mordred@inaugust.com - password: XXXXXXXXX - project_name: mordred@inaugust.com - region_name: ca-ymq-1 - dns_api_version: 1 - monty: - profile: rax - auth: - username: mordred@inaugust.com - password: XXXXXXXXX - project_name: mordred@inaugust.com - region_name: DFW - -The above snippet will tell client programs to prefer returning an IPv4 -address. - -Per-region settings -------------------- - -Sometimes you have a cloud provider that has config that is common to the -cloud, but also with some things you might want to express on a per-region -basis. For instance, Internap provides a public and private network specific -to the user in each region, and putting the values of those networks into -config can make consuming programs more efficient. - -To support this, the region list can actually be a list of dicts, and any -setting that can be set at the cloud level can be overridden for that -region. - -.. code-block:: yaml - - clouds: - internap: - profile: internap - auth: - password: XXXXXXXXXXXXXXXXX - username: api-55f9a00fb2619 - project_name: inap-17037 - regions: - - name: ams01 - values: - networks: - - name: inap-17037-WAN1654 - routes_externally: true - - name: inap-17037-LAN6745 - - name: nyj01 - values: - networks: - - name: inap-17037-WAN1654 - routes_externally: true - - name: inap-17037-LAN6745 - -Usage ------ - -The simplest and least useful thing you can do is: - -.. code-block:: python - - python -m os_client_config.config - -Which will print out whatever if finds for your config. If you want to use -it from python, which is much more likely what you want to do, things like: - -Get a named cloud. - -.. code-block:: python - - import os_client_config - - cloud_config = os_client_config.OpenStackConfig().get_one_cloud( - 'internap', region_name='ams01') - print(cloud_config.name, cloud_config.region, cloud_config.config) - -Or, get all of the clouds. - -.. code-block:: python - - import os_client_config - - cloud_config = os_client_config.OpenStackConfig().get_all_clouds() - for cloud in cloud_config: - print(cloud.name, cloud.region, cloud.config) - -argparse --------- - -If you're using os-client-config from a program that wants to process -command line options, there is a registration function to register the -arguments that both os-client-config and keystoneauth know how to deal -with - as well as a consumption argument. - -.. code-block:: python - - import argparse - import sys - - import os_client_config - - cloud_config = os_client_config.OpenStackConfig() - parser = argparse.ArgumentParser() - cloud_config.register_argparse_arguments(parser, sys.argv) - - options = parser.parse_args() - - cloud = cloud_config.get_one_cloud(argparse=options) - -Constructing OpenStack SDK object ---------------------------------- - -If what you want to do is get an OpenStack SDK Connection and you want it to -do all the normal things related to clouds.yaml, `OS_` environment variables, -a helper function is provided. The following will get you a fully configured -`openstacksdk` instance. - -.. code-block:: python - - import os_client_config - - sdk = os_client_config.make_sdk() - -If you want to do the same thing but on a named cloud. - -.. code-block:: python - - import os_client_config - - sdk = os_client_config.make_sdk(cloud='mtvexx') - -If you want to do the same thing but also support command line parsing. - -.. code-block:: python - - import argparse - - import os_client_config - - sdk = os_client_config.make_sdk(options=argparse.ArgumentParser()) - -It should be noted that OpenStack SDK has ways to construct itself that allow -for additional flexibility. If the helper function here does not meet your -needs, you should see the `from_config` method of -`openstack.connection.Connection `_ - -Constructing shade objects --------------------------- - -If what you want to do is get a -`shade `_ OpenStackCloud object, a -helper function that honors clouds.yaml and `OS_` environment variables is -provided. The following will get you a fully configured `OpenStackCloud` -instance. - -.. code-block:: python - - import os_client_config - - cloud = os_client_config.make_shade() - -If you want to do the same thing but on a named cloud. - -.. code-block:: python - - import os_client_config - - cloud = os_client_config.make_shade(cloud='mtvexx') - -If you want to do the same thing but also support command line parsing. - -.. code-block:: python - - import argparse - - import os_client_config - - cloud = os_client_config.make_shade(options=argparse.ArgumentParser()) - -Constructing REST API Clients ------------------------------ - -What if you want to make direct REST calls via a Session interface? You're -in luck. A similar interface is available as with `openstacksdk` and `shade`. -The main difference is that you need to specify which service you want to -talk to and `make_rest_client` will return you a keystoneauth Session object -that is mounted on the endpoint for the service you're looking for. - -.. code-block:: python - - import os_client_config - - session = os_client_config.make_rest_client('compute', cloud='vexxhost') - - response = session.get('/servers') - server_list = response.json()['servers'] - -Constructing Legacy Client objects ----------------------------------- - -If you want get an old-style Client object from a python-\*client library, -and you want it to do all the normal things related to clouds.yaml, `OS_` -environment variables, a helper function is also provided. The following -will get you a fully configured `novaclient` instance. - -.. code-block:: python - - import os_client_config - - nova = os_client_config.make_client('compute') - -If you want to do the same thing but on a named cloud. - -.. code-block:: python - - import os_client_config - - nova = os_client_config.make_client('compute', cloud='mtvexx') - -If you want to do the same thing but also support command line parsing. - -.. code-block:: python - - import argparse - - import os_client_config - - nova = os_client_config.make_client( - 'compute', options=argparse.ArgumentParser()) - -If you want to get fancier than that in your python, then the rest of the -API is available to you. But often times, you just want to do the one thing. - -Source ------- - -* Free software: Apache license -* Documentation: http://docs.openstack.org/developer/os-client-config -* Source: http://git.openstack.org/cgit/openstack/os-client-config -* Bugs: http://bugs.launchpad.net/os-client-config diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..7730410 --- /dev/null +++ b/README.txt @@ -0,0 +1,13 @@ +This project is no longer maintained. + +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". + +Use instead the project deb-python-os-client-config at +http://git.openstack.org/cgit/openstack/deb-python-os-client-config . + +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/doc/source/api-reference.rst b/doc/source/api-reference.rst deleted file mode 100644 index dfa7f31..0000000 --- a/doc/source/api-reference.rst +++ /dev/null @@ -1,10 +0,0 @@ -============= -API Reference -============= - -.. module:: os_client_config - :synopsis: OpenStack client configuration - -.. autoclass:: os_client_config.OpenStackConfig - :members: - :inherited-members: diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100755 index 208517c..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,76 +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. - -import os -import sys - -sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - #'sphinx.ext.intersphinx', - 'oslosphinx', - 'reno.sphinxext' -] - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'os-client-config' -copyright = u'2015, various OpenStack developers' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -# html_theme = '_theme' -# html_static_path = ['static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), -] - -# Example configuration for intersphinx: refer to the Python standard library. -#intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index ed77c12..0000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1,4 +0,0 @@ -============ -Contributing -============ -.. include:: ../../CONTRIBUTING.rst \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index f7263c9..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. include:: ../../README.rst - -.. toctree:: - :maxdepth: 2 - - vendor-support - contributing - installation - network-config - api-reference - releasenotes - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/installation.rst b/doc/source/installation.rst deleted file mode 100644 index 48bbc2f..0000000 --- a/doc/source/installation.rst +++ /dev/null @@ -1,12 +0,0 @@ -============ -Installation -============ - -At the command line:: - - $ pip install os-client-config - -Or, if you have virtualenvwrapper installed:: - - $ mkvirtualenv os-client-config - $ pip install os-client-config \ No newline at end of file diff --git a/doc/source/network-config.rst b/doc/source/network-config.rst deleted file mode 100644 index 5a27b8e..0000000 --- a/doc/source/network-config.rst +++ /dev/null @@ -1,49 +0,0 @@ -============== -Network Config -============== - -There are several different qualities that networks in OpenStack might have -that might not be able to be automatically inferred from the available -metadata. To help users navigate more complex setups, `os-client-config` -allows configuring a list of network metadata. - -.. code-block:: yaml - - clouds: - amazing: - networks: - - name: blue - routes_externally: true - - name: purple - routes_externally: true - default_interface: true - - name: green - routes_externally: false - - name: purple - routes_externally: false - nat_destination: true - -Every entry must have a name field, which can hold either the name or the id -of the network. - -`routes_externally` is a boolean field that labels the network as handling -north/south traffic off of the cloud. In a public cloud this might be thought -of as the "public" network, but in private clouds it's possible it might -be an RFC1918 address. In either case, it's provides IPs to servers that -things not on the cloud can use. This value defaults to `false`, which -indicates only servers on the same network can talk to it. - -`default_interface` is a boolean field that indicates that the network is the -one that programs should use. It defaults to false. An example of needing to -use this value is a cloud with two private networks, and where a user is -running ansible in one of the servers to talk to other servers on the private -network. Because both networks are private, there would otherwise be no way -to determine which one should be used for the traffic. There can only be one -`default_interface` per cloud. - -`nat_destination` is a boolean field that indicates which network floating -ips should be attached to. It defaults to false. Normally this can be inferred -by looking for a network that has subnets that have a gateway_ip. But it's -possible to have more than one network that satisfies that condition, so the -user might want to tell programs which one to pick. There can be only one -`nat_destination` per cloud. diff --git a/doc/source/releasenotes.rst b/doc/source/releasenotes.rst deleted file mode 100644 index 2a4bceb..0000000 --- a/doc/source/releasenotes.rst +++ /dev/null @@ -1,5 +0,0 @@ -============= -Release Notes -============= - -.. release-notes:: diff --git a/doc/source/vendor-support.rst b/doc/source/vendor-support.rst deleted file mode 100644 index c27d124..0000000 --- a/doc/source/vendor-support.rst +++ /dev/null @@ -1,324 +0,0 @@ -============== -Vendor Support -============== - -OpenStack presents deployers with many options, some of which can expose -differences to end users. `os-client-config` tries its best to collect -information about various things a user would need to know. The following -is a text representation of the vendor related defaults `os-client-config` -knows about. - -Default Values --------------- - -These are the default behaviors unless a cloud is configured differently. - -* Identity uses `password` authentication -* Identity API Version is 2 -* Image API Version is 2 -* Volume API Version is 2 -* Images must be in `qcow2` format -* Images are uploaded using PUT interface -* Public IPv4 is directly routable via DHCP from Neutron -* IPv6 is not provided -* Floating IPs are provided by Neutron -* Security groups are provided by Neutron -* Vendor specific agents are not used - -auro ----- - -https://api.auro.io:5000/v2.0 - -============== ================ -Region Name Human Name -============== ================ -van1 Vancouver, BC -============== ================ - -* Public IPv4 is provided via NAT with Neutron Floating IP - -catalyst --------- - -https://api.cloud.catalyst.net.nz:5000/v2.0 - -============== ================ -Region Name Human Name -============== ================ -nz-por-1 Porirua, NZ -nz_wlg_2 Wellington, NZ -============== ================ - -* Image API Version is 1 -* Images must be in `raw` format -* Volume API Version is 1 - -citycloud ---------- - -https://identity1.citycloud.com:5000/v3/ - -============== ================ -Region Name Human Name -============== ================ -Buf1 Buffalo, NY -Fra1 Frankfurt, DE -Kna1 Karlskrona, SE -La1 Los Angeles, CA -Lon1 London, UK -Sto2 Stockholm, SE -============== ================ - -* Identity API Version is 3 -* Public IPv4 is provided via NAT with Neutron Floating IP -* Volume API Version is 1 - -conoha ------- - -https://identity.%(region_name)s.conoha.io - -============== ================ -Region Name Human Name -============== ================ -tyo1 Tokyo, JP -sin1 Singapore -sjc1 San Jose, CA -============== ================ - -* Image upload is not supported - -datacentred ------------ - -https://compute.datacentred.io:5000 - -============== ================ -Region Name Human Name -============== ================ -sal01 Manchester, UK -============== ================ - -* Image API Version is 1 - -dreamcompute ------------- - -https://iad2.dream.io:5000 - -============== ================ -Region Name Human Name -============== ================ -RegionOne Region One -============== ================ - -* Identity API Version is 3 -* Images must be in `raw` format -* IPv6 is provided to every server - -dreamhost ---------- - -Deprecated, please use dreamcompute - -https://keystone.dream.io/v2.0 - -============== ================ -Region Name Human Name -============== ================ -RegionOne Region One -============== ================ - -* Images must be in `raw` format -* Public IPv4 is provided via NAT with Neutron Floating IP -* IPv6 is provided to every server - -elastx ------- - -https://ops.elastx.net:5000/v2.0 - -============== ================ -Region Name Human Name -============== ================ -regionOne Region One -============== ================ - -* Public IPv4 is provided via NAT with Neutron Floating IP - -entercloudsuite ---------------- - -https://api.entercloudsuite.com/v2.0 - -============== ================ -Region Name Human Name -============== ================ -nl-ams1 Amsterdam, NL -it-mil1 Milan, IT -de-fra1 Frankfurt, DE -============== ================ - -* Volume API Version is 1 - -ibmcloud --------- - -https://identity.open.softlayer.com - -============== ================ -Region Name Human Name -============== ================ -london London, UK -============== ================ - -* Public IPv4 is provided via NAT with Neutron Floating IP - -internap --------- - -https://identity.api.cloud.iweb.com/v2.0 - -============== ================ -Region Name Human Name -============== ================ -ams01 Amsterdam, NL -da01 Dallas, TX -nyj01 New York, NY -sin01 Singapore -sjc01 San Jose, CA -============== ================ - -* Floating IPs are not supported - -osic ----- - -https://cloud1.osic.org:5000 - -============== ================= -Region Name Human Name -============== ================= -RegionOne RegionOne -============== ================= - -* Public IPv4 is provided via NAT with Neutron Floating IP - -ovh ---- - -https://auth.cloud.ovh.net/v2.0 - -============== ================ -Region Name Human Name -============== ================ -BHS1 Beauharnois, QC -SBG1 Strassbourg, FR -GRA1 Gravelines, FR -============== ================ - -* Images must be in `raw` format -* Floating IPs are not supported - -rackspace ---------- - -https://identity.api.rackspacecloud.com/v2.0/ - -============== ================ -Region Name Human Name -============== ================ -DFW Dallas -HKG Hong Kong -IAD Washington, D.C. -LON London -ORD Chicago -SYD Sydney -============== ================ - -* Database Service Type is `rax:database` -* Compute Service Name is `cloudServersOpenStack` -* Images must be in `vhd` format -* Images must be uploaded using the Glance Task Interface -* Floating IPs are not supported -* Public IPv4 is directly routable via static config by Nova -* IPv6 is provided to every server -* Security groups are not supported -* Uploaded Images need properties to not use vendor agent:: - :vm_mode: hvm - :xenapi_use_agent: False -* Volume API Version is 1 - -switchengines -------------- - -https://keystone.cloud.switch.ch:5000/v2.0 - -============== ================ -Region Name Human Name -============== ================ -LS Lausanne, CH -ZH Zurich, CH -============== ================ - -* Images must be in `raw` format -* Images must be uploaded using the Glance Task Interface -* Volume API Version is 1 - -ultimum -------- - -https://console.ultimum-cloud.com:5000/v2.0 - -============== ================ -Region Name Human Name -============== ================ -RegionOne Region One -============== ================ - -* Volume API Version is 1 - -unitedstack ------------ - -https://identity.api.ustack.com/v3 - -============== ================ -Region Name Human Name -============== ================ -bj1 Beijing -gd1 Guangdong -============== ================ - -* Identity API Version is 3 -* Images must be in `raw` format -* Volume API Version is 1 - -vexxhost --------- - -http://auth.vexxhost.net - -============== ================ -Region Name Human Name -============== ================ -ca-ymq-1 Montreal -============== ================ - -* DNS API Version is 1 -* Identity API Version is 3 - -zetta ------ - -https://identity.api.zetta.io/v3 - -============== ================ -Region Name Human Name -============== ================ -no-osl1 Oslo -============== ================ - -* DNS API Version is 2 -* Identity API Version is 3 diff --git a/os_client_config/__init__.py b/os_client_config/__init__.py deleted file mode 100644 index 09d7442..0000000 --- a/os_client_config/__init__.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - -import sys - -import pbr.version - -from os_client_config import cloud_config -from os_client_config.config import OpenStackConfig # noqa - - -__version__ = pbr.version.VersionInfo('os_client_config').version_string() - - -def get_config(service_key=None, options=None, **kwargs): - config = OpenStackConfig() - if options: - config.register_argparse_options(options, sys.argv, service_key) - parsed_options = options.parse_known_args(sys.argv) - else: - parsed_options = None - - return config.get_one_cloud(options=parsed_options, **kwargs) - - -def make_rest_client(service_key, options=None, **kwargs): - """Simple wrapper function. It has almost no features. - - This will get you a raw requests Session Adapter that is mounted - on the given service from the keystone service catalog. If you leave - off cloud and region_name, it will assume that you've got env vars - set, but if you give them, it'll use clouds.yaml as you'd expect. - - This function is deliberately simple. It has no flexibility. If you - want flexibility, you can make a cloud config object and call - get_session_client on it. This function is to make it easy to poke - at OpenStack REST APIs with a properly configured keystone session. - """ - cloud = get_config(service_key=service_key, options=options, **kwargs) - return cloud.get_session_client(service_key) -# Backwards compat - simple_client was a terrible name -simple_client = make_rest_client -# Backwards compat - session_client was a terrible name -session_client = make_rest_client - - -def make_client(service_key, constructor=None, options=None, **kwargs): - """Simple wrapper for getting a client instance from a client lib. - - OpenStack Client Libraries all have a fairly consistent constructor - interface which os-client-config supports. In the simple case, there - is one and only one right way to construct a client object. If as a user - you don't want to do fancy things, just use this. It honors OS_ environment - variables and clouds.yaml - and takes as **kwargs anything you'd expect - to pass in. - """ - cloud = get_config(service_key=service_key, options=options, **kwargs) - if not constructor: - constructor = cloud_config._get_client(service_key) - return cloud.get_legacy_client(service_key, constructor) - - -def make_sdk(options=None, **kwargs): - """Simple wrapper for getting an OpenStack SDK Connection. - - For completeness, provide a mechanism that matches make_client and - make_rest_client. The heavy lifting here is done in openstacksdk. - - :rtype: :class:`~openstack.connection.Connection` - """ - from openstack import connection - cloud = get_config(options=options, **kwargs) - return connection.from_config(cloud_config=cloud, options=options) - - -def make_shade(options=None, **kwargs): - """Simple wrapper for getting a Shade OpenStackCloud object - - A mechanism that matches make_sdk, make_client and make_rest_client. - - :rtype: :class:`~shade.OpenStackCloud` - """ - import shade - cloud = get_config(options=options, **kwargs) - return shade.OpenStackCloud(cloud_config=cloud, **kwargs) diff --git a/os_client_config/_log.py b/os_client_config/_log.py deleted file mode 100644 index ff2f2ea..0000000 --- a/os_client_config/_log.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2015 IBM Corp. -# -# 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. - -import logging - - -class NullHandler(logging.Handler): - def emit(self, record): - pass - - -def setup_logging(name): - log = logging.getLogger(name) - if len(log.handlers) == 0: - h = NullHandler() - log.addHandler(h) - return log diff --git a/os_client_config/cloud_config.py b/os_client_config/cloud_config.py deleted file mode 100644 index 08c739a..0000000 --- a/os_client_config/cloud_config.py +++ /dev/null @@ -1,473 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - -import importlib -import warnings - -from keystoneauth1 import adapter -from keystoneauth1 import plugin -from keystoneauth1 import session -import requestsexceptions - -from os_client_config import _log -from os_client_config import constructors -from os_client_config import exceptions - - -def _get_client(service_key): - class_mapping = constructors.get_constructor_mapping() - if service_key not in class_mapping: - raise exceptions.OpenStackConfigException( - "Service {service_key} is unkown. Please pass in a client" - " constructor or submit a patch to os-client-config".format( - service_key=service_key)) - mod_name, ctr_name = class_mapping[service_key].rsplit('.', 1) - lib_name = mod_name.split('.')[0] - try: - mod = importlib.import_module(mod_name) - except ImportError: - raise exceptions.OpenStackConfigException( - "Client for '{service_key}' was requested, but" - " {mod_name} was unable to be imported. Either import" - " the module yourself and pass the constructor in as an argument," - " or perhaps you do not have python-{lib_name} installed.".format( - service_key=service_key, - mod_name=mod_name, - lib_name=lib_name)) - try: - ctr = getattr(mod, ctr_name) - except AttributeError: - raise exceptions.OpenStackConfigException( - "Client for '{service_key}' was requested, but although" - " {mod_name} imported fine, the constructor at {fullname}" - " as not found. Please check your installation, we have no" - " clue what is wrong with your computer.".format( - service_key=service_key, - mod_name=mod_name, - fullname=class_mapping[service_key])) - return ctr - - -def _make_key(key, service_type): - if not service_type: - return key - else: - service_type = service_type.lower().replace('-', '_') - return "_".join([service_type, key]) - - -class CloudConfig(object): - def __init__(self, name, region, config, - force_ipv4=False, auth_plugin=None, - openstack_config=None): - self.name = name - self.region = region - self.config = config - self.log = _log.setup_logging(__name__) - self._force_ipv4 = force_ipv4 - self._auth = auth_plugin - self._openstack_config = openstack_config - self._keystone_session = None - - def __getattr__(self, key): - """Return arbitrary attributes.""" - - if key.startswith('os_'): - key = key[3:] - - if key in [attr.replace('-', '_') for attr in self.config]: - return self.config[key] - else: - return None - - def __iter__(self): - return self.config.__iter__() - - def __eq__(self, other): - return (self.name == other.name and self.region == other.region - and self.config == other.config) - - def __ne__(self, other): - return not self == other - - def get_requests_verify_args(self): - """Return the verify and cert values for the requests library.""" - if self.config['verify'] and self.config['cacert']: - verify = self.config['cacert'] - else: - verify = self.config['verify'] - if self.config['cacert']: - warnings.warn( - "You are specifying a cacert for the cloud {0} but " - "also to ignore the host verification. The host SSL cert " - "will not be verified.".format(self.name)) - - cert = self.config.get('cert', None) - if cert: - if self.config['key']: - cert = (cert, self.config['key']) - return (verify, cert) - - def get_services(self): - """Return a list of service types we know something about.""" - services = [] - for key, val in self.config.items(): - if (key.endswith('api_version') - or key.endswith('service_type') - or key.endswith('service_name')): - services.append("_".join(key.split('_')[:-2])) - return list(set(services)) - - def get_auth_args(self): - return self.config['auth'] - - def get_interface(self, service_type=None): - key = _make_key('interface', service_type) - interface = self.config.get('interface') - return self.config.get(key, interface) - - def get_region_name(self, service_type=None): - if not service_type: - return self.region - key = _make_key('region_name', service_type) - return self.config.get(key, self.region) - - def get_api_version(self, service_type): - key = _make_key('api_version', service_type) - return self.config.get(key, None) - - def get_service_type(self, service_type): - key = _make_key('service_type', service_type) - # Cinder did an evil thing where they defined a second service - # type in the catalog. Of course, that's insane, so let's hide this - # atrocity from the as-yet-unsullied eyes of our users. - # Of course, if the user requests a volumev2, that structure should - # still work. - if (service_type == 'volume' and - self.get_api_version(service_type).startswith('2')): - service_type = 'volumev2' - return self.config.get(key, service_type) - - def get_service_name(self, service_type): - key = _make_key('service_name', service_type) - return self.config.get(key, None) - - def get_endpoint(self, service_type): - key = _make_key('endpoint_override', service_type) - old_key = _make_key('endpoint', service_type) - return self.config.get(key, self.config.get(old_key, None)) - - @property - def prefer_ipv6(self): - return not self._force_ipv4 - - @property - def force_ipv4(self): - return self._force_ipv4 - - def get_auth(self): - """Return a keystoneauth plugin from the auth credentials.""" - return self._auth - - def get_session(self): - """Return a keystoneauth session based on the auth credentials.""" - if self._keystone_session is None: - if not self._auth: - raise exceptions.OpenStackConfigException( - "Problem with auth parameters") - (verify, cert) = self.get_requests_verify_args() - # Turn off urllib3 warnings about insecure certs if we have - # explicitly configured requests to tell it we do not want - # cert verification - if not verify: - self.log.debug( - "Turning off SSL warnings for {cloud}:{region}" - " since verify=False".format( - cloud=self.name, region=self.region)) - requestsexceptions.squelch_warnings(insecure_requests=not verify) - self._keystone_session = session.Session( - auth=self._auth, - verify=verify, - cert=cert, - timeout=self.config['api_timeout']) - return self._keystone_session - - def get_session_client(self, service_key): - """Return a prepped requests adapter for a given service. - - This is useful for making direct requests calls against a - 'mounted' endpoint. That is, if you do: - - client = get_session_client('compute') - - then you can do: - - client.get('/flavors') - - and it will work like you think. - """ - - return adapter.Adapter( - session=self.get_session(), - service_type=self.get_service_type(service_key), - service_name=self.get_service_name(service_key), - interface=self.get_interface(service_key), - region_name=self.region) - - def get_session_endpoint(self, service_key): - """Return the endpoint from config or the catalog. - - If a configuration lists an explicit endpoint for a service, - return that. Otherwise, fetch the service catalog from the - keystone session and return the appropriate endpoint. - - :param service_key: Generic key for service, such as 'compute' or - 'network' - - :returns: Endpoint for the service, or None if not found - """ - - override_endpoint = self.get_endpoint(service_key) - if override_endpoint: - return override_endpoint - # keystone is a special case in keystone, because what? - session = self.get_session() - if service_key == 'identity': - endpoint = session.get_endpoint(interface=plugin.AUTH_INTERFACE) - else: - endpoint = session.get_endpoint( - service_type=self.get_service_type(service_key), - service_name=self.get_service_name(service_key), - interface=self.get_interface(service_key), - region_name=self.region) - return endpoint - - def get_legacy_client( - self, service_key, client_class=None, interface_key=None, - pass_version_arg=True, version=None, **kwargs): - """Return a legacy OpenStack client object for the given config. - - Most of the OpenStack python-*client libraries have the same - interface for their client constructors, but there are several - parameters one wants to pass given a :class:`CloudConfig` object. - - In the future, OpenStack API consumption should be done through - the OpenStack SDK, but that's not ready yet. This is for getting - Client objects from python-*client only. - - :param service_key: Generic key for service, such as 'compute' or - 'network' - :param client_class: Class of the client to be instantiated. This - should be the unversioned version if there - is one, such as novaclient.client.Client, or - the versioned one, such as - neutronclient.v2_0.client.Client if there isn't - :param interface_key: (optional) Some clients, such as glanceclient - only accept the parameter 'interface' instead - of 'endpoint_type' - this is a get-out-of-jail - parameter for those until they can be aligned. - os-client-config understands this to be the - case if service_key is image, so this is really - only for use with other unknown broken clients. - :param pass_version_arg: (optional) If a versioned Client constructor - was passed to client_class, set this to - False, which will tell get_client to not - pass a version parameter. os-client-config - already understand that this is the - case for network, so it can be omitted in - that case. - :param version: (optional) Version string to override the configured - version string. - :param kwargs: (optional) keyword args are passed through to the - Client constructor, so this is in case anything - additional needs to be passed in. - """ - if not client_class: - client_class = _get_client(service_key) - - # Because of course swift is different - if service_key == 'object-store': - return self._get_swift_client(client_class=client_class, **kwargs) - interface = self.get_interface(service_key) - # trigger exception on lack of service - endpoint = self.get_session_endpoint(service_key) - endpoint_override = self.get_endpoint(service_key) - - if not interface_key: - if service_key in ('image', 'key-manager'): - interface_key = 'interface' - else: - interface_key = 'endpoint_type' - - constructor_kwargs = dict( - session=self.get_session(), - service_name=self.get_service_name(service_key), - service_type=self.get_service_type(service_key), - endpoint_override=endpoint_override, - region_name=self.region) - - if service_key == 'image': - # os-client-config does not depend on glanceclient, but if - # the user passed in glanceclient.client.Client, which they - # would need to do if they were requesting 'image' - then - # they necessarily have glanceclient installed - from glanceclient.common import utils as glance_utils - endpoint, detected_version = glance_utils.strip_version(endpoint) - # If the user has passed in a version, that's explicit, use it - if not version: - version = detected_version - # If the user has passed in or configured an override, use it. - # Otherwise, ALWAYS pass in an endpoint_override becuase - # we've already done version stripping, so we don't want version - # reconstruction to happen twice - if not endpoint_override: - constructor_kwargs['endpoint_override'] = endpoint - constructor_kwargs.update(kwargs) - constructor_kwargs[interface_key] = interface - if pass_version_arg: - if not version: - version = self.get_api_version(service_key) - # Temporary workaround while we wait for python-openstackclient - # to be able to handle 2.0 which is what neutronclient expects - if service_key == 'network' and version == '2': - version = '2.0' - if service_key == 'identity': - # Workaround for bug#1513839 - if 'endpoint' not in constructor_kwargs: - endpoint = self.get_session_endpoint('identity') - constructor_kwargs['endpoint'] = endpoint - if service_key == 'network': - constructor_kwargs['api_version'] = version - else: - constructor_kwargs['version'] = version - if service_key == 'database': - # TODO(mordred) Remove when https://review.openstack.org/314032 - # has landed and released. We're passing in a Session, but the - # trove Client object has username and password as required - # args - constructor_kwargs['username'] = None - constructor_kwargs['password'] = None - - return client_class(**constructor_kwargs) - - def _get_swift_client(self, client_class, **kwargs): - auth_args = self.get_auth_args() - auth_version = self.get_api_version('identity') - session = self.get_session() - token = session.get_token() - endpoint = self.get_session_endpoint(service_key='object-store') - if not endpoint: - return None - # If we have a username/password, we want to pass them to - # swift - because otherwise it will not re-up tokens appropriately - # However, if we only have non-password auth, then get a token - # and pass it in - swift_kwargs = dict( - auth_version=auth_version, - preauthurl=endpoint, - preauthtoken=token, - os_options=dict( - region_name=self.get_region_name(), - auth_token=token, - object_storage_url=endpoint, - service_type=self.get_service_type('object-store'), - endpoint_type=self.get_interface('object-store'), - - )) - if self.config['api_timeout'] is not None: - swift_kwargs['timeout'] = float(self.config['api_timeout']) - - # create with password - swift_kwargs['user'] = auth_args.get('username') - swift_kwargs['key'] = auth_args.get('password') - swift_kwargs['authurl'] = auth_args.get('auth_url') - os_options = {} - if auth_version == '2.0': - os_options['tenant_name'] = auth_args.get('project_name') - os_options['tenant_id'] = auth_args.get('project_id') - else: - os_options['project_name'] = auth_args.get('project_name') - os_options['project_id'] = auth_args.get('project_id') - - for key in ( - 'user_id', - 'project_domain_id', - 'project_domain_name', - 'user_domain_id', - 'user_domain_name'): - os_options[key] = auth_args.get(key) - swift_kwargs['os_options'].update(os_options) - - return client_class(**swift_kwargs) - - def get_cache_expiration_time(self): - if self._openstack_config: - return self._openstack_config.get_cache_expiration_time() - - def get_cache_path(self): - if self._openstack_config: - return self._openstack_config.get_cache_path() - - def get_cache_class(self): - if self._openstack_config: - return self._openstack_config.get_cache_class() - - def get_cache_arguments(self): - if self._openstack_config: - return self._openstack_config.get_cache_arguments() - - def get_cache_expiration(self): - if self._openstack_config: - return self._openstack_config.get_cache_expiration() - - def get_cache_resource_expiration(self, resource, default=None): - """Get expiration time for a resource - - :param resource: Name of the resource type - :param default: Default value to return if not found (optional, - defaults to None) - - :returns: Expiration time for the resource type as float or default - """ - if self._openstack_config: - expiration = self._openstack_config.get_cache_expiration() - if resource not in expiration: - return default - return float(expiration[resource]) - - def get_external_networks(self): - """Get list of network names for external networks.""" - return [ - net['name'] for net in self.config['networks'] - if net['routes_externally']] - - def get_internal_networks(self): - """Get list of network names for internal networks.""" - return [ - net['name'] for net in self.config['networks'] - if not net['routes_externally']] - - def get_default_network(self): - """Get network used for default interactions.""" - for net in self.config['networks']: - if net['default_interface']: - return net['name'] - return None - - def get_nat_destination(self): - """Get network used for NAT destination.""" - for net in self.config['networks']: - if net['nat_destination']: - return net['name'] - return None diff --git a/os_client_config/config.py b/os_client_config/config.py deleted file mode 100644 index 7fa670f..0000000 --- a/os_client_config/config.py +++ /dev/null @@ -1,1130 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - - -# alias because we already had an option named argparse -import argparse as argparse_mod -import collections -import copy -import json -import os -import sys -import warnings - -import appdirs -from keystoneauth1 import adapter -from keystoneauth1 import loading -import yaml - -from os_client_config import _log -from os_client_config import cloud_config -from os_client_config import defaults -from os_client_config import exceptions -from os_client_config import vendors - -APPDIRS = appdirs.AppDirs('openstack', 'OpenStack', multipath='/etc') -CONFIG_HOME = APPDIRS.user_config_dir -CACHE_PATH = APPDIRS.user_cache_dir - -UNIX_CONFIG_HOME = os.path.join( - os.path.expanduser(os.path.join('~', '.config')), 'openstack') -UNIX_SITE_CONFIG_HOME = '/etc/openstack' - -SITE_CONFIG_HOME = APPDIRS.site_config_dir - -CONFIG_SEARCH_PATH = [ - os.getcwd(), - CONFIG_HOME, UNIX_CONFIG_HOME, - SITE_CONFIG_HOME, UNIX_SITE_CONFIG_HOME -] -YAML_SUFFIXES = ('.yaml', '.yml') -JSON_SUFFIXES = ('.json',) -CONFIG_FILES = [ - os.path.join(d, 'clouds' + s) - for d in CONFIG_SEARCH_PATH - for s in YAML_SUFFIXES + JSON_SUFFIXES -] -SECURE_FILES = [ - os.path.join(d, 'secure' + s) - for d in CONFIG_SEARCH_PATH - for s in YAML_SUFFIXES + JSON_SUFFIXES -] -VENDOR_FILES = [ - os.path.join(d, 'clouds-public' + s) - for d in CONFIG_SEARCH_PATH - for s in YAML_SUFFIXES + JSON_SUFFIXES -] - -BOOL_KEYS = ('insecure', 'cache') - - -# NOTE(dtroyer): This turns out to be not the best idea so let's move -# overriding defaults to a kwarg to OpenStackConfig.__init__() -# Remove this sometime in June 2015 once OSC is comfortably -# changed-over and global-defaults is updated. -def set_default(key, value): - warnings.warn( - "Use of set_default() is deprecated. Defaults should be set with the " - "`override_defaults` parameter of OpenStackConfig." - ) - defaults.get_defaults() # make sure the dict is initialized - defaults._defaults[key] = value - - -def get_boolean(value): - if value is None: - return False - if type(value) is bool: - return value - if value.lower() == 'true': - return True - return False - - -def _get_os_environ(envvar_prefix=None): - ret = defaults.get_defaults() - if not envvar_prefix: - # This makes the or below be OS_ or OS_ which is a no-op - envvar_prefix = 'OS_' - environkeys = [k for k in os.environ.keys() - if (k.startswith('OS_') or k.startswith(envvar_prefix)) - and not k.startswith('OS_TEST') # infra CI var - and not k.startswith('OS_STD') # infra CI var - ] - for k in environkeys: - newkey = k.split('_', 1)[-1].lower() - ret[newkey] = os.environ[k] - # If the only environ key is region name, don't make a cloud, because - # it's being used as a cloud selector - if not environkeys or ( - len(environkeys) == 1 and 'region_name' in ret): - return None - return ret - - -def _merge_clouds(old_dict, new_dict): - """Like dict.update, except handling nested dicts.""" - ret = old_dict.copy() - for (k, v) in new_dict.items(): - if isinstance(v, dict): - if k in ret: - ret[k] = _merge_clouds(ret[k], v) - else: - ret[k] = v.copy() - else: - ret[k] = v - return ret - - -def _auth_update(old_dict, new_dict_source): - """Like dict.update, except handling the nested dict called auth.""" - new_dict = copy.deepcopy(new_dict_source) - for (k, v) in new_dict.items(): - if k == 'auth': - if k in old_dict: - old_dict[k].update(v) - else: - old_dict[k] = v.copy() - else: - old_dict[k] = v - return old_dict - - -def _fix_argv(argv): - # Transform any _ characters in arg names to - so that we don't - # have to throw billions of compat argparse arguments around all - # over the place. - processed = collections.defaultdict(list) - for index in range(0, len(argv)): - if argv[index].startswith('--'): - split_args = argv[index].split('=') - orig = split_args[0] - new = orig.replace('_', '-') - if orig != new: - split_args[0] = new - argv[index] = "=".join(split_args) - # Save both for later so we can throw an error about dupes - processed[new].append(orig) - overlap = [] - for new, old in processed.items(): - if len(old) > 1: - overlap.extend(old) - if overlap: - raise exceptions.OpenStackConfigException( - "The following options were given: '{options}' which contain" - " duplicates except that one has _ and one has -. There is" - " no sane way for us to know what you're doing. Remove the" - " duplicate option and try again".format( - options=','.join(overlap))) - - -class OpenStackConfig(object): - - def __init__(self, config_files=None, vendor_files=None, - override_defaults=None, force_ipv4=None, - envvar_prefix=None, secure_files=None): - self.log = _log.setup_logging(__name__) - - self._config_files = config_files or CONFIG_FILES - self._secure_files = secure_files or SECURE_FILES - self._vendor_files = vendor_files or VENDOR_FILES - - config_file_override = os.environ.pop('OS_CLIENT_CONFIG_FILE', None) - if config_file_override: - self._config_files.insert(0, config_file_override) - - secure_file_override = os.environ.pop('OS_CLIENT_SECURE_FILE', None) - if secure_file_override: - self._secure_files.insert(0, secure_file_override) - - self.defaults = defaults.get_defaults() - if override_defaults: - self.defaults.update(override_defaults) - - # First, use a config file if it exists where expected - self.config_filename, self.cloud_config = self._load_config_file() - _, secure_config = self._load_secure_file() - if secure_config: - self.cloud_config = _merge_clouds( - self.cloud_config, secure_config) - - if not self.cloud_config: - self.cloud_config = {'clouds': {}} - if 'clouds' not in self.cloud_config: - self.cloud_config['clouds'] = {} - - # Grab ipv6 preference settings from env - client_config = self.cloud_config.get('client', {}) - - if force_ipv4 is not None: - # If it's passed in to the constructor, honor it. - self.force_ipv4 = force_ipv4 - else: - # Get the backwards compat value - prefer_ipv6 = get_boolean( - os.environ.pop( - 'OS_PREFER_IPV6', client_config.get( - 'prefer_ipv6', client_config.get( - 'prefer-ipv6', True)))) - force_ipv4 = get_boolean( - os.environ.pop( - 'OS_FORCE_IPV4', client_config.get( - 'force_ipv4', client_config.get( - 'broken-ipv6', False)))) - - self.force_ipv4 = force_ipv4 - if not prefer_ipv6: - # this will only be false if someone set it explicitly - # honor their wishes - self.force_ipv4 = True - - # Next, process environment variables and add them to the mix - self.envvar_key = os.environ.pop('OS_CLOUD_NAME', 'envvars') - if self.envvar_key in self.cloud_config['clouds']: - raise exceptions.OpenStackConfigException( - '"{0}" defines a cloud named "{1}", but' - ' OS_CLOUD_NAME is also set to "{1}". Please rename' - ' either your environment based cloud, or one of your' - ' file-based clouds.'.format(self.config_filename, - self.envvar_key)) - # Pull out OS_CLOUD so that if it's the only thing set, do not - # make an envvars cloud - self.default_cloud = os.environ.pop('OS_CLOUD', None) - - envvars = _get_os_environ(envvar_prefix=envvar_prefix) - if envvars: - self.cloud_config['clouds'][self.envvar_key] = envvars - if not self.default_cloud: - self.default_cloud = self.envvar_key - - # Finally, fall through and make a cloud that starts with defaults - # because we need somewhere to put arguments, and there are neither - # config files or env vars - if not self.cloud_config['clouds']: - self.cloud_config = dict( - clouds=dict(defaults=dict(self.defaults))) - self.default_cloud = 'defaults' - - self._cache_expiration_time = 0 - self._cache_path = CACHE_PATH - self._cache_class = 'dogpile.cache.null' - self._cache_arguments = {} - self._cache_expiration = {} - if 'cache' in self.cloud_config: - cache_settings = self._normalize_keys(self.cloud_config['cache']) - - # expiration_time used to be 'max_age' but the dogpile setting - # is expiration_time. Support max_age for backwards compat. - self._cache_expiration_time = cache_settings.get( - 'expiration_time', cache_settings.get( - 'max_age', self._cache_expiration_time)) - - # If cache class is given, use that. If not, but if cache time - # is given, default to memory. Otherwise, default to nothing. - # to memory. - if self._cache_expiration_time: - self._cache_class = 'dogpile.cache.memory' - self._cache_class = self.cloud_config['cache'].get( - 'class', self._cache_class) - - self._cache_path = os.path.expanduser( - cache_settings.get('path', self._cache_path)) - self._cache_arguments = cache_settings.get( - 'arguments', self._cache_arguments) - self._cache_expiration = cache_settings.get( - 'expiration', self._cache_expiration) - - # Flag location to hold the peeked value of an argparse timeout value - self._argv_timeout = False - - def get_extra_config(self, key, defaults=None): - """Fetch an arbitrary extra chunk of config, laying in defaults. - - :param string key: name of the config section to fetch - :param dict defaults: (optional) default values to merge under the - found config - """ - if not defaults: - defaults = {} - return _merge_clouds( - self._normalize_keys(defaults), - self._normalize_keys(self.cloud_config.get(key, {}))) - - def _load_config_file(self): - return self._load_yaml_json_file(self._config_files) - - def _load_secure_file(self): - return self._load_yaml_json_file(self._secure_files) - - def _load_vendor_file(self): - return self._load_yaml_json_file(self._vendor_files) - - def _load_yaml_json_file(self, filelist): - for path in filelist: - if os.path.exists(path): - with open(path, 'r') as f: - if path.endswith('json'): - return path, json.load(f) - else: - return path, yaml.safe_load(f) - return (None, {}) - - def _normalize_keys(self, config): - new_config = {} - for key, value in config.items(): - key = key.replace('-', '_') - if isinstance(value, dict): - new_config[key] = self._normalize_keys(value) - elif isinstance(value, bool): - new_config[key] = value - elif isinstance(value, int) and key != 'verbose_level': - new_config[key] = str(value) - elif isinstance(value, float): - new_config[key] = str(value) - else: - new_config[key] = value - return new_config - - def get_cache_expiration_time(self): - return int(self._cache_expiration_time) - - def get_cache_interval(self): - return self.get_cache_expiration_time() - - def get_cache_max_age(self): - return self.get_cache_expiration_time() - - def get_cache_path(self): - return self._cache_path - - def get_cache_class(self): - return self._cache_class - - def get_cache_arguments(self): - return copy.deepcopy(self._cache_arguments) - - def get_cache_expiration(self): - return copy.deepcopy(self._cache_expiration) - - def _expand_region_name(self, region_name): - return {'name': region_name, 'values': {}} - - def _expand_regions(self, regions): - ret = [] - for region in regions: - if isinstance(region, dict): - ret.append(copy.deepcopy(region)) - else: - ret.append(self._expand_region_name(region)) - return ret - - def _get_regions(self, cloud): - if cloud not in self.cloud_config['clouds']: - return [self._expand_region_name('')] - regions = self._get_known_regions(cloud) - if not regions: - # We don't know of any regions use a workable default. - regions = [self._expand_region_name('')] - return regions - - def _get_known_regions(self, cloud): - config = self._normalize_keys(self.cloud_config['clouds'][cloud]) - if 'regions' in config: - return self._expand_regions(config['regions']) - elif 'region_name' in config: - regions = config['region_name'].split(',') - if len(regions) > 1: - warnings.warn( - "Comma separated lists in region_name are deprecated." - " Please use a yaml list in the regions" - " parameter in {0} instead.".format(self.config_filename)) - return self._expand_regions(regions) - else: - # crappit. we don't have a region defined. - new_cloud = dict() - our_cloud = self.cloud_config['clouds'].get(cloud, dict()) - self._expand_vendor_profile(cloud, new_cloud, our_cloud) - if 'regions' in new_cloud and new_cloud['regions']: - return self._expand_regions(new_cloud['regions']) - elif 'region_name' in new_cloud and new_cloud['region_name']: - return [self._expand_region_name(new_cloud['region_name'])] - - def _get_region(self, cloud=None, region_name=''): - if region_name is None: - region_name = '' - if not cloud: - return self._expand_region_name(region_name) - - regions = self._get_known_regions(cloud) - if not regions: - return self._expand_region_name(region_name) - - if not region_name: - return regions[0] - - for region in regions: - if region['name'] == region_name: - return region - - raise exceptions.OpenStackConfigException( - 'Region {region_name} is not a valid region name for cloud' - ' {cloud}. Valid choices are {region_list}. Please note that' - ' region names are case sensitive.'.format( - region_name=region_name, - region_list=','.join([r['name'] for r in regions]), - cloud=cloud)) - - def get_cloud_names(self): - return self.cloud_config['clouds'].keys() - - def _get_base_cloud_config(self, name): - cloud = dict() - - # Only validate cloud name if one was given - if name and name not in self.cloud_config['clouds']: - raise exceptions.OpenStackConfigException( - "Cloud {name} was not found.".format( - name=name)) - - our_cloud = self.cloud_config['clouds'].get(name, dict()) - - # Get the defaults - cloud.update(self.defaults) - self._expand_vendor_profile(name, cloud, our_cloud) - - if 'auth' not in cloud: - cloud['auth'] = dict() - - _auth_update(cloud, our_cloud) - if 'cloud' in cloud: - del cloud['cloud'] - - return cloud - - def _expand_vendor_profile(self, name, cloud, our_cloud): - # Expand a profile if it exists. 'cloud' is an old confusing name - # for this. - profile_name = our_cloud.get('profile', our_cloud.get('cloud', None)) - if profile_name and profile_name != self.envvar_key: - if 'cloud' in our_cloud: - warnings.warn( - "{0} use the keyword 'cloud' to reference a known " - "vendor profile. This has been deprecated in favor of the " - "'profile' keyword.".format(self.config_filename)) - vendor_filename, vendor_file = self._load_vendor_file() - if vendor_file and profile_name in vendor_file['public-clouds']: - _auth_update(cloud, vendor_file['public-clouds'][profile_name]) - else: - profile_data = vendors.get_profile(profile_name) - if profile_data: - status = profile_data.pop('status', 'active') - message = profile_data.pop('message', '') - if status == 'deprecated': - warnings.warn( - "{profile_name} is deprecated: {message}".format( - profile_name=profile_name, message=message)) - elif status == 'shutdown': - raise exceptions.OpenStackConfigException( - "{profile_name} references a cloud that no longer" - " exists: {message}".format( - profile_name=profile_name, message=message)) - _auth_update(cloud, profile_data) - else: - # Can't find the requested vendor config, go about business - warnings.warn("Couldn't find the vendor profile '{0}', for" - " the cloud '{1}'".format(profile_name, - name)) - - def _project_scoped(self, cloud): - return ('project_id' in cloud or 'project_name' in cloud - or 'project_id' in cloud['auth'] - or 'project_name' in cloud['auth']) - - def _validate_networks(self, networks, key): - value = None - for net in networks: - if value and net[key]: - raise exceptions.OpenStackConfigException( - "Duplicate network entries for {key}: {net1} and {net2}." - " Only one network can be flagged with {key}".format( - key=key, - net1=value['name'], - net2=net['name'])) - if not value and net[key]: - value = net - - def _fix_backwards_networks(self, cloud): - # Leave the external_network and internal_network keys in the - # dict because consuming code might be expecting them. - networks = [] - # Normalize existing network entries - for net in cloud.get('networks', []): - name = net.get('name') - if not name: - raise exceptions.OpenStackConfigException( - 'Entry in network list is missing required field "name".') - network = dict( - name=name, - routes_externally=get_boolean(net.get('routes_externally')), - nat_destination=get_boolean(net.get('nat_destination')), - default_interface=get_boolean(net.get('default_interface')), - ) - networks.append(network) - - for key in ('external_network', 'internal_network'): - external = key.startswith('external') - if key in cloud and 'networks' in cloud: - raise exceptions.OpenStackConfigException( - "Both {key} and networks were specified in the config." - " Please remove {key} from the config and use the network" - " list to configure network behavior.".format(key=key)) - if key in cloud: - warnings.warn( - "{key} is deprecated. Please replace with an entry in" - " a dict inside of the networks list with name: {name}" - " and routes_externally: {external}".format( - key=key, name=cloud[key], external=external)) - networks.append(dict( - name=cloud[key], - routes_externally=external, - nat_destination=not external, - default_interface=external)) - - # Validate that we don't have duplicates - self._validate_networks(networks, 'nat_destination') - self._validate_networks(networks, 'default_interface') - - cloud['networks'] = networks - return cloud - - def _handle_domain_id(self, cloud): - # Allow people to just specify domain once if it's the same - mappings = { - 'domain_id': ('user_domain_id', 'project_domain_id'), - 'domain_name': ('user_domain_name', 'project_domain_name'), - } - for target_key, possible_values in mappings.items(): - if not self._project_scoped(cloud): - if target_key in cloud and target_key not in cloud['auth']: - cloud['auth'][target_key] = cloud.pop(target_key) - continue - for key in possible_values: - if target_key in cloud['auth'] and key not in cloud['auth']: - cloud['auth'][key] = cloud['auth'][target_key] - cloud.pop(target_key, None) - cloud['auth'].pop(target_key, None) - return cloud - - def _fix_backwards_project(self, cloud): - # Do the lists backwards so that project_name is the ultimate winner - # Also handle moving domain names into auth so that domain mapping - # is easier - mappings = { - 'domain_id': ('domain_id', 'domain-id'), - 'domain_name': ('domain_name', 'domain-name'), - 'user_domain_id': ('user_domain_id', 'user-domain-id'), - 'user_domain_name': ('user_domain_name', 'user-domain-name'), - 'project_domain_id': ('project_domain_id', 'project-domain-id'), - 'project_domain_name': ( - 'project_domain_name', 'project-domain-name'), - 'token': ('auth-token', 'auth_token', 'token'), - } - if cloud.get('auth_type', None) == 'v2password': - # If v2password is explcitly requested, this is to deal with old - # clouds. That's fine - we need to map settings in the opposite - # direction - mappings['tenant_id'] = ( - 'project_id', 'project-id', 'tenant_id', 'tenant-id') - mappings['tenant_name'] = ( - 'project_name', 'project-name', 'tenant_name', 'tenant-name') - else: - mappings['project_id'] = ( - 'tenant_id', 'tenant-id', 'project_id', 'project-id') - mappings['project_name'] = ( - 'tenant_name', 'tenant-name', 'project_name', 'project-name') - for target_key, possible_values in mappings.items(): - target = None - for key in possible_values: - if key in cloud: - target = str(cloud[key]) - del cloud[key] - if key in cloud['auth']: - target = str(cloud['auth'][key]) - del cloud['auth'][key] - if target: - cloud['auth'][target_key] = target - return cloud - - def _fix_backwards_auth_plugin(self, cloud): - # Do the lists backwards so that auth_type is the ultimate winner - mappings = { - 'auth_type': ('auth_plugin', 'auth_type'), - } - for target_key, possible_values in mappings.items(): - target = None - for key in possible_values: - if key in cloud: - target = cloud[key] - del cloud[key] - cloud[target_key] = target - # Because we force alignment to v3 nouns, we want to force - # use of the auth plugin that can do auto-selection and dealing - # with that based on auth parameters. v2password is basically - # completely broken - return cloud - - def register_argparse_arguments(self, parser, argv, service_keys=[]): - """Register all of the common argparse options needed. - - Given an argparse parser, register the keystoneauth Session arguments, - the keystoneauth Auth Plugin Options and os-cloud. Also, peek in the - argv to see if all of the auth plugin options should be registered - or merely the ones already configured. - :param argparse.ArgumentParser: parser to attach argparse options to - :param list argv: the arguments provided to the application - :param string service_keys: Service or list of services this argparse - should be specialized for, if known. - The first item in the list will be used - as the default value for service_type - (optional) - - :raises exceptions.OpenStackConfigException if an invalid auth-type - is requested - """ - - # Fix argv in place - mapping any keys with embedded _ in them to - - _fix_argv(argv) - - local_parser = argparse_mod.ArgumentParser(add_help=False) - - for p in (parser, local_parser): - p.add_argument( - '--os-cloud', - metavar='', - default=os.environ.get('OS_CLOUD', None), - help='Named cloud to connect to') - - # we need to peek to see if timeout was actually passed, since - # the keystoneauth declaration of it has a default, which means - # we have no clue if the value we get is from the ksa default - # for from the user passing it explicitly. We'll stash it for later - local_parser.add_argument('--timeout', metavar='') - - # We need for get_one_cloud to be able to peek at whether a token - # was passed so that we can swap the default from password to - # token if it was. And we need to also peek for --os-auth-token - # for novaclient backwards compat - local_parser.add_argument('--os-token') - local_parser.add_argument('--os-auth-token') - - # Peek into the future and see if we have an auth-type set in - # config AND a cloud set, so that we know which command line - # arguments to register and show to the user (the user may want - # to say something like: - # openstack --os-cloud=foo --os-oidctoken=bar - # although I think that user is the cause of my personal pain - options, _args = local_parser.parse_known_args(argv) - if options.timeout: - self._argv_timeout = True - - # validate = False because we're not _actually_ loading here - # we're only peeking, so it's the wrong time to assert that - # the rest of the arguments given are invalid for the plugin - # chosen (for instance, --help may be requested, so that the - # user can see what options he may want to give - cloud = self.get_one_cloud(argparse=options, validate=False) - default_auth_type = cloud.config['auth_type'] - - try: - loading.register_auth_argparse_arguments( - parser, argv, default=default_auth_type) - except Exception: - # Hidiing the keystoneauth exception because we're not actually - # loading the auth plugin at this point, so the error message - # from it doesn't actually make sense to os-client-config users - options, _args = parser.parse_known_args(argv) - plugin_names = loading.get_available_plugin_names() - raise exceptions.OpenStackConfigException( - "An invalid auth-type was specified: {auth_type}." - " Valid choices are: {plugin_names}.".format( - auth_type=options.os_auth_type, - plugin_names=",".join(plugin_names))) - - if service_keys: - primary_service = service_keys[0] - else: - primary_service = None - loading.register_session_argparse_arguments(parser) - adapter.register_adapter_argparse_arguments( - parser, service_type=primary_service) - for service_key in service_keys: - # legacy clients have un-prefixed api-version options - parser.add_argument( - '--{service_key}-api-version'.format( - service_key=service_key.replace('_', '-'), - help=argparse_mod.SUPPRESS)) - adapter.register_service_adapter_argparse_arguments( - parser, service_type=service_key) - - # Backwards compat options for legacy clients - parser.add_argument('--http-timeout', help=argparse_mod.SUPPRESS) - parser.add_argument('--os-endpoint-type', help=argparse_mod.SUPPRESS) - parser.add_argument('--endpoint-type', help=argparse_mod.SUPPRESS) - - def _fix_backwards_interface(self, cloud): - new_cloud = {} - for key in cloud.keys(): - if key.endswith('endpoint_type'): - target_key = key.replace('endpoint_type', 'interface') - else: - target_key = key - new_cloud[target_key] = cloud[key] - return new_cloud - - def _fix_backwards_api_timeout(self, cloud): - new_cloud = {} - # requests can only have one timeout, which means that in a single - # cloud there is no point in different timeout values. However, - # for some reason many of the legacy clients decided to shove their - # service name in to the arg name for reasons surpassin sanity. If - # we find any values that are not api_timeout, overwrite api_timeout - # with the value - service_timeout = None - for key in cloud.keys(): - if key.endswith('timeout') and not ( - key == 'timeout' or key == 'api_timeout'): - service_timeout = cloud[key] - else: - new_cloud[key] = cloud[key] - if service_timeout is not None: - new_cloud['api_timeout'] = service_timeout - # The common argparse arg from keystoneauth is called timeout, but - # os-client-config expects it to be called api_timeout - if self._argv_timeout: - if 'timeout' in new_cloud and new_cloud['timeout']: - new_cloud['api_timeout'] = new_cloud.pop('timeout') - return new_cloud - - def get_all_clouds(self): - - clouds = [] - - for cloud in self.get_cloud_names(): - for region in self._get_regions(cloud): - if region: - clouds.append(self.get_one_cloud( - cloud, region_name=region['name'])) - return clouds - - def _fix_args(self, args=None, argparse=None): - """Massage the passed-in options - - Replace - with _ and strip os_ prefixes. - - Convert an argparse Namespace object to a dict, removing values - that are either None or ''. - """ - if not args: - args = {} - - if argparse: - # Convert the passed-in Namespace - o_dict = vars(argparse) - parsed_args = dict() - for k in o_dict: - if o_dict[k] is not None and o_dict[k] != '': - parsed_args[k] = o_dict[k] - args.update(parsed_args) - - os_args = dict() - new_args = dict() - for (key, val) in iter(args.items()): - if type(args[key]) == dict: - # dive into the auth dict - new_args[key] = self._fix_args(args[key]) - continue - - key = key.replace('-', '_') - if key.startswith('os_'): - os_args[key[3:]] = val - else: - new_args[key] = val - new_args.update(os_args) - return new_args - - def _find_winning_auth_value(self, opt, config): - opt_name = opt.name.replace('-', '_') - if opt_name in config: - return config[opt_name] - else: - deprecated = getattr(opt, 'deprecated', getattr( - opt, 'deprecated_opts', [])) - for d_opt in deprecated: - d_opt_name = d_opt.name.replace('-', '_') - if d_opt_name in config: - return config[d_opt_name] - - def auth_config_hook(self, config): - """Allow examination of config values before loading auth plugin - - OpenStackClient will override this to perform additional checks - on auth_type. - """ - return config - - def _get_auth_loader(self, config): - # Re-use the admin_token plugin for the "None" plugin - # since it does not look up endpoints or tokens but rather - # does a passthrough. This is useful for things like Ironic - # that have a keystoneless operational mode, but means we're - # still dealing with a keystoneauth Session object, so all the - # _other_ things (SSL arg handling, timeout) all work consistently - if config['auth_type'] in (None, "None", ''): - config['auth_type'] = 'admin_token' - # Set to notused rather than None because validate_auth will - # strip the value if it's actually python None - config['auth']['token'] = 'notused' - return loading.get_plugin_loader(config['auth_type']) - - def _validate_auth_ksc(self, config, cloud, fixed_argparse): - try: - import keystoneclient.auth as ksc_auth - except ImportError: - return config - - # May throw a keystoneclient.exceptions.NoMatchingPlugin - plugin_options = ksc_auth.get_plugin_class( - config['auth_type']).get_options() - - for p_opt in plugin_options: - # if it's in argparse, it was passed on the command line and wins - # if it's in config.auth, win, kill it from config dict - # if it's in config and not in config.auth, move it - # deprecated loses to current - # provided beats default, deprecated or not - winning_value = self._find_winning_auth_value( - p_opt, fixed_argparse) - if winning_value: - found_in_argparse = True - else: - found_in_argparse = False - winning_value = self._find_winning_auth_value( - p_opt, config['auth']) - if not winning_value: - winning_value = self._find_winning_auth_value( - p_opt, config) - - # if the plugin tells us that this value is required - # then error if it's doesn't exist now - if not winning_value and p_opt.required: - raise exceptions.OpenStackConfigException( - 'Unable to find auth information for cloud' - ' {cloud} in config files {files}' - ' or environment variables. Missing value {auth_key}' - ' required for auth plugin {plugin}'.format( - cloud=cloud, files=','.join(self._config_files), - auth_key=p_opt.name, plugin=config.get('auth_type'))) - - # Clean up after ourselves - for opt in [p_opt.name] + [o.name for o in p_opt.deprecated_opts]: - opt = opt.replace('-', '_') - # don't do this if the value came from argparse, because we - # don't (yet) know if the value in not-auth came from argparse - # overlay or from someone passing in a dict to kwargs - # TODO(mordred) Fix that data path too - if not found_in_argparse: - config.pop(opt, None) - config['auth'].pop(opt, None) - - if winning_value: - # Prefer the plugin configuration dest value if the value's key - # is marked as depreciated. - if p_opt.dest is None: - config['auth'][p_opt.name.replace('-', '_')] = ( - winning_value) - else: - config['auth'][p_opt.dest] = winning_value - - return config - - def _validate_auth(self, config, loader, fixed_argparse): - # May throw a keystoneauth1.exceptions.NoMatchingPlugin - - plugin_options = loader.get_options() - - for p_opt in plugin_options: - # if it's in argparse, it was passed on the command line and wins - # if it's in config.auth, win, kill it from config dict - # if it's in config and not in config.auth, move it - # deprecated loses to current - # provided beats default, deprecated or not - winning_value = self._find_winning_auth_value( - p_opt, fixed_argparse) - if winning_value: - found_in_argparse = True - else: - found_in_argparse = False - winning_value = self._find_winning_auth_value( - p_opt, config['auth']) - if not winning_value: - winning_value = self._find_winning_auth_value( - p_opt, config) - - # Clean up after ourselves - for opt in [p_opt.name] + [o.name for o in p_opt.deprecated]: - opt = opt.replace('-', '_') - # don't do this if the value came from argparse, because we - # don't (yet) know if the value in not-auth came from argparse - # overlay or from someone passing in a dict to kwargs - # TODO(mordred) Fix that data path too - if not found_in_argparse: - config.pop(opt, None) - config['auth'].pop(opt, None) - - if winning_value: - # Prefer the plugin configuration dest value if the value's key - # is marked as depreciated. - if p_opt.dest is None: - config['auth'][p_opt.name.replace('-', '_')] = ( - winning_value) - else: - config['auth'][p_opt.dest] = winning_value - - return config - - def magic_fixes(self, config): - """Perform the set of magic argument fixups""" - - # Infer token plugin if a token was given - if (('auth' in config and 'token' in config['auth']) or - ('auth_token' in config and config['auth_token']) or - ('token' in config and config['token'])): - config.setdefault('token', config.pop('auth_token', None)) - - # These backwards compat values are only set via argparse. If it's - # there, it's because it was passed in explicitly, and should win - config = self._fix_backwards_api_timeout(config) - if 'endpoint_type' in config: - config['interface'] = config.pop('endpoint_type') - - config = self._fix_backwards_auth_plugin(config) - config = self._fix_backwards_project(config) - config = self._fix_backwards_interface(config) - config = self._fix_backwards_networks(config) - config = self._handle_domain_id(config) - - for key in BOOL_KEYS: - if key in config: - if type(config[key]) is not bool: - config[key] = get_boolean(config[key]) - - # TODO(mordred): Special casing auth_url here. We should - # come back to this betterer later so that it's - # more generalized - if 'auth' in config and 'auth_url' in config['auth']: - config['auth']['auth_url'] = config['auth']['auth_url'].format( - **config) - - return config - - def get_one_cloud(self, cloud=None, validate=True, - argparse=None, **kwargs): - """Retrieve a single cloud configuration and merge additional options - - :param string cloud: - The name of the configuration to load from clouds.yaml - :param boolean validate: - Validate the config. Setting this to False causes no auth plugin - to be created. It's really only useful for testing. - :param Namespace argparse: - An argparse Namespace object; allows direct passing in of - argparse options to be added to the cloud config. Values - of None and '' will be removed. - :param region_name: Name of the region of the cloud. - :param kwargs: Additional configuration options - - :raises: keystoneauth1.exceptions.MissingRequiredOptions - on missing required auth parameters - """ - - args = self._fix_args(kwargs, argparse=argparse) - # Run the fix just for argparse by itself. We need to - # have a copy of the argparse options separately from - # any merged copied later in validate_auth so that we - # can determine precedence - fixed_argparse = self._fix_args(argparse=argparse) - - if cloud is None: - if 'cloud' in args: - cloud = args['cloud'] - else: - cloud = self.default_cloud - - config = self._get_base_cloud_config(cloud) - - # Get region specific settings - if 'region_name' not in args: - args['region_name'] = '' - region = self._get_region(cloud=cloud, region_name=args['region_name']) - args['region_name'] = region['name'] - region_args = copy.deepcopy(region['values']) - - # Regions is a list that we can use to create a list of cloud/region - # objects. It does not belong in the single-cloud dict - config.pop('regions', None) - - # Can't just do update, because None values take over - for arg_list in region_args, args: - for (key, val) in iter(arg_list.items()): - if val is not None: - if key == 'auth' and config[key] is not None: - config[key] = _auth_update(config[key], val) - else: - config[key] = val - - config = self.magic_fixes(config) - - # NOTE(dtroyer): OSC needs a hook into the auth args before the - # plugin is loaded in order to maintain backward- - # compatible behaviour - config = self.auth_config_hook(config) - - if validate: - try: - loader = self._get_auth_loader(config) - config = self._validate_auth(config, loader, fixed_argparse) - auth_plugin = loader.load_from_options(**config['auth']) - except Exception as e: - # We WANT the ksa exception normally - # but OSC can't handle it right now, so we try deferring - # to ksc. If that ALSO fails, it means there is likely - # a deeper issue, so we assume the ksa error was correct - self.log.debug("Deferring keystone exception: {e}".format(e=e)) - auth_plugin = None - try: - config = self._validate_auth_ksc( - config, cloud, fixed_argparse) - except Exception: - raise e - else: - auth_plugin = None - - # If any of the defaults reference other values, we need to expand - for (key, value) in config.items(): - if hasattr(value, 'format'): - config[key] = value.format(**config) - - force_ipv4 = config.pop('force_ipv4', self.force_ipv4) - prefer_ipv6 = config.pop('prefer_ipv6', True) - if not prefer_ipv6: - force_ipv4 = True - - if cloud is None: - cloud_name = '' - else: - cloud_name = str(cloud) - return cloud_config.CloudConfig( - name=cloud_name, region=config['region_name'], - config=self._normalize_keys(config), - force_ipv4=force_ipv4, - auth_plugin=auth_plugin, - openstack_config=self - ) - - @staticmethod - def set_one_cloud(config_file, cloud, set_config=None): - """Set a single cloud configuration. - - :param string config_file: - The path to the config file to edit. If this file does not exist - it will be created. - :param string cloud: - The name of the configuration to save to clouds.yaml - :param dict set_config: Configuration options to be set - """ - - set_config = set_config or {} - cur_config = {} - try: - with open(config_file) as fh: - cur_config = yaml.safe_load(fh) - except IOError as e: - # Not no such file - if e.errno != 2: - raise - pass - - clouds_config = cur_config.get('clouds', {}) - cloud_config = _auth_update(clouds_config.get(cloud, {}), set_config) - clouds_config[cloud] = cloud_config - cur_config['clouds'] = clouds_config - - with open(config_file, 'w') as fh: - yaml.safe_dump(cur_config, fh, default_flow_style=False) - -if __name__ == '__main__': - config = OpenStackConfig().get_all_clouds() - for cloud in config: - print_cloud = False - if len(sys.argv) == 1: - print_cloud = True - elif len(sys.argv) == 3 and ( - sys.argv[1] == cloud.name and sys.argv[2] == cloud.region): - print_cloud = True - elif len(sys.argv) == 2 and ( - sys.argv[1] == cloud.name): - print_cloud = True - - if print_cloud: - print(cloud.name, cloud.region, cloud.config) diff --git a/os_client_config/constructors.json b/os_client_config/constructors.json deleted file mode 100644 index 89c844c..0000000 --- a/os_client_config/constructors.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compute": "novaclient.client.Client", - "database": "troveclient.client.Client", - "identity": "keystoneclient.client.Client", - "image": "glanceclient.Client", - "key-manager": "barbicanclient.client.Client", - "metering": "ceilometerclient.client.Client", - "network": "neutronclient.neutron.client.Client", - "object-store": "swiftclient.client.Connection", - "orchestration": "heatclient.client.Client", - "volume": "cinderclient.client.Client" -} diff --git a/os_client_config/constructors.py b/os_client_config/constructors.py deleted file mode 100644 index e88ac92..0000000 --- a/os_client_config/constructors.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - -import json -import os - -_json_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), 'constructors.json') -_class_mapping = None - - -def get_constructor_mapping(): - global _class_mapping - if not _class_mapping: - with open(_json_path, 'r') as json_file: - _class_mapping = json.load(json_file) - return _class_mapping diff --git a/os_client_config/defaults.json b/os_client_config/defaults.json deleted file mode 100644 index ba8bf39..0000000 --- a/os_client_config/defaults.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "auth_type": "password", - "baremetal_api_version": "1", - "container_api_version": "1", - "compute_api_version": "2", - "database_api_version": "1.0", - "disable_vendor_agent": {}, - "dns_api_version": "2", - "interface": "public", - "floating_ip_source": "neutron", - "identity_api_version": "2.0", - "image_api_use_tasks": false, - "image_api_version": "2", - "image_format": "qcow2", - "key_manager_api_version": "v1", - "message": "", - "metering_api_version": "2", - "network_api_version": "2", - "object_store_api_version": "1", - "orchestration_api_version": "1", - "secgroup_source": "neutron", - "status": "active", - "volume_api_version": "2" -} diff --git a/os_client_config/defaults.py b/os_client_config/defaults.py deleted file mode 100644 index c10358a..0000000 --- a/os_client_config/defaults.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - -import json -import os - -_json_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), 'defaults.json') -_defaults = None - - -def get_defaults(): - global _defaults - if not _defaults: - # Python language specific defaults - # These are defaults related to use of python libraries, they are - # not qualities of a cloud. - _defaults = dict( - api_timeout=None, - verify=True, - cacert=None, - cert=None, - key=None, - ) - with open(_json_path, 'r') as json_file: - updates = json.load(json_file) - if updates is not None: - _defaults.update(updates) - - return _defaults.copy() diff --git a/os_client_config/exceptions.py b/os_client_config/exceptions.py deleted file mode 100644 index ab78dc2..0000000 --- a/os_client_config/exceptions.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - - -class OpenStackConfigException(Exception): - """Something went wrong with parsing your OpenStack Config.""" diff --git a/os_client_config/schema.json b/os_client_config/schema.json deleted file mode 100644 index 8110d58..0000000 --- a/os_client_config/schema.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "https://git.openstack.org/cgit/openstack/cloud-data/plain/schema.json#", - "type": "object", - "properties": { - "auth_type": { - "name": "Auth Type", - "description": "Name of authentication plugin to be used", - "default": "password", - "type": "string" - }, - "disable_vendor_agent": { - "name": "Disable Vendor Agent Properties", - "description": "Image properties required to disable vendor agent", - "type": "object", - "properties": {} - }, - "floating_ip_source": { - "name": "Floating IP Source", - "description": "Which service provides Floating IPs", - "enum": [ "neutron", "nova", "None" ], - "default": "neutron" - }, - "image_api_use_tasks": { - "name": "Image Task API", - "description": "Does the cloud require the Image Task API", - "default": false, - "type": "boolean" - }, - "image_format": { - "name": "Image Format", - "description": "Format for uploaded Images", - "default": "qcow2", - "type": "string" - }, - "interface": { - "name": "API Interface", - "description": "Which API Interface should connections hit", - "default": "public", - "enum": [ "public", "internal", "admin" ] - }, - "secgroup_source": { - "name": "Security Group Source", - "description": "Which service provides security groups", - "default": "neutron", - "enum": [ "neutron", "nova", "None" ] - }, - "baremetal_api_version": { - "name": "Baremetal API Service Type", - "description": "Baremetal API Service Type", - "default": "1", - "type": "string" - }, - "compute_api_version": { - "name": "Compute API Version", - "description": "Compute API Version", - "default": "2", - "type": "string" - }, - "database_api_version": { - "name": "Database API Version", - "description": "Database API Version", - "default": "1.0", - "type": "string" - }, - "dns_api_version": { - "name": "DNS API Version", - "description": "DNS API Version", - "default": "2", - "type": "string" - }, - "identity_api_version": { - "name": "Identity API Version", - "description": "Identity API Version", - "default": "2", - "type": "string" - }, - "image_api_version": { - "name": "Image API Version", - "description": "Image API Version", - "default": "1", - "type": "string" - }, - "network_api_version": { - "name": "Network API Version", - "description": "Network API Version", - "default": "2", - "type": "string" - }, - "object_store_api_version": { - "name": "Object Storage API Version", - "description": "Object Storage API Version", - "default": "1", - "type": "string" - }, - "volume_api_version": { - "name": "Volume API Version", - "description": "Volume API Version", - "default": "2", - "type": "string" - } - }, - "required": [ - "auth_type", - "baremetal_api_version", - "compute_api_version", - "database_api_version", - "disable_vendor_agent", - "dns_api_version", - "floating_ip_source", - "identity_api_version", - "image_api_use_tasks", - "image_api_version", - "image_format", - "interface", - "network_api_version", - "object_store_api_version", - "secgroup_source", - "volume_api_version" - ] -} diff --git a/os_client_config/tests/__init__.py b/os_client_config/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/os_client_config/tests/base.py b/os_client_config/tests/base.py deleted file mode 100644 index d046a94..0000000 --- a/os_client_config/tests/base.py +++ /dev/null @@ -1,234 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2010-2011 OpenStack Foundation -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - - -import copy -import os -import tempfile - -from os_client_config import cloud_config - -import extras -import fixtures -from oslotest import base -import yaml - - -VENDOR_CONF = { - 'public-clouds': { - '_test_cloud_in_our_cloud': { - 'auth': { - 'auth_url': 'http://example.com/v2', - 'username': 'testotheruser', - 'project_name': 'testproject', - }, - }, - } -} -USER_CONF = { - 'cache': { - 'max_age': '1', - 'expiration': { - 'server': 5, - 'image': '7', - }, - }, - 'client': { - 'force_ipv4': True, - }, - 'clouds': { - '_test-cloud_': { - 'profile': '_test_cloud_in_our_cloud', - 'auth': { - 'auth_url': 'http://example.com/v2', - 'username': 'testuser', - 'password': 'testpass', - }, - 'region_name': 'test-region', - }, - '_test_cloud_no_vendor': { - 'profile': '_test_non_existant_cloud', - 'auth': { - 'auth_url': 'http://example.com/v2', - 'username': 'testuser', - 'project_name': 'testproject', - }, - 'region-name': 'test-region', - }, - '_test-cloud-int-project_': { - 'auth': { - 'username': 'testuser', - 'password': 'testpass', - 'domain_id': 'awesome-domain', - 'project_id': 12345, - 'auth_url': 'http://example.com/v2', - }, - 'region_name': 'test-region', - }, - '_test-cloud-domain-id_': { - 'auth': { - 'username': 'testuser', - 'password': 'testpass', - 'project_id': 12345, - 'auth_url': 'http://example.com/v2', - 'domain_id': '6789', - 'project_domain_id': '123456789', - }, - 'region_name': 'test-region', - }, - '_test-cloud-networks_': { - 'auth': { - 'username': 'testuser', - 'password': 'testpass', - 'project_id': 12345, - 'auth_url': 'http://example.com/v2', - 'domain_id': '6789', - 'project_domain_id': '123456789', - }, - 'networks': [{ - 'name': 'a-public', - 'routes_externally': True, - }, { - 'name': 'another-public', - 'routes_externally': True, - 'default_interface': True, - }, { - 'name': 'a-private', - 'routes_externally': False, - }, { - 'name': 'another-private', - 'routes_externally': False, - 'nat_destination': True, - }], - 'region_name': 'test-region', - }, - '_test_cloud_regions': { - 'auth': { - 'username': 'testuser', - 'password': 'testpass', - 'project-id': 'testproject', - 'auth_url': 'http://example.com/v2', - }, - 'regions': [ - { - 'name': 'region1', - 'values': { - 'external_network': 'region1-network', - } - }, - { - 'name': 'region2', - 'values': { - 'external_network': 'my-network', - } - } - ], - }, - '_test_cloud_hyphenated': { - 'auth': { - 'username': 'testuser', - 'password': 'testpass', - 'project-id': '12345', - 'auth_url': 'http://example.com/v2', - }, - 'region_name': 'test-region', - }, - '_test-cloud_no_region': { - 'profile': '_test_cloud_in_our_cloud', - 'auth': { - 'auth_url': 'http://example.com/v2', - 'username': 'testuser', - 'password': 'testpass', - }, - }, - '_test-cloud-domain-scoped_': { - 'auth': { - 'auth_url': 'http://example.com/v2', - 'username': 'testuser', - 'password': 'testpass', - 'domain-id': '12345', - }, - }, - }, - 'ansible': { - 'expand-hostvars': False, - 'use_hostnames': True, - }, -} -SECURE_CONF = { - 'clouds': { - '_test_cloud_no_vendor': { - 'auth': { - 'password': 'testpass', - }, - } - } -} -NO_CONF = { - 'cache': {'max_age': 1}, -} - - -def _write_yaml(obj): - # Assume NestedTempfile so we don't have to cleanup - with tempfile.NamedTemporaryFile(delete=False) as obj_yaml: - obj_yaml.write(yaml.safe_dump(obj).encode('utf-8')) - return obj_yaml.name - - -class TestCase(base.BaseTestCase): - """Test case base class for all unit tests.""" - - def setUp(self): - super(TestCase, self).setUp() - - self.useFixture(fixtures.NestedTempfile()) - conf = copy.deepcopy(USER_CONF) - tdir = self.useFixture(fixtures.TempDir()) - conf['cache']['path'] = tdir.path - self.cloud_yaml = _write_yaml(conf) - self.secure_yaml = _write_yaml(SECURE_CONF) - self.vendor_yaml = _write_yaml(VENDOR_CONF) - self.no_yaml = _write_yaml(NO_CONF) - - # Isolate the test runs from the environment - # Do this as two loops because you can't modify the dict in a loop - # over the dict in 3.4 - keys_to_isolate = [] - for env in os.environ.keys(): - if env.startswith('OS_'): - keys_to_isolate.append(env) - for env in keys_to_isolate: - self.useFixture(fixtures.EnvironmentVariable(env)) - - def _assert_cloud_details(self, cc): - self.assertIsInstance(cc, cloud_config.CloudConfig) - self.assertTrue(extras.safe_hasattr(cc, 'auth')) - self.assertIsInstance(cc.auth, dict) - self.assertIsNone(cc.cloud) - self.assertIn('username', cc.auth) - self.assertEqual('testuser', cc.auth['username']) - self.assertEqual('testpass', cc.auth['password']) - self.assertFalse(cc.config['image_api_use_tasks']) - self.assertTrue('project_name' in cc.auth or 'project_id' in cc.auth) - if 'project_name' in cc.auth: - self.assertEqual('testproject', cc.auth['project_name']) - elif 'project_id' in cc.auth: - self.assertEqual('testproject', cc.auth['project_id']) - self.assertEqual(cc.get_cache_expiration_time(), 1) - self.assertEqual(cc.get_cache_resource_expiration('server'), 5.0) - self.assertEqual(cc.get_cache_resource_expiration('image'), 7.0) diff --git a/os_client_config/tests/test_cloud_config.py b/os_client_config/tests/test_cloud_config.py deleted file mode 100644 index 7a8b77a..0000000 --- a/os_client_config/tests/test_cloud_config.py +++ /dev/null @@ -1,604 +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. - -import copy - -from keystoneauth1 import plugin as ksa_plugin -from keystoneauth1 import session as ksa_session -import mock - -from os_client_config import cloud_config -from os_client_config import defaults -from os_client_config import exceptions -from os_client_config.tests import base - - -fake_config_dict = {'a': 1, 'os_b': 2, 'c': 3, 'os_c': 4} -fake_services_dict = { - 'compute_api_version': '2', - 'compute_endpoint_override': 'http://compute.example.com', - 'compute_region_name': 'region-bl', - 'telemetry_endpoint': 'http://telemetry.example.com', - 'interface': 'public', - 'image_service_type': 'mage', - 'identity_interface': 'admin', - 'identity_service_name': 'locks', - 'volume_api_version': '1', - 'auth': {'password': 'hunter2', 'username': 'AzureDiamond'}, -} - - -class TestCloudConfig(base.TestCase): - - def test_arbitrary_attributes(self): - cc = cloud_config.CloudConfig("test1", "region-al", fake_config_dict) - self.assertEqual("test1", cc.name) - self.assertEqual("region-al", cc.region) - - # Look up straight value - self.assertEqual(1, cc.a) - - # Look up prefixed attribute, fail - returns None - self.assertIsNone(cc.os_b) - - # Look up straight value, then prefixed value - self.assertEqual(3, cc.c) - self.assertEqual(3, cc.os_c) - - # Lookup mystery attribute - self.assertIsNone(cc.x) - - # Test default ipv6 - self.assertFalse(cc.force_ipv4) - - def test_iteration(self): - cc = cloud_config.CloudConfig("test1", "region-al", fake_config_dict) - self.assertTrue('a' in cc) - self.assertFalse('x' in cc) - - def test_equality(self): - cc1 = cloud_config.CloudConfig("test1", "region-al", fake_config_dict) - cc2 = cloud_config.CloudConfig("test1", "region-al", fake_config_dict) - self.assertEqual(cc1, cc2) - - def test_inequality(self): - cc1 = cloud_config.CloudConfig("test1", "region-al", fake_config_dict) - - cc2 = cloud_config.CloudConfig("test2", "region-al", fake_config_dict) - self.assertNotEqual(cc1, cc2) - - cc2 = cloud_config.CloudConfig("test1", "region-xx", fake_config_dict) - self.assertNotEqual(cc1, cc2) - - cc2 = cloud_config.CloudConfig("test1", "region-al", {}) - self.assertNotEqual(cc1, cc2) - - def test_verify(self): - config_dict = copy.deepcopy(fake_config_dict) - config_dict['cacert'] = None - - config_dict['verify'] = False - cc = cloud_config.CloudConfig("test1", "region-xx", config_dict) - (verify, cert) = cc.get_requests_verify_args() - self.assertFalse(verify) - - config_dict['verify'] = True - cc = cloud_config.CloudConfig("test1", "region-xx", config_dict) - (verify, cert) = cc.get_requests_verify_args() - self.assertTrue(verify) - - def test_verify_cacert(self): - config_dict = copy.deepcopy(fake_config_dict) - config_dict['cacert'] = "certfile" - - config_dict['verify'] = False - cc = cloud_config.CloudConfig("test1", "region-xx", config_dict) - (verify, cert) = cc.get_requests_verify_args() - self.assertFalse(verify) - - config_dict['verify'] = True - cc = cloud_config.CloudConfig("test1", "region-xx", config_dict) - (verify, cert) = cc.get_requests_verify_args() - self.assertEqual("certfile", verify) - - def test_cert_with_key(self): - config_dict = copy.deepcopy(fake_config_dict) - config_dict['cacert'] = None - config_dict['verify'] = False - - config_dict['cert'] = 'cert' - config_dict['key'] = 'key' - - cc = cloud_config.CloudConfig("test1", "region-xx", config_dict) - (verify, cert) = cc.get_requests_verify_args() - self.assertEqual(("cert", "key"), cert) - - def test_ipv6(self): - cc = cloud_config.CloudConfig( - "test1", "region-al", fake_config_dict, force_ipv4=True) - self.assertTrue(cc.force_ipv4) - - def test_getters(self): - cc = cloud_config.CloudConfig("test1", "region-al", fake_services_dict) - - self.assertEqual(['compute', 'identity', 'image', 'volume'], - sorted(cc.get_services())) - self.assertEqual({'password': 'hunter2', 'username': 'AzureDiamond'}, - cc.get_auth_args()) - self.assertEqual('public', cc.get_interface()) - self.assertEqual('public', cc.get_interface('compute')) - self.assertEqual('admin', cc.get_interface('identity')) - self.assertEqual('region-al', cc.get_region_name()) - self.assertEqual('region-al', cc.get_region_name('image')) - self.assertEqual('region-bl', cc.get_region_name('compute')) - self.assertIsNone(cc.get_api_version('image')) - self.assertEqual('2', cc.get_api_version('compute')) - self.assertEqual('mage', cc.get_service_type('image')) - self.assertEqual('compute', cc.get_service_type('compute')) - self.assertEqual('1', cc.get_api_version('volume')) - self.assertEqual('volume', cc.get_service_type('volume')) - self.assertEqual('http://compute.example.com', - cc.get_endpoint('compute')) - self.assertIsNone(cc.get_endpoint('image')) - self.assertIsNone(cc.get_service_name('compute')) - self.assertEqual('locks', cc.get_service_name('identity')) - - def test_volume_override(self): - cc = cloud_config.CloudConfig("test1", "region-al", fake_services_dict) - cc.config['volume_api_version'] = '2' - self.assertEqual('volumev2', cc.get_service_type('volume')) - - def test_get_session_no_auth(self): - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig("test1", "region-al", config_dict) - self.assertRaises( - exceptions.OpenStackConfigException, - cc.get_session) - - @mock.patch.object(ksa_session, 'Session') - def test_get_session(self, mock_session): - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_session() - mock_session.assert_called_with( - auth=mock.ANY, - verify=True, cert=None, timeout=None) - - @mock.patch.object(ksa_session, 'Session') - def test_get_session_with_timeout(self, mock_session): - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - config_dict['api_timeout'] = 9 - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_session() - mock_session.assert_called_with( - auth=mock.ANY, - verify=True, cert=None, timeout=9) - - @mock.patch.object(ksa_session, 'Session') - def test_override_session_endpoint_override(self, mock_session): - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - self.assertEqual( - cc.get_session_endpoint('compute'), - fake_services_dict['compute_endpoint_override']) - - @mock.patch.object(ksa_session, 'Session') - def test_override_session_endpoint(self, mock_session): - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - self.assertEqual( - cc.get_session_endpoint('telemetry'), - fake_services_dict['telemetry_endpoint']) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session') - def test_session_endpoint_identity(self, mock_get_session): - mock_session = mock.Mock() - mock_get_session.return_value = mock_session - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_session_endpoint('identity') - mock_session.get_endpoint.assert_called_with( - interface=ksa_plugin.AUTH_INTERFACE) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session') - def test_session_endpoint(self, mock_get_session): - mock_session = mock.Mock() - mock_get_session.return_value = mock_session - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_session_endpoint('orchestration') - mock_session.get_endpoint.assert_called_with( - interface='public', - service_name=None, - region_name='region-al', - service_type='orchestration') - - @mock.patch.object(cloud_config.CloudConfig, 'get_api_version') - @mock.patch.object(cloud_config.CloudConfig, 'get_auth_args') - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_object_store_password( - self, - mock_get_session_endpoint, - mock_get_auth_args, - mock_get_api_version): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://swift.example.com' - mock_get_api_version.return_value = '3' - mock_get_auth_args.return_value = dict( - username='testuser', - password='testpassword', - project_name='testproject', - auth_url='http://example.com', - ) - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('object-store', mock_client) - mock_client.assert_called_with( - preauthtoken=mock.ANY, - auth_version=u'3', - authurl='http://example.com', - key='testpassword', - os_options={ - 'auth_token': mock.ANY, - 'region_name': 'region-al', - 'object_storage_url': 'http://swift.example.com', - 'user_id': None, - 'user_domain_name': None, - 'project_name': 'testproject', - 'project_domain_name': None, - 'project_domain_id': None, - 'project_id': None, - 'service_type': 'object-store', - 'endpoint_type': 'public', - 'user_domain_id': None - }, - preauthurl='http://swift.example.com', - user='testuser') - - @mock.patch.object(cloud_config.CloudConfig, 'get_auth_args') - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_object_store_password_v2( - self, mock_get_session_endpoint, mock_get_auth_args): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://swift.example.com' - mock_get_auth_args.return_value = dict( - username='testuser', - password='testpassword', - project_name='testproject', - auth_url='http://example.com', - ) - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('object-store', mock_client) - mock_client.assert_called_with( - preauthtoken=mock.ANY, - auth_version=u'2.0', - authurl='http://example.com', - key='testpassword', - os_options={ - 'auth_token': mock.ANY, - 'region_name': 'region-al', - 'object_storage_url': 'http://swift.example.com', - 'user_id': None, - 'user_domain_name': None, - 'tenant_name': 'testproject', - 'project_domain_name': None, - 'project_domain_id': None, - 'tenant_id': None, - 'service_type': 'object-store', - 'endpoint_type': 'public', - 'user_domain_id': None - }, - preauthurl='http://swift.example.com', - user='testuser') - - @mock.patch.object(cloud_config.CloudConfig, 'get_auth_args') - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_object_store( - self, mock_get_session_endpoint, mock_get_auth_args): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - mock_get_auth_args.return_value = {} - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('object-store', mock_client) - mock_client.assert_called_with( - preauthtoken=mock.ANY, - auth_version=u'2.0', - authurl=None, - key=None, - os_options={ - 'auth_token': mock.ANY, - 'region_name': 'region-al', - 'object_storage_url': 'http://example.com/v2', - 'user_id': None, - 'user_domain_name': None, - 'tenant_name': None, - 'project_domain_name': None, - 'project_domain_id': None, - 'tenant_id': None, - 'service_type': 'object-store', - 'endpoint_type': 'public', - 'user_domain_id': None - }, - preauthurl='http://example.com/v2', - user=None) - - @mock.patch.object(cloud_config.CloudConfig, 'get_auth_args') - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_object_store_timeout( - self, mock_get_session_endpoint, mock_get_auth_args): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - mock_get_auth_args.return_value = {} - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - config_dict['api_timeout'] = 9 - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('object-store', mock_client) - mock_client.assert_called_with( - preauthtoken=mock.ANY, - auth_version=u'2.0', - authurl=None, - key=None, - os_options={ - 'auth_token': mock.ANY, - 'region_name': 'region-al', - 'object_storage_url': 'http://example.com/v2', - 'user_id': None, - 'user_domain_name': None, - 'tenant_name': None, - 'project_domain_name': None, - 'project_domain_id': None, - 'tenant_id': None, - 'service_type': 'object-store', - 'endpoint_type': 'public', - 'user_domain_id': None - }, - preauthurl='http://example.com/v2', - timeout=9.0, - user=None) - - @mock.patch.object(cloud_config.CloudConfig, 'get_auth_args') - def test_legacy_client_object_store_endpoint( - self, mock_get_auth_args): - mock_client = mock.Mock() - mock_get_auth_args.return_value = {} - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - config_dict['object_store_endpoint'] = 'http://example.com/swift' - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('object-store', mock_client) - mock_client.assert_called_with( - preauthtoken=mock.ANY, - auth_version=u'2.0', - authurl=None, - key=None, - os_options={ - 'auth_token': mock.ANY, - 'region_name': 'region-al', - 'object_storage_url': 'http://example.com/swift', - 'user_id': None, - 'user_domain_name': None, - 'tenant_name': None, - 'project_domain_name': None, - 'project_domain_id': None, - 'tenant_id': None, - 'service_type': 'object-store', - 'endpoint_type': 'public', - 'user_domain_id': None - }, - preauthurl='http://example.com/swift', - user=None) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_image(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('image', mock_client) - mock_client.assert_called_with( - version=2.0, - service_name=None, - endpoint_override='http://example.com', - region_name='region-al', - interface='public', - session=mock.ANY, - # Not a typo - the config dict above overrides this - service_type='mage' - ) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_image_override(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - config_dict['image_endpoint_override'] = 'http://example.com/override' - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('image', mock_client) - mock_client.assert_called_with( - version=2.0, - service_name=None, - endpoint_override='http://example.com/override', - region_name='region-al', - interface='public', - session=mock.ANY, - # Not a typo - the config dict above overrides this - service_type='mage' - ) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_image_versioned(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - # v2 endpoint was passed, 1 requested in config, endpoint wins - config_dict['image_api_version'] = '1' - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('image', mock_client) - mock_client.assert_called_with( - version=2.0, - service_name=None, - endpoint_override='http://example.com', - region_name='region-al', - interface='public', - session=mock.ANY, - # Not a typo - the config dict above overrides this - service_type='mage' - ) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_image_unversioned(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - # Versionless endpoint, config wins - config_dict['image_api_version'] = '1' - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('image', mock_client) - mock_client.assert_called_with( - version='1', - service_name=None, - endpoint_override='http://example.com', - region_name='region-al', - interface='public', - session=mock.ANY, - # Not a typo - the config dict above overrides this - service_type='mage' - ) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_image_argument(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v3' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - # Versionless endpoint, config wins - config_dict['image_api_version'] = '6' - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('image', mock_client, version='beef') - mock_client.assert_called_with( - version='beef', - service_name=None, - endpoint_override='http://example.com', - region_name='region-al', - interface='public', - session=mock.ANY, - # Not a typo - the config dict above overrides this - service_type='mage' - ) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_network(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('network', mock_client) - mock_client.assert_called_with( - api_version='2.0', - endpoint_type='public', - endpoint_override=None, - region_name='region-al', - service_type='network', - session=mock.ANY, - service_name=None) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_compute(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('compute', mock_client) - mock_client.assert_called_with( - version='2', - endpoint_type='public', - endpoint_override='http://compute.example.com', - region_name='region-al', - service_type='compute', - session=mock.ANY, - service_name=None) - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_identity(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com/v2' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('identity', mock_client) - mock_client.assert_called_with( - version='2.0', - endpoint='http://example.com/v2', - endpoint_type='admin', - endpoint_override=None, - region_name='region-al', - service_type='identity', - session=mock.ANY, - service_name='locks') - - @mock.patch.object(cloud_config.CloudConfig, 'get_session_endpoint') - def test_legacy_client_identity_v3(self, mock_get_session_endpoint): - mock_client = mock.Mock() - mock_get_session_endpoint.return_value = 'http://example.com' - config_dict = defaults.get_defaults() - config_dict.update(fake_services_dict) - config_dict['identity_api_version'] = '3' - cc = cloud_config.CloudConfig( - "test1", "region-al", config_dict, auth_plugin=mock.Mock()) - cc.get_legacy_client('identity', mock_client) - mock_client.assert_called_with( - version='3', - endpoint='http://example.com', - endpoint_type='admin', - endpoint_override=None, - region_name='region-al', - service_type='identity', - session=mock.ANY, - service_name='locks') diff --git a/os_client_config/tests/test_config.py b/os_client_config/tests/test_config.py deleted file mode 100644 index baa35cd..0000000 --- a/os_client_config/tests/test_config.py +++ /dev/null @@ -1,894 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - -import argparse -import copy -import os - -import fixtures -import testtools -import yaml - -from os_client_config import cloud_config -from os_client_config import config -from os_client_config import defaults -from os_client_config import exceptions -from os_client_config.tests import base - - -class TestConfig(base.TestCase): - - def test_get_all_clouds(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - clouds = c.get_all_clouds() - # We add one by hand because the regions cloud is going to exist - # twice since it has two regions in it - user_clouds = [ - cloud for cloud in base.USER_CONF['clouds'].keys() - ] + ['_test_cloud_regions'] - configured_clouds = [cloud.name for cloud in clouds] - self.assertItemsEqual(user_clouds, configured_clouds) - - def test_get_one_cloud(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = c.get_one_cloud(validate=False) - self.assertIsInstance(cloud, cloud_config.CloudConfig) - self.assertEqual(cloud.name, '') - - def test_get_one_cloud_auth_defaults(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml]) - cc = c.get_one_cloud(cloud='_test-cloud_', auth={'username': 'user'}) - self.assertEqual('user', cc.auth['username']) - self.assertEqual( - defaults._defaults['auth_type'], - cc.auth_type, - ) - self.assertEqual( - defaults._defaults['identity_api_version'], - cc.identity_api_version, - ) - - def test_get_one_cloud_auth_override_defaults(self): - default_options = {'compute_api_version': '4'} - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - override_defaults=default_options) - cc = c.get_one_cloud(cloud='_test-cloud_', auth={'username': 'user'}) - self.assertEqual('user', cc.auth['username']) - self.assertEqual('4', cc.compute_api_version) - self.assertEqual( - defaults._defaults['identity_api_version'], - cc.identity_api_version, - ) - - def test_get_one_cloud_with_config_files(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.secure_yaml]) - self.assertIsInstance(c.cloud_config, dict) - self.assertIn('cache', c.cloud_config) - self.assertIsInstance(c.cloud_config['cache'], dict) - self.assertIn('max_age', c.cloud_config['cache']) - self.assertIn('path', c.cloud_config['cache']) - cc = c.get_one_cloud('_test-cloud_') - self._assert_cloud_details(cc) - cc = c.get_one_cloud('_test_cloud_no_vendor') - self._assert_cloud_details(cc) - - def test_get_one_cloud_with_int_project_id(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud-int-project_') - self.assertEqual('12345', cc.auth['project_id']) - - def test_get_one_cloud_with_domain_id(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud-domain-id_') - self.assertEqual('6789', cc.auth['user_domain_id']) - self.assertEqual('123456789', cc.auth['project_domain_id']) - self.assertNotIn('domain_id', cc.auth) - self.assertNotIn('domain-id', cc.auth) - self.assertNotIn('domain_id', cc) - - def test_get_one_cloud_domain_scoped(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud-domain-scoped_') - self.assertEqual('12345', cc.auth['domain_id']) - self.assertNotIn('user_domain_id', cc.auth) - self.assertNotIn('project_domain_id', cc.auth) - - def test_get_one_cloud_infer_user_domain(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud-int-project_') - self.assertEqual('awesome-domain', cc.auth['user_domain_id']) - self.assertEqual('awesome-domain', cc.auth['project_domain_id']) - self.assertNotIn('domain_id', cc.auth) - self.assertNotIn('domain_id', cc) - - def test_get_one_cloud_with_hyphenated_project_id(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test_cloud_hyphenated') - self.assertEqual('12345', cc.auth['project_id']) - - def test_get_one_cloud_with_hyphenated_kwargs(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - args = { - 'auth': { - 'username': 'testuser', - 'password': 'testpass', - 'project-id': '12345', - 'auth-url': 'http://example.com/v2', - }, - 'region_name': 'test-region', - } - cc = c.get_one_cloud(**args) - self.assertEqual('http://example.com/v2', cc.auth['auth_url']) - - def test_no_environ(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - self.assertRaises( - exceptions.OpenStackConfigException, c.get_one_cloud, 'envvars') - - def test_fallthrough(self): - c = config.OpenStackConfig(config_files=[self.no_yaml], - vendor_files=[self.no_yaml], - secure_files=[self.no_yaml]) - for k in os.environ.keys(): - if k.startswith('OS_'): - self.useFixture(fixtures.EnvironmentVariable(k)) - c.get_one_cloud(cloud='defaults', validate=False) - - def test_prefer_ipv6_true(self): - c = config.OpenStackConfig(config_files=[self.no_yaml], - vendor_files=[self.no_yaml], - secure_files=[self.no_yaml]) - cc = c.get_one_cloud(cloud='defaults', validate=False) - self.assertTrue(cc.prefer_ipv6) - - def test_prefer_ipv6_false(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud(cloud='_test-cloud_') - self.assertFalse(cc.prefer_ipv6) - - def test_force_ipv4_true(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud(cloud='_test-cloud_') - self.assertTrue(cc.force_ipv4) - - def test_force_ipv4_false(self): - c = config.OpenStackConfig(config_files=[self.no_yaml], - vendor_files=[self.no_yaml], - secure_files=[self.no_yaml]) - cc = c.get_one_cloud(cloud='defaults', validate=False) - self.assertFalse(cc.force_ipv4) - - def test_get_one_cloud_auth_merge(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml]) - cc = c.get_one_cloud(cloud='_test-cloud_', auth={'username': 'user'}) - self.assertEqual('user', cc.auth['username']) - self.assertEqual('testpass', cc.auth['password']) - - def test_get_one_cloud_networks(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud-networks_') - self.assertEqual( - ['a-public', 'another-public'], cc.get_external_networks()) - self.assertEqual( - ['a-private', 'another-private'], cc.get_internal_networks()) - self.assertEqual('another-private', cc.get_nat_destination()) - self.assertEqual('another-public', cc.get_default_network()) - - def test_get_one_cloud_no_networks(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud-domain-scoped_') - self.assertEqual([], cc.get_external_networks()) - self.assertEqual([], cc.get_internal_networks()) - self.assertIsNone(cc.get_nat_destination()) - self.assertIsNone(cc.get_default_network()) - - def test_only_secure_yaml(self): - c = config.OpenStackConfig(config_files=['nonexistent'], - vendor_files=['nonexistent'], - secure_files=[self.secure_yaml]) - cc = c.get_one_cloud(cloud='_test_cloud_no_vendor') - self.assertEqual('testpass', cc.auth['password']) - - def test_get_cloud_names(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - secure_files=[self.no_yaml]) - self.assertEqual( - ['_test-cloud-domain-id_', - '_test-cloud-domain-scoped_', - '_test-cloud-int-project_', - '_test-cloud-networks_', - '_test-cloud_', - '_test-cloud_no_region', - '_test_cloud_hyphenated', - '_test_cloud_no_vendor', - '_test_cloud_regions', - ], - sorted(c.get_cloud_names())) - c = config.OpenStackConfig(config_files=[self.no_yaml], - vendor_files=[self.no_yaml], - secure_files=[self.no_yaml]) - for k in os.environ.keys(): - if k.startswith('OS_'): - self.useFixture(fixtures.EnvironmentVariable(k)) - c.get_one_cloud(cloud='defaults', validate=False) - self.assertEqual(['defaults'], sorted(c.get_cloud_names())) - - def test_set_one_cloud_creates_file(self): - config_dir = fixtures.TempDir() - self.useFixture(config_dir) - config_path = os.path.join(config_dir.path, 'clouds.yaml') - config.OpenStackConfig.set_one_cloud(config_path, '_test_cloud_') - self.assertTrue(os.path.isfile(config_path)) - with open(config_path) as fh: - self.assertEqual({'clouds': {'_test_cloud_': {}}}, - yaml.safe_load(fh)) - - def test_set_one_cloud_updates_cloud(self): - new_config = { - 'cloud': 'new_cloud', - 'auth': { - 'password': 'newpass' - } - } - - resulting_cloud_config = { - 'auth': { - 'password': 'newpass', - 'username': 'testuser', - 'auth_url': 'http://example.com/v2', - }, - 'cloud': 'new_cloud', - 'profile': '_test_cloud_in_our_cloud', - 'region_name': 'test-region' - } - resulting_config = copy.deepcopy(base.USER_CONF) - resulting_config['clouds']['_test-cloud_'] = resulting_cloud_config - config.OpenStackConfig.set_one_cloud(self.cloud_yaml, '_test-cloud_', - new_config) - with open(self.cloud_yaml) as fh: - written_config = yaml.safe_load(fh) - # We write a cache config for testing - written_config['cache'].pop('path', None) - self.assertEqual(written_config, resulting_config) - - def test_get_region_no_region_default(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(cloud='_test-cloud_no_region') - self.assertEqual(region, {'name': '', 'values': {}}) - - def test_get_region_no_region(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(cloud='_test-cloud_no_region', - region_name='override-region') - self.assertEqual(region, {'name': 'override-region', 'values': {}}) - - def test_get_region_region_is_none(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(cloud='_test-cloud_no_region', region_name=None) - self.assertEqual(region, {'name': '', 'values': {}}) - - def test_get_region_region_set(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(cloud='_test-cloud_', region_name='test-region') - self.assertEqual(region, {'name': 'test-region', 'values': {}}) - - def test_get_region_many_regions_default(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(cloud='_test_cloud_regions', - region_name='') - self.assertEqual(region, {'name': 'region1', 'values': - {'external_network': 'region1-network'}}) - - def test_get_region_many_regions(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(cloud='_test_cloud_regions', - region_name='region2') - self.assertEqual(region, {'name': 'region2', 'values': - {'external_network': 'my-network'}}) - - def test_get_region_invalid_region(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - self.assertRaises( - exceptions.OpenStackConfigException, c._get_region, - cloud='_test_cloud_regions', region_name='invalid-region') - - def test_get_region_no_cloud(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.no_yaml]) - region = c._get_region(region_name='no-cloud-region') - self.assertEqual(region, {'name': 'no-cloud-region', 'values': {}}) - - -class TestConfigArgparse(base.TestCase): - - def setUp(self): - super(TestConfigArgparse, self).setUp() - - self.args = dict( - auth_url='http://example.com/v2', - username='user', - password='password', - project_name='project', - region_name='region2', - snack_type='cookie', - os_auth_token='no-good-things', - ) - - self.options = argparse.Namespace(**self.args) - - def test_get_one_cloud_bad_region_argparse(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - self.assertRaises( - exceptions.OpenStackConfigException, c.get_one_cloud, - cloud='_test-cloud_', argparse=self.options) - - def test_get_one_cloud_argparse(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud( - cloud='_test_cloud_regions', argparse=self.options) - self.assertEqual(cc.region_name, 'region2') - self.assertEqual(cc.snack_type, 'cookie') - - def test_get_one_cloud_precedence(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - kwargs = { - 'auth': { - 'username': 'testuser', - 'password': 'authpass', - 'project-id': 'testproject', - 'auth_url': 'http://example.com/v2', - }, - 'region_name': 'kwarg_region', - 'password': 'ansible_password', - 'arbitrary': 'value', - } - - args = dict( - auth_url='http://example.com/v2', - username='user', - password='argpass', - project_name='project', - region_name='region2', - snack_type='cookie', - ) - - options = argparse.Namespace(**args) - cc = c.get_one_cloud( - argparse=options, **kwargs) - self.assertEqual(cc.region_name, 'region2') - self.assertEqual(cc.auth['password'], 'argpass') - self.assertEqual(cc.snack_type, 'cookie') - - def test_get_one_cloud_precedence_no_argparse(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - kwargs = { - 'auth': { - 'username': 'testuser', - 'password': 'authpass', - 'project-id': 'testproject', - 'auth_url': 'http://example.com/v2', - }, - 'region_name': 'kwarg_region', - 'password': 'ansible_password', - 'arbitrary': 'value', - } - - cc = c.get_one_cloud(**kwargs) - self.assertEqual(cc.region_name, 'kwarg_region') - self.assertEqual(cc.auth['password'], 'authpass') - self.assertIsNone(cc.password) - - def test_get_one_cloud_just_argparse(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud(argparse=self.options) - self.assertIsNone(cc.cloud) - self.assertEqual(cc.region_name, 'region2') - self.assertEqual(cc.snack_type, 'cookie') - - def test_get_one_cloud_just_kwargs(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud(**self.args) - self.assertIsNone(cc.cloud) - self.assertEqual(cc.region_name, 'region2') - self.assertEqual(cc.snack_type, 'cookie') - - def test_get_one_cloud_dash_kwargs(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - args = { - 'auth-url': 'http://example.com/v2', - 'username': 'user', - 'password': 'password', - 'project_name': 'project', - 'region_name': 'other-test-region', - 'snack_type': 'cookie', - } - cc = c.get_one_cloud(**args) - self.assertIsNone(cc.cloud) - self.assertEqual(cc.region_name, 'other-test-region') - self.assertEqual(cc.snack_type, 'cookie') - - def test_get_one_cloud_no_argparse(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud(cloud='_test-cloud_', argparse=None) - self._assert_cloud_details(cc) - self.assertEqual(cc.region_name, 'test-region') - self.assertIsNone(cc.snack_type) - - def test_get_one_cloud_no_argparse_regions(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud(cloud='_test_cloud_regions', argparse=None) - self._assert_cloud_details(cc) - self.assertEqual(cc.region_name, 'region1') - self.assertIsNone(cc.snack_type) - - def test_get_one_cloud_bad_region(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - self.assertRaises( - exceptions.OpenStackConfigException, - c.get_one_cloud, - cloud='_test_cloud_regions', region_name='bad') - - def test_get_one_cloud_bad_region_no_regions(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - self.assertRaises( - exceptions.OpenStackConfigException, - c.get_one_cloud, - cloud='_test-cloud_', region_name='bad_region') - - def test_get_one_cloud_no_argparse_region2(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud( - cloud='_test_cloud_regions', region_name='region2', argparse=None) - self._assert_cloud_details(cc) - self.assertEqual(cc.region_name, 'region2') - self.assertIsNone(cc.snack_type) - - def test_get_one_cloud_network(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud( - cloud='_test_cloud_regions', region_name='region1', argparse=None) - self._assert_cloud_details(cc) - self.assertEqual(cc.region_name, 'region1') - self.assertEqual('region1-network', cc.config['external_network']) - - def test_get_one_cloud_per_region_network(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud( - cloud='_test_cloud_regions', region_name='region2', argparse=None) - self._assert_cloud_details(cc) - self.assertEqual(cc.region_name, 'region2') - self.assertEqual('my-network', cc.config['external_network']) - - def test_fix_env_args(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - env_args = {'os-compute-api-version': 1} - fixed_args = c._fix_args(env_args) - - self.assertDictEqual({'compute_api_version': 1}, fixed_args) - - def test_extra_config(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - defaults = {'use_hostnames': False, 'other-value': 'something'} - ansible_options = c.get_extra_config('ansible', defaults) - - # This should show that the default for use_hostnames above is - # overridden by the value in the config file defined in base.py - # It should also show that other-value key is normalized and passed - # through even though there is no corresponding value in the config - # file, and that expand-hostvars key is normalized and the value - # from the config comes through even though there is no default. - self.assertDictEqual( - { - 'expand_hostvars': False, - 'use_hostnames': True, - 'other_value': 'something', - }, - ansible_options) - - def test_register_argparse_cloud(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - c.register_argparse_arguments(parser, []) - opts, _remain = parser.parse_known_args(['--os-cloud', 'foo']) - self.assertEqual(opts.os_cloud, 'foo') - - def test_env_argparse_precedence(self): - self.useFixture(fixtures.EnvironmentVariable( - 'OS_TENANT_NAME', 'tenants-are-bad')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - cc = c.get_one_cloud( - cloud='envvars', argparse=self.options) - self.assertEqual(cc.auth['project_name'], 'project') - - def test_argparse_default_no_token(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - parser = argparse.ArgumentParser() - c.register_argparse_arguments(parser, []) - # novaclient will add this - parser.add_argument('--os-auth-token') - opts, _remain = parser.parse_known_args() - cc = c.get_one_cloud( - cloud='_test_cloud_regions', argparse=opts) - self.assertEqual(cc.config['auth_type'], 'password') - self.assertNotIn('token', cc.config['auth']) - - def test_argparse_token(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - - parser = argparse.ArgumentParser() - c.register_argparse_arguments(parser, []) - # novaclient will add this - parser.add_argument('--os-auth-token') - opts, _remain = parser.parse_known_args( - ['--os-auth-token', 'very-bad-things', - '--os-auth-type', 'token']) - cc = c.get_one_cloud(argparse=opts) - self.assertEqual(cc.config['auth_type'], 'token') - self.assertEqual(cc.config['auth']['token'], 'very-bad-things') - - def test_argparse_underscores(self): - c = config.OpenStackConfig(config_files=[self.no_yaml], - vendor_files=[self.no_yaml], - secure_files=[self.no_yaml]) - parser = argparse.ArgumentParser() - parser.add_argument('--os_username') - argv = [ - '--os_username', 'user', '--os_password', 'pass', - '--os-auth-url', 'auth-url', '--os-project-name', 'project'] - c.register_argparse_arguments(parser, argv=argv) - opts, _remain = parser.parse_known_args(argv) - cc = c.get_one_cloud(argparse=opts) - self.assertEqual(cc.config['auth']['username'], 'user') - self.assertEqual(cc.config['auth']['password'], 'pass') - self.assertEqual(cc.config['auth']['auth_url'], 'auth-url') - - def test_argparse_underscores_duplicate(self): - c = config.OpenStackConfig(config_files=[self.no_yaml], - vendor_files=[self.no_yaml], - secure_files=[self.no_yaml]) - parser = argparse.ArgumentParser() - parser.add_argument('--os_username') - argv = [ - '--os_username', 'user', '--os_password', 'pass', - '--os-username', 'user1', '--os-password', 'pass1', - '--os-auth-url', 'auth-url', '--os-project-name', 'project'] - self.assertRaises( - exceptions.OpenStackConfigException, - c.register_argparse_arguments, - parser=parser, argv=argv) - - def test_register_argparse_bad_plugin(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - self.assertRaises( - exceptions.OpenStackConfigException, - c.register_argparse_arguments, - parser, ['--os-auth-type', 'foo']) - - def test_register_argparse_not_password(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - args = [ - '--os-auth-type', 'v3token', - '--os-token', 'some-secret', - ] - c.register_argparse_arguments(parser, args) - opts, _remain = parser.parse_known_args(args) - self.assertEqual(opts.os_token, 'some-secret') - - def test_register_argparse_password(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - args = [ - '--os-password', 'some-secret', - ] - c.register_argparse_arguments(parser, args) - opts, _remain = parser.parse_known_args(args) - self.assertEqual(opts.os_password, 'some-secret') - with testtools.ExpectedException(AttributeError): - opts.os_token - - def test_register_argparse_service_type(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - args = [ - '--os-service-type', 'network', - '--os-endpoint-type', 'admin', - '--http-timeout', '20', - ] - c.register_argparse_arguments(parser, args) - opts, _remain = parser.parse_known_args(args) - self.assertEqual(opts.os_service_type, 'network') - self.assertEqual(opts.os_endpoint_type, 'admin') - self.assertEqual(opts.http_timeout, '20') - with testtools.ExpectedException(AttributeError): - opts.os_network_service_type - cloud = c.get_one_cloud(argparse=opts, verify=False) - self.assertEqual(cloud.config['service_type'], 'network') - self.assertEqual(cloud.config['interface'], 'admin') - self.assertEqual(cloud.config['api_timeout'], '20') - self.assertNotIn('http_timeout', cloud.config) - - def test_register_argparse_network_service_type(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - args = [ - '--os-endpoint-type', 'admin', - '--network-api-version', '4', - ] - c.register_argparse_arguments(parser, args, ['network']) - opts, _remain = parser.parse_known_args(args) - self.assertEqual(opts.os_service_type, 'network') - self.assertEqual(opts.os_endpoint_type, 'admin') - self.assertEqual(opts.os_network_service_type, None) - self.assertEqual(opts.os_network_api_version, None) - self.assertEqual(opts.network_api_version, '4') - cloud = c.get_one_cloud(argparse=opts, verify=False) - self.assertEqual(cloud.config['service_type'], 'network') - self.assertEqual(cloud.config['interface'], 'admin') - self.assertEqual(cloud.config['network_api_version'], '4') - self.assertNotIn('http_timeout', cloud.config) - - def test_register_argparse_network_service_types(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - parser = argparse.ArgumentParser() - args = [ - '--os-compute-service-name', 'cloudServers', - '--os-network-service-type', 'badtype', - '--os-endpoint-type', 'admin', - '--network-api-version', '4', - ] - c.register_argparse_arguments( - parser, args, ['compute', 'network', 'volume']) - opts, _remain = parser.parse_known_args(args) - self.assertEqual(opts.os_network_service_type, 'badtype') - self.assertEqual(opts.os_compute_service_type, None) - self.assertEqual(opts.os_volume_service_type, None) - self.assertEqual(opts.os_service_type, 'compute') - self.assertEqual(opts.os_compute_service_name, 'cloudServers') - self.assertEqual(opts.os_endpoint_type, 'admin') - self.assertEqual(opts.os_network_api_version, None) - self.assertEqual(opts.network_api_version, '4') - cloud = c.get_one_cloud(argparse=opts, verify=False) - self.assertEqual(cloud.config['service_type'], 'compute') - self.assertEqual(cloud.config['network_service_type'], 'badtype') - self.assertEqual(cloud.config['interface'], 'admin') - self.assertEqual(cloud.config['network_api_version'], '4') - self.assertNotIn('volume_service_type', cloud.config) - self.assertNotIn('http_timeout', cloud.config) - - -class TestConfigDefault(base.TestCase): - - def setUp(self): - super(TestConfigDefault, self).setUp() - - # Reset defaults after each test so that other tests are - # not affected by any changes. - self.addCleanup(self._reset_defaults) - - def _reset_defaults(self): - defaults._defaults = None - - def test_set_no_default(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud(cloud='_test-cloud_', argparse=None) - self._assert_cloud_details(cc) - self.assertEqual('password', cc.auth_type) - - def test_set_default_before_init(self): - config.set_default('identity_api_version', '4') - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud(cloud='_test-cloud_', argparse=None) - self.assertEqual('4', cc.identity_api_version) - - -class TestBackwardsCompatibility(base.TestCase): - - def test_set_no_default(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'identity_endpoint_type': 'admin', - 'compute_endpoint_type': 'private', - 'endpoint_type': 'public', - 'auth_type': 'v3password', - } - result = c._fix_backwards_interface(cloud) - expected = { - 'identity_interface': 'admin', - 'compute_interface': 'private', - 'interface': 'public', - 'auth_type': 'v3password', - } - self.assertDictEqual(expected, result) - - def test_project_v2password(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'auth_type': 'v2password', - 'auth': { - 'project-name': 'my_project_name', - 'project-id': 'my_project_id' - } - } - result = c._fix_backwards_project(cloud) - expected = { - 'auth_type': 'v2password', - 'auth': { - 'tenant_name': 'my_project_name', - 'tenant_id': 'my_project_id' - } - } - self.assertEqual(expected, result) - - def test_project_password(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'auth_type': 'password', - 'auth': { - 'project-name': 'my_project_name', - 'project-id': 'my_project_id' - } - } - result = c._fix_backwards_project(cloud) - expected = { - 'auth_type': 'password', - 'auth': { - 'project_name': 'my_project_name', - 'project_id': 'my_project_id' - } - } - self.assertEqual(expected, result) - - def test_backwards_network_fail(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'external_network': 'public', - 'networks': [ - {'name': 'private', 'routes_externally': False}, - ] - } - self.assertRaises( - exceptions.OpenStackConfigException, - c._fix_backwards_networks, cloud) - - def test_backwards_network(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'external_network': 'public', - 'internal_network': 'private', - } - result = c._fix_backwards_networks(cloud) - expected = { - 'external_network': 'public', - 'internal_network': 'private', - 'networks': [ - {'name': 'public', 'routes_externally': True, - 'nat_destination': False, 'default_interface': True}, - {'name': 'private', 'routes_externally': False, - 'nat_destination': True, 'default_interface': False}, - ] - } - self.assertEqual(expected, result) - - def test_normalize_network(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'networks': [ - {'name': 'private'} - ] - } - result = c._fix_backwards_networks(cloud) - expected = { - 'networks': [ - {'name': 'private', 'routes_externally': False, - 'nat_destination': False, 'default_interface': False}, - ] - } - self.assertEqual(expected, result) - - def test_single_default_interface(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cloud = { - 'networks': [ - {'name': 'blue', 'default_interface': True}, - {'name': 'purple', 'default_interface': True}, - ] - } - self.assertRaises( - exceptions.OpenStackConfigException, - c._fix_backwards_networks, cloud) diff --git a/os_client_config/tests/test_environ.py b/os_client_config/tests/test_environ.py deleted file mode 100644 index b75db1c..0000000 --- a/os_client_config/tests/test_environ.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - - -from os_client_config import cloud_config -from os_client_config import config -from os_client_config import exceptions -from os_client_config.tests import base - -import fixtures - - -class TestEnviron(base.TestCase): - - def setUp(self): - super(TestEnviron, self).setUp() - self.useFixture( - fixtures.EnvironmentVariable('OS_AUTH_URL', 'https://example.com')) - self.useFixture( - fixtures.EnvironmentVariable('OS_USERNAME', 'testuser')) - self.useFixture( - fixtures.EnvironmentVariable('OS_PASSWORD', 'testpass')) - self.useFixture( - fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'testproject')) - self.useFixture( - fixtures.EnvironmentVariable('NOVA_PROJECT_ID', 'testnova')) - - def test_get_one_cloud(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - self.assertIsInstance(c.get_one_cloud(), cloud_config.CloudConfig) - - def test_no_fallthrough(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - self.assertRaises( - exceptions.OpenStackConfigException, c.get_one_cloud, 'openstack') - - def test_envvar_name_override(self): - self.useFixture( - fixtures.EnvironmentVariable('OS_CLOUD_NAME', 'override')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('override') - self._assert_cloud_details(cc) - - def test_envvar_prefer_ipv6_override(self): - self.useFixture( - fixtures.EnvironmentVariable('OS_PREFER_IPV6', 'false')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.secure_yaml]) - cc = c.get_one_cloud('_test-cloud_') - self.assertFalse(cc.prefer_ipv6) - - def test_environ_exists(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.secure_yaml]) - cc = c.get_one_cloud('envvars') - self._assert_cloud_details(cc) - self.assertNotIn('auth_url', cc.config) - self.assertIn('auth_url', cc.config['auth']) - self.assertNotIn('project_id', cc.config['auth']) - self.assertNotIn('auth_url', cc.config) - cc = c.get_one_cloud('_test-cloud_') - self._assert_cloud_details(cc) - cc = c.get_one_cloud('_test_cloud_no_vendor') - self._assert_cloud_details(cc) - - def test_environ_prefix(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - envvar_prefix='NOVA_', - secure_files=[self.secure_yaml]) - cc = c.get_one_cloud('envvars') - self._assert_cloud_details(cc) - self.assertNotIn('auth_url', cc.config) - self.assertIn('auth_url', cc.config['auth']) - self.assertIn('project_id', cc.config['auth']) - self.assertNotIn('auth_url', cc.config) - cc = c.get_one_cloud('_test-cloud_') - self._assert_cloud_details(cc) - cc = c.get_one_cloud('_test_cloud_no_vendor') - self._assert_cloud_details(cc) - - def test_get_one_cloud_with_config_files(self): - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - secure_files=[self.secure_yaml]) - self.assertIsInstance(c.cloud_config, dict) - self.assertIn('cache', c.cloud_config) - self.assertIsInstance(c.cloud_config['cache'], dict) - self.assertIn('max_age', c.cloud_config['cache']) - self.assertIn('path', c.cloud_config['cache']) - cc = c.get_one_cloud('_test-cloud_') - self._assert_cloud_details(cc) - cc = c.get_one_cloud('_test_cloud_no_vendor') - self._assert_cloud_details(cc) - - def test_config_file_override(self): - self.useFixture( - fixtures.EnvironmentVariable( - 'OS_CLIENT_CONFIG_FILE', self.cloud_yaml)) - c = config.OpenStackConfig(config_files=[], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('_test-cloud_') - self._assert_cloud_details(cc) - - -class TestEnvvars(base.TestCase): - - def test_no_envvars(self): - self.useFixture( - fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - self.assertRaises( - exceptions.OpenStackConfigException, c.get_one_cloud, 'envvars') - - def test_test_envvars(self): - self.useFixture( - fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) - self.useFixture( - fixtures.EnvironmentVariable('OS_STDERR_CAPTURE', 'True')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - self.assertRaises( - exceptions.OpenStackConfigException, c.get_one_cloud, 'envvars') - - def test_have_envvars(self): - self.useFixture( - fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) - self.useFixture( - fixtures.EnvironmentVariable('OS_USERNAME', 'user')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml]) - cc = c.get_one_cloud('envvars') - self.assertEqual(cc.config['auth']['username'], 'user') - - def test_old_envvars(self): - self.useFixture( - fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) - c = config.OpenStackConfig(config_files=[self.cloud_yaml], - vendor_files=[self.vendor_yaml], - envvar_prefix='NOVA_') - cc = c.get_one_cloud('envvars') - self.assertEqual(cc.config['auth']['username'], 'nova') diff --git a/os_client_config/tests/test_json.py b/os_client_config/tests/test_json.py deleted file mode 100644 index f618f3b..0000000 --- a/os_client_config/tests/test_json.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2015 Hewlett-Packard Development Company, L.P. -# -# 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. - -import glob -import json -import os - -import jsonschema -from testtools import content - -from os_client_config import defaults -from os_client_config.tests import base - - -class TestConfig(base.TestCase): - - def json_diagnostics(self, exc_info): - self.addDetail('filename', content.text_content(self.filename)) - for error in sorted(self.validator.iter_errors(self.json_data)): - self.addDetail('jsonschema', content.text_content(str(error))) - - def test_defaults_valid_json(self): - _schema_path = os.path.join( - os.path.dirname(os.path.realpath(defaults.__file__)), - 'schema.json') - schema = json.load(open(_schema_path, 'r')) - self.validator = jsonschema.Draft4Validator(schema) - self.addOnException(self.json_diagnostics) - - self.filename = os.path.join( - os.path.dirname(os.path.realpath(defaults.__file__)), - 'defaults.json') - self.json_data = json.load(open(self.filename, 'r')) - - self.assertTrue(self.validator.is_valid(self.json_data)) - - def test_vendors_valid_json(self): - _schema_path = os.path.join( - os.path.dirname(os.path.realpath(defaults.__file__)), - 'vendor-schema.json') - schema = json.load(open(_schema_path, 'r')) - self.validator = jsonschema.Draft4Validator(schema) - self.addOnException(self.json_diagnostics) - - _vendors_path = os.path.join( - os.path.dirname(os.path.realpath(defaults.__file__)), - 'vendors') - for self.filename in glob.glob(os.path.join(_vendors_path, '*.json')): - self.json_data = json.load(open(self.filename, 'r')) - - self.assertTrue(self.validator.is_valid(self.json_data)) diff --git a/os_client_config/vendor-schema.json b/os_client_config/vendor-schema.json deleted file mode 100644 index 6a6f456..0000000 --- a/os_client_config/vendor-schema.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "https://git.openstack.org/cgit/openstack/cloud-data/plain/vendor-schema.json#", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "profile": { - "type": "object", - "properties": { - "auth": { - "type": "object", - "properties": { - "auth_url": { - "name": "Auth URL", - "description": "URL of the primary Keystone endpoint", - "type": "string" - } - } - }, - "auth_type": { - "name": "Auth Type", - "description": "Name of authentication plugin to be used", - "default": "password", - "type": "string" - }, - "disable_vendor_agent": { - "name": "Disable Vendor Agent Properties", - "description": "Image properties required to disable vendor agent", - "type": "object", - "properties": {} - }, - "floating_ip_source": { - "name": "Floating IP Source", - "description": "Which service provides Floating IPs", - "enum": [ "neutron", "nova", "None" ], - "default": "neutron" - }, - "image_api_use_tasks": { - "name": "Image Task API", - "description": "Does the cloud require the Image Task API", - "default": false, - "type": "boolean" - }, - "image_format": { - "name": "Image Format", - "description": "Format for uploaded Images", - "default": "qcow2", - "type": "string" - }, - "interface": { - "name": "API Interface", - "description": "Which API Interface should connections hit", - "default": "public", - "enum": [ "public", "internal", "admin" ] - }, - "message": { - "name": "Status message", - "description": "Optional message with information related to status", - "type": "string" - }, - "secgroup_source": { - "name": "Security Group Source", - "description": "Which service provides security groups", - "enum": [ "neutron", "nova", "None" ], - "default": "neutron" - }, - "status": { - "name": "Vendor status", - "description": "Status of the vendor's cloud", - "enum": [ "active", "deprecated", "shutdown"], - "default": "active" - }, - "compute_service_name": { - "name": "Compute API Service Name", - "description": "Compute API Service Name", - "type": "string" - }, - "database_service_name": { - "name": "Database API Service Name", - "description": "Database API Service Name", - "type": "string" - }, - "dns_service_name": { - "name": "DNS API Service Name", - "description": "DNS API Service Name", - "type": "string" - }, - "identity_service_name": { - "name": "Identity API Service Name", - "description": "Identity API Service Name", - "type": "string" - }, - "image_service_name": { - "name": "Image API Service Name", - "description": "Image API Service Name", - "type": "string" - }, - "volume_service_name": { - "name": "Volume API Service Name", - "description": "Volume API Service Name", - "type": "string" - }, - "network_service_name": { - "name": "Network API Service Name", - "description": "Network API Service Name", - "type": "string" - }, - "object_service_name": { - "name": "Object Storage API Service Name", - "description": "Object Storage API Service Name", - "type": "string" - }, - "baremetal_service_name": { - "name": "Baremetal API Service Name", - "description": "Baremetal API Service Name", - "type": "string" - }, - "compute_service_type": { - "name": "Compute API Service Type", - "description": "Compute API Service Type", - "type": "string" - }, - "database_service_type": { - "name": "Database API Service Type", - "description": "Database API Service Type", - "type": "string" - }, - "dns_service_type": { - "name": "DNS API Service Type", - "description": "DNS API Service Type", - "type": "string" - }, - "identity_service_type": { - "name": "Identity API Service Type", - "description": "Identity API Service Type", - "type": "string" - }, - "image_service_type": { - "name": "Image API Service Type", - "description": "Image API Service Type", - "type": "string" - }, - "volume_service_type": { - "name": "Volume API Service Type", - "description": "Volume API Service Type", - "type": "string" - }, - "network_service_type": { - "name": "Network API Service Type", - "description": "Network API Service Type", - "type": "string" - }, - "object_service_type": { - "name": "Object Storage API Service Type", - "description": "Object Storage API Service Type", - "type": "string" - }, - "baremetal_api_version": { - "name": "Baremetal API Service Type", - "description": "Baremetal API Service Type", - "type": "string" - }, - "compute_api_version": { - "name": "Compute API Version", - "description": "Compute API Version", - "type": "string" - }, - "database_api_version": { - "name": "Database API Version", - "description": "Database API Version", - "type": "string" - }, - "dns_api_version": { - "name": "DNS API Version", - "description": "DNS API Version", - "type": "string" - }, - "identity_api_version": { - "name": "Identity API Version", - "description": "Identity API Version", - "type": "string" - }, - "image_api_version": { - "name": "Image API Version", - "description": "Image API Version", - "type": "string" - }, - "volume_api_version": { - "name": "Volume API Version", - "description": "Volume API Version", - "type": "string" - }, - "network_api_version": { - "name": "Network API Version", - "description": "Network API Version", - "type": "string" - }, - "object_api_version": { - "name": "Object Storage API Version", - "description": "Object Storage API Version", - "type": "string" - }, - "baremetal_api_version": { - "name": "Baremetal API Version", - "description": "Baremetal API Version", - "type": "string" - } - } - } - }, - "required": [ - "name", - "profile" - ] -} diff --git a/os_client_config/vendors/__init__.py b/os_client_config/vendors/__init__.py deleted file mode 100644 index 3e1d20a..0000000 --- a/os_client_config/vendors/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - -import glob -import json -import os - -import yaml - -_vendors_path = os.path.dirname(os.path.realpath(__file__)) -_vendor_defaults = None - - -def get_profile(profile_name): - global _vendor_defaults - if _vendor_defaults is None: - _vendor_defaults = {} - for vendor in glob.glob(os.path.join(_vendors_path, '*.yaml')): - with open(vendor, 'r') as f: - vendor_data = yaml.safe_load(f) - _vendor_defaults[vendor_data['name']] = vendor_data['profile'] - for vendor in glob.glob(os.path.join(_vendors_path, '*.json')): - with open(vendor, 'r') as f: - vendor_data = json.load(f) - _vendor_defaults[vendor_data['name']] = vendor_data['profile'] - return _vendor_defaults.get(profile_name) diff --git a/os_client_config/vendors/auro.json b/os_client_config/vendors/auro.json deleted file mode 100644 index a9e709b..0000000 --- a/os_client_config/vendors/auro.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "auro", - "profile": { - "auth": { - "auth_url": "https://api.van1.auro.io:5000/v2.0" - }, - "identity_api_version": "2", - "region_name": "van1" - } -} diff --git a/os_client_config/vendors/bluebox.json b/os_client_config/vendors/bluebox.json deleted file mode 100644 index 647c842..0000000 --- a/os_client_config/vendors/bluebox.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "bluebox", - "profile": { - "volume_api_version": "1", - "region_name": "RegionOne" - } -} diff --git a/os_client_config/vendors/catalyst.json b/os_client_config/vendors/catalyst.json deleted file mode 100644 index 3ad7507..0000000 --- a/os_client_config/vendors/catalyst.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "catalyst", - "profile": { - "auth": { - "auth_url": "https://api.cloud.catalyst.net.nz:5000/v2.0" - }, - "regions": [ - "nz-por-1", - "nz_wlg_2" - ], - "image_api_version": "1", - "volume_api_version": "1", - "image_format": "raw" - } -} diff --git a/os_client_config/vendors/citycloud.json b/os_client_config/vendors/citycloud.json deleted file mode 100644 index 097ddfd..0000000 --- a/os_client_config/vendors/citycloud.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "citycloud", - "profile": { - "auth": { - "auth_url": "https://identity1.citycloud.com:5000/v3/" - }, - "regions": [ - "Buf1", - "La1", - "Fra1", - "Lon1", - "Sto2", - "Kna1" - ], - "volume_api_version": "1", - "identity_api_version": "3" - } -} diff --git a/os_client_config/vendors/conoha.json b/os_client_config/vendors/conoha.json deleted file mode 100644 index 5636f09..0000000 --- a/os_client_config/vendors/conoha.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "conoha", - "profile": { - "auth": { - "auth_url": "https://identity.{region_name}.conoha.io" - }, - "regions": [ - "sin1", - "sjc1", - "tyo1" - ], - "identity_api_version": "2" - } -} diff --git a/os_client_config/vendors/datacentred.json b/os_client_config/vendors/datacentred.json deleted file mode 100644 index 2be4a58..0000000 --- a/os_client_config/vendors/datacentred.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "datacentred", - "profile": { - "auth": { - "auth_url": "https://compute.datacentred.io:5000" - }, - "region-name": "sal01", - "identity_api_version": "2", - "image_api_version": "1" - } -} diff --git a/os_client_config/vendors/dreamcompute.json b/os_client_config/vendors/dreamcompute.json deleted file mode 100644 index 8244cf7..0000000 --- a/os_client_config/vendors/dreamcompute.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "dreamcompute", - "profile": { - "auth": { - "auth_url": "https://iad2.dream.io:5000" - }, - "identity_api_version": "3", - "region_name": "RegionOne", - "image_format": "raw" - } -} diff --git a/os_client_config/vendors/dreamhost.json b/os_client_config/vendors/dreamhost.json deleted file mode 100644 index ea2ebac..0000000 --- a/os_client_config/vendors/dreamhost.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "dreamhost", - "profile": { - "status": "deprecated", - "message": "The dreamhost profile is deprecated. Please use the dreamcompute profile instead", - "auth": { - "auth_url": "https://keystone.dream.io" - }, - "identity_api_version": "3", - "region_name": "RegionOne", - "image_format": "raw" - } -} diff --git a/os_client_config/vendors/elastx.json b/os_client_config/vendors/elastx.json deleted file mode 100644 index 1e72482..0000000 --- a/os_client_config/vendors/elastx.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "elastx", - "profile": { - "auth": { - "auth_url": "https://ops.elastx.net:5000" - }, - "identity_api_version": "3", - "region_name": "regionOne" - } -} diff --git a/os_client_config/vendors/entercloudsuite.json b/os_client_config/vendors/entercloudsuite.json deleted file mode 100644 index 6d2fc12..0000000 --- a/os_client_config/vendors/entercloudsuite.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "entercloudsuite", - "profile": { - "auth": { - "auth_url": "https://api.entercloudsuite.com/" - }, - "identity_api_version": "3", - "volume_api_version": "1", - "regions": [ - "it-mil1", - "nl-ams1", - "de-fra1" - ] - } -} diff --git a/os_client_config/vendors/ibmcloud.json b/os_client_config/vendors/ibmcloud.json deleted file mode 100644 index 90962c6..0000000 --- a/os_client_config/vendors/ibmcloud.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "ibmcloud", - "profile": { - "auth": { - "auth_url": "https://identity.open.softlayer.com" - }, - "volume_api_version": "2", - "identity_api_version": "3", - "regions": [ - "london" - ] - } -} diff --git a/os_client_config/vendors/internap.json b/os_client_config/vendors/internap.json deleted file mode 100644 index b67fc06..0000000 --- a/os_client_config/vendors/internap.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "internap", - "profile": { - "auth": { - "auth_url": "https://identity.api.cloud.iweb.com" - }, - "regions": [ - "ams01", - "da01", - "nyj01", - "sin01", - "sjc01" - ], - "identity_api_version": "3", - "floating_ip_source": "None" - } -} diff --git a/os_client_config/vendors/osic.json b/os_client_config/vendors/osic.json deleted file mode 100644 index 484d711..0000000 --- a/os_client_config/vendors/osic.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "osic", - "profile": { - "auth": { - "auth_url": "https://cloud1.osic.org:5000" - }, - "regions": [ - "RegionOne" - ] - } -} diff --git a/os_client_config/vendors/ovh.json b/os_client_config/vendors/ovh.json deleted file mode 100644 index 664f161..0000000 --- a/os_client_config/vendors/ovh.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "ovh", - "profile": { - "auth": { - "auth_url": "https://auth.cloud.ovh.net/" - }, - "regions": [ - "BHS1", - "GRA1", - "SBG1" - ], - "identity_api_version": "3", - "image_format": "raw", - "floating_ip_source": "None" - } -} diff --git a/os_client_config/vendors/rackspace.json b/os_client_config/vendors/rackspace.json deleted file mode 100644 index 3fbbacd..0000000 --- a/os_client_config/vendors/rackspace.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "rackspace", - "profile": { - "auth": { - "auth_url": "https://identity.api.rackspacecloud.com/v2.0/" - }, - "regions": [ - "DFW", - "HKG", - "IAD", - "ORD", - "SYD", - "LON" - ], - "database_service_type": "rax:database", - "compute_service_name": "cloudServersOpenStack", - "image_api_use_tasks": true, - "image_format": "vhd", - "floating_ip_source": "None", - "secgroup_source": "None", - "volume_api_version": "1", - "disable_vendor_agent": { - "vm_mode": "hvm", - "xenapi_use_agent": false - }, - "has_network": false - } -} diff --git a/os_client_config/vendors/switchengines.json b/os_client_config/vendors/switchengines.json deleted file mode 100644 index 46f6325..0000000 --- a/os_client_config/vendors/switchengines.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "switchengines", - "profile": { - "auth": { - "auth_url": "https://keystone.cloud.switch.ch:5000/v2.0" - }, - "regions": [ - "LS", - "ZH" - ], - "volume_api_version": "1", - "image_api_use_tasks": true, - "image_format": "raw" - } -} diff --git a/os_client_config/vendors/ultimum.json b/os_client_config/vendors/ultimum.json deleted file mode 100644 index 4bfd088..0000000 --- a/os_client_config/vendors/ultimum.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "ultimum", - "profile": { - "auth": { - "auth_url": "https://console.ultimum-cloud.com:5000/" - }, - "identity_api_version": "3", - "volume_api_version": "1", - "region-name": "RegionOne" - } -} diff --git a/os_client_config/vendors/unitedstack.json b/os_client_config/vendors/unitedstack.json deleted file mode 100644 index ac8be11..0000000 --- a/os_client_config/vendors/unitedstack.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "unitedstack", - "profile": { - "auth": { - "auth_url": "https://identity.api.ustack.com/v3" - }, - "regions": [ - "bj1", - "gd1" - ], - "volume_api_version": "1", - "identity_api_version": "3", - "image_format": "raw", - "floating_ip_source": "None" - } -} diff --git a/os_client_config/vendors/vexxhost.json b/os_client_config/vendors/vexxhost.json deleted file mode 100644 index aa2cedc..0000000 --- a/os_client_config/vendors/vexxhost.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "vexxhost", - "profile": { - "auth": { - "auth_url": "https://auth.vexxhost.net" - }, - "regions": [ - "ca-ymq-1" - ], - "dns_api_version": "1", - "identity_api_version": "3", - "floating_ip_source": "None" - } -} diff --git a/os_client_config/vendors/zetta.json b/os_client_config/vendors/zetta.json deleted file mode 100644 index 44e9711..0000000 --- a/os_client_config/vendors/zetta.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "zetta", - "profile": { - "auth": { - "auth_url": "https://identity.api.zetta.io/v3" - }, - "regions": [ - "no-osl1" - ], - "identity_api_version": "3", - "dns_api_version": "2" - } -} diff --git a/releasenotes/notes/catch-up-release-notes-e385fad34e9f3d6e.yaml b/releasenotes/notes/catch-up-release-notes-e385fad34e9f3d6e.yaml deleted file mode 100644 index e7b98af..0000000 --- a/releasenotes/notes/catch-up-release-notes-e385fad34e9f3d6e.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -prelude: > - Swiftclient instantiation now provides authentication - information so that long lived swiftclient objects can - reauthenticate if necessary. This should be a temporary - situation until swiftclient supports keystoneauth - sessions at which point os-client-config will instantiate - swiftclient with a keystoneauth session. -features: - - Swiftclient instantiation now provides authentication - information so that long lived swiftclient objects can - reauthenticate if necessary. - - Add support for explicit v2password auth type. - - Add SSL support to VEXXHOST vendor profile. - - Add zetta.io cloud vendor profile. -fixes: - - Fix bug where project_domain_{name,id} was set even - if project_{name,id} was not set. -other: - - HPCloud vendor profile removed due to cloud shutdown. - - RunAbove vendor profile removed due to migration to - OVH. diff --git a/releasenotes/notes/cloud-profile-status-e0d29b5e2f10e95c.yaml b/releasenotes/notes/cloud-profile-status-e0d29b5e2f10e95c.yaml deleted file mode 100644 index b447ed0..0000000 --- a/releasenotes/notes/cloud-profile-status-e0d29b5e2f10e95c.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Add a field to vendor cloud profiles to indicate - active, deprecated and shutdown status. A message to - the user is triggered when attempting to use cloud - with either deprecated or shutdown status. diff --git a/releasenotes/notes/magic-fixes-dca4ae4dac2441a8.yaml b/releasenotes/notes/magic-fixes-dca4ae4dac2441a8.yaml deleted file mode 100644 index 570e4dc..0000000 --- a/releasenotes/notes/magic-fixes-dca4ae4dac2441a8.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -fixes: - - Refactor ``OpenStackConfig._fix_backward_madness()`` into - ``OpenStackConfig.magic_fixes()`` that allows subclasses - to inject more fixup magic into the flow during - ``get_one_cloud()`` processing. diff --git a/releasenotes/notes/make-rest-client-dd3d365632a26fa0.yaml b/releasenotes/notes/make-rest-client-dd3d365632a26fa0.yaml deleted file mode 100644 index 8e34e51..0000000 --- a/releasenotes/notes/make-rest-client-dd3d365632a26fa0.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -deprecations: - - Renamed session_client to make_rest_client. session_client - will continue to be supported for backwards compatability. diff --git a/releasenotes/notes/network-list-e6e9dafdd8446263.yaml b/releasenotes/notes/network-list-e6e9dafdd8446263.yaml deleted file mode 100644 index 8f793c2..0000000 --- a/releasenotes/notes/network-list-e6e9dafdd8446263.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -features: - - Support added for configuring metadata about networks - for a cloud in a list of dicts, rather than in the - external_network and internal_network entries. The dicts - support a name, a routes_externally field, a nat_destination - field and a default_interface field. -deprecations: - - external_network and internal_network are deprecated and - should be replaced with the list of network dicts. diff --git a/releasenotes/notes/option-precedence-1fecab21fdfb2c33.yaml b/releasenotes/notes/option-precedence-1fecab21fdfb2c33.yaml deleted file mode 100644 index 06e6bd2..0000000 --- a/releasenotes/notes/option-precedence-1fecab21fdfb2c33.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -fixes: - - Reverse the order of option selction in - ``OpenStackConfig._validate_auth()`` to prefer auth options - passed in (from argparse) over those found in clouds.yaml. - This allows the application to override config profile - auth settings. diff --git a/releasenotes/notes/sdk-helper-41f8d815cfbcfb00.yaml b/releasenotes/notes/sdk-helper-41f8d815cfbcfb00.yaml deleted file mode 100644 index a18b57d..0000000 --- a/releasenotes/notes/sdk-helper-41f8d815cfbcfb00.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Added helper method for constructing OpenStack SDK - Connection objects. diff --git a/releasenotes/notes/session-client-b581a6e5d18c8f04.yaml b/releasenotes/notes/session-client-b581a6e5d18c8f04.yaml deleted file mode 100644 index 1121901..0000000 --- a/releasenotes/notes/session-client-b581a6e5d18c8f04.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Added kwargs and argparse processing for session_client. -deprecations: - - Renamed simple_client to session_client. simple_client - will remain as an alias for backwards compat. diff --git a/releasenotes/notes/shade-helper-568f8cb372eef6d9.yaml b/releasenotes/notes/shade-helper-568f8cb372eef6d9.yaml deleted file mode 100644 index 70aab0a..0000000 --- a/releasenotes/notes/shade-helper-568f8cb372eef6d9.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Added helper method for constructing shade - OpenStackCloud objects. diff --git a/releasenotes/notes/started-using-reno-242e2b0cd27f9480.yaml b/releasenotes/notes/started-using-reno-242e2b0cd27f9480.yaml deleted file mode 100644 index d7cfb51..0000000 --- a/releasenotes/notes/started-using-reno-242e2b0cd27f9480.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -other: -- Started using reno for release notes. diff --git a/releasenotes/notes/vendor-updates-f11184ba56bb27cf.yaml b/releasenotes/notes/vendor-updates-f11184ba56bb27cf.yaml deleted file mode 100644 index e1d6d41..0000000 --- a/releasenotes/notes/vendor-updates-f11184ba56bb27cf.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -other: - - Add citycloud regions for Buffalo, Frankfurt, Karlskrona and Los Angles - - Add new DreamCompute cloud and deprecate DreamHost cloud diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py deleted file mode 100644 index 282dbd7..0000000 --- a/releasenotes/source/conf.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Os-Client-Config Release Notes documentation build configuration file, created by -# sphinx-quickstart on Thu Nov 5 11:50:32 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'oslosphinx', - 'reno.sphinxext', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'os-client-config Release Notes' -copyright = u'2015, os-client-config developers' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -import pbr.version -occ_version = pbr.version.VersionInfo('os-client-config') -# The short X.Y version. -version = occ_version.canonical_version_string() -# The full version, including alpha/beta/rc tags. -release = occ_version.version_string_with_vcs() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'OCCReleaseNotesdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'OCCReleaseNotes.tex', u'os-client-config Release Notes Documentation', - u'os-client-config developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'occreleasenotes', u'os-client-config Release Notes Documentation', - [u'os-client-config developers'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'OCCReleaseNotes', u'os-client-config Release Notes Documentation', - u'os-client-config developers', 'OCCReleaseNotes', - 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst deleted file mode 100644 index 2f4234a..0000000 --- a/releasenotes/source/index.rst +++ /dev/null @@ -1,18 +0,0 @@ -================================ - os-client-config Release Notes -================================ - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - unreleased - mitaka - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` diff --git a/releasenotes/source/mitaka.rst b/releasenotes/source/mitaka.rst deleted file mode 100644 index e545609..0000000 --- a/releasenotes/source/mitaka.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Mitaka Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/mitaka diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst deleted file mode 100644 index 875030f..0000000 --- a/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -============================ -Current Series Release Notes -============================ - -.. release-notes:: diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1531be8..0000000 --- a/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -PyYAML>=3.1.0 -appdirs>=1.3.0 -keystoneauth1>=2.1.0 -requestsexceptions>=1.1.1 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 07ac625..0000000 --- a/setup.cfg +++ /dev/null @@ -1,34 +0,0 @@ -[metadata] -name = os-client-config -summary = OpenStack Client Configuation Library -description-file = - README.rst -author = OpenStack -author-email = openstack-dev@lists.openstack.org -home-page = http://www.openstack.org/ -classifier = - Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - -[files] -packages = - os_client_config - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html - -[wheel] -universal = 1 diff --git a/setup.py b/setup.py deleted file mode 100755 index 70c2b3f..0000000 --- a/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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. - -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT -import setuptools - -setuptools.setup( - setup_requires=['pbr'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 0138f13..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,21 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -hacking>=0.10.2,<0.11 # Apache-2.0 - -coverage>=3.6 -extras -fixtures>=0.3.14 -jsonschema>=2.0.0,<3.0.0,!=2.5.0 -mock>=1.2 -python-glanceclient>=0.18.0 -python-keystoneclient>=1.1.0 -python-subunit>=0.0.18 -sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 -oslosphinx>=2.5.0,<2.6.0 # Apache-2.0 -oslotest>=1.5.1,<1.6.0 # Apache-2.0 -reno>=0.1.1 # Apache2 -testrepository>=0.0.18 -testscenarios>=0.4 -testtools>=0.9.36,!=1.2.0 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index d2aac05..0000000 --- a/tox.ini +++ /dev/null @@ -1,38 +0,0 @@ -[tox] -minversion = 1.6 -envlist = py34,py27,pypy,pep8 -skipsdist = True - -[testenv] -usedevelop = True -install_command = pip install -U {opts} {packages} -setenv = - VIRTUAL_ENV={envdir} -deps = -r{toxinidir}/test-requirements.txt -commands = python setup.py testr --slowest --testr-args='{posargs}' - -[testenv:pep8] -commands = flake8 - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = python setup.py test --coverage --coverage-package-name=os_client_config --testr-args='{posargs}' - -[testenv:docs] -deps = - {[testenv]deps} - readme -commands = - python setup.py build_sphinx - python setup.py check -r -s - -[testenv:releasenotes] -commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html - -[flake8] -show-source = True -builtins = _ -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes/source/conf.py -