Просмотр исходного кода

Update enable_repo from osmorphing for all redhat based OSes

Fabian Fulga 4 месяцев назад
Родитель
Сommit
5ced0d6ac2

+ 42 - 0
coriolis/osmorphing/amazon.py

@@ -1,12 +1,18 @@
 # Copyright 2023 Cloudbase Solutions Srl
 # Copyright 2023 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+from oslo_log import log as logging
+
+from coriolis import exception
 from coriolis.osmorphing.osdetect import amazon as amazon_detect
 from coriolis.osmorphing.osdetect import amazon as amazon_detect
 from coriolis.osmorphing import redhat
 from coriolis.osmorphing import redhat
+from coriolis import utils
 
 
 
 
 AMAZON_DISTRO_NAME_IDENTIFIER = amazon_detect.AMAZON_DISTRO_NAME
 AMAZON_DISTRO_NAME_IDENTIFIER = amazon_detect.AMAZON_DISTRO_NAME
 
 
+LOG = logging.getLogger(__name__)
+
 
 
 class BaseAmazonLinuxOSMorphingTools(redhat.BaseRedHatMorphingTools):
 class BaseAmazonLinuxOSMorphingTools(redhat.BaseRedHatMorphingTools):
 
 
@@ -19,3 +25,39 @@ class BaseAmazonLinuxOSMorphingTools(redhat.BaseRedHatMorphingTools):
             return False
             return False
         return cls._version_supported_util(
         return cls._version_supported_util(
             detected_os_info['release_version'], minimum=2)
             detected_os_info['release_version'], minimum=2)
+
+    def enable_repos(self, repo_names):
+        """Enable repositories for Amazon Linux.
+
+        Uses yum-config-manager for Amazon Linux 2,
+        dnf config-manager for Amazon Linux 2023 and later.
+        """
+        if not repo_names:
+            return
+
+        # 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):
+            # Fallback to yum if version parsing fails
+            major_version = 2
+
+        if major_version >= 2023:
+            # Amazon Linux 2023+ uses dnf
+            config_manager = 'dnf config-manager'
+            enable_flag = '--set-enabled %s'
+        else:
+            # Amazon Linux 2 and earlier use yum
+            config_manager = 'yum-config-manager'
+            enable_flag = '--enable %s'
+
+        for repo in repo_names:
+            cmd = '%s %s' % (config_manager, enable_flag % repo)
+            try:
+                self._exec_cmd_chroot(cmd)
+                LOG.info("Enabled repository '%s' using %s",
+                         repo, config_manager)
+            except exception.CoriolisException:
+                LOG.warning(f"Failed to enable repository {repo}. "
+                            f"Error was: {utils.get_exception_details()}")

+ 35 - 0
coriolis/osmorphing/centos.py

@@ -1,14 +1,19 @@
 # Copyright 2020 Cloudbase Solutions Srl
 # Copyright 2020 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+from oslo_log import log as logging
 
 
+from coriolis import exception
 from coriolis.osmorphing.osdetect import centos as centos_detect
 from coriolis.osmorphing.osdetect import centos as centos_detect
 from coriolis.osmorphing import redhat
 from coriolis.osmorphing import redhat
+from coriolis import utils
 
 
 
 
 CENTOS_DISTRO_IDENTIFIER = centos_detect.CENTOS_DISTRO_IDENTIFIER
 CENTOS_DISTRO_IDENTIFIER = centos_detect.CENTOS_DISTRO_IDENTIFIER
 CENTOS_STREAM_DISTRO_IDENTIFIER = centos_detect.CENTOS_STREAM_DISTRO_IDENTIFIER
 CENTOS_STREAM_DISTRO_IDENTIFIER = centos_detect.CENTOS_STREAM_DISTRO_IDENTIFIER
 
 
+LOG = logging.getLogger(__name__)
+
 
 
 class BaseCentOSMorphingTools(redhat.BaseRedHatMorphingTools):
 class BaseCentOSMorphingTools(redhat.BaseRedHatMorphingTools):
 
 
@@ -22,3 +27,33 @@ class BaseCentOSMorphingTools(redhat.BaseRedHatMorphingTools):
             return False
             return False
         return cls._version_supported_util(
         return cls._version_supported_util(
             detected_os_info['release_version'], minimum=6)
             detected_os_info['release_version'], minimum=6)
