From 13eca55803c8320a401ccf923c29b7bfeb85d0fc Mon Sep 17 00:00:00 2001 From: Sahid Orentino Ferdjaoui Date: Mon, 28 Jan 2019 11:37:40 +0100 Subject: [PATCH] service: updates nova-api-os-compute service to use apache wsgi Due to an issue in python3 oslo_cache+eventlet when using memcached. As workaroud for Rocky it has been decided to run service nova-api-os-compute from systemd to apache2. Closes-Bug: #1812672 Depends-On: https://review.openstack.org/#/c/633218 Depends-On: https://review.openstack.org/#/c/633482 Change-Id: I3bf279638c5decf1020345f3d2e876e379144997 Signed-off-by: Sahid Orentino Ferdjaoui --- hooks/nova_cc_context.py | 9 ++++ hooks/nova_cc_hooks.py | 5 +- hooks/nova_cc_utils.py | 75 +++++++++++++++++++++++------- templates/wsgi-api-os-compute.conf | 1 + tests/basic_deployment.py | 10 ++++ unit_tests/test_nova_cc_hooks.py | 1 + unit_tests/test_nova_cc_utils.py | 24 ++++++++++ 7 files changed, 106 insertions(+), 19 deletions(-) create mode 120000 templates/wsgi-api-os-compute.conf diff --git a/hooks/nova_cc_context.py b/hooks/nova_cc_context.py index 62f0c347..159d205d 100644 --- a/hooks/nova_cc_context.py +++ b/hooks/nova_cc_context.py @@ -219,6 +219,15 @@ class PlacementAPIHAProxyContext(HAProxyContext): return ctxt +class ComputeAPIHAProxyContext(HAProxyContext): + """Context for the nova os compute api service.""" + + def __call__(self): + ctxt = super(ComputeAPIHAProxyContext, self).__call__() + ctxt['port'] = ctxt['listen_ports']['osapi_compute_listen_port'] + return ctxt + + class MetaDataHAProxyContext(HAProxyContext): """Context for the nova metadata service.""" diff --git a/hooks/nova_cc_hooks.py b/hooks/nova_cc_hooks.py index 0639f8ad..e2e5768c 100755 --- a/hooks/nova_cc_hooks.py +++ b/hooks/nova_cc_hooks.py @@ -192,8 +192,8 @@ def install(): ch_fetch.apt_update() ch_fetch.apt_install(ncc_utils.determine_packages(), fatal=True) - if ncc_utils.placement_api_enabled(): - ncc_utils.disable_package_apache_site() + ncc_utils.disable_package_apache_site() + ncc_utils.stop_deprecated_services() _files = os.path.join(hookenv.charm_dir(), 'files') if os.path.isdir(_files): @@ -902,6 +902,7 @@ def upgrade_charm(): # charm only we need ensure to not end-up with the old # 'wsgi-openstack-api' and the new 'wsgi-placement-api' apache # configurations installed at the same time. + ncc_utils.stop_deprecated_services() ncc_utils.disable_package_apache_site(service_reload=True) for r_id in hookenv.relation_ids('amqp'): diff --git a/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index 753c703d..cdd57c23 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -113,6 +113,10 @@ OLD_WSGI_NOVA_PLACEMENT_API_CONF = \ '/etc/apache2/sites-enabled/wsgi-openstack-api.conf' WSGI_NOVA_METADATA_API_CONF = \ '/etc/apache2/sites-enabled/wsgi-openstack-metadata.conf' +PACKAGE_NOVA_API_OS_COMPUTE_CONF = \ + '/etc/apache2/sites-available/nova-api-os-compute.conf' +WSGI_NOVA_API_OS_COMPUTE_CONF = \ + '/etc/apache2/sites-enabled/wsgi-api-os-compute.conf' VENDORDATA_FILE = '/etc/nova/vendor_data.json' @@ -216,6 +220,26 @@ SERIAL_CONSOLE = { } +def _replace_service_with_apache2(service, wsgi_script, wsgi_config, + resource_map, context): + for cfile in resource_map: + svcs = resource_map[cfile]['services'] + if service in svcs: + svcs.remove(service) + if 'apache2' not in svcs: + svcs.append('apache2') + resource_map[wsgi_config] = { + 'contexts': [ + ch_context.WSGIWorkerConfigContext( + name=service, + script=wsgi_script, + user='nova', + group='nova' + ), + context], + 'services': ['apache2']} + + def resource_map(actual_services=True): ''' Dynamically generate a map of resources that will be managed for a single @@ -273,24 +297,24 @@ def resource_map(actual_services=True): 'contexts': [ch_context.MemcacheContext()], 'services': ['memcached']} + if (actual_services and + ch_utils.CompareOpenStackReleases(release) >= 'rocky'): + # For Rocky we decided to switch from systemd to use apache2 + # wsgi mod for the service nova-api-os-compute. + _replace_service_with_apache2( + 'nova-api-os-compute', + '/usr/bin/nova-api-wsgi', + WSGI_NOVA_API_OS_COMPUTE_CONF, + _resource_map, + nova_cc_context.ComputeAPIHAProxyContext()) + if actual_services and placement_api_enabled(): - for cfile in _resource_map: - svcs = _resource_map[cfile]['services'] - if 'nova-placement-api' in svcs: - svcs.remove('nova-placement-api') - if 'apache2' not in svcs: - svcs.append('apache2') - wsgi_script = "/usr/bin/nova-placement-api" - _resource_map[WSGI_NOVA_PLACEMENT_API_CONF] = { - 'contexts': [ - ch_context.WSGIWorkerConfigContext( - name="nova_placement", - user="nova", - group="nova", - script=wsgi_script), - nova_cc_context.PlacementAPIHAProxyContext()], - 'services': ['apache2'] - } + _replace_service_with_apache2( + 'nova-placement-api', + '/usr/bin/nova-placement-api', + WSGI_NOVA_PLACEMENT_API_CONF, + _resource_map, + nova_cc_context.PlacementAPIHAProxyContext()) elif not placement_api_enabled(): for cfile in _resource_map: svcs = _resource_map[cfile]['services'] @@ -570,6 +594,7 @@ def _do_openstack_upgrade(new_src): disable_package_apache_site() disable_policy_rcd() + stop_deprecated_services() # NOTE(jamespage) upgrade with existing config files as the # havana->icehouse migration enables new service_plugins which @@ -1480,11 +1505,27 @@ def disable_package_apache_site(service_reload=False): # "file" that is not symlink from sites-available. os.remove(OLD_WSGI_NOVA_PLACEMENT_API_CONF) site_changed = True + if os.path.exists(PACKAGE_NOVA_API_OS_COMPUTE_CONF): + # Even if using systemd or apache for the service we want + # remove the conf created by the package installed if exists + subprocess.check_call(['a2dissite', 'nova-api-os-compute']) + site_changed = True if site_changed and service_reload: ch_host.service_reload('apache2', restart_on_failure=True) +def stop_deprecated_services(): + """Stop services that are not used anymore. + + Note: It may be important to also disable the service, see: + resource_map. + """ + release = ch_utils.os_release('nova-common') + if ch_utils.CompareOpenStackReleases(release) >= 'rocky': + ch_host.service_pause('nova-api-os-compute') + + def get_shared_metadatasecret(): """Return the shared metadata secret.""" return hookenv.leader_get(SHARED_METADATA_SECRET_KEY) diff --git a/templates/wsgi-api-os-compute.conf b/templates/wsgi-api-os-compute.conf new file mode 120000 index 00000000..c9067c89 --- /dev/null +++ b/templates/wsgi-api-os-compute.conf @@ -0,0 +1 @@ +../charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf \ No newline at end of file diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index fd4f4faa..59d82415 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -81,6 +81,8 @@ class NovaCCBasicDeployment(OpenStackAmuletDeployment): ) if cmp_os_release >= 'newton': services.remove('nova-cert') + if cmp_os_release >= 'rocky': + services.remove('nova-api-os-compute') u.get_unit_process_ids( {self.nova_cc_sentry: services}, expect_success=should_run) @@ -325,6 +327,10 @@ class NovaCCBasicDeployment(OpenStackAmuletDeployment): services[self.nova_compute_sentry].remove('nova-network') services[self.nova_compute_sentry].remove('nova-api') + if self._get_openstack_release() >= self.bionic_rocky: + services[self.nova_cc_sentry].remove('nova-api-os-compute') + services[self.nova_cc_sentry].append('apache2') + ret = u.validate_services_by_name(services) if ret: amulet.raise_status(amulet.FAIL, msg=ret) @@ -819,6 +825,10 @@ class NovaCCBasicDeployment(OpenStackAmuletDeployment): if cmp_os_release >= 'newton': del services['nova-cert'] + if cmp_os_release >= 'rocky': + del services['nova-api-os-compute'] + services['apache2'] = conf_file + if self._get_openstack_release() >= self.xenial_ocata: # nova-placement-api is run under apache2 with mod_wsgi services['apache2'] = conf_file diff --git a/unit_tests/test_nova_cc_hooks.py b/unit_tests/test_nova_cc_hooks.py index 1f6f5b57..f92db27b 100644 --- a/unit_tests/test_nova_cc_hooks.py +++ b/unit_tests/test_nova_cc_hooks.py @@ -111,6 +111,7 @@ class NovaCCHooksTests(CharmTestCase): super(NovaCCHooksTests, self).tearDown() def test_install_hook(self): + self.os_release.return_value = 'rocky' self.determine_packages.return_value = [ 'nova-scheduler', 'nova-api-ec2'] self.determine_ports.return_value = [80, 81, 82] diff --git a/unit_tests/test_nova_cc_utils.py b/unit_tests/test_nova_cc_utils.py index b08b3dd2..775bda26 100644 --- a/unit_tests/test_nova_cc_utils.py +++ b/unit_tests/test_nova_cc_utils.py @@ -153,6 +153,17 @@ RESTART_MAP_OCATA_BASE = OrderedDict([ ('/etc/haproxy/haproxy.cfg', ['haproxy']), ('/etc/apache2/sites-available/openstack_https_frontend', ['apache2']) ]) +RESTART_MAP_ROCKY_ACTUAL = OrderedDict([ + ('/etc/nova/nova.conf', [ + 'nova-scheduler', 'nova-conductor', 'apache2', + ]), + ('/etc/nova/api-paste.ini', ['apache2']), + ('/etc/haproxy/haproxy.cfg', ['haproxy']), + ('/etc/apache2/sites-available/openstack_https_frontend', ['apache2']), + ('/etc/apache2/sites-enabled/wsgi-api-os-compute.conf', ['apache2']), + ('/etc/apache2/sites-enabled/wsgi-placement-api.conf', ['apache2']), + ('/etc/apache2/sites-enabled/wsgi-openstack-metadata.conf', ['apache2']), +]) DPKG_OPTS = [ @@ -352,6 +363,19 @@ class NovaCCUtilsTests(CharmTestCase): self.assertIsInstance(_map, OrderedDict) self.assertEqual(_map, RESTART_MAP_OCATA_ACTUAL) + @patch('charmhelpers.contrib.openstack.neutron.os_release') + @patch('os.path.exists') + @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext') + def test_restart_map_api_actual_rocky( + self, subcontext, _exists, _os_release): + _os_release.return_value = 'rocky' + self.os_release.return_value = 'rocky' + _exists.return_value = False + self.enable_memcache.return_value = False + _map = utils.restart_map() + self.assertIsInstance(_map, OrderedDict) + self.assertEqual(_map, RESTART_MAP_ROCKY_ACTUAL) + @patch('charmhelpers.contrib.openstack.neutron.os_release') @patch('os.path.exists') @patch('charmhelpers.contrib.openstack.context.SubordinateConfigContext')