From 88fe0af9cb2f39ea804a958ccf5c0e1a09ccd2ae Mon Sep 17 00:00:00 2001 From: mkalcok <69471063+mkalcok@users.noreply.github.com> Date: Sat, 27 Mar 2021 09:27:02 +0100 Subject: [PATCH] Functional tests for nova-compute 'cloud' actions (#470) * Functional test for 'enable' and 'disable' actions on nova-compute * lint fixes * Put tests of new functionality into separate class * Added tests for actions `remove-from-cloud` and `register-to-cloud` * Set longer timeout for action verification * Add logging to help with CI debugging * Raise error if action fails * Cleanup running VMs before test starts * Add cleanup to tests that create VMs --- zaza/openstack/charm_tests/nova/tests.py | 143 +++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/zaza/openstack/charm_tests/nova/tests.py b/zaza/openstack/charm_tests/nova/tests.py index edad683..f457ea9 100644 --- a/zaza/openstack/charm_tests/nova/tests.py +++ b/zaza/openstack/charm_tests/nova/tests.py @@ -19,6 +19,8 @@ import json import logging import unittest +from configparser import ConfigParser +from time import sleep import zaza.model import zaza.openstack.charm_tests.glance.setup as glance_setup @@ -47,6 +49,10 @@ class CirrosGuestCreateTest(test_utils.OpenStackBaseTest): self.launch_guest( 'cirros', instance_key=glance_setup.CIRROS_IMAGE_NAME) + def tearDown(self): + """Cleanup of VM guests.""" + self.resource_cleanup() + class LTSGuestCreateTest(test_utils.OpenStackBaseTest): """Tests to launch a LTS image.""" @@ -57,6 +63,10 @@ class LTSGuestCreateTest(test_utils.OpenStackBaseTest): self.launch_guest( 'ubuntu', instance_key=glance_setup.LTS_IMAGE_NAME) + def tearDown(self): + """Cleanup of VM guests.""" + self.resource_cleanup() + class LTSGuestCreateVolumeBackedTest(test_utils.OpenStackBaseTest): """Tests to launch a LTS image.""" @@ -68,6 +78,139 @@ class LTSGuestCreateVolumeBackedTest(test_utils.OpenStackBaseTest): 'volume-backed-ubuntu', instance_key=glance_setup.LTS_IMAGE_NAME, use_boot_volume=True) + def tearDown(self): + """Cleanup of VM guests.""" + self.resource_cleanup() + + +class CloudActions(test_utils.OpenStackBaseTest): + """Test actions from actions/cloud.py.""" + + def fetch_nova_service_hostname(self, unit_name): + """ + Fetch hostname used to register with nova-cloud-controller. + + When nova-compute registers with nova-cloud-controller it uses either + config variable from '/etc/nova/nova.conf` or host's hostname to + identify itself. We need to fetch this value directly from the unit, + otherwise it's not possible to correlate entries from + `nova service-list` with nova-compute units. + + :param unit_name: nova-compute unit name. + :return: hostname used when registering to cloud-controller + """ + nova_cfg = ConfigParser() + + result = zaza.model.run_on_unit(unit_name, + 'cat /etc/nova/nova.conf') + nova_cfg.read_string(result['Stdout']) + + try: + nova_service_name = nova_cfg['DEFAULT']['host'] + except KeyError: + # Fallback to hostname if 'host' variable is not present in the + # config + result = zaza.model.run_on_unit(unit_name, 'hostname') + nova_service_name = result['Stdout'].rstrip('\n') + + if not nova_service_name: + self.fail("Failed to fetch nova service name from" + " nova-compute unit.") + return nova_service_name + + def test_940_enable_disable_actions(self): + """Test disable/enable actions on nova-compute units.""" + nova_units = zaza.model.get_units('nova-compute', + model_name=self.model_name) + + # Check that nova-compute services are enabled before testing + for service in self.nova_client.services.list(binary='nova-compute'): + self.assertEqual(service.status, 'enabled') + + # Run 'disable' action on units + zaza.model.run_action_on_units([unit.name for unit in nova_units], + 'disable') + + # Check action results via nova API + for service in self.nova_client.services.list(binary='nova-compute'): + self.assertEqual(service.status, 'disabled') + + # Run 'enable' action on units + zaza.model.run_action_on_units([unit.name for unit in nova_units], + 'enable') + + # Check action results via nova API + for service in self.nova_client.services.list(binary='nova-compute'): + self.assertEqual(service.status, 'enabled') + + def test_950_remove_from_cloud_actions(self): + """Test actions remove-from-cloud and register-to-cloud. + + Note (martin-kalcok): This test requires that nova-compute unit is not + running any VMs. If there are any leftover VMs from previous tests, + action `remove-from-cloud` will fail. + """ + all_units = zaza.model.get_units('nova-compute', + model_name=self.model_name) + + unit_to_remove = all_units[0] + + service_name = self.fetch_nova_service_hostname(unit_to_remove.name) + + registered_nova_services = self.nova_client.services.list( + host=service_name, binary='nova-compute') + + service_count = len(registered_nova_services) + if service_count < 1: + self.fail("Unit '{}' has no nova-compute services registered in" + " nova-cloud-controller".format(unit_to_remove.name)) + elif service_count > 1: + self.fail("Unexpected number of nova-compute services registered" + " in nova-cloud controller. Expecting: 1, found: " + "{}".format(service_count)) + + # run action remove-from-cloud and wait for the results in + # nova-cloud-controller + zaza.model.run_action_on_units([unit_to_remove.name], + 'remove-from-cloud', + raise_on_failure=True) + + # Wait for nova-compute service to be removed from the + # nova-cloud-controller + sleep_timeout = 1 # don't waste 10 seconds on the first run + + for _ in range(31): + sleep(sleep_timeout) + service_list = self.nova_client.services.list( + host=service_name, binary='nova-compute') + if len(service_list) == 0: + break + sleep_timeout = 10 + else: + self.fail("nova-compute service was not unregistered from the " + "nova-cloud-controller as expected.") + + # run action register-to-cloud to revert previous action + # and wait for the results in nova-cloud-controller + zaza.model.run_action_on_units([unit_to_remove.name], + 'register-to-cloud', + raise_on_failure=True) + + # Wait for nova-compute service to be registered to the + # nova-cloud-controller + sleep_timeout = 1 # don't waste 10 seconds on the first run + + for _ in range(31): + sleep(sleep_timeout) + service_list = self.nova_client.services.list( + host=service_name, binary='nova-compute') + if len(service_list) == 1: + break + sleep_timeout = 10 + else: + self.fail("nova-compute service was not re-registered to the " + "nova-cloud-controller as expected.") + class NovaCompute(test_utils.OpenStackBaseTest): """Run nova-compute specific tests."""