From 522d3e53b8d87c05d64fbbb8722c861cdd52638d Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Fri, 25 Sep 2020 10:39:13 +0200 Subject: [PATCH 01/14] Only configure private Neutron CIDR if it is configured --- zaza/openstack/charm_tests/tempest/setup.py | 7 +++++-- zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 | 2 ++ zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/tempest/setup.py b/zaza/openstack/charm_tests/tempest/setup.py index 98ea327..5c85d9c 100644 --- a/zaza/openstack/charm_tests/tempest/setup.py +++ b/zaza/openstack/charm_tests/tempest/setup.py @@ -26,10 +26,12 @@ import zaza.openstack.charm_tests.glance.setup as glance_setup SETUP_ENV_VARS = { 'neutron': ['TEST_GATEWAY', 'TEST_CIDR_EXT', 'TEST_FIP_RANGE', - 'TEST_NAMESERVER', 'TEST_CIDR_PRIV'], + 'TEST_NAME_SERVER', 'TEST_CIDR_PRIV'], 'swift': ['TEST_SWIFT_IP'], } +IGNORABLE_VARS = ['TEST_CIDR_PRIV'] + TEMPEST_FLAVOR_NAME = 'm1.tempest' TEMPEST_ALT_FLAVOR_NAME = 'm2.tempest' TEMPEST_SVC_LIST = ['ceilometer', 'cinder', 'glance', 'heat', 'horizon', @@ -198,7 +200,8 @@ def add_environment_var_config(ctxt, services): if value: ctxt[var.lower()] = value else: - missing_vars.append(var) + if var not in IGNORABLE_VARS: + missing_vars.append(var) if missing_vars: raise ValueError( ("Environment variables [{}] must all be set to run this" diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 index 83a05ca..8c9b6c6 100644 --- a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 @@ -52,7 +52,9 @@ http_image = http://{{ test_swift_ip }}:80/swift/v1/images/cirros-0.3.4-x86_64-u {% if 'neutron' in enabled_services %} [network] +{% if test_cidr_priv %} project_network_cidr = {{ test_cidr_priv }} +{% endif %} public_network_id = {{ ext_net }} dns_servers = {{ test_nameserver }} project_networks_reachable = false diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 b/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 index 0ed401a..fc92dd2 100644 --- a/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 @@ -54,7 +54,9 @@ http_image = http://{{ test_swift_ip }}:80/swift/v1/images/cirros-0.3.4-x86_64-u {% if 'neutron' in enabled_services %} [network] +{% if test_cidr_priv %} project_network_cidr = {{ test_cidr_priv }} +{% endif %} public_network_id = {{ ext_net }} dns_servers = {{ test_nameserver }} project_networks_reachable = false From 09789767f301e1b7133d76b441e4d15ca7c5251e Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 25 Sep 2020 11:19:32 +0200 Subject: [PATCH 02/14] Enhance common launch instance helper Move retry logic to the launch single guest method. Add optional arguments that is passed on to the downstream launch guest helper. --- zaza/openstack/charm_tests/test_utils.py | 35 ++++++++++++++---------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index a3614f0..1f24aa9 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -526,7 +526,8 @@ class OpenStackBaseTest(BaseCharmTest): # Test did not define self.RESOURCE_PREFIX, ignore. pass - def launch_guest(self, guest_name, userdata=None): + def launch_guest(self, guest_name, userdata=None, use_boot_volume=False, + instance_key=None): """Launch two guests to use in tests. Note that it is up to the caller to have set the RESOURCE_PREFIX class @@ -539,9 +540,14 @@ class OpenStackBaseTest(BaseCharmTest): :type guest_name: str :param userdata: Userdata to attach to instance :type userdata: Optional[str] + :param use_boot_volume: Whether to boot guest from a shared volume. + :type use_boot_volume: boolean + :param instance_key: Key to collect associated config data with. + :type instance_key: Optional[str] :returns: Nova instance objects :rtype: Server """ + instance_key = instance_key or glance_setup.LTS_IMAGE_NAME instance_name = '{}-{}'.format(self.RESOURCE_PREFIX, guest_name) instance = self.retrieve_guest(instance_name) @@ -554,10 +560,16 @@ class OpenStackBaseTest(BaseCharmTest): instance.id, msg="server") - return configure_guest.launch_instance( - glance_setup.LTS_IMAGE_NAME, - vm_name=instance_name, - userdata=userdata) + for attempt in tenacity.Retrying( + stop=tenacity.stop_after_attempt(3), + wait=tenacity.wait_exponential( + multiplier=1, min=2, max=10)): + with attempt: + return configure_guest.launch_instance( + instance_key, + vm_name=instance_name, + use_boot_volume=use_boot_volume, + userdata=userdata) def launch_guests(self, userdata=None): """Launch two guests to use in tests. @@ -572,15 +584,10 @@ class OpenStackBaseTest(BaseCharmTest): """ launched_instances = [] for guest_number in range(1, 2+1): - for attempt in tenacity.Retrying( - stop=tenacity.stop_after_attempt(3), - wait=tenacity.wait_exponential( - multiplier=1, min=2, max=10)): - with attempt: - launched_instances.append( - self.launch_guest( - guest_name='ins-{}'.format(guest_number), - userdata=userdata)) + launched_instances.append( + self.launch_guest( + guest_name='ins-{}'.format(guest_number), + userdata=userdata)) return launched_instances def retrieve_guest(self, guest_name): From 786b40fc53bf3a13d2df1faafd2c15c21b17aca5 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 25 Sep 2020 11:21:15 +0200 Subject: [PATCH 03/14] nova: Use common launch guest helper The common helper adds automatic retry handling and resource cleanup. --- zaza/openstack/charm_tests/nova/tests.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/zaza/openstack/charm_tests/nova/tests.py b/zaza/openstack/charm_tests/nova/tests.py index c4dd5b4..16c9337 100644 --- a/zaza/openstack/charm_tests/nova/tests.py +++ b/zaza/openstack/charm_tests/nova/tests.py @@ -38,32 +38,35 @@ class BaseGuestCreateTest(unittest.TestCase): zaza.openstack.configure.guest.launch_instance(instance_key) -class CirrosGuestCreateTest(BaseGuestCreateTest): +class CirrosGuestCreateTest(test_utils.OpenStackBaseTest): """Tests to launch a cirros image.""" def test_launch_small_instance(self): """Launch a cirros instance and test connectivity.""" - zaza.openstack.configure.guest.launch_instance( - glance_setup.CIRROS_IMAGE_NAME) + self.RESOURCE_PREFIX = 'zaza-nova' + self.launch_guest( + 'cirros', instance_key=glance_setup.CIRROS_IMAGE_NAME) -class LTSGuestCreateTest(BaseGuestCreateTest): +class LTSGuestCreateTest(test_utils.OpenStackBaseTest): """Tests to launch a LTS image.""" def test_launch_small_instance(self): """Launch a Bionic instance and test connectivity.""" - zaza.openstack.configure.guest.launch_instance( - glance_setup.LTS_IMAGE_NAME) + self.RESOURCE_PREFIX = 'zaza-nova' + self.launch_guest( + 'ubuntu', instance_key=glance_setup.LTS_IMAGE_NAME) -class LTSGuestCreateVolumeBackedTest(BaseGuestCreateTest): +class LTSGuestCreateVolumeBackedTest(test_utils.OpenStackBaseTest): """Tests to launch a LTS image.""" def test_launch_small_instance(self): """Launch a Bionic instance and test connectivity.""" - zaza.openstack.configure.guest.launch_instance( - glance_setup.LTS_IMAGE_NAME, - use_boot_volume=True) + self.RESOURCE_PREFIX = 'zaza-nova' + self.launch_guest( + 'volume-backed-ubuntu', instance_key=glance_setup.LTS_IMAGE_NAME, + use_boot_volmue=True) class NovaCompute(test_utils.OpenStackBaseTest): From 7bf624b007f4b65b4ce0709abc967906a86ee9ed Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Fri, 25 Sep 2020 12:49:06 +0200 Subject: [PATCH 04/14] Update tempest config files to use newly renamed test_name_server variable --- unit_tests/charm_tests/test_tempest.py | 4 ++-- zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 | 2 +- zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unit_tests/charm_tests/test_tempest.py b/unit_tests/charm_tests/test_tempest.py index 9c4fc9a..3c4c161 100644 --- a/unit_tests/charm_tests/test_tempest.py +++ b/unit_tests/charm_tests/test_tempest.py @@ -42,7 +42,7 @@ class TestTempestSetup(unittest.TestCase): 'TEST_GATEWAY': 'test', 'TEST_CIDR_EXT': 'test', 'TEST_FIP_RANGE': 'test', - 'TEST_NAMESERVER': 'test', + 'TEST_NAME_SERVER': 'test', 'TEST_CIDR_PRIV': 'test', } tempest_setup.add_environment_var_config(ctxt, ['neutron']) @@ -55,7 +55,7 @@ class TestTempestSetup(unittest.TestCase): ctxt = {} get_deployment_context.return_value = { 'TEST_GATEWAY': 'test', - 'TEST_NAMESERVER': 'test', + 'TEST_NAME_SERVER': 'test', 'TEST_CIDR_PRIV': 'test', } with self.assertRaises(Exception) as context: diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 index 8c9b6c6..d4b8810 100644 --- a/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v2.j2 @@ -56,7 +56,7 @@ http_image = http://{{ test_swift_ip }}:80/swift/v1/images/cirros-0.3.4-x86_64-u project_network_cidr = {{ test_cidr_priv }} {% endif %} public_network_id = {{ ext_net }} -dns_servers = {{ test_nameserver }} +dns_servers = {{ test_name_server }} project_networks_reachable = false [network-feature-enabled] diff --git a/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 b/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 index fc92dd2..b441f2d 100644 --- a/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 +++ b/zaza/openstack/charm_tests/tempest/templates/tempest_v3.j2 @@ -58,7 +58,7 @@ http_image = http://{{ test_swift_ip }}:80/swift/v1/images/cirros-0.3.4-x86_64-u project_network_cidr = {{ test_cidr_priv }} {% endif %} public_network_id = {{ ext_net }} -dns_servers = {{ test_nameserver }} +dns_servers = {{ test_name_server }} project_networks_reachable = false floating_network_name = {{ ext_net }} From e70be304e5d1cc45ab07b8251dfad393260e486d Mon Sep 17 00:00:00 2001 From: Xav Paice Date: Fri, 25 Sep 2020 18:22:09 +1200 Subject: [PATCH 05/14] Add tests for OVN charm NRPE checks The ovn-* charms have added NRPE service checks, this ensures that the service check files have been added to the units as expected. --- zaza/openstack/charm_tests/ovn/tests.py | 63 ++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/ovn/tests.py b/zaza/openstack/charm_tests/ovn/tests.py index d12911e..30abddd 100644 --- a/zaza/openstack/charm_tests/ovn/tests.py +++ b/zaza/openstack/charm_tests/ovn/tests.py @@ -15,11 +15,14 @@ """Encapsulate OVN testing.""" import logging -import tenacity import juju +import tenacity + import zaza + +import zaza.model import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.generic as generic_utils import zaza.openstack.utilities.openstack as openstack_utils @@ -36,10 +39,19 @@ class BaseCharmOperationTest(test_utils.BaseCharmTest): """Run class setup for OVN charm operation tests.""" super(BaseCharmOperationTest, cls).setUpClass() cls.services = ['NotImplemented'] # This must be overridden + cls.nrpe_checks = ['NotImplemented'] # This must be overridden cls.current_release = openstack_utils.get_os_release( openstack_utils.get_current_os_release_pair( cls.release_application or cls.application_name)) + @tenacity.retry( + retry=tenacity.retry_if_result(lambda ret: ret is not None), + # sleep for 2mins to allow 1min cron job to run... + wait=tenacity.wait_fixed(120), + stop=tenacity.stop_after_attempt(2)) + def _retry_check_commands_on_units(self, cmds, units): + return generic_utils.check_commands_on_units(cmds, units) + def test_pause_resume(self): """Run pause and resume tests. @@ -50,6 +62,19 @@ class BaseCharmOperationTest(test_utils.BaseCharmTest): logging.info('Testing pause resume (services="{}")' .format(self.services)) + def test_nrpe_configured(self): + """Confirm that the NRPE service check files are created.""" + units = zaza.model.get_units(self.application_name) + cmds = [] + for check_name in self.nrpe_checks: + cmds.append( + 'egrep -oh /usr/local.* /etc/nagios/nrpe.d/' + 'check_{}.cfg'.format(check_name) + ) + ret = self._retry_check_commands_on_units(cmds, units) + logging.info(ret) + self.assertIsNone(ret, msg=ret) + class CentralCharmOperationTest(BaseCharmOperationTest): """OVN Central Charm operation tests.""" @@ -62,6 +87,22 @@ class CentralCharmOperationTest(BaseCharmOperationTest): 'ovn-northd', 'ovsdb-server', ] + source = zaza.model.get_application_config( + cls.application_name)['source']['value'] + logging.info(source) + if 'train' in source: + cls.nrpe_checks = [ + 'ovn-northd', + 'ovn-nb-ovsdb', + 'ovn-sb-ovsdb', + ] + else: + # Ussuri or later (distro or cloudarchive) + cls.nrpe_checks = [ + 'ovn-northd', + 'ovn-ovsdb-server-sb', + 'ovn-ovsdb-server-nb', + ] class ChassisCharmOperationTest(BaseCharmOperationTest): @@ -76,6 +117,26 @@ class ChassisCharmOperationTest(BaseCharmOperationTest): cls.services = [ 'ovn-controller', ] + if cls.application_name == 'ovn-chassis': + principal_app_name = 'magpie' + else: + principal_app_name = cls.application_name + source = zaza.model.get_application_config( + principal_app_name)['source']['value'] + logging.info(source) + if 'train' in source: + cls.nrpe_checks = [ + 'ovn-host', + 'ovs-vswitchd', + 'ovsdb-server', + ] + else: + # Ussuri or later (distro or cloudarchive) + cls.nrpe_checks = [ + 'ovn-controller', + 'ovsdb-server', + 'ovs-vswitchd', + ] class OVSOVNMigrationTest(test_utils.BaseCharmTest): From 7a67d98b4585756398e47f745a445e91677062fb Mon Sep 17 00:00:00 2001 From: Xav Paice Date: Fri, 2 Oct 2020 11:00:51 +1300 Subject: [PATCH 06/14] Only log OVN NRPE failure when it fails --- zaza/openstack/charm_tests/ovn/tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/ovn/tests.py b/zaza/openstack/charm_tests/ovn/tests.py index 30abddd..c5eae5e 100644 --- a/zaza/openstack/charm_tests/ovn/tests.py +++ b/zaza/openstack/charm_tests/ovn/tests.py @@ -72,7 +72,8 @@ class BaseCharmOperationTest(test_utils.BaseCharmTest): 'check_{}.cfg'.format(check_name) ) ret = self._retry_check_commands_on_units(cmds, units) - logging.info(ret) + if ret: + logging.info(ret) self.assertIsNone(ret, msg=ret) From efcb7a7e0653a1b5a475064ced25608f1d94ee2f Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Fri, 2 Oct 2020 10:50:10 +0200 Subject: [PATCH 07/14] Fix typo --- zaza/openstack/charm_tests/nova/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/nova/tests.py b/zaza/openstack/charm_tests/nova/tests.py index 16c9337..79e0e70 100644 --- a/zaza/openstack/charm_tests/nova/tests.py +++ b/zaza/openstack/charm_tests/nova/tests.py @@ -66,7 +66,7 @@ class LTSGuestCreateVolumeBackedTest(test_utils.OpenStackBaseTest): self.RESOURCE_PREFIX = 'zaza-nova' self.launch_guest( 'volume-backed-ubuntu', instance_key=glance_setup.LTS_IMAGE_NAME, - use_boot_volmue=True) + use_boot_volume=True) class NovaCompute(test_utils.OpenStackBaseTest): From 7c2f5cdf2493d39505b28d1b3243c6ab76b59e01 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Sat, 3 Oct 2020 00:23:05 +0000 Subject: [PATCH 08/14] Add Ironic tests --- requirements.txt | 1 + .../test_zaza_utilities_openstack.py | 14 +- zaza/openstack/charm_tests/glance/setup.py | 49 ++++- zaza/openstack/charm_tests/ironic/__init__.py | 15 ++ zaza/openstack/charm_tests/ironic/setup.py | 167 ++++++++++++++++++ zaza/openstack/charm_tests/ironic/tests.py | 83 +++++++++ zaza/openstack/utilities/openstack.py | 13 +- 7 files changed, 329 insertions(+), 13 deletions(-) create mode 100644 zaza/openstack/charm_tests/ironic/__init__.py create mode 100644 zaza/openstack/charm_tests/ironic/setup.py create mode 100644 zaza/openstack/charm_tests/ironic/tests.py diff --git a/requirements.txt b/requirements.txt index 8b15edc..cec0286 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,6 +29,7 @@ python-ceilometerclient python-cinderclient python-glanceclient python-heatclient +python-ironicclient python-keystoneclient python-manilaclient python-neutronclient diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index 02bd578..5ad0b9b 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -504,7 +504,7 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): glance_mock.images.upload.assert_called_once_with( '9d1125af', f(), - ) + backend=None) self.resource_reaches_status.assert_called_once_with( glance_mock.images, '9d1125af', @@ -529,7 +529,11 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.upload_image_to_glance.assert_called_once_with( glance_mock, 'wibbly/c.img', - 'bob') + 'bob', + backend=None, + disk_format='qcow2', + visibility='public', + container_format='bare') def test_create_image_pass_directory(self): glance_mock = mock.MagicMock() @@ -549,7 +553,11 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): self.upload_image_to_glance.assert_called_once_with( glance_mock, 'tests/c.img', - 'bob') + 'bob', + backend=None, + disk_format='qcow2', + visibility='public', + container_format='bare') self.gettempdir.assert_not_called() def test_create_ssh_key(self): diff --git a/zaza/openstack/charm_tests/glance/setup.py b/zaza/openstack/charm_tests/glance/setup.py index ab0a3b3..6686d70 100644 --- a/zaza/openstack/charm_tests/glance/setup.py +++ b/zaza/openstack/charm_tests/glance/setup.py @@ -32,8 +32,37 @@ def basic_setup(): """ +def _get_default_glance_client(): + """Create default Glance client using overcloud credentials.""" + keystone_session = openstack_utils.get_overcloud_keystone_session() + glance_client = openstack_utils.get_glance_session_client(keystone_session) + return glance_client + + +def get_stores_info(glance_client=None): + """Retrieve glance backing store info. + + :param glance_client: Authenticated glanceclient + :type glance_client: glanceclient.Client + """ + glance_client = glance_client or _get_default_glance_client() + stores = glance_client.images.get_stores_info().get("stores", []) + return stores + + +def get_store_ids(glance_client=None): + """Retrieve glance backing store ids. + + :param glance_client: Authenticated glanceclient + :type glance_client: glanceclient.Client + """ + stores = get_stores_info(glance_client) + return [store["id"] for store in stores] + + def add_image(image_url, glance_client=None, image_name=None, tags=[], - properties=None): + properties=None, backend=None, disk_format='qcow2', + visibility='public', container_format='bare'): """Retrieve image from ``image_url`` and add it to glance. :param image_url: Retrievable URL with image data @@ -47,10 +76,14 @@ def add_image(image_url, glance_client=None, image_name=None, tags=[], :param properties: Properties to add to image :type properties: dict """ - if not glance_client: - keystone_session = openstack_utils.get_overcloud_keystone_session() - glance_client = openstack_utils.get_glance_session_client( - keystone_session) + glance_client = glance_client or _get_default_glance_client() + if backend is not None: + stores = get_store_ids(glance_client) + if backend not in stores: + raise ValueError("Invalid backend: %(backend)s " + "(available: %(available)s)" % { + "backend": backend, + "available": ", ".join(stores)}) if image_name: image = openstack_utils.get_images_by_name( glance_client, image_name) @@ -65,7 +98,11 @@ def add_image(image_url, glance_client=None, image_name=None, tags=[], image_url, image_name, tags=tags, - properties=properties) + properties=properties, + backend=backend, + disk_format=disk_format, + visibility=visibility, + container_format=container_format) def add_cirros_image(glance_client=None, image_name=None): diff --git a/zaza/openstack/charm_tests/ironic/__init__.py b/zaza/openstack/charm_tests/ironic/__init__.py new file mode 100644 index 0000000..aedad05 --- /dev/null +++ b/zaza/openstack/charm_tests/ironic/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Collection of code for setting up and testing ironic.""" diff --git a/zaza/openstack/charm_tests/ironic/setup.py b/zaza/openstack/charm_tests/ironic/setup.py new file mode 100644 index 0000000..b03c8cb --- /dev/null +++ b/zaza/openstack/charm_tests/ironic/setup.py @@ -0,0 +1,167 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Code for configuring ironic.""" + +import copy +import os + +import zaza.openstack.charm_tests.glance.setup as glance_setup +import zaza.openstack.utilities.openstack as openstack_utils +from zaza.openstack.utilities import ( + cli as cli_utils, +) +import zaza.model as zaza_model + + +FLAVORS = { + 'bm1.small': { + 'flavorid': 2, + 'ram': 2048, + 'disk': 20, + 'vcpus': 1, + 'properties': { + "resources:CUSTOM_BAREMETAL1_SMALL": 1, + }, + }, + 'bm1.medium': { + 'flavorid': 3, + 'ram': 4096, + 'disk': 40, + 'vcpus': 2, + 'properties': { + "resources:CUSTOM_BAREMETAL1_MEDIUM": 1, + }, + }, + 'bm1.large': { + 'flavorid': 4, + 'ram': 8192, + 'disk': 40, + 'vcpus': 4, + 'properties': { + "resources:CUSTOM_BAREMETAL1_LARGE": 1, + }, + }, + 'bm1.tempest': { + 'flavorid': 6, + 'ram': 256, + 'disk': 1, + 'vcpus': 1, + 'properties': { + "resources:CUSTOM_BAREMETAL1_TEMPEST": 1, + }, + }, + 'bm2.tempest': { + 'flavorid': 7, + 'ram': 512, + 'disk': 1, + 'vcpus': 1, + 'properties': { + "resources:CUSTOM_BAREMETAL2_TEMPEST": 1, + }, + }, +} + + +def add_ironic_deployment_image(initrd_url=None, kernel_url=None): + """Add Ironic deploy images to glance. + + :param initrd_url: URL where the ari image resides + :type initrd_url: str + :param kernel_url: URL where the aki image resides + :type kernel_url: str + """ + base_name = 'ironic-deploy' + initrd_name = "{}-initrd".format(base_name) + vmlinuz_name = "{}-vmlinuz".format(base_name) + if not initrd_url: + initrd_url = os.environ.get('TEST_IRONIC_DEPLOY_INITRD', None) + if not kernel_url: + kernel_url = os.environ.get('TEST_IRONIC_DEPLOY_VMLINUZ', None) + if not all([initrd_url, kernel_url]): + raise ValueError("Missing required deployment image URLs") + glance_setup.add_image( + initrd_url, + image_name=initrd_name, + backend="swift", + disk_format="ari", + container_format="ari") + glance_setup.add_image( + kernel_url, + image_name=vmlinuz_name, + backend="swift", + disk_format="aki", + container_format="aki") + + +def add_ironic_os_image(image_url=None): + """Upload the operating system images built for bare metal deployments. + + :param image_url: URL where the image resides + :type image_url: str + """ + image_url = image_url or os.environ.get( + 'TEST_IRONIC_RAW_BM_IMAGE', None) + image_name = "baremetal-ubuntu-image" + if image_url is None: + raise ValueError("Missing image_url") + + glance_setup.add_image( + image_url, + image_name=image_name, + backend="swift", + disk_format="raw", + container_format="bare") + + +def set_temp_url_secret(): + """Run the set-temp-url-secret on the ironic-conductor leader. + + This is needed if direct boot method is enabled. + """ + zaza_model.run_action_on_leader( + 'ironic-conductor', + 'set-temp-url-secret', + action_params={}) + + +def create_bm_flavors(nova_client=None): + """Create baremetal flavors. + + :param nova_client: Authenticated nova client + :type nova_client: novaclient.v2.client.Client + """ + if not nova_client: + keystone_session = openstack_utils.get_overcloud_keystone_session() + nova_client = openstack_utils.get_nova_session_client( + keystone_session) + cli_utils.setup_logging() + names = [flavor.name for flavor in nova_client.flavors.list()] + # Disable scheduling based on standard flavor properties + default_properties = { + "resources:VCPU": 0, + "resources:MEMORY_MB": 0, + "resources:DISK_GB": 0, + } + for flavor in FLAVORS.keys(): + if flavor not in names: + properties = copy.deepcopy(default_properties) + properties.update(FLAVORS[flavor]["properties"]) + bm_flavor = nova_client.flavors.create( + name=flavor, + ram=FLAVORS[flavor]['ram'], + vcpus=FLAVORS[flavor]['vcpus'], + disk=FLAVORS[flavor]['disk'], + flavorid=FLAVORS[flavor]['flavorid']) + bm_flavor.set_keys(properties) diff --git a/zaza/openstack/charm_tests/ironic/tests.py b/zaza/openstack/charm_tests/ironic/tests.py new file mode 100644 index 0000000..9cfb85a --- /dev/null +++ b/zaza/openstack/charm_tests/ironic/tests.py @@ -0,0 +1,83 @@ +# Copyright 2020 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Encapsulate ironic testing.""" + +import logging + +import ironicclient.client as ironic_client +import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities.openstack as openstack_utils + + +def _get_ironic_client(ironic_api_version="1.58"): + keystone_session = openstack_utils.get_overcloud_keystone_session() + ironic = ironic_client.Client(1, session=keystone_session, + os_ironic_api_version=ironic_api_version) + return ironic + + +class IronicTest(test_utils.OpenStackBaseTest): + """Run Ironic specific tests.""" + + _SERVICES = ['ironic-api'] + + def test_110_catalog_endpoints(self): + """Verify that the endpoints are present in the catalog.""" + overcloud_auth = openstack_utils.get_overcloud_auth() + keystone_client = openstack_utils.get_keystone_client( + overcloud_auth) + actual_endpoints = keystone_client.service_catalog.get_endpoints() + actual_interfaces = [endpoint['interface'] for endpoint in + actual_endpoints["baremetal"]] + for expected_interface in ('internal', 'admin', 'public'): + assert(expected_interface in actual_interfaces) + + def test_400_api_connection(self): + """Simple api calls to check service is up and responding.""" + ironic = _get_ironic_client() + + logging.info('listing conductors') + conductors = ironic.conductor.list() + assert(len(conductors) > 0) + + # By default, only IPMI HW type is enabled. iDrac and Redfish + # can optionally be enabled + drivers = ironic.driver.list() + driver_names = [drv.name for drv in drivers] + + expected = ['intel-ipmi', 'ipmi'] + for exp in expected: + assert(exp in driver_names) + assert(len(driver_names) == 2) + + def test_900_restart_on_config_change(self): + """Checking restart happens on config change. + + Change debug mode and assert that change propagates to the correct + file and that services are restarted as a result + """ + self.restart_on_changed_debug_oslo_config_file( + '/etc/ironic/ironic.conf', self._SERVICES) + + def test_910_pause_resume(self): + """Run pause and resume tests. + + Pause service and check services are stopped then resume and check + they are started + """ + logging.info('Skipping pause resume test LP: #1886202...') + return + with self.pause_resume(self._SERVICES): + logging.info("Testing pause resume") diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index 35972c6..b62885e 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -2060,7 +2060,8 @@ def delete_volume_backup(cinder, vol_backup_id): def upload_image_to_glance(glance, local_path, image_name, disk_format='qcow2', - visibility='public', container_format='bare'): + visibility='public', container_format='bare', + backend=None): """Upload the given image to glance and apply the given label. :param glance: Authenticated glanceclient @@ -2086,7 +2087,7 @@ def upload_image_to_glance(glance, local_path, image_name, disk_format='qcow2', disk_format=disk_format, visibility=visibility, container_format=container_format) - glance.images.upload(image.id, open(local_path, 'rb')) + glance.images.upload(image.id, open(local_path, 'rb'), backend=backend) resource_reaches_status( glance.images, @@ -2098,7 +2099,8 @@ def upload_image_to_glance(glance, local_path, image_name, disk_format='qcow2', def create_image(glance, image_url, image_name, image_cache_dir=None, tags=[], - properties=None): + properties=None, backend=None, disk_format='qcow2', + visibility='public', container_format='bare'): """Download the image and upload it to glance. Download an image from image_url and upload it to glance labelling @@ -2132,7 +2134,10 @@ def create_image(glance, image_url, image_name, image_cache_dir=None, tags=[], if not os.path.exists(local_path): download_image(image_url, local_path) - image = upload_image_to_glance(glance, local_path, image_name) + image = upload_image_to_glance( + glance, local_path, image_name, backend=backend, + disk_format=disk_format, visibility=visibility, + container_format=container_format) for tag in tags: result = glance.image_tags.update(image.id, tag) logging.debug( From 181bb98d2060f1edbacb8beb0555918f3e58c5e1 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Fri, 9 Oct 2020 09:39:00 +0200 Subject: [PATCH 09/14] Clean up newly created instances if faulty (#439) --- zaza/openstack/charm_tests/test_utils.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index 1f24aa9..9157bd3 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -550,21 +550,23 @@ class OpenStackBaseTest(BaseCharmTest): instance_key = instance_key or glance_setup.LTS_IMAGE_NAME instance_name = '{}-{}'.format(self.RESOURCE_PREFIX, guest_name) - instance = self.retrieve_guest(instance_name) - if instance: - logging.info('Removing already existing instance ({}) with ' - 'requested name ({})' - .format(instance.id, instance_name)) - openstack_utils.delete_resource( - self.nova_client.servers, - instance.id, - msg="server") - for attempt in tenacity.Retrying( stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_exponential( multiplier=1, min=2, max=10)): with attempt: + old_instance_with_same_name = self.retrieve_guest( + instance_name) + if old_instance_with_same_name: + logging.info( + 'Removing already existing instance ({}) with ' + 'requested name ({})' + .format(old_instance_with_same_name.id, instance_name)) + openstack_utils.delete_resource( + self.nova_client.servers, + old_instance_with_same_name.id, + msg="server") + return configure_guest.launch_instance( instance_key, vm_name=instance_name, From f27ee7c1f6972dbaa6b3e22b0259ac3dda8140a2 Mon Sep 17 00:00:00 2001 From: Aurelien Lourot Date: Fri, 9 Oct 2020 11:44:03 +0200 Subject: [PATCH 10/14] Make determination of app under test more robust --- zaza/openstack/charm_tests/test_utils.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index 9157bd3..74b76b2 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -127,10 +127,28 @@ class BaseCharmTest(unittest.TestCase): else: cls.model_name = model.get_juju_model() cls.test_config = lifecycle_utils.get_charm_config(fatal=False) + charm_under_test_name = cls.test_config['charm_name'] + if application_name: cls.application_name = application_name else: - cls.application_name = cls.test_config['charm_name'] + deployed_app_names = model.sync_deployed(model_name=cls.model_name) + if charm_under_test_name in deployed_app_names: + # There is an application named like the charm under test. + # Let's consider it the application under test: + cls.application_name = charm_under_test_name + else: + # Let's search for any application whose name starts with the + # name of the charm under test and assume it's the application + # under test: + for app_name in deployed_app_names: + if app_name.startswith(charm_under_test_name): + cls.application_name = app_name + break + else: + logging.warning('Could not find application under test') + return + cls.lead_unit = model.get_lead_unit_name( cls.application_name, model_name=cls.model_name) From 299fae65dbc561ad766cef3da2a3b2c2e0f84ba4 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 12 Oct 2020 12:03:44 +0000 Subject: [PATCH 11/14] Fix test runs with no charm_name Fix z-o-t so that tests that specify an application name can be run irrespective of whether the charm_name has been set in tests.yaml. Closes issue #442 --- zaza/openstack/charm_tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/test_utils.py b/zaza/openstack/charm_tests/test_utils.py index 74b76b2..493a868 100644 --- a/zaza/openstack/charm_tests/test_utils.py +++ b/zaza/openstack/charm_tests/test_utils.py @@ -127,11 +127,11 @@ class BaseCharmTest(unittest.TestCase): else: cls.model_name = model.get_juju_model() cls.test_config = lifecycle_utils.get_charm_config(fatal=False) - charm_under_test_name = cls.test_config['charm_name'] if application_name: cls.application_name = application_name else: + charm_under_test_name = cls.test_config['charm_name'] deployed_app_names = model.sync_deployed(model_name=cls.model_name) if charm_under_test_name in deployed_app_names: # There is an application named like the charm under test. From 4f83645b1c51857eb9bf073f564d00ff773df4c4 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Mon, 19 Oct 2020 08:40:23 +0000 Subject: [PATCH 12/14] Add python-ironicclient to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index f4e31f8..b18cd9a 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ install_require = [ 'python-barbicanclient>=4.0.1,<5.0.0', 'python-designateclient>=1.5,<3.0.0', 'python-heatclient<2.0.0', + 'python-ironicclient', 'python-glanceclient<3.0.0', 'python-keystoneclient<3.22.0', 'python-manilaclient<2.0.0', From a75d49d9595f9d34c1a96e2371517f140306ffa2 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Mon, 19 Oct 2020 11:44:13 +0000 Subject: [PATCH 13/14] Add tenacity to image uploads --- zaza/openstack/charm_tests/ironic/setup.py | 29 +++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/zaza/openstack/charm_tests/ironic/setup.py b/zaza/openstack/charm_tests/ironic/setup.py index b03c8cb..fe7d034 100644 --- a/zaza/openstack/charm_tests/ironic/setup.py +++ b/zaza/openstack/charm_tests/ironic/setup.py @@ -16,6 +16,7 @@ import copy import os +import tenacity import zaza.openstack.charm_tests.glance.setup as glance_setup import zaza.openstack.utilities.openstack as openstack_utils @@ -74,6 +75,20 @@ FLAVORS = { } +def _add_image(url, image_name, backend="swift", + disk_format="raw", container_format="bare"): + for attempt in tenacity.Retrying( + stop=tenacity.stop_after_attempt(3), + reraise=True): + with attempt: + glance_setup.add_image( + url, + image_name=image_name, + backend=backend, + disk_format=disk_format, + container_format=container_format) + + def add_ironic_deployment_image(initrd_url=None, kernel_url=None): """Add Ironic deploy images to glance. @@ -91,15 +106,17 @@ def add_ironic_deployment_image(initrd_url=None, kernel_url=None): kernel_url = os.environ.get('TEST_IRONIC_DEPLOY_VMLINUZ', None) if not all([initrd_url, kernel_url]): raise ValueError("Missing required deployment image URLs") - glance_setup.add_image( + + _add_image( initrd_url, - image_name=initrd_name, + initrd_name, backend="swift", disk_format="ari", container_format="ari") - glance_setup.add_image( + + _add_image( kernel_url, - image_name=vmlinuz_name, + vmlinuz_name, backend="swift", disk_format="aki", container_format="aki") @@ -117,9 +134,9 @@ def add_ironic_os_image(image_url=None): if image_url is None: raise ValueError("Missing image_url") - glance_setup.add_image( + _add_image( image_url, - image_name=image_name, + image_name, backend="swift", disk_format="raw", container_format="bare") From 79e43c7f684f18839a5ff59457cfc8e9116b3b59 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 22 Oct 2020 15:28:16 +0100 Subject: [PATCH 14/14] bionic-train -> ussuri needs to upgrade placement For the openstack upgrade of train to ussuri, the placement charm needs to be upgraded. Add it to the UPGRADE_SERVICES. Related launchpad bug: LP:1724174 [1] [1]: https://bugs.launchpad.net/openstack-mojo-specs/+bug/1724174 --- zaza/openstack/utilities/openstack.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zaza/openstack/utilities/openstack.py b/zaza/openstack/utilities/openstack.py index b62885e..8d25433 100644 --- a/zaza/openstack/utilities/openstack.py +++ b/zaza/openstack/utilities/openstack.py @@ -123,6 +123,10 @@ CHARM_TYPES = { 'pkg': 'ceph-common', 'origin_setting': 'source' }, + 'placement': { + 'pkg': 'placement-common', + 'origin_setting': 'openstack-origin' + }, } # Older tests use the order the services appear in the list to imply @@ -143,6 +147,7 @@ UPGRADE_SERVICES = [ 'type': CHARM_TYPES['openstack-dashboard']}, {'name': 'ovn-central', 'type': CHARM_TYPES['ovn-central']}, {'name': 'ceph-mon', 'type': CHARM_TYPES['ceph-mon']}, + {'name': 'placement', 'type': CHARM_TYPES['placement']}, ]