summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-01-21 20:43:56 +0000
committerGerrit Code Review <review@openstack.org>2015-01-21 20:43:56 +0000
commit3b3eb4cc3a730c06e0c8a00c9e3323755d2c50cd (patch)
tree6e3c97b0a35620c8544f8dc6e5e1c21fcee79918
parent7fdd3a2d173e306f362b9489d8f1e8f349d67147 (diff)
parentc75daaa1f9f0881cae63337e0d9799555f569c06 (diff)
downloados-client-config-3b3eb4cc3a730c06e0c8a00c9e3323755d2c50cd.tar.gz
Merge "Support keystone auth plugins in a generic way."
-rw-r--r--README.rst42
-rw-r--r--os_client_config/config.py87
-rw-r--r--os_client_config/vendors.py9
3 files changed, 104 insertions, 34 deletions
diff --git a/README.rst b/README.rst
index 4981422..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
diff --git a/os_client_config/config.py b/os_client_config/config.py
index 9c3ca87..67436fa 100644
--- a/os_client_config/config.py
+++ b/os_client_config/config.py
@@ -17,6 +17,11 @@ 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 exceptions
from os_client_config import vendors
@@ -31,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,7 +48,7 @@ def get_boolean(value):
def _get_os_environ():
- ret = dict()
+ ret = dict(auth_plugin='password', auth=dict())
for (k, v) in os.environ.items():
if k.startswith('OS_'):
newkey = k[3:].lower()
@@ -52,6 +56,16 @@ def _get_os_environ():
return ret
+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):
@@ -124,15 +138,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']
@@ -186,6 +200,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
@@ -219,20 +280,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/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',
)