diff --git a/unit_tests/charm_tests/test_magnum.py b/unit_tests/charm_tests/test_magnum.py new file mode 100644 index 0000000..77c8c5e --- /dev/null +++ b/unit_tests/charm_tests/test_magnum.py @@ -0,0 +1,72 @@ +# Copyright 2023 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. + +import os +import unittest + +from unittest import mock + +import zaza.openstack.charm_tests.magnum.setup as magnum_setup + + +class TestMagnumSetup(unittest.TestCase): + + @mock.patch.object(magnum_setup.openstack_utils, + 'get_current_os_release_pair') + def test_get_fedora_coreos_image_url(self, get_current_os_release_pair): + get_current_os_release_pair.return_value = 'focal_ussuri' + self.assertEqual(magnum_setup.FEDORA_COREOS_IMAGE['ussuri'], + magnum_setup.get_fedora_coreos_image_url()) + + self.assertEqual(magnum_setup.FEDORA_COREOS_IMAGE['xena'], + magnum_setup.get_fedora_coreos_image_url('xena')) + self.assertEqual(magnum_setup.DEFAULT_FEDORA_COREOS_IMAGE_URL, + magnum_setup.get_fedora_coreos_image_url('foobar')) + + @mock.patch.object(magnum_setup, 'get_fedora_coreos_image_url') + @mock.patch.object(magnum_setup.openstack_utils, + 'get_overcloud_keystone_session') + @mock.patch.object(magnum_setup.openstack_utils, + 'get_glance_session_client') + @mock.patch.object(magnum_setup.openstack_utils, + 'create_image') + def test_add_image(self, create_image, get_glance_session_client, + get_overcloud_keystone_session, + get_fedora_coreos_image_url): + image_url = 'http://example.com/image.qcow2' + with mock.patch.dict(os.environ, + {'TEST_MAGNUM_QCOW2_IMAGE_URL': + image_url}, + clear=True) as environ: # noqa:F841 + magnum_setup.add_image() + create_image.assert_called_with( + get_glance_session_client(), + image_url, + magnum_setup.IMAGE_NAME, + properties={'os_distro': magnum_setup.IMAGE_NAME} + ) + get_fedora_coreos_image_url.assert_not_called() + + image_url = 'http://example.com/fedora-coreos.qcow2' + get_fedora_coreos_image_url.return_value = image_url + with mock.patch.dict(os.environ, {}, + clear=True) as environ: # noqa:F841 + magnum_setup.add_image() + create_image.assert_called_with( + get_glance_session_client(), + image_url, + magnum_setup.IMAGE_NAME, + properties={'os_distro': magnum_setup.IMAGE_NAME} + ) + get_fedora_coreos_image_url.assert_called() diff --git a/unit_tests/charm_tests/test_tempest.py b/unit_tests/charm_tests/test_tempest.py index ac6962e..ddbcdeb 100644 --- a/unit_tests/charm_tests/test_tempest.py +++ b/unit_tests/charm_tests/test_tempest.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import mock +import os import unittest +from unittest import mock + import zaza.openstack.charm_tests.tempest.utils as tempest_utils @@ -61,3 +63,21 @@ class TestTempestUtils(unittest.TestCase): ('Environment variables [TEST_CIDR_EXT, TEST_FIP_RANGE] must ' 'all be set to run this test'), str(context.exception)) + + @mock.patch.object(tempest_utils, '_add_image_id') + def test_add_magnum_config(self, _add_image_id): + ctxt = {} + keystone_session = mock.MagicMock() + with mock.patch.dict(os.environ, + {'TEST_REGISTRY_PREFIX': '1.2.3.4:5000'}, + clear=True) as environ: # noqa:F841 + tempest_utils._add_magnum_config(ctxt, keystone_session) + self.assertIn('test_registry_prefix', ctxt) + self.assertEqual(ctxt['test_registry_prefix'], '1.2.3.4:5000') + + _add_image_id.assert_called() + ctxt = {} + with mock.patch.dict(os.environ, {}, + clear=True) as environ: # noqa:F841 + tempest_utils._add_magnum_config(ctxt, keystone_session) + self.assertNotIn('test_registry_prefix', ctxt) diff --git a/zaza/openstack/charm_tests/magnum/setup.py b/zaza/openstack/charm_tests/magnum/setup.py index 4255f8c..3baa72a 100644 --- a/zaza/openstack/charm_tests/magnum/setup.py +++ b/zaza/openstack/charm_tests/magnum/setup.py @@ -23,8 +23,30 @@ import tenacity import zaza.model import zaza.openstack.utilities.openstack as openstack_utils +TEST_SWIFT_IP = os.environ.get('TEST_SWIFT_IP') IMAGE_NAME = 'fedora-coreos' +# https://docs.openstack.org/magnum/latest/user/index.html#supported-versions +# List of published image available at: +# https://builds.coreos.fedoraproject.org/browser?stream=stable&arch=x86_64 +# +# Source images: +# https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/35.20220424.3.0/x86_64/fedora-coreos-35.20220424.3.0-openstack.x86_64.qcow2.xz # noqa +# https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/31.20200517.3.0/x86_64/fedora-coreos-31.20200517.3.0-openstack.x86_64.qcow2.xz # noqa +# https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/32.20201104.3.0/x86_64/fedora-coreos-32.20201104.3.0-openstack.x86_64.qcow2.xz # noqa +FEDORA_COREOS_31 = 'http://%s/magnum/images/fedora-coreos-31.qcow2' % TEST_SWIFT_IP # noqa +FEDORA_COREOS_32 = 'http://%s/magnum/images/fedora-coreos-32.qcow2' % TEST_SWIFT_IP # noqa +FEDORA_COREOS_35 = 'http://%s/magnum/images/fedora-coreos-35.qcow2' % TEST_SWIFT_IP # noqa +DEFAULT_FEDORA_COREOS_IMAGE_URL = FEDORA_COREOS_35 +FEDORA_COREOS_IMAGE = { + 'ussuri': FEDORA_COREOS_32, + 'victoria': FEDORA_COREOS_31, + 'wallaby': FEDORA_COREOS_31, + 'xena': FEDORA_COREOS_31, + 'yoga': FEDORA_COREOS_35, + 'zed': FEDORA_COREOS_35, +} + def domain_setup(application_name='magnum'): """Run required action for a working Magnum application.""" @@ -37,6 +59,25 @@ def domain_setup(application_name='magnum'): "Unit is ready") +def get_fedora_coreos_image_url(os_release: str = None) -> str: + """Get Fedora CoreOS image url compatible with the Magnum release deployed. + + :param os_release: OpenStack release codename (e.g. ussuri). + :returns: url where the image can be GET. + """ + if not os_release: + pair = openstack_utils.get_current_os_release_pair('keystone') + os_release = pair.split('_', 1)[1] + if os_release in FEDORA_COREOS_IMAGE: + return FEDORA_COREOS_IMAGE[os_release] + else: + logging.warning( + 'No image found for OpenStack %s, using default image %s', + os_release, DEFAULT_FEDORA_COREOS_IMAGE_URL + ) + return DEFAULT_FEDORA_COREOS_IMAGE_URL + + def add_image(image_url=None): """Upload Magnum image. @@ -47,9 +88,8 @@ def add_image(image_url=None): :type image_url: str """ image_url = image_url or os.environ.get( - 'TEST_MAGNUM_QCOW2_IMAGE_URL', None) - if image_url is None: - raise ValueError("Missing image_url") + 'TEST_MAGNUM_QCOW2_IMAGE_URL', None) or get_fedora_coreos_image_url() + for attempt in tenacity.Retrying( stop=tenacity.stop_after_attempt(3), reraise=True): diff --git a/zaza/openstack/charm_tests/magnum/upload_fedora_coreos_images.sh b/zaza/openstack/charm_tests/magnum/upload_fedora_coreos_images.sh new file mode 100755 index 0000000..604d419 --- /dev/null +++ b/zaza/openstack/charm_tests/magnum/upload_fedora_coreos_images.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +wget -O - https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/31.20200517.3.0/x86_64/fedora-coreos-31.20200517.3.0-openstack.x86_64.qcow2.xz \ + | xzcat > ./fedora-coreos-31.qcow2 + +openstack object create --name images/fedora-coreos-31.qcow2 magnum ./fedora-coreos-31.qcow2 + +wget -O - https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/32.20201104.3.0/x86_64/fedora-coreos-32.20201104.3.0-openstack.x86_64.qcow2.xz \ + | xzcat > ./fedora-coreos-32.qcow2 + +openstack object create --name images/fedora-coreos-32.qcow2 magnum ./fedora-coreos-32.qcow2 + +wget -O - https://builds.coreos.fedoraproject.org/prod/streams/stable/builds/35.20220424.3.0/x86_64/fedora-coreos-35.20220424.3.0-openstack.x86_64.qcow2.xz \ + | xzcat > ./fedora-coreos-35.qcow2 + +openstack object create --name images/fedora-coreos-35.qcow2 magnum ./fedora-coreos-35.qcow2 diff --git a/zaza/openstack/charm_tests/tempest/utils.py b/zaza/openstack/charm_tests/tempest/utils.py index d1ef96c..a811bdb 100644 --- a/zaza/openstack/charm_tests/tempest/utils.py +++ b/zaza/openstack/charm_tests/tempest/utils.py @@ -15,6 +15,7 @@ """Utility code for working with tempest workspaces.""" import jinja2 +import logging import os from pathlib import Path import shutil @@ -28,6 +29,7 @@ import zaza.utilities.deployment_env as deployment_env import zaza.openstack.utilities.juju as juju_utils import zaza.openstack.utilities.openstack as openstack_utils import zaza.openstack.charm_tests.glance.setup as glance_setup +import zaza.openstack.charm_tests.magnum.setup as magnum_setup SETUP_ENV_VARS = { 'neutron': ['TEST_GATEWAY', 'TEST_CIDR_EXT', 'TEST_FIP_RANGE', @@ -156,7 +158,9 @@ def _get_tempest_context(workspace_path, missing_fatal=True): 'neutron': _add_neutron_config, 'glance': _add_glance_config, 'cinder': _add_cinder_config, - 'keystone': _add_keystone_config} + 'keystone': _add_keystone_config, + 'magnum': _add_magnum_config, + } ctxt['enabled_services'] = _get_service_list(keystone_session) if set(['cinderv2', 'cinderv3']) \ .intersection(set(ctxt['enabled_services'])): @@ -284,16 +288,39 @@ def _add_glance_config(ctxt, keystone_session, missing_fatal=True): :returns: None :rtype: None """ + _add_image_id(ctxt, keystone_session, + glance_setup.CIRROS_IMAGE_NAME, 'image_id', + missing_fatal) + _add_image_id(ctxt, keystone_session, + glance_setup.CIRROS_ALT_IMAGE_NAME, 'image_alt_id', + missing_fatal) + + +def _add_image_id(ctxt, keystone_session, image_name, ctxt_key, + missing_fatal=True): + """Add an image id to the context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param keystone_session: keystoneauth1.session.Session object + :type: keystoneauth1.session.Session + :param image_name: Image's name to search in glance. + :type image_name: str + :param ctxt_key: key to use when adding the image id to the context. + :type ctxt_key: str + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool + """ glance_client = openstack_utils.get_glance_session_client( keystone_session) - image = openstack_utils.get_images_by_name( - glance_client, glance_setup.CIRROS_IMAGE_NAME) - image_alt = openstack_utils.get_images_by_name( - glance_client, glance_setup.CIRROS_ALT_IMAGE_NAME) + image = openstack_utils.get_images_by_name(glance_client, image_name) if image: - ctxt['image_id'] = image[0].id - if image_alt: - ctxt['image_alt_id'] = image_alt[0].id + ctxt[ctxt_key] = image[0].id + else: + msg = 'Image %s not found' % image_name + logging.warning(msg) + if missing_fatal: + raise Exception(msg) def _add_cinder_config(ctxt, keystone_session, missing_fatal=True): @@ -360,6 +387,27 @@ def _add_octavia_config(ctxt, missing_fatal=True): ]) +def _add_magnum_config(ctxt, keystone_session, missing_fatal=True): + """Add magnum config to context. + + :param ctxt: Context dictionary + :type ctxt: dict + :param missing_fatal: Raise an exception if a resource is missing + :type missing_fatal: bool + :returns: None + :rtype: None + :raises: subprocess.CalledProcessError + """ + _add_image_id(ctxt, keystone_session, + magnum_setup.IMAGE_NAME, 'fedora_coreos_id', + missing_fatal) + test_registry_prefix = os.environ.get('TEST_REGISTRY_PREFIX') + if test_registry_prefix: + ctxt['test_registry_prefix'] = test_registry_prefix + else: + logging.warning('Environment variable TEST_REGISTRY_PREFIX not found') + + def _add_environment_var_config(ctxt, services, missing_fatal=True): """Add environment variable config to context.