summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rst46
-rw-r--r--os_client_config/config.py119
-rw-r--r--os_client_config/defaults_dict.py27
-rw-r--r--os_client_config/vendors.py9
4 files changed, 118 insertions, 83 deletions
diff --git a/README.rst b/README.rst
index 30819f3..e6dbed0 100644
--- a/README.rst
+++ b/README.rst
@@ -57,23 +57,27 @@ An example config file is probably helpful:
clouds:
mordred:
cloud: hp
- username: mordred@inaugust.com
- password: XXXXXXXXX
- project_id: mordred@inaugust.com
+ auth:
+ username: mordred@inaugust.com
+ password: XXXXXXXXX
+ project_name: mordred@inaugust.com
region_name: region-b.geo-1
dns_service_type: hpext:dns
+ compute_api_version: 1.1
monty:
- auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0
- username: monty.taylor@hp.com
- password: XXXXXXXX
- project_id: monty.taylor@hp.com-default-tenant
+ auth:
+ auth_url: https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0
+ username: monty.taylor@hp.com
+ password: XXXXXXXX
+ project_name: monty.taylor@hp.com-default-tenant
region_name: region-b.geo-1
dns_service_type: hpext:dns
infra:
cloud: rackspace
- username: openstackci
- password: XXXXXXXX
- project_id: 610275
+ auth:
+ username: openstackci
+ password: XXXXXXXX
+ project_id: 610275
region_name: DFW,ORD,IAD
You may note a few things. First, since auth_url settings are silly
@@ -92,6 +96,17 @@ 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.
+
Cache Settings
--------------
@@ -107,9 +122,10 @@ understands a simple set of cache control settings.
clouds:
mordred:
cloud: hp
- username: mordred@inaugust.com
- password: XXXXXXXXX
- project_id: mordred@inaugust.com
+ auth:
+ username: mordred@inaugust.com
+ password: XXXXXXXXX
+ project_name: mordred@inaugust.com
region_name: region-b.geo-1
dns_service_type: hpext:dns
@@ -143,6 +159,4 @@ Or, get all of the clouds.
print(cloud.name, cloud.region, cloud.config)
* 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
+* Source: http://git.openstack.org/cgit/stackforge/os-client-config
diff --git a/os_client_config/config.py b/os_client_config/config.py
index 9656f6c..bfc030e 100644
--- a/os_client_config/config.py
+++ b/os_client_config/config.py
@@ -17,8 +17,12 @@ import os
import yaml
+try:
+ import keystoneclient.auth as ksc_auth
+except ImportError:
+ ksc_auth = None
+
from os_client_config import cloud_config
-from os_client_config import defaults_dict
from os_client_config import exceptions
from os_client_config import vendors
@@ -32,7 +36,6 @@ CACHE_PATH = os.path.join(os.path.expanduser(
os.environ.get('XDG_CACHE_PATH', os.path.join('~', '.cache'))),
'openstack')
BOOL_KEYS = ('insecure', 'cache')
-REQUIRED_VALUES = ('auth_url', 'username', 'password')
VENDOR_SEARCH_PATH = [os.getcwd(), CONFIG_HOME, '/etc/openstack']
VENDOR_FILES = [
os.path.join(d, 'clouds-public.yaml') for d in VENDOR_SEARCH_PATH]
@@ -44,31 +47,36 @@ def get_boolean(value):
return False
+def _get_os_environ(defaults):
+ for (k, v) in os.environ.items():
+ if k.startswith('OS_'):
+ newkey = k[3:].lower()
+ defaults[newkey] = v
+ return defaults
+
+
+def _auth_update(old_dict, new_dict):
+ """Like dict.update, except handling the nested dict called auth."""
+ for (k, v) in new_dict.items():
+ if k == 'auth':
+ old_dict[k].update(v)
+ else:
+ old_dict[k] = v
+ return old_dict
+
+
class OpenStackConfig(object):
def __init__(self, config_files=None, vendor_files=None):
self._config_files = config_files or CONFIG_FILES
self._vendor_files = vendor_files or VENDOR_FILES
- defaults = defaults_dict.DefaultsDict()
- defaults.add('username')
- defaults.add('user_domain_name')
- defaults.add('password')
- defaults.add(
- 'project_name', defaults.get('username', None),
- also='tenant_name')
- defaults.add('project_id', also='tenant_id')
- defaults.add('project_domain_name')
- defaults.add('auth_url')
- defaults.add('region_name')
- defaults.add('cache')
- defaults.add('auth_token')
- defaults.add('insecure')
- defaults.add('endpoint_type')
- defaults.add('cacert')
- defaults.add('auth_type')
-
- self.defaults = defaults
+ defaults = dict(
+ auth_plugin='password',
+ auth=dict(),
+ compute_api_version='1.1',
+ )
+ self.defaults = _get_os_environ(defaults)
# use a config file if it exists where expected
self.cloud_config = self._load_config_file()
@@ -134,15 +142,15 @@ class OpenStackConfig(object):
cloud_name = our_cloud['cloud']
vendor_file = self._load_vendor_file()
if vendor_file and cloud_name in vendor_file['public-clouds']:
- cloud.update(vendor_file['public-clouds'][cloud_name])
+ _auth_update(cloud, vendor_file['public-clouds'][cloud_name])
else:
try:
- cloud.update(vendors.CLOUD_DEFAULTS[cloud_name])
+ _auth_update(cloud, vendors.CLOUD_DEFAULTS[cloud_name])
except KeyError:
# Can't find the requested vendor config, go about business
pass
- cloud.update(our_cloud)
+ _auth_update(cloud, our_cloud)
if 'cloud' in cloud:
del cloud['cloud']
@@ -196,6 +204,53 @@ class OpenStackConfig(object):
new_args.update(os_args)
return new_args
+ def _find_winning_auth_value(self, opt, config):
+ opt_name = opt.name.replace('-', '_')
+ if opt_name in config:
+ return config[opt_name]
+ else:
+ for d_opt in opt.deprecated_opts:
+ d_opt_name = d_opt.name.replace('-', '_')
+ if d_opt_name in config:
+ return config[d_opt_name]
+
+ def _validate_auth(self, config):
+ # May throw a keystoneclient.exceptions.NoMatchingPlugin
+ plugin_options = ksc_auth.get_plugin_class(
+ config['auth_plugin']).get_options()
+
+ for p_opt in plugin_options:
+ # if it's in config.auth, win, kill it from config dict
+ # if it's in config and not in config.auth, move it
+ # deprecated loses to current
+ # provided beats default, deprecated or not
+ winning_value = self._find_winning_auth_value(
+ p_opt, config['auth'])
+ if not winning_value:
+ winning_value = self._find_winning_auth_value(p_opt, config)
+
+ # if the plugin tells us that this value is required
+ # then error if it's doesn't exist now
+ if not winning_value and p_opt.required:
+ raise exceptions.OpenStackConfigException(
+ 'Unable to find auth information for cloud'
+ ' {cloud} in config files {files}'
+ ' or environment variables. Missing value {auth_key}'
+ ' required for auth plugin {plugin}'.format(
+ cloud=cloud, files=','.join(self._config_files),
+ auth_key=p_opt.name, plugin=config['auth_plugin']))
+
+ # Clean up after ourselves
+ for opt in [p_opt.name] + [o.name for o in p_opt.deprecated_opts]:
+ opt = opt.replace('-', '_')
+ config.pop(opt, None)
+ config['auth'].pop(opt, None)
+
+ if winning_value:
+ config['auth'][p_opt.name.replace('-', '_')] = winning_value
+
+ return config
+
def get_one_cloud(self, cloud=None, validate=True,
argparse=None, **kwargs):
"""Retrieve a single cloud configuration and merge additional options
@@ -229,20 +284,8 @@ class OpenStackConfig(object):
if type(config[key]) is not bool:
config[key] = get_boolean(config[key])
- if validate:
- for key in REQUIRED_VALUES:
- if key not in config or not config[key]:
- raise exceptions.OpenStackConfigException(
- 'Unable to find full auth information for cloud'
- ' {cloud} in config files {files}'
- ' or environment variables.'.format(
- cloud=cloud, files=','.join(self._config_files)))
- if 'project_name' not in config and 'project_id' not in config:
- raise exceptions.OpenStackConfigException(
- 'Neither project_name or project_id information found'
- ' for cloud {cloud} in config files {files}'
- ' or environment variables.'.format(
- cloud=cloud, files=','.join(self._config_files)))
+ if validate and ksc_auth:
+ config = self._validate_auth(config)
# If any of the defaults reference other values, we need to expand
for (key, value) in config.items():
diff --git a/os_client_config/defaults_dict.py b/os_client_config/defaults_dict.py
deleted file mode 100644
index 43de77b..0000000
--- a/os_client_config/defaults_dict.py
+++ /dev/null
@@ -1,27 +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 os
-
-
-class DefaultsDict(dict):
-
- def add(self, key, default_value=None, also=None, prefix=None):
- if prefix:
- key = '%s_%s' % (prefix.replace('-', '_'), key)
- if also:
- value = os.environ.get(also, default_value)
- value = os.environ.get('OS_%s' % key.upper(), default_value)
- if value is not None:
- self.__setitem__(key, value)
diff --git a/os_client_config/vendors.py b/os_client_config/vendors.py
index c78aaca..eca4376 100644
--- a/os_client_config/vendors.py
+++ b/os_client_config/vendors.py
@@ -1,3 +1,4 @@
+# flake8: noqa
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -14,12 +15,16 @@
CLOUD_DEFAULTS = dict(
hp=dict(
- auth_url='https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0',
+ auth=dict(
+ auth_url='https://region-b.geo-1.identity.hpcloudsvc.com:35357/v2.0',
+ ),
region_name='region-b.geo-1',
dns_service_type='hpext:dns',
),
rackspace=dict(
- auth_url='https://identity.api.rackspacecloud.com/v2.0/',
+ auth=dict(
+ auth_url='https://identity.api.rackspacecloud.com/v2.0/',
+ ),
database_service_type='rax:database',
image_api_version='2',
)