diff --git a/config.yaml b/config.yaml index a129b3d7..dbe20e50 100644 --- a/config.yaml +++ b/config.yaml @@ -207,6 +207,19 @@ options: 192.168.0.0/24) . This network will be used for public endpoints. + endpoint-public-name: + type: string + default: + description: | + The hostname or address of the public endpoints created for cinder + in the keystone identity provider. + . + This value will be used for public endpoints. For example, an + endpoint-public-name set to 'cinder.example.com' with ssl enabled will + create two public endpoints for cinder. + . + https://cinder.example.com:443/v1/$(tenant_id)s and + https://cinder.example.com:443/v2/$(tenant_id)s prefer-ipv6: type: boolean default: False diff --git a/hooks/charmhelpers/contrib/openstack/ip.py b/hooks/charmhelpers/contrib/openstack/ip.py index 29bbddcb..fbc313e2 100644 --- a/hooks/charmhelpers/contrib/openstack/ip.py +++ b/hooks/charmhelpers/contrib/openstack/ip.py @@ -26,8 +26,6 @@ from charmhelpers.contrib.network.ip import ( ) from charmhelpers.contrib.hahelpers.cluster import is_clustered -from functools import partial - PUBLIC = 'public' INTERNAL = 'int' ADMIN = 'admin' @@ -35,15 +33,18 @@ ADMIN = 'admin' ADDRESS_MAP = { PUBLIC: { 'config': 'os-public-network', - 'fallback': 'public-address' + 'fallback': 'public-address', + 'override': 'endpoint-public-name', }, INTERNAL: { 'config': 'os-internal-network', - 'fallback': 'private-address' + 'fallback': 'private-address', + 'override': 'endpoint-internal-name', }, ADMIN: { 'config': 'os-admin-network', - 'fallback': 'private-address' + 'fallback': 'private-address', + 'override': 'endpoint-admin-name', } } @@ -57,13 +58,35 @@ def canonical_url(configs, endpoint_type=PUBLIC): :param endpoint_type: str endpoint type to resolve. :param returns: str base URL for services on the current service unit. """ + scheme = _get_scheme(configs) + + # Allow the user to override the address which is used. This is + # useful for proxy services or exposing a public endpoint url, etc. + override_key = ADDRESS_MAP[endpoint_type]['override'] + addr_override = config(override_key) + if addr_override: + address = addr_override + else: + address = resolve_address(endpoint_type) + if is_ipv6(address): + address = "[{}]".format(address) + + return '%s://%s' % (scheme, address) + + +def _get_scheme(configs): + """Returns the scheme to use for the url (either http or https) + depending upon whether https is in the configs value. + + :param configs: OSTemplateRenderer config templating object to inspect + for a complete https context. + :returns: either 'http' or 'https' depending on whether https is + configured within the configs context. + """ scheme = 'http' if 'https' in configs.complete_contexts(): scheme = 'https' - address = resolve_address(endpoint_type) - if is_ipv6(address): - address = "[{}]".format(address) - return '%s://%s' % (scheme, address) + return scheme def resolve_address(endpoint_type=PUBLIC): @@ -109,38 +132,3 @@ def resolve_address(endpoint_type=PUBLIC): "clustered=%s)" % (net_type, clustered)) return resolved_address - - -def endpoint_url(configs, url_template, port, endpoint_type=PUBLIC, - override=None): - """Returns the correct endpoint URL to advertise to Keystone. - - This method provides the correct endpoint URL which should be advertised to - the keystone charm for endpoint creation. This method allows for the url to - be overridden to force a keystone endpoint to have specific URL for any of - the defined scopes (admin, internal, public). - - :param configs: OSTemplateRenderer config templating object to inspect - for a complete https context. - :param url_template: str format string for creating the url template. Only - two values will be passed - the scheme+hostname - returned by the canonical_url and the port. - :param endpoint_type: str endpoint type to resolve. - :param override: str the name of the config option which overrides the - endpoint URL defined by the charm itself. None will - disable any overrides (default). - """ - if override: - # Return any user-defined overrides for the keystone endpoint URL. - user_value = config(override) - if user_value: - return user_value.strip() - - return url_template % (canonical_url(configs, endpoint_type), port) - - -public_endpoint = partial(endpoint_url, endpoint_type=PUBLIC) - -internal_endpoint = partial(endpoint_url, endpoint_type=INTERNAL) - -admin_endpoint = partial(endpoint_url, endpoint_type=ADMIN) diff --git a/unit_tests/test_cinder_hooks.py b/unit_tests/test_cinder_hooks.py index 1cbef574..d1cd1bb9 100644 --- a/unit_tests/test_cinder_hooks.py +++ b/unit_tests/test_cinder_hooks.py @@ -67,7 +67,6 @@ TO_PATCH = [ 'openstack_upgrade_available', 'os_release', # charmhelpers.contrib.hahelpers.cluster_utils - 'canonical_url', 'eligible_leader', 'get_hacluster_config', 'execd_preinstall', @@ -412,12 +411,13 @@ class TestJoinedHooks(CharmTestCase): vhost='openstack', relation_id='amqp:1') - def test_identity_service_joined(self): + @patch.object(hooks, 'canonical_url') + def test_identity_service_joined(self, _canonical_url): 'It properly requests unclustered endpoint via identity-service' self.os_release.return_value = 'havana' self.unit_get.return_value = 'cindernode1' self.config.side_effect = self.test_config.get - self.canonical_url.return_value = 'http://cindernode1' + _canonical_url.return_value = 'http://cindernode1' hooks.hooks.execute(['hooks/identity-service-relation-joined']) expected = { 'region': None, @@ -434,12 +434,13 @@ class TestJoinedHooks(CharmTestCase): } self.relation_set.assert_called_with(**expected) - def test_identity_service_joined_icehouse(self): + @patch.object(hooks, 'canonical_url') + def test_identity_service_joined_icehouse(self, _canonical_url): 'It properly requests unclustered endpoint via identity-service' self.os_release.return_value = 'icehouse' self.unit_get.return_value = 'cindernode1' self.config.side_effect = self.test_config.get - self.canonical_url.return_value = 'http://cindernode1' + _canonical_url.return_value = 'http://cindernode1' hooks.hooks.execute(['hooks/identity-service-relation-joined']) expected = { 'region': None, @@ -462,6 +463,37 @@ class TestJoinedHooks(CharmTestCase): } self.relation_set.assert_called_with(**expected) + @patch('charmhelpers.contrib.openstack.ip.config') + @patch('charmhelpers.contrib.openstack.ip.resolve_address') + def test_identity_service_joined_public_name(self, _resolve_address, _config): + self.os_release.return_value = 'icehouse' + self.unit_get.return_value = 'cindernode1' + self.config.side_effect = self.test_config.get + _config.side_effect = self.test_config.get + self.test_config.set('endpoint-public-name', 'public.example.com') + _resolve_address.return_value = 'cindernode1' + hooks.hooks.execute(['hooks/identity-service-relation-joined']) + expected = { + 'region': None, + 'service': None, + 'public_url': None, + 'internal_url': None, + 'admin_url': None, + 'cinder_service': 'cinder', + 'cinder_region': 'RegionOne', + 'cinder_public_url': 'http://public.example.com:8776/v1/$(tenant_id)s', + 'cinder_admin_url': 'http://cindernode1:8776/v1/$(tenant_id)s', + 'cinder_internal_url': 'http://cindernode1:8776/v1/$(tenant_id)s', + 'cinderv2_service': 'cinderv2', + 'cinderv2_region': 'RegionOne', + 'cinderv2_public_url': 'http://public.example.com:8776/v2/$(tenant_id)s', + 'cinderv2_admin_url': 'http://cindernode1:8776/v2/$(tenant_id)s', + 'cinderv2_internal_url': 'http://cindernode1:8776/' + 'v2/$(tenant_id)s', + 'relation_id': None, + } + self.relation_set.assert_called_with(**expected) + @patch('os.mkdir') def test_ceph_joined(self, mkdir): 'It correctly prepares for a ceph changed hook' @@ -501,8 +533,10 @@ class TestJoinedHooks(CharmTestCase): self.assertNotIn(c, self.CONFIGS.write.call_args_list) self.assertFalse(self.set_ceph_env_variables.called) + @patch('charmhelpers.core.host.service') @patch("cinder_hooks.relation_get", autospec=True) - def test_ceph_changed_broker_success(self, mock_relation_get): + def test_ceph_changed_broker_success(self, mock_relation_get, + _service): 'It ensures ceph assets created on ceph changed' self.CONFIGS.complete_contexts.return_value = ['ceph'] self.service_name.return_value = 'cinder'