[wolsen,r=] Add support for overriding public endpoint addresses.
Adds in the config option for overriding public endpoint addresses and introduces a unit tests to ensure that the override for the public address is functioning correctly. Additionally fixed unit test which invoked an apt-get update. Closes-Bug: #1398182
This commit is contained in:
11
config.yaml
11
config.yaml
@@ -230,6 +230,17 @@ 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 provided by the
|
||||
nova-cloud-controller in the keystone identity provider.
|
||||
.
|
||||
This value will be used for public endpoints. For example, an
|
||||
endpoint-public-name set to 'ncc.example.com' with ssl enabled will
|
||||
create public endpoints such as
|
||||
https://ncc.example.com:8775/v2/$(tenant_id)s
|
||||
service-guard:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -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,15 +58,30 @@ 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 = 'http'
|
||||
if 'https' in configs.complete_contexts():
|
||||
scheme = 'https'
|
||||
scheme = _get_scheme(configs)
|
||||
|
||||
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 configs and 'https' in configs.complete_contexts():
|
||||
scheme = 'https'
|
||||
return scheme
|
||||
|
||||
|
||||
def resolve_address(endpoint_type=PUBLIC):
|
||||
"""Return unit address depending on net config.
|
||||
|
||||
@@ -78,6 +94,14 @@ def resolve_address(endpoint_type=PUBLIC):
|
||||
:param endpoint_type: Network endpoing type
|
||||
"""
|
||||
resolved_address = None
|
||||
|
||||
# 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:
|
||||
return addr_override
|
||||
|
||||
vips = config('vip')
|
||||
if vips:
|
||||
vips = vips.split()
|
||||
@@ -109,38 +133,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)
|
||||
|
||||
@@ -90,11 +90,13 @@ class NovaComputeContextTests(CharmTestCase):
|
||||
self.assertEqual({'memcached_servers': "%s:11211" % (formated_ip, )},
|
||||
instance_console())
|
||||
|
||||
@mock.patch('charmhelpers.contrib.openstack.neutron.os_release')
|
||||
@mock.patch.object(context, 'use_local_neutron_api')
|
||||
@mock.patch('charmhelpers.contrib.openstack.ip.config')
|
||||
@mock.patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
||||
def test_neutron_context_single_vip(self, mock_is_clustered, mock_config,
|
||||
mock_use_local_neutron_api):
|
||||
mock_use_local_neutron_api,
|
||||
_os_release):
|
||||
mock_use_local_neutron_api.return_value = True
|
||||
self.https.return_value = False
|
||||
mock_is_clustered.return_value = True
|
||||
@@ -102,7 +104,7 @@ class NovaComputeContextTests(CharmTestCase):
|
||||
'os-internal-network': '10.0.0.1/24',
|
||||
'os-admin-network': '10.0.1.0/24',
|
||||
'os-public-network': '10.0.2.0/24'}
|
||||
mock_config.side_effect = lambda key: config[key]
|
||||
mock_config.side_effect = lambda key: config.get(key)
|
||||
|
||||
mock_use_local_neutron_api.return_value = False
|
||||
ctxt = context.NeutronCCContext()()
|
||||
@@ -114,18 +116,20 @@ class NovaComputeContextTests(CharmTestCase):
|
||||
self.assertEqual(ctxt['nova_url'], 'http://10.0.0.1:8774/v2')
|
||||
self.assertEqual(ctxt['neutron_url'], 'http://10.0.0.1:9696')
|
||||
|
||||
@mock.patch('charmhelpers.contrib.openstack.neutron.os_release')
|
||||
@mock.patch.object(context, 'use_local_neutron_api')
|
||||
@mock.patch('charmhelpers.contrib.openstack.ip.config')
|
||||
@mock.patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
||||
def test_neutron_context_multi_vip(self, mock_is_clustered, mock_config,
|
||||
mock_use_local_neutron_api):
|
||||
mock_use_local_neutron_api,
|
||||
_os_release):
|
||||
self.https.return_value = False
|
||||
mock_is_clustered.return_value = True
|
||||
config = {'vip': '10.0.0.1 10.0.1.1 10.0.2.1',
|
||||
'os-internal-network': '10.0.1.0/24',
|
||||
'os-admin-network': '10.0.0.0/24',
|
||||
'os-public-network': '10.0.2.0/24'}
|
||||
mock_config.side_effect = lambda key: config[key]
|
||||
mock_config.side_effect = lambda key: config.get(key)
|
||||
|
||||
mock_use_local_neutron_api.return_value = False
|
||||
ctxt = context.NeutronCCContext()()
|
||||
|
||||
@@ -25,13 +25,13 @@ TO_PATCH = [
|
||||
'api_port',
|
||||
'apt_update',
|
||||
'apt_install',
|
||||
'canonical_url',
|
||||
'configure_installation_source',
|
||||
'charm_dir',
|
||||
'do_openstack_upgrade',
|
||||
'openstack_upgrade_available',
|
||||
'cmd_all_services',
|
||||
'config',
|
||||
'determine_endpoints',
|
||||
'determine_packages',
|
||||
'determine_ports',
|
||||
'disable_services',
|
||||
@@ -188,9 +188,11 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
self.assertEquals(sorted(self.relation_set.call_args_list),
|
||||
sorted(expected_relations))
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
@patch.object(hooks, '_auth_config')
|
||||
def test_compute_joined_neutron(self, auth_config, _util_config):
|
||||
def test_compute_joined_neutron(self, auth_config, _util_config,
|
||||
_canonical_url):
|
||||
_util_config.return_value = None
|
||||
self.is_relation_made.return_value = False
|
||||
self.network_manager.return_value = 'neutron'
|
||||
@@ -198,7 +200,7 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
self.keystone_ca_cert_b64.return_value = 'foocert64'
|
||||
self.volume_service.return_value = 'cinder'
|
||||
self.unit_get.return_value = 'nova-cc-host1'
|
||||
self.canonical_url.return_value = 'http://nova-cc-host1'
|
||||
_canonical_url.return_value = 'http://nova-cc-host1'
|
||||
self.api_port.return_value = '9696'
|
||||
self.neutron_plugin.return_value = 'nvp'
|
||||
auth_config.return_value = FAKE_KS_AUTH_CFG
|
||||
@@ -217,11 +219,12 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
quantum_plugin='nvp',
|
||||
network_manager='neutron', **FAKE_KS_AUTH_CFG)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
@patch.object(hooks, 'NeutronAPIContext')
|
||||
@patch.object(hooks, '_auth_config')
|
||||
def test_compute_joined_neutron_api_rel(self, auth_config, napi,
|
||||
_util_config):
|
||||
_util_config, _canonical_url):
|
||||
def mock_NeutronAPIContext():
|
||||
return {
|
||||
'neutron_plugin': 'bob',
|
||||
@@ -236,7 +239,7 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
self.keystone_ca_cert_b64.return_value = 'foocert64'
|
||||
self.volume_service.return_value = 'cinder'
|
||||
self.unit_get.return_value = 'nova-cc-host1'
|
||||
self.canonical_url.return_value = 'http://nova-cc-host1'
|
||||
_canonical_url.return_value = 'http://nova-cc-host1'
|
||||
self.api_port.return_value = '9696'
|
||||
self.neutron_plugin.return_value = 'nvp'
|
||||
auth_config.return_value = FAKE_KS_AUTH_CFG
|
||||
@@ -254,13 +257,14 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
quantum_plugin='bob',
|
||||
network_manager='neutron', **FAKE_KS_AUTH_CFG)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(hooks, '_auth_config')
|
||||
def test_nova_vmware_joined(self, auth_config):
|
||||
def test_nova_vmware_joined(self, auth_config, _canonical_url):
|
||||
auth_config.return_value = FAKE_KS_AUTH_CFG
|
||||
# quantum-security-groups, plugin
|
||||
self.neutron_plugin.return_value = 'nvp'
|
||||
self.network_manager.return_value = 'neutron'
|
||||
self.canonical_url.return_value = 'http://nova-cc-host1'
|
||||
_canonical_url.return_value = 'http://nova-cc-host1'
|
||||
self.api_port.return_value = '9696'
|
||||
hooks.nova_vmware_relation_joined()
|
||||
self.relation_set.assert_called_with(
|
||||
@@ -278,6 +282,23 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
nova_hostname='nova.foohost.com')
|
||||
self.unit_get.assert_called_with('private-address')
|
||||
|
||||
@patch('charmhelpers.contrib.openstack.ip.unit_get')
|
||||
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
|
||||
@patch('charmhelpers.contrib.openstack.ip.config')
|
||||
def test_identity_joined(self, _ip_config, _is_clustered, _unit_get):
|
||||
_is_clustered.return_value = False
|
||||
_unit_get.return_value = '127.0.0.1'
|
||||
_ip_config.side_effect = self.test_config.get
|
||||
|
||||
self.test_config.set('endpoint-public-name', 'ncc.example.com')
|
||||
hooks.identity_joined()
|
||||
|
||||
self.determine_endpoints.asssert_called_with(
|
||||
public_url='http://ncc.example.com',
|
||||
internal_url='http://127.0.0.1',
|
||||
admin_url='http://127.0.0.1'
|
||||
)
|
||||
|
||||
def test_postgresql_nova_db_joined(self):
|
||||
self.is_relation_made.return_value = False
|
||||
hooks.pgsql_nova_db_joined()
|
||||
@@ -442,9 +463,10 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
call('/etc/neutron/neutron.conf')])
|
||||
cell_joined.assert_called_with(rid='nova-cell-api/0')
|
||||
|
||||
def test_nova_cell_relation_joined(self):
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
def test_nova_cell_relation_joined(self, _canonical_url):
|
||||
self.uuid.uuid4.return_value = 'bob'
|
||||
self.canonical_url.return_value = 'http://novaurl'
|
||||
_canonical_url.return_value = 'http://novaurl'
|
||||
hooks.nova_cell_relation_joined(rid='rid',
|
||||
remote_restart=True)
|
||||
self.relation_set.assert_called_with(restart_trigger='bob',
|
||||
@@ -463,19 +485,20 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
}
|
||||
self.assertEquals(hooks.get_cell_type(), 'parent')
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(os, 'rename')
|
||||
@patch.object(os.path, 'isfile')
|
||||
@patch.object(hooks, 'CONFIGS')
|
||||
@patch.object(hooks, 'get_cell_type')
|
||||
def test_neutron_api_relation_joined(self, get_cell_type, configs, isfile,
|
||||
rename):
|
||||
rename, _canonical_url):
|
||||
neutron_conf = '/etc/neutron/neutron.conf'
|
||||
nova_url = 'http://novaurl:8774/v2'
|
||||
isfile.return_value = True
|
||||
self.service_running.return_value = True
|
||||
_identity_joined = self.patch('identity_joined')
|
||||
self.relation_ids.return_value = ['relid']
|
||||
self.canonical_url.return_value = 'http://novaurl'
|
||||
_canonical_url.return_value = 'http://novaurl'
|
||||
get_cell_type.return_value = 'parent'
|
||||
self.uuid.uuid4.return_value = 'bob'
|
||||
with patch_open() as (_open, _file):
|
||||
@@ -512,11 +535,12 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
self.assertTrue(_compute_joined.called)
|
||||
self.assertTrue(_quantum_joined.called)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
def test_console_settings_vnc(self, _utils_config):
|
||||
def test_console_settings_vnc(self, _utils_config, _canonical_url):
|
||||
_utils_config.return_value = 'vnc'
|
||||
_cc_host = "nova-cc-host1"
|
||||
self.canonical_url.return_value = 'http://' + _cc_host
|
||||
_canonical_url.return_value = 'http://' + _cc_host
|
||||
_con_sets = hooks.console_settings()
|
||||
console_settings = {
|
||||
'console_proxy_novnc_address': 'http://%s:6080/vnc_auto.html' %
|
||||
@@ -532,11 +556,12 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
}
|
||||
self.assertEqual(_con_sets, console_settings)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
def test_console_settings_xvpvnc(self, _utils_config):
|
||||
def test_console_settings_xvpvnc(self, _utils_config, _canonical_url):
|
||||
_utils_config.return_value = 'xvpvnc'
|
||||
_cc_host = "nova-cc-host1"
|
||||
self.canonical_url.return_value = 'http://' + _cc_host
|
||||
_canonical_url.return_value = 'http://' + _cc_host
|
||||
_con_sets = hooks.console_settings()
|
||||
console_settings = {
|
||||
'console_access_protocol': 'xvpvnc',
|
||||
@@ -548,11 +573,12 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
}
|
||||
self.assertEqual(_con_sets, console_settings)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
def test_console_settings_novnc(self, _utils_config):
|
||||
def test_console_settings_novnc(self, _utils_config, _canonical_url):
|
||||
_utils_config.return_value = 'novnc'
|
||||
_cc_host = "nova-cc-host1"
|
||||
self.canonical_url.return_value = 'http://' + _cc_host
|
||||
_canonical_url.return_value = 'http://' + _cc_host
|
||||
_con_sets = hooks.console_settings()
|
||||
console_settings = {
|
||||
'console_proxy_novnc_address': 'http://%s:6080/vnc_auto.html' %
|
||||
@@ -564,11 +590,12 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
}
|
||||
self.assertEqual(_con_sets, console_settings)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
def test_console_settings_spice(self, _utils_config):
|
||||
def test_console_settings_spice(self, _utils_config, _canonical_url):
|
||||
_utils_config.return_value = 'spice'
|
||||
_cc_host = "nova-cc-host1"
|
||||
self.canonical_url.return_value = 'http://' + _cc_host
|
||||
_canonical_url.return_value = 'http://' + _cc_host
|
||||
_con_sets = hooks.console_settings()
|
||||
console_settings = {
|
||||
'console_proxy_spice_address': 'http://%s:6082/spice_auto.html' %
|
||||
@@ -580,14 +607,16 @@ class NovaCCHooksTests(CharmTestCase):
|
||||
}
|
||||
self.assertEqual(_con_sets, console_settings)
|
||||
|
||||
@patch.object(hooks, 'canonical_url')
|
||||
@patch.object(utils, 'config')
|
||||
def test_console_settings_explicit_ip(self, _utils_config):
|
||||
def test_console_settings_explicit_ip(self, _utils_config,
|
||||
_canonical_url):
|
||||
_utils_config.return_value = 'spice'
|
||||
_cc_public_host = "public-host"
|
||||
_cc_private_host = "private-host"
|
||||
self.test_config.set('console-proxy-ip', _cc_public_host)
|
||||
_con_sets = hooks.console_settings()
|
||||
self.canonical_url.return_value = 'http://' + _cc_private_host
|
||||
_canonical_url.return_value = 'http://' + _cc_private_host
|
||||
console_settings = {
|
||||
'console_proxy_spice_address': 'http://%s:6082/spice_auto.html' %
|
||||
(_cc_public_host),
|
||||
|
||||
Reference in New Issue
Block a user