+
+    def enable_repos(self, repo_names):
+        """Enable repositories for CentOS.
+
+        Uses yum-config-manager for CentOS 7 and earlier,
+        dnf config-manager for CentOS 8 and later.
+        """
+        if not repo_names:
+            return
+
+        # Determine package manager based on version
+        major_version = int(str(self._version).split('.')[0])
+        if major_version >= 8:
+            # CentOS 8+ uses dnf
+            config_manager = 'dnf config-manager'
+            enable_flag = '--set-enabled %s'
+        else:
+            # CentOS 7 and earlier use yum
+            config_manager = 'yum-config-manager'
+            enable_flag = '--enable %s'
+
+        for repo in repo_names:
+            cmd = '%s %s' % (config_manager, enable_flag % repo)
+            try:
+                self._exec_cmd_chroot(cmd)
+                LOG.info("Enabled repository '%s' using %s",
+                         repo, config_manager)
+            except exception.CoriolisException:
+                LOG.warning(f"Failed to enable repository {repo}. "
+                            f"Error was: {utils.get_exception_details()}")

+ 33 - 31
coriolis/osmorphing/oracle.py

@@ -1,14 +1,18 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
-import uuid
+from oslo_log import log as logging
 
 
+from coriolis import exception
 from coriolis.osmorphing.osdetect import oracle as oracle_detect
 from coriolis.osmorphing.osdetect import oracle as oracle_detect
 from coriolis.osmorphing import redhat
 from coriolis.osmorphing import redhat
+from coriolis import utils
 
 
 
 
 ORACLE_DISTRO_IDENTIFIER = oracle_detect.ORACLE_DISTRO_IDENTIFIER
 ORACLE_DISTRO_IDENTIFIER = oracle_detect.ORACLE_DISTRO_IDENTIFIER
 
 
+LOG = logging.getLogger(__name__)
+
 
 
 class BaseOracleMorphingTools(redhat.BaseRedHatMorphingTools):
 class BaseOracleMorphingTools(redhat.BaseRedHatMorphingTools):
 
 
@@ -20,34 +24,32 @@ class BaseOracleMorphingTools(redhat.BaseRedHatMorphingTools):
         return cls._version_supported_util(
         return cls._version_supported_util(
             detected_os_info['release_version'], minimum=6)
             detected_os_info['release_version'], minimum=6)
 
 
-    def _get_oracle_repos(self):
-        repos = []
-        major_version = int(self._version.split(".")[0])
-        uekr_version = int(major_version) - 2
-        if major_version < 8:
-            repo_file_path = (
-                '/etc/yum.repos.d/%s.repo' % str(uuid.uuid4()))
-            self._exec_cmd_chroot(
-                "curl -L http://public-yum.oracle.com/public-yum-ol%s.repo "
-                "-o %s" % (major_version, repo_file_path))
-            # During OSMorphing, we temporarily enable needed package repos,
-            # so we make sure we disable all downloaded repos here.
-            self._exec_cmd_chroot(
-                'sed -i "s/^enabled=1$/enabled=0/g" %s' % repo_file_path)
-
-            repos_to_enable = ["ol%s_software_collections" % major_version,
-                               "ol%s_addons" % major_version,
-                               "ol%s_UEKR" % major_version,
-                               "ol%s_latest" % major_version]
-            repos = self._find_yum_repos(repos_to_enable)
+    def enable_repos(self, repo_names):
+        """Enable repositories for Oracle Linux.
+
+        Uses yum-config-manager for OL7 and earlier,
+        dnf config-manager for OL8 and later.
+        """
+        if not repo_names:
+            return
+
+        # Determine package manager based on version
+        major_version = int(str(self._version).split('.')[0])
+        if major_version >= 8:
+            # OL8+ uses dnf
+            config_manager = 'dnf config-manager'
+            enable_flag = '--set-enabled %s'
         else:
         else:
-            self._yum_install(
-                ['oraclelinux-release-el%s' % major_version],
-                self._find_yum_repos(['ol%s_baseos_latest' % major_version]))
-            repos_to_enable = ["ol%s_baseos_latest" % major_version,
-                               "ol%s_appstream" % major_version,
-                               "ol%d_addons" % major_version,
-                               "ol%s_UEKR%s" % (major_version, uekr_version)]
-            repos = self._find_yum_repos(repos_to_enable)
-
-        return repos
+            # OL7 and earlier use yum
+            config_manager = 'yum-config-manager'
+            enable_flag = '--enable %s'
+
+        for repo in repo_names:
+            cmd = '%s %s' % (config_manager, enable_flag % repo)
+            try:
+                self._exec_cmd_chroot(cmd)
+                LOG.info("Enabled repository '%s' using %s",
+                         repo, config_manager)
+            except exception.CoriolisException:
+                LOG.warning(f"Failed to enable repository {repo}. "
+                            f"Error was: {utils.get_exception_details()}")

