From d4d415c859273e378b227dcab668384c7ab95e92 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 30 Nov 2021 18:50:14 +0000 Subject: [PATCH] Ensure that series upgrades ignore percona at focal When doing a series upgrade from bionic to focal, the percona-cluster charm needs to be avoided. This filters the applications for percona if the target is focal. Note if percona is placed on the same unit as something else that needs to be upgraded (bad idea) then it may still get 'upgraded' and the operation will fail. --- .../test_zaza_utilities_upgrade_utils.py | 15 +++ .../charm_tests/rabbitmq_server/tests.py | 2 +- .../series_upgrade/parallel_tests.py | 3 +- .../charm_tests/series_upgrade/tests.py | 3 +- zaza/openstack/utilities/os_versions.py | 102 ++++++++++++++++++ zaza/openstack/utilities/upgrade_utils.py | 32 +++++- 6 files changed, 152 insertions(+), 5 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py b/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py index f42ba5a..986ba38 100644 --- a/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py +++ b/unit_tests/utilities/test_zaza_utilities_upgrade_utils.py @@ -116,6 +116,21 @@ class TestUpgradeUtils(ut_utils.BaseTestCase): self.assertEqual( actual, expected) + # test that, at focal, there are no database services. + expected = [ + ('Database Services', []), + ('Stateful Services', []), + ('Core Identity', []), + ('Control Plane', ['cinder']), + ('Data Plane', ['nova-compute']), + ('sweep_up', ['ntp'])] + actual = openstack_upgrade.get_series_upgrade_groups( + target_series='focal') + pprint.pprint(expected) + pprint.pprint(actual) + self.assertEqual( + actual, + expected) def test_extract_charm_name_from_url(self): self.assertEqual( diff --git a/zaza/openstack/charm_tests/rabbitmq_server/tests.py b/zaza/openstack/charm_tests/rabbitmq_server/tests.py index fed72df..6ee156e 100644 --- a/zaza/openstack/charm_tests/rabbitmq_server/tests.py +++ b/zaza/openstack/charm_tests/rabbitmq_server/tests.py @@ -26,8 +26,8 @@ import zaza.model import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.generic as generic_utils -from charmhelpers.core.host import CompareHostReleases from zaza.openstack.utilities.generic import get_series +from zaza.openstack.utilities.os_versions import CompareHostReleases from . import utils as rmq_utils from .utils import RmqNoMessageException diff --git a/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py b/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py index e524fa2..4dd2432 100644 --- a/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/parallel_tests.py @@ -73,7 +73,8 @@ class ParallelSeriesUpgradeTest(unittest.TestCase): # Set Feature Flag os.environ["JUJU_DEV_FEATURE_FLAGS"] = "upgrade-series" upgrade_groups = upgrade_utils.get_series_upgrade_groups( - extra_filters=[_filter_etcd, _filter_easyrsa]) + extra_filters=[_filter_etcd, _filter_easyrsa], + target_series=self.to_series) from_series = self.from_series to_series = self.to_series completed_machines = [] diff --git a/zaza/openstack/charm_tests/series_upgrade/tests.py b/zaza/openstack/charm_tests/series_upgrade/tests.py index 122af11..3bb37ed 100644 --- a/zaza/openstack/charm_tests/series_upgrade/tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/tests.py @@ -193,7 +193,8 @@ class ParallelSeriesUpgradeTest(unittest.TestCase): os.environ["JUJU_DEV_FEATURE_FLAGS"] = "upgrade-series" upgrade_groups = upgrade_utils.get_series_upgrade_groups( extra_filters=[upgrade_utils._filter_etcd, - upgrade_utils._filter_easyrsa]) + upgrade_utils._filter_easyrsa], + target_series=self.to_series) applications = model.get_status().applications completed_machines = [] for group_name, group in upgrade_groups: diff --git a/zaza/openstack/utilities/os_versions.py b/zaza/openstack/utilities/os_versions.py index e67a97f..d69f9cf 100644 --- a/zaza/openstack/utilities/os_versions.py +++ b/zaza/openstack/utilities/os_versions.py @@ -278,3 +278,105 @@ PACKAGE_CODENAMES = { ('4', 'victoria'), ]), } + + +UBUNTU_RELEASES = ( + 'lucid', + 'maverick', + 'natty', + 'oneiric', + 'precise', + 'quantal', + 'raring', + 'saucy', + 'trusty', + 'utopic', + 'vivid', + 'wily', + 'xenial', + 'yakkety', + 'zesty', + 'artful', + 'bionic', + 'cosmic', + 'disco', + 'eoan', + 'focal', + 'groovy', + 'hirsute', + 'impish', +) + + +class BasicStringComparator(object): + """Provides a class that will compare strings from an iterator type object. + + Used to provide > and < comparisons on strings that may not necessarily be + alphanumerically ordered. e.g. OpenStack or Ubuntu releases AFTER the + z-wrap. + """ + + _list = None + + def __init__(self, item): + """Do init.""" + if self._list is None: + raise Exception("Must define the _list in the class definition!") + try: + self.index = self._list.index(item) + except Exception: + raise KeyError("Item '{}' is not in list '{}'" + .format(item, self._list)) + + def __eq__(self, other): + """Do equals.""" + assert isinstance(other, str) or isinstance(other, self.__class__) + return self.index == self._list.index(other) + + def __ne__(self, other): + """Do not equals.""" + return not self.__eq__(other) + + def __lt__(self, other): + """Do less than.""" + assert isinstance(other, str) or isinstance(other, self.__class__) + return self.index < self._list.index(other) + + def __ge__(self, other): + """Do greater than or equal.""" + return not self.__lt__(other) + + def __gt__(self, other): + """Do greater than.""" + assert isinstance(other, str) or isinstance(other, self.__class__) + return self.index > self._list.index(other) + + def __le__(self, other): + """Do less than or equals.""" + return not self.__gt__(other) + + def __str__(self): + """Give back the item at the index. + + This is so it can be used in comparisons like: + + s_mitaka = CompareOpenStack('mitaka') + s_newton = CompareOpenstack('newton') + + assert s_newton > s_mitaka + + :returns: + """ + return self._list[self.index] + + +class CompareHostReleases(BasicStringComparator): + """Provide comparisons of Ubuntu releases. + + Use in the form of + + if CompareHostReleases(release) > 'trusty': + # do something with mitaka + """ + + _list = UBUNTU_RELEASES diff --git a/zaza/openstack/utilities/upgrade_utils.py b/zaza/openstack/utilities/upgrade_utils.py index ecc0b8b..6ce7254 100644 --- a/zaza/openstack/utilities/upgrade_utils.py +++ b/zaza/openstack/utilities/upgrade_utils.py @@ -24,6 +24,7 @@ from zaza.openstack.utilities.os_versions import ( OPENSTACK_CODENAMES, UBUNTU_OPENSTACK_RELEASE, OPENSTACK_RELEASES_PAIRS, + CompareHostReleases, ) """ @@ -96,6 +97,25 @@ def _filter_openstack_upgrade_list(app, app_config, model_name=None): return False +def _make_filter_percona_cluster_at(target_series): + def _filter_percona_cluster(app, app_config, model_name=None): + charm_name = extract_charm_name_from_url(app_config['charm']) + if charm_name == "percona-cluster": + logging.warning( + "Excluding percona-cluster from upgrade, " + "as no candidate in %s", target_series) + return True + return False + + def _noop_filter(*args, **kwargs): + return False + + if target_series and CompareHostReleases(target_series) >= "focal": + return _filter_percona_cluster + + return _noop_filter + + def _filter_non_openstack_services(app, app_config, model_name=None): charm_options = zaza.model.get_application_config( app, model_name=model_name).keys() @@ -168,7 +188,8 @@ def get_upgrade_groups(model_name=None, extra_filters=None): return _build_service_groups(apps_in_model) -def get_series_upgrade_groups(model_name=None, extra_filters=None): +def get_series_upgrade_groups(model_name=None, extra_filters=None, + target_series=None): """Place apps in the model into their upgrade groups. Place apps in the model into their upgrade groups. If an app is deployed @@ -176,10 +197,17 @@ def get_series_upgrade_groups(model_name=None, extra_filters=None): :param model_name: Name of model to query. :type model_name: str + :param extra_filters: filters to apply to the upgrade groups + :type extra_filters: Callable + :param target_series: The series that will be series upgraded to. + :type target_series: Optional[str] :returns: List of tuples(group name, applications) :rtype: List[Tuple[str, Dict[str, ANY]]] """ - filters = [_filter_subordinates] + filters = [ + _filter_subordinates, + _make_filter_percona_cluster_at(target_series), + ] filters = _apply_extra_filters(filters, extra_filters) apps_in_model = get_upgrade_candidates( model_name=model_name,