diff --git a/glance_store/_drivers/swift/store.py b/glance_store/_drivers/swift/store.py index 4a4bc31d..6ac77404 100644 --- a/glance_store/_drivers/swift/store.py +++ b/glance_store/_drivers/swift/store.py @@ -48,10 +48,6 @@ DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 200 # 200M ONE_MB = units.k * units.Ki # Here we used the mixed meaning of MB _SWIFT_OPTS = [ - cfg.StrOpt('swift_store_auth_version', default='2', - help=_('Version of the authentication service to use. ' - 'Valid versions are 2 for keystone and 1 for swauth ' - 'and rackspace. (deprecated)')), cfg.BoolOpt('swift_store_auth_insecure', default=False, help=_('If True, swiftclient won\'t check for a valid SSL ' 'certificate when authenticating.')), @@ -728,8 +724,14 @@ class SingleTenantStore(BaseStore): self.ref_params = sutils.SwiftParams(self.conf).params def configure(self, re_raise_bsc=False): - super(SingleTenantStore, self).configure(re_raise_bsc=re_raise_bsc) + # set configuration before super so configure_add can override self.auth_version = self._option_get('swift_store_auth_version') + self.user_domain_id = None + self.user_domain_name = None + self.project_domain_id = None + self.project_domain_name = None + + super(SingleTenantStore, self).configure(re_raise_bsc=re_raise_bsc) def configure_add(self): default_ref = self.conf.glance_store.default_swift_reference @@ -746,8 +748,15 @@ class SingleTenantStore(BaseStore): else: self.scheme = 'swift+https' self.container = self.conf.glance_store.swift_store_container + self.auth_version = default_swift_reference.get('auth_version') self.user = default_swift_reference.get('user') self.key = default_swift_reference.get('key') + self.user_domain_id = default_swift_reference.get('user_domain_id') + self.user_domain_name = default_swift_reference.get('user_domain_name') + self.project_domain_id = default_swift_reference.get( + 'project_domain_id') + self.project_domain_name = default_swift_reference.get( + 'project_domain_name') if not (self.user or self.key): reason = _("A value for swift_store_ref_params is required.") @@ -811,7 +820,7 @@ class SingleTenantStore(BaseStore): if not auth_url.endswith('/'): auth_url += '/' - if self.auth_version == '2': + if self.auth_version in ('2', '3'): try: tenant_name, user = location.user.split(':') except ValueError: @@ -828,6 +837,14 @@ class SingleTenantStore(BaseStore): os_options['region_name'] = self.region os_options['endpoint_type'] = self.endpoint_type os_options['service_type'] = self.service_type + if self.user_domain_id: + os_options['user_domain_id'] = self.user_domain_id + if self.user_domain_name: + os_options['user_domain_name'] = self.user_domain_name + if self.project_domain_id: + os_options['project_domain_id'] = self.project_domain_id + if self.project_domain_name: + os_options['project_domain_name'] = self.project_domain_name return swiftclient.Connection( auth_url, user, location.key, preauthurl=self.conf_endpoint, diff --git a/glance_store/_drivers/swift/utils.py b/glance_store/_drivers/swift/utils.py index 9489c17d..d43abf8b 100644 --- a/glance_store/_drivers/swift/utils.py +++ b/glance_store/_drivers/swift/utils.py @@ -27,23 +27,38 @@ swift_opts = [ default="ref1", help=i18n._('The reference to the default swift account/backing' ' store parameters to use for adding new images.')), + cfg.StrOpt('swift_store_auth_version', default='2', + help=i18n._('Version of the authentication service to use. ' + 'Valid versions are 2 and 3 for keystone and 1 ' + '(deprecated) for swauth and rackspace. ' + '(deprecated - use "auth_version" in ' + 'swift_store_config_file)')), cfg.StrOpt('swift_store_auth_address', help=i18n._('The address where the Swift authentication ' - 'service is listening.(deprecated)')), + 'service is listening. (deprecated - use ' + '"auth_address" in swift_store_config_file)')), cfg.StrOpt('swift_store_user', secret=True, help=i18n._('The user to authenticate against the Swift ' - 'authentication service (deprecated)')), + 'authentication service (deprecated - use "user" ' + 'in swift_store_config_file)')), cfg.StrOpt('swift_store_key', secret=True, help=i18n._('Auth key for the user authenticating against the ' - 'Swift authentication service. (deprecated)')), + 'Swift authentication service. (deprecated - use ' + '"key" in swift_store_config_file)')), cfg.StrOpt('swift_store_config_file', secret=True, help=i18n._('The config file that has the swift account(s)' 'configs.')), ] +_config_defaults = {'user_domain_id': None, + 'user_domain_name': None, + 'project_domain_id': None, + 'project_domain_name': None} + # NOTE(bourke): The default dict_type is collections.OrderedDict in py27, but # we must set manually for compatibility with py26 -CONFIG = configparser.SafeConfigParser(dict_type=OrderedDict) +CONFIG = configparser.SafeConfigParser(defaults=_config_defaults, + dict_type=OrderedDict) LOG = logging.getLogger(__name__) @@ -74,6 +89,11 @@ class SwiftParams(object): default['user'] = glance_store.swift_store_user default['key'] = glance_store.swift_store_key default['auth_address'] = glance_store.swift_store_auth_address + default['project_domain_id'] = None + default['project_domain_name'] = None + default['user_domain_id'] = None + default['user_domain_name'] = None + default['auth_version'] = glance_store.swift_store_auth_version return {glance_store.default_swift_reference: default} return {} @@ -92,12 +112,25 @@ class SwiftParams(object): reason=msg) account_params = {} account_references = CONFIG.sections() + for ref in account_references: reference = {} try: - reference['auth_address'] = CONFIG.get(ref, 'auth_address') - reference['user'] = CONFIG.get(ref, 'user') - reference['key'] = CONFIG.get(ref, 'key') + for param in ('auth_address', + 'user', + 'key', + 'project_domain_id', + 'project_domain_name', + 'user_domain_id', + 'user_domain_name'): + reference[param] = CONFIG.get(ref, param) + + try: + reference['auth_version'] = CONFIG.get(ref, 'auth_version') + except configparser.NoOptionError: + av = self.conf.glance_store.swift_store_auth_version + reference['auth_version'] = av + account_params[ref] = reference except (ValueError, SyntaxError, configparser.NoOptionError) as e: LOG.exception(i18n._("Invalid format of swift store config" diff --git a/glance_store/tests/etc/glance-swift.conf b/glance_store/tests/etc/glance-swift.conf index 956433b2..c5af3ddd 100644 --- a/glance_store/tests/etc/glance-swift.conf +++ b/glance_store/tests/etc/glance-swift.conf @@ -6,6 +6,9 @@ auth_address = example.com [ref2] user = user2 key = key2 +user_domain_id = default +project_domain_id = default +auth_version = 3 auth_address = http://example.com [store_2] diff --git a/glance_store/tests/unit/test_swift_store.py b/glance_store/tests/unit/test_swift_store.py index b3225764..f738cf94 100644 --- a/glance_store/tests/unit/test_swift_store.py +++ b/glance_store/tests/unit/test_swift_store.py @@ -1041,6 +1041,15 @@ class TestStoreAuthV2(TestStoreAuthV1): self.assertEqual('swift', loc.store_name) +class TestStoreAuthV3(TestStoreAuthV1): + + def getConfig(self): + conf = super(TestStoreAuthV3, self).getConfig() + conf['swift_store_auth_version'] = '3' + conf['swift_store_user'] = 'tenant:user1' + return conf + + class FakeConnection(object): def __init__(self, authurl, user, key, retries=5, preauthurl=None, preauthtoken=None, starting_backoff=1, tenant_name=None, @@ -1202,6 +1211,52 @@ class TestSingleTenantStoreConnections(base.StoreBaseTest): self.location.parse_uri, self.location.uri) + def test_ref_overrides_defaults(self): + self.config(swift_store_auth_version='2', + swift_store_user='testuser', + swift_store_key='testpass', + swift_store_auth_address='testaddress', + swift_store_endpoint_type='internalURL', + swift_store_config_file='somefile') + + self.store.ref_params = {'ref1': {'auth_address': 'authurl.com', + 'auth_version': '3', + 'user': 'user:pass', + 'user_domain_id': 'default', + 'user_domain_name': 'ignored', + 'project_domain_id': 'default', + 'project_domain_name': 'ignored'}} + + self.store.configure() + + self.assertEqual('user:pass', self.store.user) + self.assertEqual('3', self.store.auth_version) + self.assertEqual('authurl.com', self.store.auth_address) + self.assertEqual('default', self.store.user_domain_id) + self.assertEqual('ignored', self.store.user_domain_name) + self.assertEqual('default', self.store.project_domain_id) + self.assertEqual('ignored', self.store.project_domain_name) + + def test_with_v3_auth(self): + self.store.ref_params = {'ref1': {'auth_address': 'authurl.com', + 'auth_version': '3', + 'user': 'user:pass', + 'key': 'password', + 'user_domain_id': 'default', + 'user_domain_name': 'ignored', + 'project_domain_id': 'default', + 'project_domain_name': 'ignored'}} + self.store.configure() + connection = self.store.get_connection(self.location) + self.assertEqual('3', connection.auth_version) + self.assertEqual(connection.os_options, + {'service_type': 'object-store', + 'endpoint_type': 'publicURL', + 'user_domain_id': 'default', + 'user_domain_name': 'ignored', + 'project_domain_id': 'default', + 'project_domain_name': 'ignored'}) + class TestMultiTenantStoreConnections(base.StoreBaseTest): def setUp(self):