From 3a02ecd1400734b50f3309287edaa9aa1d2613cd Mon Sep 17 00:00:00 2001 From: Mihaela Balutoiu Date: Mon, 20 Jul 2026 12:16:54 +0300 Subject: [PATCH] Add version-specific support utilities for RHEL-family tools Add dynamic version detection to RHEL-family OSMorphing tools, enabling support for RHEL 6-10+ through unified implementations. Signed-off-by: Mihaela Balutoiu --- coriolis/osmorphing/amazon.py | 4 +-- coriolis/osmorphing/base.py | 16 +++++++++++ coriolis/osmorphing/centos.py | 2 +- coriolis/osmorphing/oracle.py | 2 +- coriolis/tests/osmorphing/test_base.py | 28 +++++++++++++++++++ coriolis/tests/osmorphing/test_centos.py | 34 +++++++++++++++--------- coriolis/tests/osmorphing/test_oracle.py | 20 ++++++++++++-- coriolis/tests/osmorphing/test_redhat.py | 21 ++++++++++++--- coriolis/tests/osmorphing/test_rocky.py | 21 ++++++++++++--- 9 files changed, 123 insertions(+), 25 deletions(-) diff --git a/coriolis/osmorphing/amazon.py b/coriolis/osmorphing/amazon.py index 69cb401cf..8d9e281bd 100644 --- a/coriolis/osmorphing/amazon.py +++ b/coriolis/osmorphing/amazon.py @@ -38,8 +38,8 @@ def enable_repos(self, repo_names): # Determine package manager based on version # Amazon Linux 2 has version "2", AL2023 has version "2023" try: - major_version = int(str(self._version).split('.')[0]) - except (ValueError, AttributeError): + major_version = self._parse_version_util(self._version).major + except ValueError: # Fallback to yum if version parsing fails major_version = 2 diff --git a/coriolis/osmorphing/base.py b/coriolis/osmorphing/base.py index 290ac0fa8..912a23dd8 100644 --- a/coriolis/osmorphing/base.py +++ b/coriolis/osmorphing/base.py @@ -11,6 +11,9 @@ from six import with_metaclass import yaml +from packaging.version import InvalidVersion +from packaging.version import Version + from coriolis import exception from coriolis.osmorphing.netpreserver import factory from coriolis import utils @@ -334,6 +337,19 @@ def _version_supported_util(cls, version, minimum, maximum=None): return True + @classmethod + def _parse_version_util(cls, version: str) -> Version: + if not version: + raise ValueError(f"Empty or missing version: {version!r}") + + try: + return Version(str(version)) + except InvalidVersion as exc: + raise ValueError( + f"Could not parse version from release string: " + f"{version!r}" + ) from exc + def get_packages(self): k_add = [h for h in self._packages.keys() if h is None or h == self._hypervisor] diff --git a/coriolis/osmorphing/centos.py b/coriolis/osmorphing/centos.py index f25956c1a..136668a1f 100644 --- a/coriolis/osmorphing/centos.py +++ b/coriolis/osmorphing/centos.py @@ -38,7 +38,7 @@ def enable_repos(self, repo_names): return # Determine package manager based on version - major_version = int(str(self._version).split('.')[0]) + major_version = self._parse_version_util(self._version).major if major_version >= 8: # CentOS 8+ uses dnf config_manager = 'dnf config-manager' diff --git a/coriolis/osmorphing/oracle.py b/coriolis/osmorphing/oracle.py index e41415fa2..1019d37bb 100644 --- a/coriolis/osmorphing/oracle.py +++ b/coriolis/osmorphing/oracle.py @@ -34,7 +34,7 @@ def enable_repos(self, repo_names): return # Determine package manager based on version - major_version = int(str(self._version).split('.')[0]) + major_version = self._parse_version_util(self._version).major if major_version >= 8: # OL8+ uses dnf config_manager = 'dnf config-manager' diff --git a/coriolis/tests/osmorphing/test_base.py b/coriolis/tests/osmorphing/test_base.py index c3ca3ff0d..8a66747e3 100644 --- a/coriolis/tests/osmorphing/test_base.py +++ b/coriolis/tests/osmorphing/test_base.py @@ -5,6 +5,7 @@ from unittest import mock import ddt +from packaging.version import Version from coriolis import exception from coriolis.osmorphing import base @@ -180,6 +181,33 @@ def test_version_supported_util_warnings_no_match(self): version, minimum) self.assertFalse(result) + @ddt.data( + ("8", 8), + ("8.0", 8), + ("8.10", 8), + ("9", 9), + ("9.4", 9), + ("10", 10), + ("10.0", 10), + ("10.10", 10), + ) + @ddt.unpack + def test__parse_version_util(self, version, expected_major): + result = self.os_morphing_tools._parse_version_util(version) + self.assertIsInstance(result, Version) + self.assertEqual(result.major, expected_major) + + @ddt.data( + "", + None, + "abc", + "x10", + ) + def test__parse_version_util_invalid(self, version): + self.assertRaises( + ValueError, + self.os_morphing_tools._parse_version_util, version) + def test_get_packages(self): self.os_morphing_tools._packages = { None: [('pkg1', False), ('pkg2', True)], diff --git a/coriolis/tests/osmorphing/test_centos.py b/coriolis/tests/osmorphing/test_centos.py index ed73a161b..95361b12d 100644 --- a/coriolis/tests/osmorphing/test_centos.py +++ b/coriolis/tests/osmorphing/test_centos.py @@ -31,25 +31,32 @@ def setUp(self): mock.sentinel.event_manager, self.detected_os_info, mock.sentinel.osmorphing_parameters) - def test_check_os_supported(self): + @ddt.data( + (centos.CENTOS_DISTRO_IDENTIFIER, '6', True), + (centos.CENTOS_DISTRO_IDENTIFIER, '7', True), + (centos.CENTOS_DISTRO_IDENTIFIER, '8', True), + (centos.CENTOS_DISTRO_IDENTIFIER, '9', True), + (centos.CENTOS_DISTRO_IDENTIFIER, '10', True), + (centos.CENTOS_DISTRO_IDENTIFIER, '10.0', True), + (centos.CENTOS_STREAM_DISTRO_IDENTIFIER, '8', True), + (centos.CENTOS_STREAM_DISTRO_IDENTIFIER, '9', True), + (centos.CENTOS_STREAM_DISTRO_IDENTIFIER, '10', True), + ('unsupported', '8', False), + (centos.CENTOS_DISTRO_IDENTIFIER, '5', False), + (centos.CENTOS_DISTRO_IDENTIFIER, 'abc', False), + ) + @ddt.unpack + def test_check_os_supported(self, distribution_name, release_version, + expected): detected_os_info = { - "distribution_name": centos.CENTOS_DISTRO_IDENTIFIER, - "release_version": "6" + "distribution_name": distribution_name, + "release_version": release_version } result = centos.BaseCentOSMorphingTools.check_os_supported( detected_os_info) - self.assertTrue(result) - - def test_check_os_not_supported(self): - detected_os_info = { - "distribution_name": 'unsupported', - } - result = centos.BaseCentOSMorphingTools.check_os_supported( - detected_os_info) - - self.assertFalse(result) + self.assertEqual(expected, result) @ddt.data( # CentOS 7 and earlier use yum-config-manager. @@ -58,6 +65,7 @@ def test_check_os_not_supported(self): # CentOS 8+ uses dnf config-manager. ('8', 'dnf config-manager --set-enabled'), ('9', 'dnf config-manager --set-enabled'), + ('10', 'dnf config-manager --set-enabled'), ) @ddt.unpack @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot') diff --git a/coriolis/tests/osmorphing/test_oracle.py b/coriolis/tests/osmorphing/test_oracle.py index 751ca3c70..d6e8374c3 100644 --- a/coriolis/tests/osmorphing/test_oracle.py +++ b/coriolis/tests/osmorphing/test_oracle.py @@ -32,11 +32,25 @@ def setUp(self): mock.sentinel.event_manager, self.detected_os_info, mock.sentinel.osmorphing_parameters) - def test_check_os_supported(self): + @ddt.data( + ('6', True), + ('7', True), + ('8', True), + ('9', True), + ('9.4', True), + ('10', True), + ('10.0', True), + ('11', True), + ('5', False), + ('abc', False), + ) + @ddt.unpack + def test_check_os_supported(self, release_version, expected): + self.detected_os_info['release_version'] = release_version result = oracle.BaseOracleMorphingTools.check_os_supported( self.detected_os_info) - self.assertTrue(result) + self.assertEqual(expected, result) def test_check_os_not_supported(self): self.detected_os_info['distribution_name'] = 'unsupported' @@ -52,6 +66,8 @@ def test_check_os_not_supported(self): ('7', 'yum-config-manager --enable'), # OL8+ uses dnf config-manager. ('8', 'dnf config-manager --set-enabled'), + ('9', 'dnf config-manager --set-enabled'), + ('10', 'dnf config-manager --set-enabled'), ) @ddt.unpack @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot') diff --git a/coriolis/tests/osmorphing/test_redhat.py b/coriolis/tests/osmorphing/test_redhat.py index 761d07252..162b0d9eb 100644 --- a/coriolis/tests/osmorphing/test_redhat.py +++ b/coriolis/tests/osmorphing/test_redhat.py @@ -35,11 +35,26 @@ def setUp(self): mock.sentinel.operation_timeout) self.morphing_tools._os_root_dir = '/root' - def test_check_os_supported(self): + @ddt.data( + ('6', True), + ('7', True), + ('8', True), + ('9', True), + ('9.4', True), + ('10', True), + ('10.0', True), + ('11', True), + ('5', False), + ('abc', False), + ('', False), + ) + @ddt.unpack + def test_check_os_supported_release_version( + self, release_version, expected): + self.detected_os_info['release_version'] = release_version result = redhat.BaseRedHatMorphingTools.check_os_supported( self.detected_os_info) - - self.assertTrue(result) + self.assertEqual(expected, result) def test_check_os_not_supported(self): self.detected_os_info['distribution_name'] = 'unsupported' diff --git a/coriolis/tests/osmorphing/test_rocky.py b/coriolis/tests/osmorphing/test_rocky.py index 99e6afec8..1107a943e 100644 --- a/coriolis/tests/osmorphing/test_rocky.py +++ b/coriolis/tests/osmorphing/test_rocky.py @@ -4,12 +4,15 @@ import logging from unittest import mock +import ddt + from coriolis import exception from coriolis.osmorphing import base from coriolis.osmorphing import rocky from coriolis.tests import test_base +@ddt.ddt class BaseRockyLinuxMorphingToolsTestCase(test_base.CoriolisBaseTestCase): """Test suite for the BaseRockyLinuxMorphingTools class.""" @@ -28,16 +31,28 @@ def setUp(self): mock.sentinel.event_manager, self.detected_os_info, mock.sentinel.osmorphing_parameters) - def test_check_os_supported(self): + @ddt.data( + ('8', True), + ('8.4', True), + ('9', True), + ('9.4', True), + ('10', True), + ('10.0', True), + ('11', True), + ('7', False), + ('abc', False), + ) + @ddt.unpack + def test_check_os_supported(self, release_version, expected): detected_os_info = { "distribution_name": rocky.ROCKY_LINUX_DISTRO_IDENTIFIER, - "release_version": "8" + "release_version": release_version } result = rocky.BaseRockyLinuxMorphingTools.check_os_supported( detected_os_info ) - self.assertTrue(result) + self.assertEqual(expected, result) def test_check_os_not_supported(self): detected_os_info = {