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 -