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([