diff options
-rw-r--r-- | doc/source/vendor-support.rst | 4 | ||||
-rw-r--r-- | os_client_config/config.py | 87 | ||||
-rw-r--r-- | os_client_config/tests/test_config.py | 15 |
3 files changed, 97 insertions, 9 deletions
diff --git a/doc/source/vendor-support.rst b/doc/source/vendor-support.rst index c4f5a4f..77d10eb 100644 --- a/doc/source/vendor-support.rst +++ b/doc/source/vendor-support.rst @@ -145,8 +145,8 @@ https://auth.cloud.ovh.net/v2.0 ============== ================ Region Name Human Name ============== ================ -SBG-1 Strassbourg, FR -GRA-1 Gravelines, FR +SBG1 Strassbourg, FR +GRA1 Gravelines, FR ============== ================ * Images must be in `raw` format diff --git a/os_client_config/config.py b/os_client_config/config.py index e8e76a5..1b6193e 100644 --- a/os_client_config/config.py +++ b/os_client_config/config.py @@ -17,7 +17,10 @@ import os import warnings import appdirs -from keystoneauth1 import loading +try: + from keystoneauth1 import loading +except ImportError: + loading = None import yaml from os_client_config import cloud_config @@ -382,8 +385,13 @@ class OpenStackConfig(object): os_args = dict() new_args = dict() for (key, val) in iter(args.items()): + if type(args[key]) == dict: + # dive into the auth dict + new_args[key] = self._fix_args(args[key]) + continue + key = key.replace('-', '_') - if key.startswith('os'): + if key.startswith('os_'): os_args[key[3:]] = val else: new_args[key] = val @@ -395,7 +403,9 @@ class OpenStackConfig(object): if opt_name in config: return config[opt_name] else: - for d_opt in opt.deprecated: + deprecated = getattr(opt, 'deprecated', getattr( + opt, 'deprecated_opts', [])) + for d_opt in deprecated: d_opt_name = d_opt.name.replace('-', '_') if d_opt_name in config: return config[d_opt_name] @@ -412,6 +422,54 @@ class OpenStackConfig(object): config['auth']['token'] = None return loading.get_plugin_loader(config['auth_type']) + def _validate_auth_ksc(self, config): + try: + import keystoneclient.auth as ksc_auth + except ImportError: + return config + + # May throw a keystoneclient.exceptions.NoMatchingPlugin + plugin_options = ksc_auth.get_plugin_class( + config['auth_type']).get_options() + + for p_opt in plugin_options: + # if it's in 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.get('auth_type'))) + + # Clean up after ourselves + for opt in [p_opt.name] + [o.name for o in p_opt.deprecated_opts]: + opt = opt.replace('-', '_') + config.pop(opt, None) + config['auth'].pop(opt, None) + + if winning_value: + # Prefer the plugin configuration dest value if the value's key + # is marked as depreciated. + if p_opt.dest is None: + config['auth'][p_opt.name.replace('-', '_')] = ( + winning_value) + else: + config['auth'][p_opt.dest] = winning_value + + return config + def _validate_auth(self, config, loader): # May throw a keystoneauth1.exceptions.NoMatchingPlugin @@ -490,12 +548,27 @@ class OpenStackConfig(object): if type(config[key]) is not bool: config[key] = get_boolean(config[key]) - loader = self._get_auth_loader(config) - if validate: - config = self._validate_auth(config, loader) - auth_plugin = loader.load_from_options(**config['auth']) + if loading: + if validate: + try: + loader = self._get_auth_loader(config) + config = self._validate_auth(config, loader) + auth_plugin = loader.load_from_options(**config['auth']) + except Exception as e: + # We WANT the ksa exception normally + # but OSC can't handle it right now, so we try deferring + # to ksc. If that ALSO fails, it means there is likely + # a deeper issue, so we assume the ksa error was correct + auth_plugin = None + try: + config = self._validate_auth_ksc(config) + except Exception: + raise e + else: + auth_plugin = None else: auth_plugin = None + config = self._validate_auth_ksc(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/tests/test_config.py b/os_client_config/tests/test_config.py index b4320ad..3331b33 100644 --- a/os_client_config/tests/test_config.py +++ b/os_client_config/tests/test_config.py @@ -106,6 +106,21 @@ class TestConfig(base.TestCase): 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]) |