Add new interface 'dashboard'

This new interface consumes information exposed by openstack-dashboard
to correctly configure nova-serialproxy and allow requests coming from
the web browser that tries to load the serial console.

Change-Id: I2d82abffb9649f16a792f180806cea36cc5e25df
Closes-Bug: #2030094
This commit is contained in:
Felipe Reyes 2023-09-01 17:39:23 -04:00
parent b58f88263f
commit 816ee80cd0
10 changed files with 120 additions and 2 deletions

View File

@ -0,0 +1 @@
nova_cc_hooks.py

View File

@ -0,0 +1 @@
nova_cc_hooks.py

View File

@ -587,8 +587,33 @@ class SerialConsoleContext(ch_context.OSContextGenerator):
ctxt = { ctxt = {
'enable_serial_console': 'enable_serial_console':
str(hookenv.config('enable-serial-console')).lower(), str(hookenv.config('enable-serial-console')).lower(),
'serial_console_base_url': 'ws://{}:6083/'.format(ip_addr) 'serial_console_base_url': 'ws://{}:6083/'.format(ip_addr),
} }
if hookenv.config('enable-serial-console'):
for rel_id in hookenv.relation_ids('dashboard'):
ctxt['console_allowed_origins'] = []
rel_units = hookenv.related_units(rel_id)
if not rel_units:
continue
os_public_hostname = hookenv.relation_get('os-public-hostname',
rid=rel_id,
app=rel_units[0])
if os_public_hostname:
ctxt['console_allowed_origins'].append(os_public_hostname)
vip = hookenv.relation_get('vip',
rid=rel_id,
app=rel_units[0])
if vip:
ip_addresses = [ip.strip() for ip in vip.split(' ')]
ctxt['console_allowed_origins'] += ip_addresses
for unit in rel_units:
ingress_address = hookenv.ingress_address(rel_id, unit)
if ingress_address:
ctxt['console_allowed_origins'].append(ingress_address)
return ctxt return ctxt

View File

@ -1403,6 +1403,15 @@ def placement_relation_changed(rid=None, unit=None):
ch_host.service_restart(s) ch_host.service_restart(s)
@hooks.hook('dashboard-relation-joined',
'dashboard-relation-changed')
def dashboard_relation_changed():
"""
Gather information from the related openstack-dashboard units
"""
CONFIGS.write_all()
@hooks.hook('update-status') @hooks.hook('update-status')
@ch_harden.harden() @ch_harden.harden()
def update_status(): def update_status():

View File

@ -1763,7 +1763,9 @@ def check_optional_relations(configs):
if hookenv.relation_ids('ha'): if hookenv.relation_ids('ha'):
try: try:
ch_cluster.get_hacluster_config() ch_cluster.get_hacluster_config()
except Exception: except Exception as ex:
hookenv.log("get_hacluster_config exception: %s" % str(ex),
hookenv.DEBUG)
return ('blocked', return ('blocked',
'hacluster missing configuration: ' 'hacluster missing configuration: '
'vip, vip_iface, vip_cidr') 'vip, vip_iface, vip_cidr')
@ -1780,6 +1782,12 @@ def check_optional_relations(configs):
'false, this configuration is only availabe for releases>=Train' 'false, this configuration is only availabe for releases>=Train'
) )
if hookenv.config('enable-serial-console'):
if not hookenv.relation_ids('dashboard'):
return ('blocked',
("Required relation 'dashboard' needed when "
"enable-serial-console is set to True"))
# return 'unknown' as the lowest priority to not clobber an existing # return 'unknown' as the lowest priority to not clobber an existing
# status. # status.
return "unknown", None return "unknown", None

View File

@ -45,6 +45,8 @@ requires:
interface: nova-compute interface: nova-compute
cinder-volume-service: cinder-volume-service:
interface: cinder interface: cinder
dashboard:
interface: dashboard
quantum-network-service: quantum-network-service:
interface: quantum interface: quantum
neutron-api: neutron-api:

View File

@ -0,0 +1,6 @@
{% if console_allowed_origins -%}
[console]
# List of allowed origins to the console websocket proxy to allow connections
# from other origin hostnames.
allowed_origins = {{ console_allowed_origins|join(',') }}
{% endif -%}