+ 36 - 53
coriolis/osmorphing/redhat.py

@@ -185,11 +185,13 @@ class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
             LOG.trace(utils.get_exception_details())
             LOG.trace(utils.get_exception_details())
             self.installed_packages = []
             self.installed_packages = []
 
 
-    def _yum_install(self, package_names, enable_repos=[]):
+    def _yum_install(self, package_names):
         try:
         try:
-            yum_cmd = 'yum install %s -y%s' % (
+            yum_cmd = 'yum install %s -y %s %s' % (
                 " ".join(package_names),
                 " ".join(package_names),
-                "".join([" --enablerepo=%s" % r for r in enable_repos]))
+                "--setopt=strict=1",
+                "--setopt=skip_missing_names_on_install=0"
+            )
             self._exec_cmd_chroot(yum_cmd)
             self._exec_cmd_chroot(yum_cmd)
         except exception.CoriolisException as err:
         except exception.CoriolisException as err:
             raise exception.FailedPackageInstallationException(
             raise exception.FailedPackageInstallationException(
@@ -207,60 +209,40 @@ class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
                 error=str(err)) from err
                 error=str(err)) from err
 
 
     def _yum_clean_all(self):
     def _yum_clean_all(self):
-        self._exec_cmd_chroot("yum clean all")
+        try:
+            self._exec_cmd_chroot("yum clean all")
+        except exception.CoriolisException:
+            # On systems with no enabled repos, yum clean fails with
+            # "There are no enabled repos". This is non-fatal since
+            # there is nothing to clean.
+            LOG.warning("yum clean all failed (e.g. no enabled repos), "
+                        "continuing. Error: %s", utils.get_exception_details())
         if self._test_path('var/cache/yum'):
         if self._test_path('var/cache/yum'):
             self._exec_cmd_chroot("rm -rf /var/cache/yum")
             self._exec_cmd_chroot("rm -rf /var/cache/yum")
 
 
-    def _find_yum_repos(self, repos_to_enable=[]):
-        """
-        Looks for required repositories passed as `repos_to_enable` in
-        /etc/yum.repos.d and returns the found repository names, so they can
-        be temporarily enabled when installing packages using yum.
-
-        Yum only looks for repos in files with '.repo' extension, anything
-        else gets ignored, therefore this method should filter files by that
-        extension.
-
-        Also, yum repository names might be different in some guest releases,
-        but still be similar. Therefore, repo name substrings should ideally be
-        passed in `repos_to_enable`. For example, we might be looking for repo
-        name 'ol7_latest', but the guest has it named as 'public_ol7_latest' in
-        the repo file.
-        """
-        found_repos = []
+    def _get_repos_to_enable(self):
+        return []
 
 
-        reposdir_path = 'etc/yum.repos.d'
+    def enable_repos(self, repo_names):
+        """Enable repositories using subscription-manager for
+        Red Hat Enterprise Linux.
 
 
-        repofiles = [
-            f for f in self._list_dir(reposdir_path) if f.endswith('.repo')]
-        installed_repos = []
-        for file in repofiles:
-            path = os.path.join(reposdir_path, file)
-            try:
-                content = self._read_file_sudo(path)
-            except Exception as e:
-                LOG.warning(
-                    "Could not read yum repository file %s: %s", path, e)
-                continue
-            for line in content.splitlines():
-                m = re.match(r'^\[(.+)\]$', line)
-                if m:
-                    installed_repos.append(m.group(1))
-
-        for repo in repos_to_enable:
-            available_repos = [ir for ir in installed_repos if repo in ir]
-            available_repos.sort(key=len)
-            if available_repos:
-                found_repos.append(available_repos[0])
-            else:
-                LOG.warn(
-                    "Could not find yum repository while searching for "
-                    "repositories to enable: %s.", repo)
-
-        return found_repos
+        This method should be overridden by subclasses that use different
+        repository management tools.
+        """
+        if not repo_names:
+            return
 
 
-    def _get_repos_to_enable(self):
-        return []
+        # RHEL uses subscription-manager for all versions
+        for repo in repo_names:
+            cmd = 'subscription-manager repos --enable=%s' % repo
+            try:
+                self._exec_cmd_chroot(cmd)
+                LOG.info("Enabled repository '%s' using subscription-manager",
+                         repo)
+            except exception.CoriolisException:
+                LOG.warning(f"Failed to enable repository {repo}. "
+                            f"Error was: {utils.get_exception_details()}")
 
 
     def pre_packages_install(self, package_names):
     def pre_packages_install(self, package_names):
         super(BaseRedHatMorphingTools, self).pre_packages_install(
         super(BaseRedHatMorphingTools, self).pre_packages_install(
@@ -278,8 +260,9 @@ class BaseRedHatMorphingTools(base.BaseLinuxOSMorphingTools):
             package_names)
             package_names)
 
 
     def install_packages(self, package_names):
     def install_packages(self, package_names):
