Add support for Magnum to use different images per release.

OpenStack Magnum relies on specific versions of Fedora CoreOS, this
patch addresses this maintaining a map of images per release according
to the upstream documentation[0]

The images are expected to be stored in the object store pointed out by
TEST_SWIFT_IP[1] environment variable in a container named 'magnum'. A
bash script to upload images can be found at
./zaza/openstack/charm_tests/magnum/upload_fedora_coreos_images.sh

[0] https://docs.openstack.org/magnum/latest/user/index.html#supported-versions
[1] https://github.com/openstack-charmers/zosci-config/pull/262
This commit is contained in:
Felipe Reyes
2023-02-08 16:30:20 -03:00
parent 49cd74e12a
commit fa09a20b4b
5 changed files with 208 additions and 12 deletions

View File

@@ -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()

View File

@@ -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)

View File

@@ -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):

View File

@@ -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

View File

@@ -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.