View File

@ -193,6 +193,8 @@ html5proxy_port = {{ console_access_port }}
{% include "parts/section-serial-console" %} {% include "parts/section-serial-console" %}
{% include "parts/section-console" %}
{% if memcached_servers %} {% if memcached_servers %}
[cache] [cache]
enabled = true enabled = true

View File

@ -517,6 +517,8 @@ class NovaComputeContextTests(CharmTestCase):
self.test_config.set('enable-serial-console', True) self.test_config.set('enable-serial-console', True)
mock_format_ipv6_address.return_value = None mock_format_ipv6_address.return_value = None
mock_resolve_address.return_value = '10.10.10.1' mock_resolve_address.return_value = '10.10.10.1'
# no relation to openstack-dashboard:dashboard
ctxt = context.SerialConsoleContext()() ctxt = context.SerialConsoleContext()()
self.assertEqual( self.assertEqual(
ctxt, ctxt,
@ -526,6 +528,40 @@ class NovaComputeContextTests(CharmTestCase):
mock_resolve_address.assert_called_with( mock_resolve_address.assert_called_with(
endpoint_type=context.ch_ip.PUBLIC) endpoint_type=context.ch_ip.PUBLIC)
# generated context when related to openstack-dashboard:dashboard
fake_relation_ids = {
'dashboard': ['dashboard:1'],
}
self.relation_ids.side_effect = fake_relation_ids.get
fake_related_units = {'dashboard:1': ['openstack-dashboard/0']}
self.related_units.side_effect = fake_related_units.get
def fake_relation_get(attribute=None, rid=None, unit=None, app=None):
data = {
'dashboard:1': {
'openstack-dashboard/0': {
'os-public-hostname': 'myhostname',
'vip': '1.2.3.4',
'ingress-address': '10.20.30.40',
},
}
}
if attribute:
return data[rid][unit or app][attribute]
else:
return data[rid][unit or app]
self.relation_get.side_effect = fake_relation_get
ctxt = context.SerialConsoleContext()()
self.assertEqual(
ctxt,
{'serial_console_base_url': 'ws://10.10.10.1:6083/',
'enable_serial_console': 'true',
'console_allowed_origins': ['myhostname', '1.2.3.4',
'10.20.30.40']}
)
@mock.patch.object(context, 'ch_cluster') @mock.patch.object(context, 'ch_cluster')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('charmhelpers.contrib.openstack.ip.resolve_address') @mock.patch('charmhelpers.contrib.openstack.ip.resolve_address')

View File

@ -1947,3 +1947,31 @@ class NovaCCUtilsTests(CharmTestCase):
call(['nova-manage', 'cell_v2', 'update_cell', call(['nova-manage', 'cell_v2', 'update_cell',
'--cell_uuid', 'cell1uuid']), '--cell_uuid', 'cell1uuid']),
]) ])
@patch('charmhelpers.core.hookenv.relation_ids')
def test_check_optional_relations(self, relation_ids):
self.get_os_codename_install_source.return_value = 'ussuri'
self.os_release.return_value = 'ussuri'
fake_relation_ids = {'placement': ['placement:0'],
'ha': ['ha:1'],
'dashboard': []}
relation_ids.side_effect = fake_relation_ids.get
self.test_config.set('enable-serial-console', True)
(status, workload_msg) = utils.check_optional_relations(None)
self.assertEqual('blocked', status)
self.assertEqual(('hacluster missing configuration: vip, vip_iface, '
'vip_cidr'),
workload_msg)
with patch.object(utils.ch_cluster,
'get_hacluster_config') as get_hacluster_config: # noqa
(status, workload_msg) = utils.check_optional_relations(None)
self.assertEqual('blocked', status)
self.assertEqual(("Required relation 'dashboard' needed when "
"enable-serial-console is set to True"),
workload_msg)
fake_relation_ids['dashboard'] = ['dashboard:2']
(status, workload_msg) = utils.check_optional_relations(None)
self.assertEqual('unknown', status)
self.assertEqual(None, workload_msg)