From 93fd631b1ae9e4246faad3c9acbf5af49e329766 Mon Sep 17 00:00:00 2001 From: Chris MacNaughton Date: Tue, 3 Mar 2020 09:33:10 +0100 Subject: [PATCH] Split up and re-organize upgrade helpers --- requirements.txt | 1 + .../utilities/test_zaza_utilities_generic.py | 182 ------- .../test_zaza_utilities_series_upgrade.py | 239 +++++++++ .../charm_tests/series_upgrade/tests.py | 6 +- zaza/openstack/utilities/charm_upgrade.py | 15 + zaza/openstack/utilities/generic.py | 334 ------------ zaza/openstack/utilities/openstack_upgrade.py | 18 +- zaza/openstack/utilities/series_upgrade.py | 479 ++++++++++++++++++ zaza/openstack/utilities/upgrade_utils.py | 30 ++ 9 files changed, 771 insertions(+), 533 deletions(-) create mode 100644 unit_tests/utilities/test_zaza_utilities_series_upgrade.py create mode 100644 zaza/openstack/utilities/charm_upgrade.py create mode 100644 zaza/openstack/utilities/series_upgrade.py create mode 100644 zaza/openstack/utilities/upgrade_utils.py diff --git a/requirements.txt b/requirements.txt index cbf31df..68f8f71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ aiounittest +asyncio async_generator boto3 juju diff --git a/unit_tests/utilities/test_zaza_utilities_generic.py b/unit_tests/utilities/test_zaza_utilities_generic.py index d18b646..0f3ff9c 100644 --- a/unit_tests/utilities/test_zaza_utilities_generic.py +++ b/unit_tests/utilities/test_zaza_utilities_generic.py @@ -173,45 +173,6 @@ class TestGenericUtils(ut_utils.BaseTestCase): _yaml_dict) self._open.assert_called_once_with(_filename, "r") - def test_dist_upgrade(self): - _unit = "app/2" - generic_utils.dist_upgrade(_unit) - dist_upgrade_cmd = ( - """sudo DEBIAN_FRONTEND=noninteractive apt --assume-yes """ - """-o "Dpkg::Options::=--force-confdef" """ - """-o "Dpkg::Options::=--force-confold" dist-upgrade""") - self.model.run_on_unit.assert_has_calls([ - mock.call(_unit, 'sudo apt update'), - mock.call(_unit, dist_upgrade_cmd)]) - - def test_do_release_upgrade(self): - _unit = "app/2" - generic_utils.do_release_upgrade(_unit) - self.subprocess.check_call.assert_called_once_with( - ['juju', 'ssh', _unit, 'sudo', 'DEBIAN_FRONTEND=noninteractive', - 'do-release-upgrade', '-f', 'DistUpgradeViewNonInteractive']) - - def test_wrap_do_release_upgrade(self): - self.patch_object(generic_utils, "do_release_upgrade") - self.patch_object(generic_utils, "run_via_ssh") - self.patch_object(generic_utils.model, "scp_to_unit") - _unit = "app/2" - _from_series = "xenial" - _to_series = "bionic" - _workaround_script = "scriptname" - _files = ["filename", _workaround_script] - _scp_calls = [] - _run_calls = [ - mock.call(_unit, _workaround_script)] - for filename in _files: - _scp_calls.append(mock.call(_unit, filename, filename)) - generic_utils.wrap_do_release_upgrade( - _unit, to_series=_to_series, from_series=_from_series, - workaround_script=_workaround_script, files=_files) - self.scp_to_unit.assert_has_calls(_scp_calls) - self.run_via_ssh.assert_has_calls(_run_calls) - self.do_release_upgrade.assert_called_once_with(_unit) - def test_reboot(self): _unit = "app/2" generic_utils.reboot(_unit) @@ -237,149 +198,6 @@ class TestGenericUtils(ut_utils.BaseTestCase): self.set_application_config.assert_called_once_with( _application, {_origin: _pocket}) - def test_series_upgrade(self): - self.patch_object(generic_utils.model, "block_until_all_units_idle") - self.patch_object(generic_utils.model, "block_until_unit_wl_status") - self.patch_object(generic_utils.model, "prepare_series_upgrade") - self.patch_object(generic_utils.model, "complete_series_upgrade") - self.patch_object(generic_utils.model, "set_series") - self.patch_object(generic_utils, "set_origin") - self.patch_object(generic_utils, "wrap_do_release_upgrade") - self.patch_object(generic_utils, "reboot") - _unit = "app/2" - _application = "app" - _machine_num = "4" - _from_series = "xenial" - _to_series = "bionic" - _origin = "source" - _files = ["filename", "scriptname"] - _workaround_script = "scriptname" - generic_utils.series_upgrade( - _unit, _machine_num, origin=_origin, - to_series=_to_series, from_series=_from_series, - workaround_script=_workaround_script, files=_files) - self.block_until_all_units_idle.called_with() - self.prepare_series_upgrade.assert_called_once_with( - _machine_num, to_series=_to_series) - self.wrap_do_release_upgrade.assert_called_once_with( - _unit, to_series=_to_series, from_series=_from_series, - workaround_script=_workaround_script, files=_files) - self.complete_series_upgrade.assert_called_once_with(_machine_num) - self.set_series.assert_called_once_with(_application, _to_series) - self.set_origin.assert_called_once_with(_application, _origin) - self.reboot.assert_called_once_with(_unit) - - def test_series_upgrade_application_pause_peers_and_subordinates(self): - self.patch_object(generic_utils.model, "run_action") - self.patch_object(generic_utils, "series_upgrade") - _application = "app" - _from_series = "xenial" - _to_series = "bionic" - _origin = "source" - _files = ["filename", "scriptname"] - _workaround_script = "scriptname" - _completed_machines = [] - # Peers and Subordinates - _run_action_calls = [ - mock.call("{}-hacluster/1".format(_application), - "pause", action_params={}), - mock.call("{}/1".format(_application), "pause", action_params={}), - mock.call("{}-hacluster/2".format(_application), - "pause", action_params={}), - mock.call("{}/2".format(_application), "pause", action_params={}), - ] - _series_upgrade_calls = [] - for machine_num in ("0", "1", "2"): - _series_upgrade_calls.append( - mock.call("{}/{}".format(_application, machine_num), - machine_num, origin=_origin, - from_series=_from_series, to_series=_to_series, - workaround_script=_workaround_script, files=_files, - post_upgrade_functions=None), - ) - - # Pause primary peers and subordinates - generic_utils.series_upgrade_application( - _application, origin=_origin, - to_series=_to_series, from_series=_from_series, - pause_non_leader_primary=True, - pause_non_leader_subordinate=True, - completed_machines=_completed_machines, - workaround_script=_workaround_script, files=_files), - self.run_action.assert_has_calls(_run_action_calls) - self.series_upgrade.assert_has_calls(_series_upgrade_calls) - - def test_series_upgrade_application_pause_subordinates(self): - self.patch_object(generic_utils.model, "run_action") - self.patch_object(generic_utils, "series_upgrade") - _application = "app" - _from_series = "xenial" - _to_series = "bionic" - _origin = "source" - _files = ["filename", "scriptname"] - _workaround_script = "scriptname" - _completed_machines = [] - # Subordinates only - _run_action_calls = [ - mock.call("{}-hacluster/1".format(_application), - "pause", action_params={}), - mock.call("{}-hacluster/2".format(_application), - "pause", action_params={}), - ] - _series_upgrade_calls = [] - - for machine_num in ("0", "1", "2"): - _series_upgrade_calls.append( - mock.call("{}/{}".format(_application, machine_num), - machine_num, origin=_origin, - from_series=_from_series, to_series=_to_series, - workaround_script=_workaround_script, files=_files, - post_upgrade_functions=None), - ) - - # Pause subordinates - generic_utils.series_upgrade_application( - _application, origin=_origin, - to_series=_to_series, from_series=_from_series, - pause_non_leader_primary=False, - pause_non_leader_subordinate=True, - completed_machines=_completed_machines, - workaround_script=_workaround_script, files=_files), - self.run_action.assert_has_calls(_run_action_calls) - self.series_upgrade.assert_has_calls(_series_upgrade_calls) - - def test_series_upgrade_application_no_pause(self): - self.patch_object(generic_utils.model, "run_action") - self.patch_object(generic_utils, "series_upgrade") - _application = "app" - _from_series = "xenial" - _to_series = "bionic" - _origin = "source" - _series_upgrade_calls = [] - _files = ["filename", "scriptname"] - _workaround_script = "scriptname" - _completed_machines = [] - - for machine_num in ("0", "1", "2"): - _series_upgrade_calls.append( - mock.call("{}/{}".format(_application, machine_num), - machine_num, origin=_origin, - from_series=_from_series, to_series=_to_series, - workaround_script=_workaround_script, files=_files, - post_upgrade_functions=None), - ) - - # No Pausiing - generic_utils.series_upgrade_application( - _application, origin=_origin, - to_series=_to_series, from_series=_from_series, - pause_non_leader_primary=False, - pause_non_leader_subordinate=False, - completed_machines=_completed_machines, - workaround_script=_workaround_script, files=_files) - self.run_action.assert_not_called() - self.series_upgrade.assert_has_calls(_series_upgrade_calls) - def test_set_dpkg_non_interactive_on_unit(self): self.patch_object(generic_utils, "model") _unit_name = "app/1" diff --git a/unit_tests/utilities/test_zaza_utilities_series_upgrade.py b/unit_tests/utilities/test_zaza_utilities_series_upgrade.py new file mode 100644 index 0000000..b6f21e9 --- /dev/null +++ b/unit_tests/utilities/test_zaza_utilities_series_upgrade.py @@ -0,0 +1,239 @@ +# 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. + +import mock +import unit_tests.utils as ut_utils +import zaza.openstack.utilities.generic as generic_utils +import zaza.openstack.utilities.series_upgrade as series_upgrade_utils + +FAKE_STATUS = { + 'can-upgrade-to': '', + 'charm': 'local:trusty/app-136', + 'subordinate-to': [], + 'units': {'app/0': {'leader': True, + 'machine': '0', + 'subordinates': { + 'app-hacluster/0': { + 'charm': 'local:trusty/hacluster-0', + 'leader': True}}}, + 'app/1': {'machine': '1', + 'subordinates': { + 'app-hacluster/1': { + 'charm': 'local:trusty/hacluster-0'}}}, + 'app/2': {'machine': '2', + 'subordinates': { + 'app-hacluster/2': { + 'charm': 'local:trusty/hacluster-0'}}}}} + + +class TestSeriesUpgrade(ut_utils.BaseTestCase): + def setUp(self): + super(TestSeriesUpgrade, self).setUp() + # Patch all subprocess calls + self.patch( + 'zaza.openstack.utilities.generic.subprocess', + new_callable=mock.MagicMock(), + name='subprocess' + ) + self.patch_object(generic_utils, "run_via_ssh") + # Juju Status Object and data + self.juju_status = mock.MagicMock() + self.juju_status.applications.__getitem__.return_value = FAKE_STATUS + self.patch_object(series_upgrade_utils, "model") + self.model.get_status.return_value = self.juju_status + + def test_series_upgrade(self): + self.patch_object( + series_upgrade_utils.model, "block_until_all_units_idle") + self.patch_object( + series_upgrade_utils.model, "block_until_unit_wl_status") + self.patch_object(series_upgrade_utils.model, "prepare_series_upgrade") + self.patch_object( + series_upgrade_utils.model, "complete_series_upgrade") + self.patch_object(series_upgrade_utils.model, "set_series") + self.patch_object(generic_utils, "set_origin") + self.patch_object(series_upgrade_utils, "wrap_do_release_upgrade") + self.patch_object(generic_utils, "reboot") + _unit = "app/2" + _application = "app" + _machine_num = "4" + _from_series = "xenial" + _to_series = "bionic" + _origin = "source" + _files = ["filename", "scriptname"] + _workaround_script = "scriptname" + series_upgrade_utils.series_upgrade( + _unit, _machine_num, origin=_origin, + to_series=_to_series, from_series=_from_series, + workaround_script=_workaround_script, files=_files) + self.block_until_all_units_idle.called_with() + self.prepare_series_upgrade.assert_called_once_with( + _machine_num, to_series=_to_series) + self.wrap_do_release_upgrade.assert_called_once_with( + _unit, to_series=_to_series, from_series=_from_series, + workaround_script=_workaround_script, files=_files) + self.complete_series_upgrade.assert_called_once_with(_machine_num) + self.set_series.assert_called_once_with(_application, _to_series) + self.set_origin.assert_called_once_with(_application, _origin) + self.reboot.assert_called_once_with(_unit) + + def test_series_upgrade_application_pause_peers_and_subordinates(self): + self.patch_object(series_upgrade_utils.model, "run_action") + self.patch_object(series_upgrade_utils, "series_upgrade") + _application = "app" + _from_series = "xenial" + _to_series = "bionic" + _origin = "source" + _files = ["filename", "scriptname"] + _workaround_script = "scriptname" + _completed_machines = [] + # Peers and Subordinates + _run_action_calls = [ + mock.call("{}-hacluster/1".format(_application), + "pause", action_params={}), + mock.call("{}/1".format(_application), "pause", action_params={}), + mock.call("{}-hacluster/2".format(_application), + "pause", action_params={}), + mock.call("{}/2".format(_application), "pause", action_params={}), + ] + _series_upgrade_calls = [] + for machine_num in ("0", "1", "2"): + _series_upgrade_calls.append( + mock.call("{}/{}".format(_application, machine_num), + machine_num, origin=_origin, + from_series=_from_series, to_series=_to_series, + workaround_script=_workaround_script, files=_files, + post_upgrade_functions=None), + ) + + # Pause primary peers and subordinates + series_upgrade_utils.series_upgrade_application( + _application, origin=_origin, + to_series=_to_series, from_series=_from_series, + pause_non_leader_primary=True, + pause_non_leader_subordinate=True, + completed_machines=_completed_machines, + workaround_script=_workaround_script, files=_files), + self.run_action.assert_has_calls(_run_action_calls) + self.series_upgrade.assert_has_calls(_series_upgrade_calls) + + def test_series_upgrade_application_pause_subordinates(self): + self.patch_object(series_upgrade_utils.model, "run_action") + self.patch_object(series_upgrade_utils, "series_upgrade") + _application = "app" + _from_series = "xenial" + _to_series = "bionic" + _origin = "source" + _files = ["filename", "scriptname"] + _workaround_script = "scriptname" + _completed_machines = [] + # Subordinates only + _run_action_calls = [ + mock.call("{}-hacluster/1".format(_application), + "pause", action_params={}), + mock.call("{}-hacluster/2".format(_application), + "pause", action_params={}), + ] + _series_upgrade_calls = [] + + for machine_num in ("0", "1", "2"): + _series_upgrade_calls.append( + mock.call("{}/{}".format(_application, machine_num), + machine_num, origin=_origin, + from_series=_from_series, to_series=_to_series, + workaround_script=_workaround_script, files=_files, + post_upgrade_functions=None), + ) + + # Pause subordinates + series_upgrade_utils.series_upgrade_application( + _application, origin=_origin, + to_series=_to_series, from_series=_from_series, + pause_non_leader_primary=False, + pause_non_leader_subordinate=True, + completed_machines=_completed_machines, + workaround_script=_workaround_script, files=_files), + self.run_action.assert_has_calls(_run_action_calls) + self.series_upgrade.assert_has_calls(_series_upgrade_calls) + + def test_series_upgrade_application_no_pause(self): + self.patch_object(series_upgrade_utils.model, "run_action") + self.patch_object(series_upgrade_utils, "series_upgrade") + _application = "app" + _from_series = "xenial" + _to_series = "bionic" + _origin = "source" + _series_upgrade_calls = [] + _files = ["filename", "scriptname"] + _workaround_script = "scriptname" + _completed_machines = [] + + for machine_num in ("0", "1", "2"): + _series_upgrade_calls.append( + mock.call("{}/{}".format(_application, machine_num), + machine_num, origin=_origin, + from_series=_from_series, to_series=_to_series, + workaround_script=_workaround_script, files=_files, + post_upgrade_functions=None), + ) + + # No Pausiing + series_upgrade_utils.series_upgrade_application( + _application, origin=_origin, + to_series=_to_series, from_series=_from_series, + pause_non_leader_primary=False, + pause_non_leader_subordinate=False, + completed_machines=_completed_machines, + workaround_script=_workaround_script, files=_files) + self.run_action.assert_not_called() + self.series_upgrade.assert_has_calls(_series_upgrade_calls) + + def test_dist_upgrade(self): + _unit = "app/2" + series_upgrade_utils.dist_upgrade(_unit) + dist_upgrade_cmd = ( + """sudo DEBIAN_FRONTEND=noninteractive apt --assume-yes """ + """-o "Dpkg::Options::=--force-confdef" """ + """-o "Dpkg::Options::=--force-confold" dist-upgrade""") + self.model.run_on_unit.assert_has_calls([ + mock.call(_unit, 'sudo apt update'), + mock.call(_unit, dist_upgrade_cmd)]) + + def test_do_release_upgrade(self): + _unit = "app/2" + series_upgrade_utils.do_release_upgrade(_unit) + self.run_via_ssh.assert_called_once_with( + _unit, + 'DEBIAN_FRONTEND=noninteractive do-release-upgrade ' + '-f DistUpgradeViewNonInteractive') + + def test_wrap_do_release_upgrade(self): + self.patch_object(series_upgrade_utils, "do_release_upgrade") + self.patch_object(series_upgrade_utils.model, "scp_to_unit") + _unit = "app/2" + _from_series = "xenial" + _to_series = "bionic" + _workaround_script = "scriptname" + _files = ["filename", _workaround_script] + _scp_calls = [] + _run_calls = [ + mock.call(_unit, _workaround_script)] + for filename in _files: + _scp_calls.append(mock.call(_unit, filename, filename)) + series_upgrade_utils.wrap_do_release_upgrade( + _unit, to_series=_to_series, from_series=_from_series, + workaround_script=_workaround_script, files=_files) + self.scp_to_unit.assert_has_calls(_scp_calls) + self.run_via_ssh.assert_has_calls(_run_calls) + self.do_release_upgrade.assert_called_once_with(_unit) diff --git a/zaza/openstack/charm_tests/series_upgrade/tests.py b/zaza/openstack/charm_tests/series_upgrade/tests.py index eb7add6..f94e93d 100644 --- a/zaza/openstack/charm_tests/series_upgrade/tests.py +++ b/zaza/openstack/charm_tests/series_upgrade/tests.py @@ -23,7 +23,7 @@ import unittest from zaza import model from zaza.openstack.utilities import ( cli as cli_utils, - generic as generic_utils, + series_upgrade as series_upgrade_utils, ) from zaza.openstack.charm_tests.nova.tests import LTSGuestCreateTest @@ -95,7 +95,7 @@ class SeriesUpgradeTest(unittest.TestCase): if "mongodb" in applications[application]["charm"]: # Mongodb needs to run series upgrade # on its secondaries first. - generic_utils.series_upgrade_non_leaders_first( + series_upgrade_utils.series_upgrade_non_leaders_first( application, from_series=self.from_series, to_series=self.to_series, @@ -105,7 +105,7 @@ class SeriesUpgradeTest(unittest.TestCase): # The rest are likley APIs use defaults - generic_utils.series_upgrade_application( + series_upgrade_utils.series_upgrade_application( application, pause_non_leader_primary=pause_non_leader_primary, pause_non_leader_subordinate=pause_non_leader_subordinate, diff --git a/zaza/openstack/utilities/charm_upgrade.py b/zaza/openstack/utilities/charm_upgrade.py new file mode 100644 index 0000000..017b642 --- /dev/null +++ b/zaza/openstack/utilities/charm_upgrade.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 functions to support charm upgrade testing.""" diff --git a/zaza/openstack/utilities/generic.py b/zaza/openstack/utilities/generic.py index c0566b0..065d8ce 100644 --- a/zaza/openstack/utilities/generic.py +++ b/zaza/openstack/utilities/generic.py @@ -25,11 +25,6 @@ from zaza import model from zaza.openstack.utilities import juju as juju_utils from zaza.openstack.utilities import exceptions as zaza_exceptions from zaza.openstack.utilities.os_versions import UBUNTU_OPENSTACK_RELEASE -from zaza.charm_lifecycle import utils as cl_utils - -SUBORDINATE_PAUSE_RESUME_BLACKLIST = [ - "cinder-ceph", -] def dict_to_yaml(dict_data): @@ -192,255 +187,6 @@ def get_yaml_config(config_file): return yaml.safe_load(open(config_file, 'r').read()) -def run_post_upgrade_functions(post_upgrade_functions): - """Execute list supplied functions. - - :param post_upgrade_functions: List of functions - :type post_upgrade_functions: [function, function, ...] - """ - if post_upgrade_functions: - for func in post_upgrade_functions: - logging.info("Running {}".format(func)) - cl_utils.get_class(func)() - - -def series_upgrade_non_leaders_first(application, from_series="trusty", - to_series="xenial", - completed_machines=[], - post_upgrade_functions=None): - """Series upgrade non leaders first. - - Wrap all the functionality to handle series upgrade for charms - which must have non leaders upgraded first. - - :param application: Name of application to upgrade series - :type application: str - :param from_series: The series from which to upgrade - :type from_series: str - :param to_series: The series to which to upgrade - :type to_series: str - :param completed_machines: List of completed machines which do no longer - require series upgrade. - :type completed_machines: list - :returns: None - :rtype: None - """ - status = model.get_status().applications[application] - leader = None - non_leaders = [] - for unit in status["units"]: - if status["units"][unit].get("leader"): - leader = unit - else: - non_leaders.append(unit) - - # Series upgrade the non-leaders first - for unit in non_leaders: - machine = status["units"][unit]["machine"] - if machine not in completed_machines: - logging.info("Series upgrade non-leader unit: {}" - .format(unit)) - series_upgrade(unit, machine, - from_series=from_series, to_series=to_series, - origin=None, - post_upgrade_functions=post_upgrade_functions) - run_post_upgrade_functions(post_upgrade_functions) - completed_machines.append(machine) - else: - logging.info("Skipping unit: {}. Machine: {} already upgraded. " - .format(unit, machine, application)) - model.block_until_all_units_idle() - - # Series upgrade the leader - machine = status["units"][leader]["machine"] - logging.info("Series upgrade leader: {}".format(leader)) - if machine not in completed_machines: - series_upgrade(leader, machine, - from_series=from_series, to_series=to_series, - origin=None, - post_upgrade_functions=post_upgrade_functions) - completed_machines.append(machine) - else: - logging.info("Skipping unit: {}. Machine: {} already upgraded." - .format(unit, machine, application)) - model.block_until_all_units_idle() - - -def series_upgrade_application(application, pause_non_leader_primary=True, - pause_non_leader_subordinate=True, - from_series="trusty", to_series="xenial", - origin='openstack-origin', - completed_machines=[], - files=None, workaround_script=None, - post_upgrade_functions=None): - """Series upgrade application. - - Wrap all the functionality to handle series upgrade for a given - application. Including pausing non-leader units. - - :param application: Name of application to upgrade series - :type application: str - :param pause_non_leader_primary: Whether the non-leader applications should - be paused - :type pause_non_leader_primary: bool - :param pause_non_leader_subordinate: Whether the non-leader subordinate - hacluster applications should be - paused - :type pause_non_leader_subordinate: bool - :param from_series: The series from which to upgrade - :type from_series: str - :param to_series: The series to which to upgrade - :type to_series: str - :param origin: The configuration setting variable name for changing origin - source. (openstack-origin or source) - :type origin: str - :param completed_machines: List of completed machines which do no longer - require series upgrade. - :type completed_machines: list - :param files: Workaround files to scp to unit under upgrade - :type files: list - :param workaround_script: Workaround script to run during series upgrade - :type workaround_script: str - :returns: None - :rtype: None - """ - status = model.get_status().applications[application] - - # For some applications (percona-cluster) the leader unit must upgrade - # first. For API applications the non-leader haclusters must be paused - # before upgrade. Finally, for some applications this is arbitrary but - # generalized. - leader = None - non_leaders = [] - for unit in status["units"]: - if status["units"][unit].get("leader"): - leader = unit - else: - non_leaders.append(unit) - - # Pause the non-leaders - for unit in non_leaders: - if pause_non_leader_subordinate: - if status["units"][unit].get("subordinates"): - for subordinate in status["units"][unit]["subordinates"]: - _app = subordinate.split('/')[0] - if _app in SUBORDINATE_PAUSE_RESUME_BLACKLIST: - logging.info("Skipping pausing {} - blacklisted" - .format(subordinate)) - else: - logging.info("Pausing {}".format(subordinate)) - model.run_action( - subordinate, "pause", action_params={}) - if pause_non_leader_primary: - logging.info("Pausing {}".format(unit)) - model.run_action(unit, "pause", action_params={}) - - machine = status["units"][leader]["machine"] - # Series upgrade the leader - logging.info("Series upgrade leader: {}".format(leader)) - if machine not in completed_machines: - series_upgrade(leader, machine, - from_series=from_series, to_series=to_series, - origin=origin, workaround_script=workaround_script, - files=files, - post_upgrade_functions=post_upgrade_functions) - completed_machines.append(machine) - else: - logging.info("Skipping unit: {}. Machine: {} already upgraded." - "But setting origin on the application {}" - .format(unit, machine, application)) - logging.info("Set origin on {}".format(application)) - set_origin(application, origin) - model.block_until_all_units_idle() - - # Series upgrade the non-leaders - for unit in non_leaders: - machine = status["units"][unit]["machine"] - if machine not in completed_machines: - logging.info("Series upgrade non-leader unit: {}" - .format(unit)) - series_upgrade(unit, machine, - from_series=from_series, to_series=to_series, - origin=origin, workaround_script=workaround_script, - files=files, - post_upgrade_functions=post_upgrade_functions) - completed_machines.append(machine) - else: - logging.info("Skipping unit: {}. Machine: {} already upgraded. " - "But setting origin on the application {}" - .format(unit, machine, application)) - logging.info("Set origin on {}".format(application)) - set_origin(application, origin) - model.block_until_all_units_idle() - - -def series_upgrade(unit_name, machine_num, - from_series="trusty", to_series="xenial", - origin='openstack-origin', - files=None, workaround_script=None, - post_upgrade_functions=None): - """Perform series upgrade on a unit. - - :param unit_name: Unit Name - :type unit_name: str - :param machine_num: Machine number - :type machine_num: str - :param from_series: The series from which to upgrade - :type from_series: str - :param to_series: The series to which to upgrade - :type to_series: str - :param origin: The configuration setting variable name for changing origin - source. (openstack-origin or source) - :type origin: str - :param files: Workaround files to scp to unit under upgrade - :type files: list - :param workaround_script: Workaround script to run during series upgrade - :type workaround_script: str - :returns: None - :rtype: None - """ - logging.info("Series upgrade {}".format(unit_name)) - application = unit_name.split('/')[0] - set_dpkg_non_interactive_on_unit(unit_name) - dist_upgrade(unit_name) - model.block_until_all_units_idle() - logging.info("Prepare series upgrade on {}".format(machine_num)) - model.prepare_series_upgrade(machine_num, to_series=to_series) - logging.info("Waiting for workload status 'blocked' on {}" - .format(unit_name)) - model.block_until_unit_wl_status(unit_name, "blocked") - logging.info("Waiting for model idleness") - model.block_until_all_units_idle() - wrap_do_release_upgrade(unit_name, from_series=from_series, - to_series=to_series, files=files, - workaround_script=workaround_script) - logging.info("Reboot {}".format(unit_name)) - reboot(unit_name) - logging.info("Waiting for workload status 'blocked' on {}" - .format(unit_name)) - model.block_until_unit_wl_status(unit_name, "blocked") - logging.info("Waiting for model idleness") - model.block_until_all_units_idle() - logging.info("Set origin on {}".format(application)) - # Allow for charms which have neither source nor openstack-origin - if origin: - set_origin(application, origin) - model.block_until_all_units_idle() - logging.info("Complete series upgrade on {}".format(machine_num)) - model.complete_series_upgrade(machine_num) - model.block_until_all_units_idle() - logging.info("Running run_post_upgrade_functions {}".format( - post_upgrade_functions)) - run_post_upgrade_functions(post_upgrade_functions) - logging.info("Waiting for workload status 'active' on {}" - .format(unit_name)) - model.block_until_unit_wl_status(unit_name, "active") - model.block_until_all_units_idle() - # This step may be performed by juju in the future - logging.info("Set series on {} to {}".format(application, to_series)) - model.set_series(application, to_series) - - def set_origin(application, origin='openstack-origin', pocket='distro'): """Set the configuration option for origin source. @@ -459,46 +205,6 @@ def set_origin(application, origin='openstack-origin', pocket='distro'): model.set_application_config(application, {origin: pocket}) -def wrap_do_release_upgrade(unit_name, from_series="trusty", - to_series="xenial", - files=None, workaround_script=None): - """Wrap do release upgrade. - - In a production environment this step would be run administratively. - For testing purposes we need this automated. - - :param unit_name: Unit Name - :type unit_name: str - :param from_series: The series from which to upgrade - :type from_series: str - :param to_series: The series to which to upgrade - :type to_series: str - :param files: Workaround files to scp to unit under upgrade - :type files: list - :param workaround_script: Workaround script to run during series upgrade - :type workaround_script: str - :returns: None - :rtype: None - """ - # Pre upgrade hacks - # There are a few necessary hacks to accomplish an automated upgrade - # to overcome some packaging bugs. - # Copy scripts - if files: - logging.info("SCP files") - for _file in files: - logging.info("SCP {}".format(_file)) - model.scp_to_unit(unit_name, _file, os.path.basename(_file)) - - # Run Script - if workaround_script: - logging.info("Running workaround script") - run_via_ssh(unit_name, workaround_script) - - # Actually do the do_release_upgrade - do_release_upgrade(unit_name) - - def run_via_ssh(unit_name, cmd): """Run command on unit via ssh. @@ -521,26 +227,6 @@ def run_via_ssh(unit_name, cmd): logging.warn(e) -def dist_upgrade(unit_name): - """Run dist-upgrade on unit after update package db. - - :param unit_name: Unit Name - :type unit_name: str - :returns: None - :rtype: None - """ - logging.info('Updating package db ' + unit_name) - update_cmd = 'sudo apt update' - model.run_on_unit(unit_name, update_cmd) - - logging.info('Updating existing packages ' + unit_name) - dist_upgrade_cmd = ( - """sudo DEBIAN_FRONTEND=noninteractive apt --assume-yes """ - """-o "Dpkg::Options::=--force-confdef" """ - """-o "Dpkg::Options::=--force-confold" dist-upgrade""") - model.run_on_unit(unit_name, dist_upgrade_cmd) - - def check_commands_on_units(commands, units): """Check that all commands in a list exit zero on all units in a list. @@ -566,26 +252,6 @@ def check_commands_on_units(commands, units): return None -def do_release_upgrade(unit_name): - """Run do-release-upgrade noninteractive. - - :param unit_name: Unit Name - :type unit_name: str - :returns: None - :rtype: None - """ - logging.info('Upgrading ' + unit_name) - # NOTE: It is necessary to run this via juju ssh rather than juju run due - # to timeout restrictions and error handling. - cmd = ['juju', 'ssh', unit_name, 'sudo', 'DEBIAN_FRONTEND=noninteractive', - 'do-release-upgrade', '-f', 'DistUpgradeViewNonInteractive'] - try: - subprocess.check_call(cmd) - except subprocess.CalledProcessError as e: - logging.warn("Failed do-release-upgrade for {}".format(unit_name)) - logging.warn(e) - - def reboot(unit_name): """Reboot unit. diff --git a/zaza/openstack/utilities/openstack_upgrade.py b/zaza/openstack/utilities/openstack_upgrade.py index 3c4aa8f..5d5c729 100755 --- a/zaza/openstack/utilities/openstack_upgrade.py +++ b/zaza/openstack/utilities/openstack_upgrade.py @@ -22,20 +22,10 @@ import zaza.openstack.utilities.juju as juju_utils import zaza.model from zaza import sync_wrapper - -SERVICE_GROUPS = { - 'Core Identity': ['keystone'], - 'Storage': [ - 'ceph-mon', 'ceph-osd', 'ceph-fs', 'ceph-radosgw', 'swift-proxy', - 'swift-storage'], - 'Control Plane': [ - 'aodh', 'barbican', 'ceilometer', 'cinder', 'designate', - 'designate-bind', 'glance', 'gnocchi', 'heat', 'manila', - 'manila-generic', 'neutron-api', 'neutron-gateway', 'placement', - 'nova-cloud-controller', 'openstack-dashboard'], - 'Compute': ['nova-compute']} - -UPGRADE_EXCLUDE_LIST = ['rabbitmq-server', 'percona-cluster'] +from zaza.openstack.utilities.upgrade_utils import ( + SERVICE_GROUPS, + UPGRADE_EXCLUDE_LIST, +) async def async_pause_units(units, model_name=None): diff --git a/zaza/openstack/utilities/series_upgrade.py b/zaza/openstack/utilities/series_upgrade.py new file mode 100644 index 0000000..e495c64 --- /dev/null +++ b/zaza/openstack/utilities/series_upgrade.py @@ -0,0 +1,479 @@ +# 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 functions for testing series upgrade.""" + +import logging +import os + +from zaza import model +from zaza.charm_lifecycle import utils as cl_utils +import zaza.openstack.utilities.generic as os_utils + + +SUBORDINATE_PAUSE_RESUME_BLACKLIST = [ + "cinder-ceph", +] + + +def run_post_upgrade_functions(post_upgrade_functions): + """Execute list supplied functions. + + :param post_upgrade_functions: List of functions + :type post_upgrade_functions: [function, function, ...] + """ + if post_upgrade_functions: + for func in post_upgrade_functions: + logging.info("Running {}".format(func)) + cl_utils.get_class(func)() + + +def series_upgrade_non_leaders_first(application, from_series="trusty", + to_series="xenial", + completed_machines=[], + post_upgrade_functions=None): + """Series upgrade non leaders first. + + Wrap all the functionality to handle series upgrade for charms + which must have non leaders upgraded first. + + :param application: Name of application to upgrade series + :type application: str + :param from_series: The series from which to upgrade + :type from_series: str + :param to_series: The series to which to upgrade + :type to_series: str + :param completed_machines: List of completed machines which do no longer + require series upgrade. + :type completed_machines: list + :returns: None + :rtype: None + """ + status = model.get_status().applications[application] + leader = None + non_leaders = [] + for unit in status["units"]: + if status["units"][unit].get("leader"): + leader = unit + else: + non_leaders.append(unit) + + # Series upgrade the non-leaders first + for unit in non_leaders: + machine = status["units"][unit]["machine"] + if machine not in completed_machines: + logging.info("Series upgrade non-leader unit: {}" + .format(unit)) + series_upgrade(unit, machine, + from_series=from_series, to_series=to_series, + origin=None, + post_upgrade_functions=post_upgrade_functions) + run_post_upgrade_functions(post_upgrade_functions) + completed_machines.append(machine) + else: + logging.info("Skipping unit: {}. Machine: {} already upgraded. " + .format(unit, machine, application)) + model.block_until_all_units_idle() + + # Series upgrade the leader + machine = status["units"][leader]["machine"] + logging.info("Series upgrade leader: {}".format(leader)) + if machine not in completed_machines: + series_upgrade(leader, machine, + from_series=from_series, to_series=to_series, + origin=None, + post_upgrade_functions=post_upgrade_functions) + completed_machines.append(machine) + else: + logging.info("Skipping unit: {}. Machine: {} already upgraded." + .format(unit, machine, application)) + model.block_until_all_units_idle() + + +def series_upgrade_application(application, pause_non_leader_primary=True, + pause_non_leader_subordinate=True, + from_series="trusty", to_series="xenial", + origin='openstack-origin', + completed_machines=[], + files=None, workaround_script=None, + post_upgrade_functions=None): + """Series upgrade application. + + Wrap all the functionality to handle series upgrade for a given + application. Including pausing non-leader units. + + :param application: Name of application to upgrade series + :type application: str + :param pause_non_leader_primary: Whether the non-leader applications should + be paused + :type pause_non_leader_primary: bool + :param pause_non_leader_subordinate: Whether the non-leader subordinate + hacluster applications should be + paused + :type pause_non_leader_subordinate: bool + :param from_series: The series from which to upgrade + :type from_series: str + :param to_series: The series to which to upgrade + :type to_series: str + :param origin: The configuration setting variable name for changing origin + source. (openstack-origin or source) + :type origin: str + :param completed_machines: List of completed machines which do no longer + require series upgrade. + :type completed_machines: list + :param files: Workaround files to scp to unit under upgrade + :type files: list + :param workaround_script: Workaround script to run during series upgrade + :type workaround_script: str + :returns: None + :rtype: None + """ + status = model.get_status().applications[application] + + # For some applications (percona-cluster) the leader unit must upgrade + # first. For API applications the non-leader haclusters must be paused + # before upgrade. Finally, for some applications this is arbitrary but + # generalized. + leader = None + non_leaders = [] + for unit in status["units"]: + if status["units"][unit].get("leader"): + leader = unit + else: + non_leaders.append(unit) + + # Pause the non-leaders + for unit in non_leaders: + if pause_non_leader_subordinate: + if status["units"][unit].get("subordinates"): + for subordinate in status["units"][unit]["subordinates"]: + _app = subordinate.split('/')[0] + if _app in SUBORDINATE_PAUSE_RESUME_BLACKLIST: + logging.info("Skipping pausing {} - blacklisted" + .format(subordinate)) + else: + logging.info("Pausing {}".format(subordinate)) + model.run_action( + subordinate, "pause", action_params={}) + if pause_non_leader_primary: + logging.info("Pausing {}".format(unit)) + model.run_action(unit, "pause", action_params={}) + + machine = status["units"][leader]["machine"] + # Series upgrade the leader + logging.info("Series upgrade leader: {}".format(leader)) + if machine not in completed_machines: + series_upgrade(leader, machine, + from_series=from_series, to_series=to_series, + origin=origin, workaround_script=workaround_script, + files=files, + post_upgrade_functions=post_upgrade_functions) + completed_machines.append(machine) + else: + logging.info("Skipping unit: {}. Machine: {} already upgraded." + "But setting origin on the application {}" + .format(unit, machine, application)) + logging.info("Set origin on {}".format(application)) + os_utils.set_origin(application, origin) + model.block_until_all_units_idle() + + # Series upgrade the non-leaders + for unit in non_leaders: + machine = status["units"][unit]["machine"] + if machine not in completed_machines: + logging.info("Series upgrade non-leader unit: {}" + .format(unit)) + series_upgrade(unit, machine, + from_series=from_series, to_series=to_series, + origin=origin, workaround_script=workaround_script, + files=files, + post_upgrade_functions=post_upgrade_functions) + completed_machines.append(machine) + else: + logging.info("Skipping unit: {}. Machine: {} already upgraded. " + "But setting origin on the application {}" + .format(unit, machine, application)) + logging.info("Set origin on {}".format(application)) + os_utils.set_origin(application, origin) + model.block_until_all_units_idle() + + +def series_upgrade(unit_name, machine_num, + from_series="trusty", to_series="xenial", + origin='openstack-origin', + files=None, workaround_script=None, + post_upgrade_functions=None): + """Perform series upgrade on a unit. + + :param unit_name: Unit Name + :type unit_name: str + :param machine_num: Machine number + :type machine_num: str + :param from_series: The series from which to upgrade + :type from_series: str + :param to_series: The series to which to upgrade + :type to_series: str + :param origin: The configuration setting variable name for changing origin + source. (openstack-origin or source) + :type origin: str + :param files: Workaround files to scp to unit under upgrade + :type files: list + :param workaround_script: Workaround script to run during series upgrade + :type workaround_script: str + :returns: None + :rtype: None + """ + logging.info("Series upgrade {}".format(unit_name)) + application = unit_name.split('/')[0] + os_utils.set_dpkg_non_interactive_on_unit(unit_name) + dist_upgrade(unit_name) + model.block_until_all_units_idle() + logging.info("Prepare series upgrade on {}".format(machine_num)) + model.prepare_series_upgrade(machine_num, to_series=to_series) + logging.info("Waiting for workload status 'blocked' on {}" + .format(unit_name)) + model.block_until_unit_wl_status(unit_name, "blocked") + logging.info("Waiting for model idleness") + model.block_until_all_units_idle() + wrap_do_release_upgrade(unit_name, from_series=from_series, + to_series=to_series, files=files, + workaround_script=workaround_script) + logging.info("Reboot {}".format(unit_name)) + os_utils.reboot(unit_name) + logging.info("Waiting for workload status 'blocked' on {}" + .format(unit_name)) + model.block_until_unit_wl_status(unit_name, "blocked") + logging.info("Waiting for model idleness") + model.block_until_all_units_idle() + logging.info("Set origin on {}".format(application)) + # Allow for charms which have neither source nor openstack-origin + if origin: + os_utils.set_origin(application, origin) + model.block_until_all_units_idle() + logging.info("Complete series upgrade on {}".format(machine_num)) + model.complete_series_upgrade(machine_num) + model.block_until_all_units_idle() + logging.info("Running run_post_upgrade_functions {}".format( + post_upgrade_functions)) + run_post_upgrade_functions(post_upgrade_functions) + logging.info("Waiting for workload status 'active' on {}" + .format(unit_name)) + model.block_until_unit_wl_status(unit_name, "active") + model.block_until_all_units_idle() + # This step may be performed by juju in the future + logging.info("Set series on {} to {}".format(application, to_series)) + model.set_series(application, to_series) + + +async def async_series_upgrade(unit_name, machine_num, + from_series="trusty", to_series="xenial", + origin='openstack-origin', + files=None, workaround_script=None, + post_upgrade_functions=None): + """Perform series upgrade on a unit. + + :param unit_name: Unit Name + :type unit_name: str + :param machine_num: Machine number + :type machine_num: str + :param from_series: The series from which to upgrade + :type from_series: str + :param to_series: The series to which to upgrade + :type to_series: str + :param origin: The configuration setting variable name for changing origin + source. (openstack-origin or source) + :type origin: str + :param files: Workaround files to scp to unit under upgrade + :type files: list + :param workaround_script: Workaround script to run during series upgrade + :type workaround_script: str + :returns: None + :rtype: None + """ + logging.info("Series upgrade {}".format(unit_name)) + application = unit_name.split('/')[0] + await os_utils.async_set_dpkg_non_interactive_on_unit(unit_name) + await async_dist_upgrade(unit_name) + await model.async_block_until_all_units_idle() + logging.info("Prepare series upgrade on {}".format(machine_num)) + await model.async_prepare_series_upgrade(machine_num, to_series=to_series) + logging.info("Waiting for workload status 'blocked' on {}" + .format(unit_name)) + await model.async_block_until_unit_wl_status(unit_name, "blocked") + logging.info("Waiting for model idleness") + await model.async_block_until_all_units_idle() + await async_wrap_do_release_upgrade(unit_name, from_series=from_series, + to_series=to_series, files=files, + workaround_script=workaround_script) + logging.info("Reboot {}".format(unit_name)) + os_utils.reboot(unit_name) + logging.info("Waiting for workload status 'blocked' on {}" + .format(unit_name)) + await model.async_block_until_unit_wl_status(unit_name, "blocked") + logging.info("Waiting for model idleness") + await model.async_block_until_all_units_idle() + logging.info("Set origin on {}".format(application)) + # Allow for charms which have neither source nor openstack-origin + if origin: + await os_utils.async_set_origin(application, origin) + await model.async_block_until_all_units_idle() + logging.info("Complete series upgrade on {}".format(machine_num)) + await model.async_complete_series_upgrade(machine_num) + await model.async_block_until_all_units_idle() + logging.info("Running run_post_upgrade_functions {}".format( + post_upgrade_functions)) + run_post_upgrade_functions(post_upgrade_functions) + logging.info("Waiting for workload status 'active' on {}" + .format(unit_name)) + await model.async_block_until_unit_wl_status(unit_name, "active") + await model.async_block_until_all_units_idle() + # This step may be performed by juju in the future + logging.info("Set series on {} to {}".format(application, to_series)) + await model.async_set_series(application, to_series) + +def wrap_do_release_upgrade(unit_name, from_series="trusty", + to_series="xenial", + files=None, workaround_script=None): + """Wrap do release upgrade. + + In a production environment this step would be run administratively. + For testing purposes we need this automated. + + :param unit_name: Unit Name + :type unit_name: str + :param from_series: The series from which to upgrade + :type from_series: str + :param to_series: The series to which to upgrade + :type to_series: str + :param files: Workaround files to scp to unit under upgrade + :type files: list + :param workaround_script: Workaround script to run during series upgrade + :type workaround_script: str + :returns: None + :rtype: None + """ + # Pre upgrade hacks + # There are a few necessary hacks to accomplish an automated upgrade + # to overcome some packaging bugs. + # Copy scripts + if files: + logging.info("SCP files") + for _file in files: + logging.info("SCP {}".format(_file)) + model.scp_to_unit(unit_name, _file, os.path.basename(_file)) + + # Run Script + if workaround_script: + logging.info("Running workaround script") + os_utils.run_via_ssh(unit_name, workaround_script) + + # Actually do the do_release_upgrade + do_release_upgrade(unit_name) + + +async def async_wrap_do_release_upgrade(unit_name, from_series="trusty", + to_series="xenial", + files=None, workaround_script=None): + """Wrap do release upgrade. + + In a production environment this step would be run administratively. + For testing purposes we need this automated. + + :param unit_name: Unit Name + :type unit_name: str + :param from_series: The series from which to upgrade + :type from_series: str + :param to_series: The series to which to upgrade + :type to_series: str + :param files: Workaround files to scp to unit under upgrade + :type files: list + :param workaround_script: Workaround script to run during series upgrade + :type workaround_script: str + :returns: None + :rtype: None + """ + # Pre upgrade hacks + # There are a few necessary hacks to accomplish an automated upgrade + # to overcome some packaging bugs. + # Copy scripts + if files: + logging.info("SCP files") + for _file in files: + logging.info("SCP {}".format(_file)) + await model.async_scp_to_unit(unit_name, _file, os.path.basename(_file)) + + # Run Script + if workaround_script: + logging.info("Running workaround script") + os_utils.run_via_ssh(unit_name, workaround_script) + + # Actually do the do_release_upgrade + do_release_upgrade(unit_name) + + +def dist_upgrade(unit_name): + """Run dist-upgrade on unit after update package db. + + :param unit_name: Unit Name + :type unit_name: str + :returns: None + :rtype: None + """ + logging.info('Updating package db ' + unit_name) + update_cmd = 'sudo apt update' + model.run_on_unit(unit_name, update_cmd) + + logging.info('Updating existing packages ' + unit_name) + dist_upgrade_cmd = ( + """sudo DEBIAN_FRONTEND=noninteractive apt --assume-yes """ + """-o "Dpkg::Options::=--force-confdef" """ + """-o "Dpkg::Options::=--force-confold" dist-upgrade""") + model.run_on_unit(unit_name, dist_upgrade_cmd) + + +async def async_dist_upgrade(unit_name): + """Run dist-upgrade on unit after update package db. + + :param unit_name: Unit Name + :type unit_name: str + :returns: None + :rtype: None + """ + logging.info('Updating package db ' + unit_name) + update_cmd = 'sudo apt update' + await model.async_run_on_unit(unit_name, update_cmd) + + logging.info('Updating existing packages ' + unit_name) + dist_upgrade_cmd = ( + """sudo DEBIAN_FRONTEND=noninteractive apt --assume-yes """ + """-o "Dpkg::Options::=--force-confdef" """ + """-o "Dpkg::Options::=--force-confold" dist-upgrade""") + await model.async_run_on_unit(unit_name, dist_upgrade_cmd) + + +def do_release_upgrade(unit_name): + """Run do-release-upgrade noninteractive. + + :param unit_name: Unit Name + :type unit_name: str + :returns: None + :rtype: None + """ + logging.info('Upgrading ' + unit_name) + # NOTE: It is necessary to run this via juju ssh rather than juju run due + # to timeout restrictions and error handling. + os_utils.run_via_ssh( + unit_name, + 'DEBIAN_FRONTEND=noninteractive ' + 'do-release-upgrade -f DistUpgradeViewNonInteractive') diff --git a/zaza/openstack/utilities/upgrade_utils.py b/zaza/openstack/utilities/upgrade_utils.py new file mode 100644 index 0000000..445d529 --- /dev/null +++ b/zaza/openstack/utilities/upgrade_utils.py @@ -0,0 +1,30 @@ +# 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 functions to support upgrade testing.""" + + +SERVICE_GROUPS = { + 'Core Identity': ['keystone'], + 'Storage': [ + 'ceph-mon', 'ceph-osd', 'ceph-fs', 'ceph-radosgw', 'swift-proxy', + 'swift-storage'], + 'Control Plane': [ + 'aodh', 'barbican', 'ceilometer', 'cinder', 'designate', + 'designate-bind', 'glance', 'gnocchi', 'heat', 'manila', + 'manila-generic', 'neutron-api', 'neutron-gateway', 'placement', + 'nova-cloud-controller', 'openstack-dashboard'], + 'Compute': ['nova-compute']} + +UPGRADE_EXCLUDE_LIST = ['rabbitmq-server', 'percona-cluster']