Browse Source

Add version-specific support utilities for RHEL-family tools

Add dynamic version detection to RHEL-family OSMorphing tools, enabling
support for RHEL 7-10+ through unified implementations.

Signed-off-by: Mihaela Balutoiu <mbalutoiu@cloudbasesolutions.com>
Mihaela Balutoiu 1 month ago
parent
commit
9a2b37aacd

+ 2 - 2
coriolis/osmorphing/amazon.py

@@ -37,8 +37,8 @@ class BaseAmazonLinuxOSMorphingTools(redhat.BaseRedHatMorphingTools):
         # 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
 

+ 16 - 0
coriolis/osmorphing/base.py

@@ -11,6 +11,9 @@ from oslo_log import log as logging
 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 @@ class BaseLinuxOSMorphingTools(BaseOSMorphingTools):
 
         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]

+ 1 - 1
coriolis/osmorphing/centos.py

@@ -43,7 +43,7 @@ class BaseCentOSMorphingTools(redhat.BaseRedHatMorphingTools):
             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'

+ 1 - 1
coriolis/osmorphing/oracle.py

@@ -35,7 +35,7 @@ class BaseOracleMorphingTools(redhat.BaseRedHatMorphingTools):
             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'

+ 28 - 0
coriolis/tests/osmorphing/test_base.py

@@ -5,6 +5,7 @@ import logging
 from unittest import mock
 
 import ddt
+from packaging.version import Version
 
 from coriolis import exception
 from coriolis.osmorphing import base
@@ -180,6 +181,33 @@ class BaseLinuxOSMorphingToolsTestBase(test_base.CoriolisBaseTestCase):
                 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)],

+ 20 - 14
coriolis/tests/osmorphing/test_centos.py

@@ -31,33 +31,39 @@ class BaseCentOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             mock.sentinel.event_manager, self.detected_os_info,
             mock.sentinel.osmorphing_parameters)
 
-    def test_check_os_supported(self):
+    @ddt.data(
+        (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": "7"
+            "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.
-        ('6', 'yum-config-manager --enable'),
         ('7', 'yum-config-manager --enable'),
         # 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')

+ 17 - 3
coriolis/tests/osmorphing/test_oracle.py

@@ -31,11 +31,24 @@ class BaseOracleMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             mock.sentinel.event_manager, self.detected_os_info,
             mock.sentinel.osmorphing_parameters)
 
-    def test_check_os_supported(self):
+    @ddt.data(
+        ('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'
@@ -47,10 +60,11 @@ class BaseOracleMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 
     @ddt.data(
         # OL7 and earlier use yum-config-manager.
-        ('6', 'yum-config-manager --enable'),
         ('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')

+ 17 - 3
coriolis/tests/osmorphing/test_redhat.py

@@ -35,11 +35,25 @@ class BaseRedHatMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             mock.sentinel.operation_timeout)
         self.morphing_tools._os_root_dir = '/root'
 
-    def test_check_os_supported(self):
+    @ddt.data(
+        ('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'

+ 18 - 3
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 @@ class BaseRockyLinuxMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             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 = {