-        enable_repos = self._get_repos_to_enable()
-        self._yum_install(package_names, enable_repos)
+        repos_to_enable = self._get_repos_to_enable()
+        self.enable_repos(repos_to_enable)
+        self._yum_install(package_names)
 
 
     def uninstall_packages(self, package_names):
     def uninstall_packages(self, package_names):
         self._yum_uninstall(package_names)
         self._yum_uninstall(package_names)

+ 26 - 0
coriolis/osmorphing/rocky.py

@@ -1,12 +1,18 @@
 # Copyright 2023 Cloudbase Solutions Srl
 # Copyright 2023 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+from oslo_log import log as logging
+
+from coriolis import exception
 from coriolis.osmorphing import centos
 from coriolis.osmorphing import centos
 from coriolis.osmorphing.osdetect import rocky as rocky_osdetect
 from coriolis.osmorphing.osdetect import rocky as rocky_osdetect
+from coriolis import utils
 
 
 
 
 ROCKY_LINUX_DISTRO_IDENTIFIER = rocky_osdetect.ROCKY_LINUX_DISTRO_IDENTIFIER
 ROCKY_LINUX_DISTRO_IDENTIFIER = rocky_osdetect.ROCKY_LINUX_DISTRO_IDENTIFIER
 
 
+LOG = logging.getLogger(__name__)
+
 
 
 class BaseRockyLinuxMorphingTools(centos.BaseCentOSMorphingTools):
 class BaseRockyLinuxMorphingTools(centos.BaseCentOSMorphingTools):
 
 
@@ -19,3 +25,23 @@ class BaseRockyLinuxMorphingTools(centos.BaseCentOSMorphingTools):
             return False
             return False
         return cls._version_supported_util(
         return cls._version_supported_util(
             detected_os_info['release_version'], minimum=8)
             detected_os_info['release_version'], minimum=8)
+
+    def enable_repos(self, repo_names):
+        """Enable repositories for Rocky Linux.
+
+        Uses dnf config-manager for all Rocky Linux versions (8+).
+        """
+        if not repo_names:
+            return
+
+        # Rocky Linux only exists as version 8+, always uses dnf
+        config_manager = 'dnf config-manager'
+        for repo in repo_names:
+            cmd = '%s --set-enabled %s' % (config_manager, repo)
+            try:
+                self._exec_cmd_chroot(cmd)
+                LOG.info("Enabled repository '%s' using %s",
+                         repo, config_manager)
+            except exception.CoriolisException:
+                LOG.warning(f"Failed to enable repository {repo}. "
+                            f"Error was: {utils.get_exception_details()}")

+ 58 - 0
coriolis/tests/osmorphing/test_amazon.py

@@ -1,14 +1,36 @@
 # Copyright 2024 Cloudbase Solutions Srl
 # Copyright 2024 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+import logging
+from unittest import mock
 
 
+import ddt
+
+from coriolis import exception
 from coriolis.osmorphing import amazon
 from coriolis.osmorphing import amazon
+from coriolis.osmorphing import base
 from coriolis.tests import test_base
 from coriolis.tests import test_base
 
 
 
 
+@ddt.ddt
 class BaseAmazonLinuxOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 class BaseAmazonLinuxOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
     """Test suite for the BaseAmazonLinuxOSMorphingTools class."""
     """Test suite for the BaseAmazonLinuxOSMorphingTools class."""
 
 
+    def setUp(self):
+        super(BaseAmazonLinuxOSMorphingToolsTestCase, self).setUp()
+        self.detected_os_info = {
+            'os_type': 'linux',
+            'distribution_name': amazon.AMAZON_DISTRO_NAME_IDENTIFIER,
+            'release_version': '2',
+            'friendly_release_name': mock.sentinel.friendly_release_name,
+        }
+        self.enable_repos = ['repo1', 'repo2']
+        self.morphing_tools = amazon.BaseAmazonLinuxOSMorphingTools(
+            mock.sentinel.conn, mock.sentinel.os_root_dir,
+            mock.sentinel.os_root_dir, mock.sentinel.hypervisor,
+            mock.sentinel.event_manager, self.detected_os_info,
+            mock.sentinel.osmorphing_parameters)
+
     def test_check_os_supported(self):
     def test_check_os_supported(self):
         detected_os_info = {
         detected_os_info = {
             "distribution_name": amazon.AMAZON_DISTRO_NAME_IDENTIFIER,
             "distribution_name": amazon.AMAZON_DISTRO_NAME_IDENTIFIER,
@@ -28,3 +50,39 @@ class BaseAmazonLinuxOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             detected_os_info)
             detected_os_info)
 
 
         self.assertFalse(result)
         self.assertFalse(result)
+
+    @ddt.data(
+        # Amazon Linux 2 and earlier use yum-config-manager.
+        ('2', 'yum-config-manager --enable'),
+        # Amazon Linux 2023+ uses dnf config-manager.
+        ('2023', 'dnf config-manager --set-enabled'),
+    )
+    @ddt.unpack
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos(self, version, expected_cmd, mock_exec_cmd_chroot):
+        self.morphing_tools._version = version
+
+        self.morphing_tools.enable_repos(self.enable_repos)
+
+        mock_exec_cmd_chroot.assert_has_calls([
+            mock.call("%s repo1" % expected_cmd),
+            mock.call("%s repo2" % expected_cmd),
+        ])
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_empty(self, mock_exec_cmd_chroot):
+        self.morphing_tools.enable_repos([])
+
+        mock_exec_cmd_chroot.assert_not_called()
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_with_exception(self, mock_exec_cmd_chroot):
+        self.morphing_tools._version = '2'
+        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()
+
+        with self.assertLogs(
+                'coriolis.osmorphing.amazon', level=logging.WARN):
+            self.morphing_tools.enable_repos(['repo1'])
+
+        mock_exec_cmd_chroot.assert_called_once_with(
+            "yum-config-manager --enable repo1")

+ 60 - 0
coriolis/tests/osmorphing/test_centos.py

@@ -1,14 +1,36 @@
 # Copyright 2024 Cloudbase Solutions Srl
 # Copyright 2024 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+import logging
+from unittest import mock
 
 
+import ddt
+
+from coriolis import exception
+from coriolis.osmorphing import base
 from coriolis.osmorphing import centos
 from coriolis.osmorphing import centos
 from coriolis.tests import test_base
 from coriolis.tests import test_base
 
 
 
 
+@ddt.ddt
 class BaseCentOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 class BaseCentOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
     """Test suite for the BaseCentOSMorphingTools class."""
     """Test suite for the BaseCentOSMorphingTools class."""
 
 
+    def setUp(self):
+        super(BaseCentOSMorphingToolsTestCase, self).setUp()
+        self.detected_os_info = {
+            'os_type': 'linux',
+            'distribution_name': centos.CENTOS_DISTRO_IDENTIFIER,
+            'release_version': '7',
+            'friendly_release_name': mock.sentinel.friendly_release_name,
+        }
+        self.enable_repos = ['repo1', 'repo2']
+        self.morphing_tools = centos.BaseCentOSMorphingTools(
+            mock.sentinel.conn, mock.sentinel.os_root_dir,
+            mock.sentinel.os_root_dir, mock.sentinel.hypervisor,
+            mock.sentinel.event_manager, self.detected_os_info,
+            mock.sentinel.osmorphing_parameters)
+
     def test_check_os_supported(self):
     def test_check_os_supported(self):
         detected_os_info = {
         detected_os_info = {
             "distribution_name": centos.CENTOS_DISTRO_IDENTIFIER,
             "distribution_name": centos.CENTOS_DISTRO_IDENTIFIER,
@@ -28,3 +50,41 @@ class BaseCentOSMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             detected_os_info)
             detected_os_info)
 
 
         self.assertFalse(result)
         self.assertFalse(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'),
+    )
+    @ddt.unpack
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos(self, version, expected_cmd, mock_exec_cmd_chroot):
+        self.morphing_tools._version = version
+
+        self.morphing_tools.enable_repos(self.enable_repos)
+
+        mock_exec_cmd_chroot.assert_has_calls([
+            mock.call("%s repo1" % expected_cmd),
+            mock.call("%s repo2" % expected_cmd),
+        ])
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_empty(self, mock_exec_cmd_chroot):
+        self.morphing_tools.enable_repos([])
+
+        mock_exec_cmd_chroot.assert_not_called()
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_with_exception(self, mock_exec_cmd_chroot):
+        self.morphing_tools._version = '7'
+        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()
+
+        with self.assertLogs(
+                'coriolis.osmorphing.centos', level=logging.WARN):
+            self.morphing_tools.enable_repos(['repo1'])
+
+        mock_exec_cmd_chroot.assert_called_once_with(
+            "yum-config-manager --enable repo1")

+ 37 - 45
coriolis/tests/osmorphing/test_oracle.py

@@ -1,15 +1,19 @@
 # Copyright 2024 Cloudbase Solutions Srl
 # Copyright 2024 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+import logging
 from unittest import mock
 from unittest import mock
 
 
+import ddt
+
+from coriolis import exception
 from coriolis.osmorphing import base
 from coriolis.osmorphing import base
 from coriolis.osmorphing import oracle
 from coriolis.osmorphing import oracle
 from coriolis.osmorphing.osdetect import oracle as oracle_detect
 from coriolis.osmorphing.osdetect import oracle as oracle_detect
-from coriolis.osmorphing import redhat
 from coriolis.tests import test_base
 from coriolis.tests import test_base
 
 
 
 
+@ddt.ddt
 class BaseOracleMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 class BaseOracleMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
     """Test case for the BaseOracleMorphingTools class."""
     """Test case for the BaseOracleMorphingTools class."""
 
 
@@ -21,6 +25,7 @@ class BaseOracleMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
             'release_version': '6',
             'release_version': '6',
             'friendly_release_name': mock.sentinel.friendly_release_name,
             'friendly_release_name': mock.sentinel.friendly_release_name,
         }
         }
+        self.enable_repos = ['repo1', 'repo2']
         self.oracle_morphing_tools = oracle.BaseOracleMorphingTools(
         self.oracle_morphing_tools = oracle.BaseOracleMorphingTools(
             mock.sentinel.conn, mock.sentinel.os_root_dir,
             mock.sentinel.conn, mock.sentinel.os_root_dir,
             mock.sentinel.os_root_dir, mock.sentinel.hypervisor,
             mock.sentinel.os_root_dir, mock.sentinel.hypervisor,
@@ -41,52 +46,39 @@ class BaseOracleMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 
 
         self.assertFalse(result)
         self.assertFalse(result)
 
 
+    @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'),
+    )
+    @ddt.unpack
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
-    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_find_yum_repos')
-    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
-    def test__get_oracle_repos(self, mock_yum_install, mock_find_yum_repos,
-                               mock_exec_cmd_chroot):
-        self.oracle_morphing_tools._version = '10'
-
-        result = self.oracle_morphing_tools._get_oracle_repos()
-
-        self.assertEqual(result, mock_find_yum_repos.return_value)
-
-        mock_find_yum_repos.assert_has_calls([
-            mock.call(["ol10_baseos_latest"]),
-            mock.call([
-                "ol10_baseos_latest",
-                "ol10_appstream",
-                "ol10_addons",
-                "ol10_UEKR8"
-            ]),
+    def test_enable_repos(self, version, expected_cmd, mock_exec_cmd_chroot):
+        self.oracle_morphing_tools._version = version
+
+        self.oracle_morphing_tools.enable_repos(self.enable_repos)
+
+        mock_exec_cmd_chroot.assert_has_calls([
+            mock.call("%s repo1" % expected_cmd),
+            mock.call("%s repo2" % expected_cmd),
         ])
         ])
-        mock_yum_install.assert_called_once_with(
-            ['oraclelinux-release-el10'], mock_find_yum_repos.return_value)
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_empty(self, mock_exec_cmd_chroot):
+        self.oracle_morphing_tools.enable_repos([])
+
         mock_exec_cmd_chroot.assert_not_called()
         mock_exec_cmd_chroot.assert_not_called()
 
 
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
-    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_find_yum_repos')
-    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
-    @mock.patch.object(oracle.uuid, 'uuid4')
-    def test__get_oracle_repos_major_version_lt_8(
-            self, mock_uuid4, mock_yum_install, mock_find_yum_repos,
-            mock_exec_cmd_chroot):
-        result = self.oracle_morphing_tools._get_oracle_repos()
-
-        self.assertEqual(result, mock_find_yum_repos.return_value)
-
-        mock_find_yum_repos.assert_called_once_with([
-            "ol6_software_collections",
-            "ol6_addons",
-            "ol6_UEKR",
-            "ol6_latest"
-        ])
-        mock_yum_install.assert_not_called()
-        mock_exec_cmd_chroot.assert_has_calls([
-            mock.call(
-                "curl -L http://public-yum.oracle.com/public-yum-ol6.repo "
-                "-o /etc/yum.repos.d/%s.repo" % mock_uuid4.return_value),
-            mock.call('sed -i "s/^enabled=1$/enabled=0/g" %s' % (
-                "/etc/yum.repos.d/%s.repo" % mock_uuid4.return_value))
-        ])
+    def test_enable_repos_with_exception(self, mock_exec_cmd_chroot):
+        self.oracle_morphing_tools._version = '7'
+        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()
+
+        with self.assertLogs(
+                'coriolis.osmorphing.oracle', level=logging.WARN):
+            self.oracle_morphing_tools.enable_repos(['repo1'])
+
+        mock_exec_cmd_chroot.assert_called_once_with(
+            "yum-config-manager --enable repo1")

+ 28 - 27
coriolis/tests/osmorphing/test_redhat.py

@@ -308,11 +308,11 @@ class BaseRedHatMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 
 
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
     def test__yum_install(self, mock_exec_cmd_chroot):
     def test__yum_install(self, mock_exec_cmd_chroot):
-        self.morphing_tools._yum_install(self.package_names, self.enable_repos)
+        self.morphing_tools._yum_install(self.package_names)
 
 
         mock_exec_cmd_chroot.assert_called_once_with(
         mock_exec_cmd_chroot.assert_called_once_with(
             "yum install package1 package2 -y "
             "yum install package1 package2 -y "
-            "--enablerepo=repo1 --enablerepo=repo2"
+            "--setopt=strict=1 --setopt=skip_missing_names_on_install=0"
         )
         )
 
 
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
@@ -322,7 +322,7 @@ class BaseRedHatMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
         self.assertRaises(
         self.assertRaises(
             exception.FailedPackageInstallationException,
             exception.FailedPackageInstallationException,
             self.morphing_tools._yum_install,
             self.morphing_tools._yum_install,
-            self.package_names, self.enable_repos
+            self.package_names
         )
         )
 
 
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
     @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
@@ -366,32 +366,31 @@ class BaseRedHatMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 
 
         mock_exec_cmd_chroot.assert_called_once_with("yum clean all")
         mock_exec_cmd_chroot.assert_called_once_with("yum clean all")
 
 
-    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_list_dir')
-    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_read_file_sudo')
-    def test__find_yum_repos_found(self, mock_read_file_sudo, mock_list_dir):
-        mock_list_dir.return_value = ['file1.repo', 'file2.repo']
-        mock_read_file_sudo.return_value = '[repo1]\n[repo2]'
-        repos_to_enable = ['repo1']
-
-        result = self.morphing_tools._find_yum_repos(repos_to_enable)
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos(self, mock_exec_cmd_chroot):
+        self.morphing_tools.enable_repos(self.enable_repos)
 
 
-        mock_read_file_sudo.assert_has_calls([
-            mock.call('etc/yum.repos.d/file1.repo'),
-            mock.call('etc/yum.repos.d/file2.repo')
+        mock_exec_cmd_chroot.assert_has_calls([
+            mock.call("subscription-manager repos --enable=repo1"),
+            mock.call("subscription-manager repos --enable=repo2"),
         ])
         ])
 
 
-        self.assertEqual(result, ['repo1'])
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_empty(self, mock_exec_cmd_chroot):
+        self.morphing_tools.enable_repos([])
+
+        mock_exec_cmd_chroot.assert_not_called()
 
 
-    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_list_dir')
-    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_read_file_sudo')
-    def test__find_yum_repos_not_found(self, mock_read_file_sudo,
-                                       mock_list_dir):
-        mock_list_dir.return_value = ['file1.repo', 'file2.repo']
-        mock_read_file_sudo.return_value = '[repo1]\n[repo2]'
-        repos_to_enable = ['repo3']
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_with_exception(self, mock_exec_cmd_chroot):
+        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()
 
 
-        with self.assertLogs('coriolis.osmorphing.redhat', level=logging.WARN):
-            self.morphing_tools._find_yum_repos(repos_to_enable)
+        with self.assertLogs(
+                'coriolis.osmorphing.redhat', level=logging.WARN):
+            self.morphing_tools.enable_repos(['repo1'])
+
+        mock_exec_cmd_chroot.assert_called_once_with(
+            "subscription-manager repos --enable=repo1")
 
 
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_clean_all')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_clean_all')
@@ -433,14 +432,16 @@ class BaseRedHatMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
         mock_post_packages_install.assert_called_once_with(self.package_names)
         mock_post_packages_install.assert_called_once_with(self.package_names)
 
 
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
+    @mock.patch.object(redhat.BaseRedHatMorphingTools, 'enable_repos')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_get_repos_to_enable')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_get_repos_to_enable')
     def test_install_packages(self, mock_get_repos_to_enable,
     def test_install_packages(self, mock_get_repos_to_enable,
-                              mock_yum_install):
+                              mock_enable_repos, mock_yum_install):
         self.morphing_tools.install_packages(self.package_names)
         self.morphing_tools.install_packages(self.package_names)
 
 
         mock_get_repos_to_enable.assert_called_once()
         mock_get_repos_to_enable.assert_called_once()
