diff --git a/heat/common/config.py b/heat/common/config.py index 92140824c2..dac70a0576 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -60,6 +60,13 @@ service_opts = [ 'SSL is used.')), cfg.StrOpt('region_name_for_services', help=_('Default region name used to get services endpoints.')), + cfg.StrOpt('region_name_for_shared_services', + help=_('Region name for shared services endpoints.')), + cfg.ListOpt('shared_services_types', + default=['image', 'volume', 'volumev2'], + help=_('The shared services located in the other region.' + 'Needs region_name_for_shared_services option to ' + 'be set for this to take effect.')), cfg.StrOpt('heat_stack_user_role', default="heat_stack_user", help=_('Keystone role for heat template-defined users.')), diff --git a/heat/engine/clients/client_plugin.py b/heat/engine/clients/client_plugin.py index ab737da6f9..36a2f933a8 100644 --- a/heat/engine/clients/client_plugin.py +++ b/heat/engine/clients/client_plugin.py @@ -88,7 +88,14 @@ class ClientPlugin(object): pass def _get_region_name(self): - return self.context.region_name or cfg.CONF.region_name_for_services + reg = self.context.region_name or cfg.CONF.region_name_for_services + # If Shared Services configured, override region for image/volumes + shared_services_region_name = cfg.CONF.region_name_for_shared_services + shared_services_types = cfg.CONF.shared_services_types + if shared_services_region_name: + if set(self.service_types) & set(shared_services_types): + reg = shared_services_region_name + return reg def url_for(self, **kwargs): keystone_session = self.context.keystone_session diff --git a/heat/engine/clients/os/keystone/__init__.py b/heat/engine/clients/os/keystone/__init__.py index 1fa5b65277..3c4067616d 100644 --- a/heat/engine/clients/os/keystone/__init__.py +++ b/heat/engine/clients/os/keystone/__init__.py @@ -27,7 +27,8 @@ class KeystoneClientPlugin(client_plugin.ClientPlugin): service_types = [IDENTITY] = ['identity'] def _create(self): - return hkc.KeystoneClient(self.context) + region_name = self._get_region_name() + return hkc.KeystoneClient(self.context, region_name) def is_not_found(self, ex): return isinstance(ex, (ks_exceptions.NotFound, diff --git a/heat/engine/clients/os/keystone/heat_keystoneclient.py b/heat/engine/clients/os/keystone/heat_keystoneclient.py index 0d9130dfc1..d99c5fab45 100644 --- a/heat/engine/clients/os/keystone/heat_keystoneclient.py +++ b/heat/engine/clients/os/keystone/heat_keystoneclient.py @@ -58,7 +58,7 @@ class KsClientWrapper(object): directly instantiate instances of this class inside resources themselves. """ - def __init__(self, context): + def __init__(self, context, region_name): # If a trust_id is specified in the context, we immediately # authenticate so we can populate the context with a trust token # otherwise, we delay client authentication until needed to avoid @@ -75,6 +75,7 @@ class KsClientWrapper(object): self._admin_auth = None self._domain_admin_auth = None self._domain_admin_client = None + self._region_name = region_name self.session = self.context.keystone_session self.v3_endpoint = self.context.keystone_v3_endpoint @@ -123,8 +124,7 @@ class KsClientWrapper(object): importutils.import_module('keystonemiddleware.auth_token') auth_region = cfg.CONF.keystone_authtoken.region_name if not auth_region: - auth_region = (self.context.region_name or - cfg.CONF.region_name_for_services) + auth_region = self._region_name return auth_region @property @@ -596,9 +596,9 @@ class KeystoneClient(object): needs to be initialized. """ - def __new__(cls, context): + def __new__(cls, context, region_name=None): if cfg.CONF.keystone_backend == _default_keystone_backend: - return KsClientWrapper(context) + return KsClientWrapper(context, region_name) else: return importutils.import_object( cfg.CONF.keystone_backend, diff --git a/heat/engine/resources/openstack/heat/deployed_server.py b/heat/engine/resources/openstack/heat/deployed_server.py index cd62dae248..8402e107d8 100644 --- a/heat/engine/resources/openstack/heat/deployed_server.py +++ b/heat/engine/resources/openstack/heat/deployed_server.py @@ -150,6 +150,8 @@ class DeployedServer(server_base.BaseServer): ), } + default_client_name = 'heat' + def __init__(self, name, json_snippet, stack): super(DeployedServer, self).__init__(name, json_snippet, stack) self._register_access_key() diff --git a/heat/engine/resources/server_base.py b/heat/engine/resources/server_base.py index 5f0b5d00c1..ce12693a00 100644 --- a/heat/engine/resources/server_base.py +++ b/heat/engine/resources/server_base.py @@ -61,14 +61,15 @@ class BaseServer(stack_user.StackUser): return container_name, object_name + def _get_region_name(self): + return self.client_plugin()._get_region_name() + def _populate_deployments_metadata(self, meta, props): meta['deployments'] = meta.get('deployments', []) meta['os-collect-config'] = meta.get('os-collect-config', {}) occ = meta['os-collect-config'] collectors = list(self.default_collectors) occ['collectors'] = collectors - region_name = (self.context.region_name or - cfg.CONF.region_name_for_services) # set existing values to None to override any boot-time config occ_keys = ('heat', 'zaqar', 'cfn', 'request') @@ -89,7 +90,7 @@ class BaseServer(stack_user.StackUser): 'project_id': self.stack.stack_user_project_id, 'stack_id': self.stack.identifier().stack_path(), 'resource_name': self.name, - 'region_name': region_name}}) + 'region_name': self._get_region_name()}}) collectors.append('heat') elif self.transport_zaqar_message(props): @@ -101,7 +102,7 @@ class BaseServer(stack_user.StackUser): fallback_endpoint=self.context.auth_url), 'project_id': self.stack.stack_user_project_id, 'queue_id': queue_id, - 'region_name': region_name}}) + 'region_name': self._get_region_name()}}) collectors.append('zaqar') elif self.transport_poll_server_cfn(props): diff --git a/heat/engine/resources/signal_responder.py b/heat/engine/resources/signal_responder.py index b9127d9f19..34c191c512 100644 --- a/heat/engine/resources/signal_responder.py +++ b/heat/engine/resources/signal_responder.py @@ -93,6 +93,9 @@ class SignalResponder(stack_user.StackUser): return self.properties.get( self.SIGNAL_TRANSPORT) == self.ZAQAR_SIGNAL + def _get_region_name(self): + return self.client_plugin('heat')._get_region_name() + def _get_heat_signal_credentials(self): """Return OpenStack credentials that can be used to send a signal. @@ -110,8 +113,7 @@ class SignalResponder(stack_user.StackUser): 'password': self.password, 'project_id': self.stack.stack_user_project_id, 'domain_id': self.keystone().stack_domain_id, - 'region_name': (self.context.region_name or - cfg.CONF.region_name_for_services)} + 'region_name': self._get_region_name()} def _get_ec2_signed_url(self, signal_type=SIGNAL): """Create properly formatted and pre-signed URL. diff --git a/releasenotes/notes/support-shared-servies-multi-region-mode-d9f167fb52d9c0a8.yaml b/releasenotes/notes/support-shared-servies-multi-region-mode-d9f167fb52d9c0a8.yaml new file mode 100644 index 0000000000..756730f9c1 --- /dev/null +++ b/releasenotes/notes/support-shared-servies-multi-region-mode-d9f167fb52d9c0a8.yaml @@ -0,0 +1,4 @@ +--- +features: + - Support shared services in multi region mode. The services are declared + in a list in config. shared_services_types=image, volume, volumev2.