From d891227494214dd640e693b5a3f6cc8d69ea77e8 Mon Sep 17 00:00:00 2001 From: Nicolas Pochet Date: Wed, 8 Aug 2018 15:23:40 +0200 Subject: [PATCH 1/4] Add a way to compare OS release pair It was a functionality available in charm helpers. It is implemented in a similar way: * Get the current series from the `keystone` machine * Get the OS version from `get_current_os_versions` * Try to find the built string in `OPENSTACK_RELEASES_PAIRS` It is thus possible to compare release pairs as it returns integers. --- .gitignore | 1 + .../test_zaza_utilities_openstack.py | 69 +++++++++++++++++++ zaza/utilities/exceptions.py | 24 +++++++ zaza/utilities/openstack.py | 47 +++++++++++++ zaza/utilities/os_versions.py | 6 ++ 5 files changed, 147 insertions(+) diff --git a/.gitignore b/.gitignore index 30ded82..aad4c2b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ dist/ .local zaza.egg-info/ .coverage +.vscode/ # Sphinx doc/build diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index ea63dc5..409a5c5 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -645,3 +645,72 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): with self.assertRaises(exceptions.NeutronBGPSpeakerMissing): openstack_utils.neutron_bgp_speaker_appears_on_agent( _neutronclient, 'FAKE_AGENT_ID') + + def test_get_current_openstack_release_pair(self): + self.patch( + 'zaza.utilities.openstack.get_current_os_versions', + new_callable=mock.MagicMock(), + name='_get_os_version' + ) + self.patch( + 'zaza.utilities.juju.get_machines_for_application', + new_callable=mock.MagicMock(), + name='_get_machines' + ) + self.patch( + 'zaza.utilities.juju.get_machine_status', + new_callable=mock.MagicMock(), + name='_get_machine_status' + ) + + # No machine returned + self._get_machines.return_value = [] + with self.assertRaises(exceptions.NoKeystoneFound): + openstack_utils.get_current_os_release_pair() + + # No series returned + self._get_machines.return_value = ['6'] + self._get_machine_status.return_value = None + with self.assertRaises(exceptions.SeriesNotFound): + openstack_utils.get_current_os_release_pair() + + # No OS Version returned + self._get_machine_status.return_value = 'xenial' + self._get_os_version.return_value = {} + with self.assertRaises(exceptions.OSVersionNotFound): + openstack_utils.get_current_os_release_pair() + + # Normal scenario + self._get_os_version.return_value = {'keystone': 'mitaka'} + expected = 'xenial_mitaka' + result = openstack_utils.get_current_os_release_pair() + self.assertEqual(expected, result) + + def test_get_openstack_release(self): + self.patch( + 'zaza.utilities.openstack.get_current_os_release_pair', + new_callable=mock.MagicMock(), + name='_get_os_rel_pair' + ) + + # Bad release pair + release_pair = 'bad' + with self.assertRaises(exceptions.ReleasePairNotFound): + openstack_utils.get_os_release(release_pair) + + # Normal scenario + expected = 4 + result = openstack_utils.get_os_release('xenial_mitaka') + self.assertEqual(expected, result) + + # Normal scenario with current release pair + self._get_os_rel_pair.return_value = 'xenial_mitaka' + expected = 4 + result = openstack_utils.get_os_release() + self.assertEqual(expected, result) + + # We can compare releases xenial_queens > xenial_mitaka + xenial_queens = openstack_utils.get_os_release('xenial_queens') + xenial_mitaka = openstack_utils.get_os_release('xenial_mitaka') + release_comp = xenial_queens > xenial_mitaka + self.assertTrue(release_comp) diff --git a/zaza/utilities/exceptions.py b/zaza/utilities/exceptions.py index 453f29f..c6cdc8a 100644 --- a/zaza/utilities/exceptions.py +++ b/zaza/utilities/exceptions.py @@ -29,3 +29,27 @@ class NeutronBGPSpeakerMissing(Exception): """No BGP speaker appeared on agent.""" pass + + +class NoKeystoneFound(Exception): + """No Keystone found in machines.""" + + pass + + +class SeriesNotFound(Exception): + """Series not found in status.""" + + pass + + +class OSVersionNotFound(Exception): + """OS Version not found.""" + + pass + + +class ReleasePairNotFound(Exception): + """Release pair was not found in OPENSTACK_RELEASES_PAIRS.""" + + pass diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index 0e6f8aa..0e906f9 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -6,6 +6,7 @@ from .os_versions import ( OPENSTACK_CODENAMES, SWIFT_CODENAMES, PACKAGE_CODENAMES, + OPENSTACK_RELEASES_PAIRS, ) from glanceclient import Client as GlanceClient @@ -1123,6 +1124,52 @@ def get_application_config_keys(application): return list(application_config.keys()) +def get_current_os_release_pair(): + """Return OpenStack Release pair name. + + :returns: Name of the OpenStack release pair + :rtype: str + :raises: exceptions.NoKeystoneFound + :raises: exceptions.SeriesNotFound + :raises: exceptions.OSVersionNotFound + """ + keystone = juju_utils.get_machines_for_application('keystone') + if len(keystone) >= 1: + keystone = keystone[0] + else: + raise exceptions.NoKeystoneFound() + + series = juju_utils.get_machine_status(keystone, key='series') + if not series: + raise exceptions.SeriesNotFound() + + os_version = get_current_os_versions(['keystone']).get('keystone') + if not os_version: + raise exceptions.OSVersionNotFound() + + return '{}_{}'.format(series, os_version) + + +def get_os_release(release_pair=None): + """Return index of release in OPENSTACK_RELEASES_PAIRS. + + :returns: Index of the release + :rtype: int + :raises: exceptions.ReleasePairNotFound + """ + if release_pair is None: + release_pair = get_current_os_release_pair() + try: + index = OPENSTACK_RELEASES_PAIRS.index(release_pair) + except ValueError: + msg = 'Release pair: {} not found in {}'.format( + release_pair, + OPENSTACK_RELEASES_PAIRS + ) + raise exceptions.ReleasePairNotFound(msg) + return index + + def get_application_config_option(application, option): """Return application configuration. diff --git a/zaza/utilities/os_versions.py b/zaza/utilities/os_versions.py index a6b0f29..66f9f99 100644 --- a/zaza/utilities/os_versions.py +++ b/zaza/utilities/os_versions.py @@ -37,6 +37,12 @@ OPENSTACK_CODENAMES = OrderedDict([ ('2018.1', 'queens'), ]) +OPENSTACK_RELEASES_PAIRS = [ + 'trusty_icehouse', 'trusty_kilo', 'trusty_liberty', + 'trusty_mitaka', 'xenial_mitaka', 'xenial_newton', + 'yakkety_newton', 'xenial_ocata', 'zesty_ocata', + 'xenial_pike', 'artful_pike', 'xenial_queens', + 'bionic_queens', 'bionic_rocky', 'cosmic_rocky'] # The ugly duckling - must list releases oldest to newest SWIFT_CODENAMES = OrderedDict([ From 077f2af87c366750c8f6fa392413edf414dcf9f3 Mon Sep 17 00:00:00 2001 From: Nicolas Pochet Date: Wed, 8 Aug 2018 16:14:09 +0200 Subject: [PATCH 2/4] Make `get_current_os_release_pair` variable It is possible to use `get_current_os_release_pair` with an argument or to use the default one (`keystone`) --- .../utilities/test_zaza_utilities_openstack.py | 8 +++++++- zaza/utilities/openstack.py | 14 ++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index 409a5c5..f2fd04b 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -680,7 +680,13 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): with self.assertRaises(exceptions.OSVersionNotFound): openstack_utils.get_current_os_release_pair() - # Normal scenario + # Normal scenario, argument passed + self._get_os_version.return_value = {'keystone': 'mitaka'} + expected = 'xenial_mitaka' + result = openstack_utils.get_current_os_release_pair('keystone') + self.assertEqual(expected, result) + + # Normal scenario, default value used self._get_os_version.return_value = {'keystone': 'mitaka'} expected = 'xenial_mitaka' result = openstack_utils.get_current_os_release_pair() diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index 0e906f9..0f10187 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -1124,26 +1124,28 @@ def get_application_config_keys(application): return list(application_config.keys()) -def get_current_os_release_pair(): +def get_current_os_release_pair(application='keystone'): """Return OpenStack Release pair name. + :param application: Name of application + :type application: string :returns: Name of the OpenStack release pair :rtype: str :raises: exceptions.NoKeystoneFound :raises: exceptions.SeriesNotFound :raises: exceptions.OSVersionNotFound """ - keystone = juju_utils.get_machines_for_application('keystone') - if len(keystone) >= 1: - keystone = keystone[0] + machines = juju_utils.get_machines_for_application(application) + if len(machines) >= 1: + machine = machines[0] else: raise exceptions.NoKeystoneFound() - series = juju_utils.get_machine_status(keystone, key='series') + series = juju_utils.get_machine_status(machine, key='series') if not series: raise exceptions.SeriesNotFound() - os_version = get_current_os_versions(['keystone']).get('keystone') + os_version = get_current_os_versions([application]).get(application) if not os_version: raise exceptions.OSVersionNotFound() From f0e0cdd3c2c57340ab3bdb79e1eb84c6048eb0b4 Mon Sep 17 00:00:00 2001 From: Nicolas Pochet Date: Wed, 8 Aug 2018 17:04:05 +0200 Subject: [PATCH 3/4] Add `get_machine_series` --- unit_tests/utilities/test_zaza_utilities_juju.py | 15 +++++++++++++++ .../utilities/test_zaza_utilities_openstack.py | 8 ++++---- zaza/utilities/juju.py | 14 ++++++++++++++ zaza/utilities/openstack.py | 2 +- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_juju.py b/unit_tests/utilities/test_zaza_utilities_juju.py index db49cb7..9ca0249 100644 --- a/unit_tests/utilities/test_zaza_utilities_juju.py +++ b/unit_tests/utilities/test_zaza_utilities_juju.py @@ -227,3 +227,18 @@ class TestJujuUtils(ut_utils.BaseTestCase): self.model.run_on_leader.assert_called_with( 'application', 'leader-get --format=yaml ') self.assertFalse(self.yaml.load.called) + + def test_get_machine_series(self): + self.patch( + 'zaza.utilities.juju.get_machine_status', + new_callable=mock.MagicMock(), + name='_get_machine_status' + ) + self._get_machine_status.return_value = 'xenial' + expected = 'xenial' + actual = juju_utils.get_machine_series('6') + self._get_machine_status.assert_called_with( + machine='6', + key='series' + ) + self.assertEqual(expected, actual) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index f2fd04b..22a5124 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -658,9 +658,9 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): name='_get_machines' ) self.patch( - 'zaza.utilities.juju.get_machine_status', + 'zaza.utilities.juju.get_machine_series', new_callable=mock.MagicMock(), - name='_get_machine_status' + name='_get_machine_series' ) # No machine returned @@ -670,12 +670,12 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): # No series returned self._get_machines.return_value = ['6'] - self._get_machine_status.return_value = None + self._get_machine_series.return_value = None with self.assertRaises(exceptions.SeriesNotFound): openstack_utils.get_current_os_release_pair() # No OS Version returned - self._get_machine_status.return_value = 'xenial' + self._get_machine_series.return_value = 'xenial' self._get_os_version.return_value = {} with self.assertRaises(exceptions.OSVersionNotFound): openstack_utils.get_current_os_release_pair() diff --git a/zaza/utilities/juju.py b/zaza/utilities/juju.py index b1a1f5b..d364e26 100644 --- a/zaza/utilities/juju.py +++ b/zaza/utilities/juju.py @@ -115,6 +115,20 @@ def get_machine_status(machine, key=None): return status +def get_machine_series(machine): + """Return the juju series for a machine. + + :param machine: Machine number + :type machine: string + :returns: Juju series + :rtype: string + """ + return get_machine_status( + machine=machine, + key='series' + ) + + def get_machine_uuids_for_application(application): """Return machine uuids for a given application. diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index 0f10187..4762983 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -1141,7 +1141,7 @@ def get_current_os_release_pair(application='keystone'): else: raise exceptions.NoKeystoneFound() - series = juju_utils.get_machine_status(machine, key='series') + series = juju_utils.get_machine_series(machine) if not series: raise exceptions.SeriesNotFound() From d8f07e66ab740482523635bdef4e80dc1ee139b9 Mon Sep 17 00:00:00 2001 From: Nicolas Pochet Date: Wed, 8 Aug 2018 23:21:07 +0200 Subject: [PATCH 4/4] Make exception more generic --- .../utilities/test_zaza_utilities_openstack.py | 2 +- zaza/utilities/exceptions.py | 15 ++++++++++++--- zaza/utilities/openstack.py | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/unit_tests/utilities/test_zaza_utilities_openstack.py b/unit_tests/utilities/test_zaza_utilities_openstack.py index 22a5124..2be209c 100644 --- a/unit_tests/utilities/test_zaza_utilities_openstack.py +++ b/unit_tests/utilities/test_zaza_utilities_openstack.py @@ -665,7 +665,7 @@ class TestOpenStackUtils(ut_utils.BaseTestCase): # No machine returned self._get_machines.return_value = [] - with self.assertRaises(exceptions.NoKeystoneFound): + with self.assertRaises(exceptions.ApplicationNotFound): openstack_utils.get_current_os_release_pair() # No series returned diff --git a/zaza/utilities/exceptions.py b/zaza/utilities/exceptions.py index c6cdc8a..14a609d 100644 --- a/zaza/utilities/exceptions.py +++ b/zaza/utilities/exceptions.py @@ -31,10 +31,19 @@ class NeutronBGPSpeakerMissing(Exception): pass -class NoKeystoneFound(Exception): - """No Keystone found in machines.""" +class ApplicationNotFound(Exception): + """Application not found in machines.""" - pass + def __init__(self, application): + """Create Application not found exception. + + :param application: Name of the application + :type application: string + :returns: ApplicationNotFound Exception + """ + msg = ("{} application was not found in machines.". + format(application)) + super(ApplicationNotFound, self).__init__(msg) class SeriesNotFound(Exception): diff --git a/zaza/utilities/openstack.py b/zaza/utilities/openstack.py index 4762983..ead3b57 100644 --- a/zaza/utilities/openstack.py +++ b/zaza/utilities/openstack.py @@ -1131,7 +1131,7 @@ def get_current_os_release_pair(application='keystone'): :type application: string :returns: Name of the OpenStack release pair :rtype: str - :raises: exceptions.NoKeystoneFound + :raises: exceptions.ApplicationNotFound :raises: exceptions.SeriesNotFound :raises: exceptions.OSVersionNotFound """ @@ -1139,7 +1139,7 @@ def get_current_os_release_pair(application='keystone'): if len(machines) >= 1: machine = machines[0] else: - raise exceptions.NoKeystoneFound() + raise exceptions.ApplicationNotFound(application) series = juju_utils.get_machine_series(machine) if not series: