Retire repo

This repo was created by accident, use deb-python-os-client-config
instead.

Needed-By: I1ac1a06931c8b6dd7c2e73620a0302c29e605f03
Change-Id: I81894aea69b9d09b0977039623c26781093a397a
This commit is contained in:
Andreas Jaeger 2017-04-17 19:25:35 +02:00
parent a6840f69ff
commit 8011cb177c
78 changed files with 13 additions and 6199 deletions

View File

@ -1,7 +0,0 @@
[run]
branch = True
source = os_client_config
omit = os_client_config/tests/*,os_client_config/openstack/*
[report]
ignore_errors = True

54
.gitignore vendored
View File

@ -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?

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/os-client-config.git

View File

@ -1,3 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>

View File

@ -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

View File

@ -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

View File

@ -1,4 +0,0 @@
os-client-config Style Commandments
===============================================
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/

175
LICENSE
View File

@ -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.

View File

@ -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 <http://developer.openstack.org/sdks/python/openstacksdk/users/guides/connect_from_config.html>`_
Constructing shade objects
--------------------------
If what you want to do is get a
`shade <http://docs.openstack.org/infra/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

13
README.txt Normal file
View File

@ -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.

View File

@ -1,10 +0,0 @@
=============
API Reference
=============
.. module:: os_client_config
:synopsis: OpenStack client configuration
.. autoclass:: os_client_config.OpenStackConfig
:members:
:inherited-members:

View File

@ -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}

View File

@ -1,4 +0,0 @@
============
Contributing
============
.. include:: ../../CONTRIBUTING.rst

View File

@ -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`

View File

@ -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

View File

@ -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.

View File

@ -1,5 +0,0 @@
=============
Release Notes
=============
.. release-notes::

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}

View File

@ -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

View File

@ -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"
}

View File

@ -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()

View File

@ -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."""

View File

@ -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"
]
}

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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')

View File

@ -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))

View File

@ -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"
]
}

View File

@ -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)

View File

@ -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"
}
}

View File

@ -1,7 +0,0 @@
{
"name": "bluebox",
"profile": {
"volume_api_version": "1",
"region_name": "RegionOne"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -1,14 +0,0 @@
{
"name": "conoha",
"profile": {
"auth": {
"auth_url": "https://identity.{region_name}.conoha.io"
},
"regions": [
"sin1",
"sjc1",
"tyo1"
],
"identity_api_version": "2"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -1,10 +0,0 @@
{
"name": "elastx",
"profile": {
"auth": {
"auth_url": "https://ops.elastx.net:5000"
},
"identity_api_version": "3",
"region_name": "regionOne"
}
}

View File

@ -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"
]
}
}

View File

@ -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"
]
}
}

View File

@ -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"
}
}

View File

@ -1,11 +0,0 @@
{
"name": "osic",
"profile": {
"auth": {
"auth_url": "https://cloud1.osic.org:5000"
},
"regions": [
"RegionOne"
]
}
}

View File

@ -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"
}
}

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -1,4 +0,0 @@
---
deprecations:
- Renamed session_client to make_rest_client. session_client
will continue to be supported for backwards compatability.

View File

@ -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.

View File

@ -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.

View File

@ -1,4 +0,0 @@
---
features:
- Added helper method for constructing OpenStack SDK
Connection objects.

View File

@ -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.

View File

@ -1,4 +0,0 @@
---
features:
- Added helper method for constructing shade
OpenStackCloud objects.

View File

@ -1,3 +0,0 @@
---
other:
- Started using reno for release notes.

View File

@ -1,4 +0,0 @@
---
other:
- Add citycloud regions for Buffalo, Frankfurt, Karlskrona and Los Angles
- Add new DreamCompute cloud and deprecate DreamHost cloud

View File

@ -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
# "<project> v<release> 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 <link> 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

View File

@ -1,18 +0,0 @@
================================
os-client-config Release Notes
================================
Contents
========
.. toctree::
:maxdepth: 2
unreleased
mitaka
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

View File

@ -1,6 +0,0 @@
===================================
Mitaka Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/mitaka

View File

@ -1,5 +0,0 @@
============================
Current Series Release Notes
============================
.. release-notes::

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

38
tox.ini
View File

@ -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