-        mock_yum_install.assert_called_once_with(
-            self.package_names, mock_get_repos_to_enable.return_value)
+        mock_enable_repos.assert_called_once_with(
+            mock_get_repos_to_enable.return_value)
+        mock_yum_install.assert_called_once_with(self.package_names)
 
 
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_uninstall')
     @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_uninstall')
     def test_uninstall_packages(self, mock_yum_uninstall):
     def test_uninstall_packages(self, mock_yum_uninstall):

+ 45 - 0
coriolis/tests/osmorphing/test_rocky.py

@@ -1,7 +1,11 @@
 # Copyright 2024 Cloudbase Solutions Srl
 # Copyright 2024 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+import logging
+from unittest import mock
 
 
+from coriolis import exception
+from coriolis.osmorphing import base
 from coriolis.osmorphing import rocky
 from coriolis.osmorphing import rocky
 from coriolis.tests import test_base
 from coriolis.tests import test_base
 
 
@@ -9,6 +13,21 @@ from coriolis.tests import test_base
 class BaseRockyLinuxMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
 class BaseRockyLinuxMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
     """Test suite for the BaseRockyLinuxMorphingTools class."""
     """Test suite for the BaseRockyLinuxMorphingTools class."""
 
 
+    def setUp(self):
+        super(BaseRockyLinuxMorphingToolsTestCase, self).setUp()
+        self.detected_os_info = {
+            'os_type': 'linux',
+            'distribution_name': rocky.ROCKY_LINUX_DISTRO_IDENTIFIER,
+            'release_version': '8',
+            'friendly_release_name': mock.sentinel.friendly_release_name,
+        }
+        self.enable_repos = ['repo1', 'repo2']
+        self.morphing_tools = rocky.BaseRockyLinuxMorphingTools(
+            mock.sentinel.conn, mock.sentinel.os_root_dir,
+            mock.sentinel.os_root_dir, mock.sentinel.hypervisor,
+            mock.sentinel.event_manager, self.detected_os_info,
+            mock.sentinel.osmorphing_parameters)
+
     def test_check_os_supported(self):
     def test_check_os_supported(self):
         detected_os_info = {
         detected_os_info = {
             "distribution_name": rocky.ROCKY_LINUX_DISTRO_IDENTIFIER,
             "distribution_name": rocky.ROCKY_LINUX_DISTRO_IDENTIFIER,
@@ -29,3 +48,29 @@ class BaseRockyLinuxMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
         )
         )
 
 
         self.assertFalse(result)
         self.assertFalse(result)
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos(self, mock_exec_cmd_chroot):
+        self.morphing_tools.enable_repos(self.enable_repos)
+
+        mock_exec_cmd_chroot.assert_has_calls([
+            mock.call("dnf config-manager --set-enabled repo1"),
+            mock.call("dnf config-manager --set-enabled repo2"),
+        ])
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_empty(self, mock_exec_cmd_chroot):
+        self.morphing_tools.enable_repos([])
+
+        mock_exec_cmd_chroot.assert_not_called()
+
+    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
+    def test_enable_repos_with_exception(self, mock_exec_cmd_chroot):
+        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()
+
+        with self.assertLogs(
+                'coriolis.osmorphing.rocky', level=logging.WARN):
+            self.morphing_tools.enable_repos(['repo1'])
+
+        mock_exec_cmd_chroot.assert_called_once_with(
+            "dnf config-manager --set-enabled repo1")