From d4ee838d4b0cefd8c1017290a06d7f13ccc2e0ef Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Thu, 19 Mar 2020 12:16:18 +0100 Subject: [PATCH] some refactoring and updates from review --- .../charm_tests/series_upgrade/tests.py | 16 +--- zaza/openstack/utilities/series_upgrade.py | 41 ++++++++- zaza/openstack/utilities/upgrade_utils.py | 84 ++++++++++++++----- 3 files changed, 104 insertions(+), 37 deletions(-) diff --git a/zaza/openstack/charm_tests/series_upgrade/tests.py b/zaza/openstack/charm_tests/series_upgrade/tests.py index 9d10c1a..941e000 100644 --- a/zaza/openstack/charm_tests/series_upgrade/tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/tests.py @@ -233,12 +233,8 @@ class ParallelSeriesUpgradeTest(unittest.TestCase): """Run series upgrade.""" # Set Feature Flag os.environ["JUJU_DEV_FEATURE_FLAGS"] = "upgrade-series" - upgrade_groups = upgrade_utils.get_upgrade_groups() + upgrade_groups = upgrade_utils.get_series_upgrade_groups() applications = model.get_status().applications - upgrade_groups['support'] = [ - app for app in upgrade_utils.UPGRADE_EXCLUDE_LIST - if app in applications.keys()] - upgrade_groups['deferred'] = [] completed_machines = [] for group_name, group in upgrade_groups.items(): logging.warn("About to upgrade {} ({})".format(group_name, group)) @@ -249,16 +245,6 @@ class ParallelSeriesUpgradeTest(unittest.TestCase): pause_non_leader_subordinate = True pause_non_leader_primary = True post_upgrade_functions = [] - name = upgrade_utils.extract_charm_name_from_url( - app_details['charm']) - if name not in group and application not in group: - if group_name != "deferred" and \ - name not in upgrade_groups['deferred']: - upgrade_groups['deferred'].append(name) - continue - if group_name != "deferred" and \ - name in upgrade_groups['deferred']: - upgrade_groups['deferred'].remove(name) # Skip subordinates if app_details["subordinate-to"]: continue diff --git a/zaza/openstack/utilities/series_upgrade.py b/zaza/openstack/utilities/series_upgrade.py index e32a06f..52e9c49 100644 --- a/zaza/openstack/utilities/series_upgrade.py +++ b/zaza/openstack/utilities/series_upgrade.py @@ -14,6 +14,8 @@ """Collection of functions for testing series upgrade.""" +import collections +import copy import concurrent import logging import os @@ -40,6 +42,34 @@ def run_post_upgrade_functions(post_upgrade_functions): cl_utils.get_class(func)() +def get_charm_settings(): + default = { + 'origin': 'openstack-origin', + 'pause_non_leader_subordinate': True, + 'pause_non_leader_primary': True, + 'upgrade_function': async_series_upgrade_application, + 'post_upgrade_functions': []} + + _app_settings = collections.defaultdict(lambda: default) + exceptions = { + 'rabbitmq-server': { + 'origin': 'source', + 'pause_non_leader_subordinate': False}, + 'percona-cluster': {'origin': 'source'}, + 'nova-compute' : { + 'pause_non_leader_primary': False, + 'pause_non_leader_subordinate': False}, + 'mongodb': { + 'upgrade_function': async_series_upgrade_non_leaders_first, + 'origin': None, + + }} + for key, value in exceptions.items(): + _app_settings[key] = copy.deepcopy(default) + _app_settings[key].update(value) + return _app_settings + + def series_upgrade_non_leaders_first(application, from_series="trusty", to_series="xenial", completed_machines=[], @@ -283,7 +313,8 @@ async def async_series_upgrade_application(application, origin='openstack-origin', completed_machines=None, files=None, workaround_script=None, - post_upgrade_functions=None): + post_upgrade_functions=None, + post_application_upgrade_functions=None): """Series upgrade application. Wrap all the functionality to handle series upgrade for a given @@ -312,6 +343,13 @@ async def async_series_upgrade_application(application, :type files: list :param workaround_script: Workaround script to run during series upgrade :type workaround_script: str + :param post_upgrade_functions: A list of functions to call after upgrading + each unit of an application + :type post_upgrade_functions: List[fn] + :param post_application_upgrade_functions: A list of functions to call + once after updating all units + of an application + :type post_application_upgrade_functions: List[fn] :returns: None :rtype: None """ @@ -391,6 +429,7 @@ async def async_series_upgrade_application(application, logging.info("Set origin on {}".format(application)) await os_utils.async_set_origin(application, origin) await wait_for_unit_idle(unit) + run_post_upgrade_functions(post_application_upgrade_functions) # TODO: Move these functions into zaza.model diff --git a/zaza/openstack/utilities/upgrade_utils.py b/zaza/openstack/utilities/upgrade_utils.py index 4e2fbf5..1c19569 100644 --- a/zaza/openstack/utilities/upgrade_utils.py +++ b/zaza/openstack/utilities/upgrade_utils.py @@ -34,41 +34,54 @@ SERVICE_GROUPS = collections.OrderedDict([ UPGRADE_EXCLUDE_LIST = ['rabbitmq-server', 'percona-cluster'] -def get_upgrade_candidates(model_name=None): +def get_upgrade_candidates(model_name=None, filters=None): """Extract list of apps from model that can be upgraded. :param model_name: Name of model to query. :type model_name: str + :param filters: List of filter functions to apply + :type filters: List[fn] :returns: List of application that can have their payload upgraded. :rtype: [] """ + if filters is None: + filters = [] status = zaza.model.get_status(model_name=model_name) candidates = {} for app, app_config in status.applications.items(): - # Filter out subordinates - if app_config.get("subordinate-to"): - logging.warning( - "Excluding {} from upgrade, it is a subordinate".format(app)) - continue + for f in filters: + if f(app, model_name=model_name): + continue + candidates[app] = app_config + return candidates - # Filter out charms on the naughty list - charm_name = extract_charm_name_from_url(app_config['charm']) - if app in UPGRADE_EXCLUDE_LIST or charm_name in UPGRADE_EXCLUDE_LIST: - logging.warning( - "Excluding {} from upgrade, on the exclude list".format(app)) - continue - # Filter out charms that have no source option +def _filter_subordinates(app, model_name=None): + if app_config.get("subordinate-to"): + logging.warning( + "Excluding {} from upgrade, it is a subordinate".format(app)) + return True + return False + + +def _filter_openstack_upgrade_list(app, model_name=None): + charm_name = extract_charm_name_from_url(app_config['charm']) + if app in UPGRADE_EXCLUDE_LIST or charm_name in UPGRADE_EXCLUDE_LIST: + logging.warning( + "Excluding {} from upgrade, on the exclude list".format(app)) + return True + return False + + +def _filter_non_openstack_services(app, model_name=None): charm_options = zaza.model.get_application_config( app, model_name=model_name).keys() src_options = ['openstack-origin', 'source'] if not [x for x in src_options if x in charm_options]: logging.warning( "Excluding {} from upgrade, no src option".format(app)) - continue - - candidates[app] = app_config - return candidates + return True + return False def get_upgrade_groups(model_name=None): @@ -82,26 +95,55 @@ def get_upgrade_groups(model_name=None): :returns: Dict of group lists keyed on group name. :rtype: collections.OrderedDict """ - apps_in_model = get_upgrade_candidates(model_name=model_name) + apps_in_model = get_upgrade_candidates( + model_name=model_name, + filters=[ + _filter_subordinates, + _filter_openstack_upgrade_list, + _filter_non_openstack_services, + ]) + return _build_service_groups(apps_in_model) + +def get_series_upgrade_groups(model_name=None): + """Place apps in the model into their upgrade groups. + + Place apps in the model into their upgrade groups. If an app is deployed + but is not in SERVICE_GROUPS then it is placed in a sweep_up group. + + :param model_name: Name of model to query. + :type model_name: str + :returns: Dict of group lists keyed on group name. + :rtype: collections.OrderedDict + """ + apps_in_model = get_upgrade_candidates( + model_name=model_name, + filters=[ + _filter_subordinates, + ]) + + return _build_service_groups(apps_in_model) + + +def _build_service_groups(applications): groups = collections.OrderedDict() for phase_name, charms in SERVICE_GROUPS.items(): group = [] - for app, app_config in apps_in_model.items(): + for app, app_config in applications.items(): charm_name = extract_charm_name_from_url(app_config['charm']) if charm_name in charms: group.append(app) groups[phase_name] = group sweep_up = [] - for app in apps_in_model: + for app in applications: if not (app in [a for group in groups.values() for a in group]): sweep_up.append(app) - groups['sweep_up'] = sweep_up return groups + def extract_charm_name_from_url(charm_url): """Extract the charm name from the charm url.