# Copyright 2024 Cloudbase Solutions Srl
# All Rights Reserved.

import logging
from unittest import mock

import ddt

from coriolis import exception
from coriolis.osmorphing import base
from coriolis.osmorphing import redhat
from coriolis.tests import test_base


@ddt.ddt
class BaseRedHatMorphingToolsTestCase(test_base.CoriolisBaseTestCase):
    """Test suite for the BaseRedHatMorphingTools class."""

    def setUp(self):
        super(BaseRedHatMorphingToolsTestCase, self).setUp()
        self.detected_os_info = {
            'os_type': 'linux',
            'distribution_name': redhat.RED_HAT_DISTRO_IDENTIFIER,
            'release_version': '7',
            'friendly_release_name': mock.sentinel.friendly_release_name,
        }
        self.package_names = ['package1', 'package2']
        self.enable_repos = ['repo1', 'repo2']
        self.event_manager = mock.MagicMock()
        self.morphing_tools = redhat.BaseRedHatMorphingTools(
            mock.sentinel.conn, mock.sentinel.os_root_dir,
            mock.sentinel.os_root_dir, mock.sentinel.hypervisor,
            self.event_manager, self.detected_os_info,
            mock.sentinel.osmorphing_parameters,
            mock.sentinel.operation_timeout)
        self.morphing_tools._os_root_dir = '/root'

    @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.assertEqual(expected, result)

    def test_check_os_not_supported(self):
        self.detected_os_info['distribution_name'] = 'unsupported'

        result = redhat.BaseRedHatMorphingTools.check_os_supported(
            self.detected_os_info)

        self.assertFalse(result)

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test_disable_predictable_nic_names(self, mock_exec_cmd_chroot):
        self.morphing_tools.disable_predictable_nic_names()
        mock_exec_cmd_chroot.assert_called_once_with(
            'grubby --update-kernel=ALL --args="net.ifnames=0 biosdevname=0"')

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_get_grub2_cfg_location'
    )
    def test_get_update_grub2_command(self, mock_get_grub2_cfg_location):
        result = self.morphing_tools.get_update_grub2_command()

        mock_get_grub2_cfg_location.assert_called_once()

        self.assertEqual(
            result,
            'grub2-mkconfig -o %s' % mock_get_grub2_cfg_location.return_value
        )

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path_chroot')
    def test__get_grub2_cfg_location_bios(self, mock_test_path_chroot):
        mock_test_path_chroot.return_value = True

        result = self.morphing_tools._get_grub2_cfg_location()

        self.assertEqual(result, '/boot/grub2/grub.cfg')
        mock_test_path_chroot.assert_called_once_with(
            '/boot/grub2/grub.cfg')

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path_chroot')
    def test__get_grub2_cfg_location_uefi(self, mock_test_path_chroot):
        mock_test_path_chroot.side_effect = [False, True]

        result = self.morphing_tools._get_grub2_cfg_location()

        self.assertEqual(result, '/boot/efi/EFI/redhat/grub.cfg')
        mock_test_path_chroot.assert_has_calls([
            mock.call('/boot/grub2/grub.cfg'),
            mock.call('/boot/efi/EFI/redhat/grub.cfg')
        ])

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path_chroot')
    def test__get_grub2_cfg_location_unknown(self, mock_test_path_chroot):
        mock_test_path_chroot.return_value = False

        self.assertRaisesRegex(
            Exception,
            "could not determine grub location. boot partition not mounted?",
            self.morphing_tools._get_grub2_cfg_location
        )

        mock_test_path_chroot.assert_has_calls([
            mock.call('/boot/grub2/grub.cfg'),
            mock.call('/boot/efi/EFI/redhat/grub.cfg')
        ])

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__has_systemd(self, mock_exec_cmd_chroot):
        result = self.morphing_tools._has_systemd()

        self.assertTrue(result)
        mock_exec_cmd_chroot.assert_called_once_with("rpm -q systemd")

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__has_systemd_with_exception(self, mock_exec_cmd_chroot):
        mock_exec_cmd_chroot.side_effect = Exception()

        result = self.morphing_tools._has_systemd()

        self.assertFalse(result)

    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_write_config_file')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_read_config_file')
    def test__set_dhcp_net_config(self, mock_read_config_file,
                                  mock_write_config_file):
        ifcfgs_ethernet = [
            (mock.sentinel.ifcfg_file,
             {
                 "BOOTPROTO": "none",
                 "IPADDR": mock.sentinel.ip_address,
                 "GATEWAY": mock.sentinel.gateway,
                 "NETMASK": mock.sentinel.netmask,
                 "NETWORK": mock.sentinel.network,
             }),
        ]
        mock_read_config_file.return_value = {
            "GATEWAY": mock.sentinel.gateway,
        }

        self.morphing_tools._set_dhcp_net_config(ifcfgs_ethernet)

        mock_read_config_file.assert_called_once_with(
            "etc/sysconfig/network", check_exists=True)
        mock_write_config_file.assert_has_calls([
            mock.call(mock.sentinel.ifcfg_file,
                      {"BOOTPROTO": "dhcp", "UUID": mock.ANY}),
            mock.call("etc/sysconfig/network",
                      mock_read_config_file.return_value)
        ])

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_backup_ethernet_ifcfg_configs')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
    def test_write_nic_configs(
            self, mock_write_file_sudo, mock_backup_ethernet_ifcfg_configs):
        nics_info = [{'name': 'eth0'}, {'name': 'eth1'}]

        self.morphing_tools._write_nic_configs(nics_info)

        mock_backup_ethernet_ifcfg_configs.assert_called_once_with()
        mock_write_file_sudo.assert_has_calls([
            mock.call(
                "etc/sysconfig/network-scripts/ifcfg-eth0",
                base.IFCFG_TEMPLATE % {
                    "device_name": "eth0",
                    "nm_controlled": "no",
                },
            ),
            mock.call(
                "etc/sysconfig/network-scripts/ifcfg-eth1",
                base.IFCFG_TEMPLATE % {
                    "device_name": "eth1",
                    "nm_controlled": "no",
                },
            )
        ])

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_backup_ethernet_ifcfg_configs')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
    def test_write_nic_configs_rhel8(
            self, mock_write_file_sudo, mock_backup_ethernet_ifcfg_configs):
        self.morphing_tools._version = '8.10'
        nics_info = [{'name': 'eth0'}]

        self.morphing_tools._write_nic_configs(nics_info)

        mock_backup_ethernet_ifcfg_configs.assert_called_once_with()
        mock_write_file_sudo.assert_called_once_with(
            "etc/sysconfig/network-scripts/ifcfg-eth0",
            base.IFCFG_TEMPLATE % {
                "device_name": "eth0",
                "nm_controlled": "yes",
            },
        )

    @ddt.data(('6', 'no'), ('7.9', 'no'), ('8.10', 'yes'))
    @ddt.unpack
    def test__get_ifcfg_nm_controlled(self, release_version, expected):
        self.morphing_tools._version = release_version

        result = self.morphing_tools._get_ifcfg_nm_controlled()

        self.assertEqual(expected, result)

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_backup_ethernet_ifcfg_configs'
    )
    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_backup_nmconnection_files'
    )
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test_write_nmconnection_configs(
            self, mock_exec_cmd_chroot, mock_write_file_sudo,
            mock_backup_nmconnection_files,
            mock_backup_ethernet_ifcfg_configs):
        nics_info = [{'name': 'eth0'}]
        nmconnection_files = [
            'etc/NetworkManager/system-connections/eth0.nmconnection']

        self.morphing_tools._write_nmconnection_configs(
            nics_info, nmconnection_files)

        mock_backup_nmconnection_files.assert_called_once_with(
            nmconnection_files)
        mock_backup_ethernet_ifcfg_configs.assert_called_once_with()
        mock_write_file_sudo.assert_called_once()
        args, _ = mock_write_file_sudo.call_args
        self.assertEqual(
            args[0],
            "etc/NetworkManager/system-connections/eth0.nmconnection")
        self.assertIn("[connection]", args[1])
        self.assertIn("interface-name=eth0", args[1])
        self.assertIn("method=auto", args[1])
        self.assertIn("may-fail=false", args[1])
        mock_exec_cmd_chroot.assert_called_once_with(
            "chmod 600 /etc/NetworkManager/system-connections/"
            "eth0.nmconnection"
        )

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_backup_ethernet_ifcfg_configs'
    )
    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_backup_nmconnection_files'
    )
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test_write_nmconnection_configs_no_nics(
            self, mock_exec_cmd_chroot, mock_write_file_sudo,
            mock_backup_nmconnection_files,
            mock_backup_ethernet_ifcfg_configs):
        self.morphing_tools._write_nmconnection_configs(
            None, ['etc/NetworkManager/system-connections/eth0.nmconnection'])

        mock_backup_nmconnection_files.assert_not_called()
        mock_backup_ethernet_ifcfg_configs.assert_not_called()
        mock_write_file_sudo.assert_not_called()
        mock_exec_cmd_chroot.assert_not_called()

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__backup_nmconnection_files(self, mock_exec_cmd_chroot):
        nmconnection_files = [
            'etc/NetworkManager/system-connections/ens192.nmconnection',
        ]

        self.morphing_tools._backup_nmconnection_files(nmconnection_files)

        mock_exec_cmd_chroot.assert_called_once_with(
            'mv "etc/NetworkManager/system-connections/'
            'ens192.nmconnection" '
            '"etc/NetworkManager/system-connections/'
            'ens192.nmconnection.bak"'
        )

    @mock.patch.object(
        base.BaseLinuxOSMorphingTools,
        '_get_existing_ethernet_nmconnection_files')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__backup_nmconnection_files_fetches_files(
            self, mock_exec_cmd_chroot,
            mock_get_ethernet_nm_files):
        mock_get_ethernet_nm_files.return_value = [
            'etc/NetworkManager/system-connections/eth0.nmconnection']

        self.morphing_tools._backup_nmconnection_files()

        mock_get_ethernet_nm_files.assert_called_once_with()
        mock_exec_cmd_chroot.assert_called_once()

    @mock.patch.object(
        base.BaseLinuxOSMorphingTools,
        '_get_existing_ethernet_nmconnection_files')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__backup_nmconnection_files_no_files(
            self, mock_exec_cmd_chroot,
            mock_get_ethernet_nm_files):
        mock_get_ethernet_nm_files.return_value = []

        self.morphing_tools._backup_nmconnection_files()

        mock_exec_cmd_chroot.assert_not_called()

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_get_ifcfgs_by_type')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
    def test__backup_ethernet_ifcfg_configs(self, mock_test_path,
                                            mock_get_ifcfgs_by_type,
                                            mock_exec_cmd_chroot):
        mock_test_path.return_value = True
        mock_get_ifcfgs_by_type.return_value = [
            ("etc/sysconfig/network-scripts/ifcfg-ens33",
             {"TYPE": "Ethernet"}),
            ("etc/sysconfig/network-scripts/ifcfg-eth0",
             {"TYPE": "Ethernet"}),
        ]

        self.morphing_tools._backup_ethernet_ifcfg_configs()

        mock_get_ifcfgs_by_type.assert_called_once_with(
            "Ethernet", self.morphing_tools._NETWORK_SCRIPTS_PATH)
        mock_exec_cmd_chroot.assert_has_calls([
            mock.call(
                'mv "etc/sysconfig/network-scripts/ifcfg-ens33" '
                '"etc/sysconfig/network-scripts/ifcfg-ens33.bak"'
            ),
            mock.call(
                'mv "etc/sysconfig/network-scripts/ifcfg-eth0" '
                '"etc/sysconfig/network-scripts/ifcfg-eth0.bak"'
            ),
        ])
        self.assertEqual(mock_exec_cmd_chroot.call_count, 2)

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_get_ifcfgs_by_type')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
    def test__backup_ethernet_ifcfg_configs_no_dir(self, mock_test_path,
                                                   mock_get_ifcfgs_by_type,
                                                   mock_exec_cmd_chroot):
        mock_test_path.return_value = False

        self.morphing_tools._backup_ethernet_ifcfg_configs()

        mock_get_ifcfgs_by_type.assert_not_called()
        mock_exec_cmd_chroot.assert_not_called()

    @mock.patch.object(
        base.BaseLinuxOSMorphingTools, '_get_keyfiles_by_type')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
    def test__get_existing_ethernet_nmconnection_files(
            self, mock_test_path, mock_get_keyfiles_by_type):
        mock_test_path.return_value = True
        mock_get_keyfiles_by_type.return_value = [
            ('etc/NetworkManager/system-connections/eth0.nmconnection',
             {'type': 'ethernet'})]

        result = (
            self.morphing_tools._get_existing_ethernet_nmconnection_files())

        mock_get_keyfiles_by_type.assert_called_once_with(
            "ethernet", self.morphing_tools._NM_CONNECTIONS_PATH)
        self.assertEqual(
            result,
            ['etc/NetworkManager/system-connections/eth0.nmconnection'])

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
    def test__get_existing_ethernet_nmconnection_files_no_dir(
            self, mock_test_path):
        mock_test_path.return_value = False

        result = (
            self.morphing_tools._get_existing_ethernet_nmconnection_files())

        self.assertEqual(result, [])

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, 'disable_predictable_nic_names'
    )
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_write_nic_configs')
    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_write_nmconnection_configs'
    )
    @mock.patch.object(
        base.BaseLinuxOSMorphingTools,
        '_get_existing_ethernet_nmconnection_files',
    )
    def test_set_net_config_dhcp(
            self, mock_get_existing_ethernet_nmconnection_files,
            mock_write_nmconnection_configs,
            mock_write_nic_configs,
            mock_disable_predictable_nic_names):
        mock_get_existing_ethernet_nmconnection_files.return_value = []
        nics_info = [{
            'mac_address': mock.sentinel.mac_address,
        }]
        dhcp = True

        self.morphing_tools.set_net_config(nics_info, dhcp)

        mock_get_existing_ethernet_nmconnection_files.assert_called_once_with()
        mock_write_nmconnection_configs.assert_not_called()
        mock_disable_predictable_nic_names.assert_called_once()
        mock_write_nic_configs.assert_called_once_with(nics_info)

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, 'disable_predictable_nic_names'
    )
    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_write_nmconnection_configs'
    )
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_write_nic_configs')
    @mock.patch.object(
        base.BaseLinuxOSMorphingTools,
        '_get_existing_ethernet_nmconnection_files',
    )
    def test_set_net_config_dhcp_nmconnection(
            self, mock_get_existing_ethernet_nmconnection_files,
            mock_write_nic_configs,
            mock_write_nmconnection_configs,
            mock_disable_predictable_nic_names):
        nm_files = [
            'etc/NetworkManager/system-connections/eth0.nmconnection']
        mock_get_existing_ethernet_nmconnection_files.return_value = nm_files
        nics_info = [{
            'mac_address': mock.sentinel.mac_address,
        }]
        dhcp = True

        self.morphing_tools.set_net_config(nics_info, dhcp)

        mock_disable_predictable_nic_names.assert_called_once()
        mock_write_nmconnection_configs.assert_called_once_with(
            nics_info, nm_files)
        mock_write_nic_configs.assert_not_called()

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, 'disable_predictable_nic_names'
    )
    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_write_nmconnection_configs'
    )
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_write_nic_configs')
    @mock.patch.object(
        base.BaseLinuxOSMorphingTools,
        '_get_existing_ethernet_nmconnection_files',
    )
    def test_set_net_config_dhcp_nmconnection_no_nics(
            self, mock_get_existing_ethernet_nmconnection_files,
            mock_write_nic_configs,
            mock_write_nmconnection_configs,
            mock_disable_predictable_nic_names):
        mock_get_existing_ethernet_nmconnection_files.return_value = [
            'etc/NetworkManager/system-connections/eth0.nmconnection']

        self.morphing_tools.set_net_config(None, True)

        mock_get_existing_ethernet_nmconnection_files.assert_not_called()
        mock_disable_predictable_nic_names.assert_not_called()
        mock_write_nmconnection_configs.assert_not_called()
        mock_write_nic_configs.assert_not_called()

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd')
    @mock.patch.object(redhat.utils, 'list_ssh_dir')
    def test__comment_keys_from_ifcfg_files(
            self, mock_list_ssh_dir, mock_exec_cmd):
        keys = ['KEY1', 'KEY2']
        interfaces = ['eth0', 'eth1']
        mock_list_ssh_dir.return_value = [
            'ifcfg-eth0', 'ifcfg-eth1', 'unknown-file', 'ifcfg-eth2']

        self.morphing_tools._comment_keys_from_ifcfg_files(
            keys, interfaces=interfaces)

        mock_list_ssh_dir.assert_called_once_with(
            mock.sentinel.conn, '/root/etc/sysconfig/network-scripts')
        mock_exec_cmd.assert_has_calls([
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY1=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth0"
            ),
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY2=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth0"
            ),
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY1=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth1"
            ),
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY2=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth1"
            ),
        ])

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd')
    @mock.patch.object(redhat.utils, 'list_ssh_dir')
    def test__comment_keys_from_ifcfg_files_no_interfaces(
            self, mock_list_ssh_dir, mock_exec_cmd):
        keys = ['KEY1', 'KEY2']
        mock_list_ssh_dir.return_value = ['ifcfg-eth0', 'ifcfg-eth1']

        self.morphing_tools._comment_keys_from_ifcfg_files(keys)

        mock_list_ssh_dir.assert_called_once_with(
            mock.sentinel.conn, '/root/etc/sysconfig/network-scripts')

        mock_exec_cmd.assert_has_calls([
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY1=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth0"
            ),
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY2=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth0"
            ),
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY1=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth1"
            ),
            mock.call(
                "sudo sed -i.bak -E -e 's/^(KEY2=.*)$/# \\1/g' "
                "/root/etc/sysconfig/network-scripts/ifcfg-eth1"
            ),
        ])

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, 'disable_predictable_nic_names'
    )
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_write_nic_configs')
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_add_net_udev_rules')
    @mock.patch.object(base.BaseLinuxOSMorphingTools,
                       '_setup_network_preservation')
    def test_set_net_config_no_dhcp(
            self, mock_setup_network_preservation, mock_add_net_udev_rules,
            mock_write_nic_configs,
            mock_disable_predictable_nic_names):
        nics_info = [{
            'mac_address': mock.sentinel.mac_address,
        }]
        dhcp = False

        self.morphing_tools.set_net_config(nics_info, dhcp)

        mock_disable_predictable_nic_names.assert_not_called()
        mock_write_nic_configs.assert_not_called()
        mock_setup_network_preservation.assert_called_once()

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test_get_installed_packages(self, mock_exec_cmd_chroot):
        mock_exec_cmd_chroot.return_value = "package1\npackage2"

        self.morphing_tools.get_installed_packages()

        self.assertEqual(
            self.morphing_tools.installed_packages,
            ['package1', 'package2']
        )
        mock_exec_cmd_chroot.assert_called_once_with(
            'rpm -qa --qf "%{NAME}\\n"')

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test_get_installed_packages_none(self, mock_exec_cmd_chroot):
        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()

        with self.assertLogs(
            'coriolis.osmorphing.redhat', level=logging.DEBUG):
            self.morphing_tools.get_installed_packages()

        self.assertEqual(
            self.morphing_tools.installed_packages,
            []
        )
        mock_exec_cmd_chroot.assert_called_once_with(
            'rpm -qa --qf "%{NAME}\\n"')

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__yum_install(self, mock_exec_cmd_chroot):
        self.morphing_tools._yum_install(self.package_names)

        mock_exec_cmd_chroot.assert_called_once_with(
            "yum install package1 package2 -y "
            "--setopt=strict=1 --setopt=skip_missing_names_on_install=0"
        )

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__yum_install_with_exception(self, mock_exec_cmd_chroot):
        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()

        self.assertRaises(
            exception.FailedPackageInstallationException,
            self.morphing_tools._yum_install,
            self.package_names
        )

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__yum_uninstall(self, mock_exec_cmd_chroot):
        self.morphing_tools._yum_uninstall(self.package_names)

        mock_exec_cmd_chroot.assert_has_calls([
            mock.call("yum remove package1 -y"),
            mock.call("yum remove package2 -y")
        ])

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__yum_uninstall_with_exception(self, mock_exec_cmd_chroot):
        mock_exec_cmd_chroot.side_effect = exception.CoriolisException()

        self.assertRaises(
            exception.FailedPackageUninstallationException,
            self.morphing_tools._yum_uninstall, self.package_names
        )

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
    def test__yum_clean_all(self, mock_test_path, mock_exec_cmd_chroot):
        mock_test_path.return_value = True

        self.morphing_tools._yum_clean_all()

        mock_test_path.assert_called_once_with("var/cache/yum")
        mock_exec_cmd_chroot.assert_has_calls([
            mock.call("yum clean all"),
            mock.call("rm -rf /var/cache/yum")
        ])

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path')
    def test__yum_clean_all_path_not_exists(self, mock_test_path,
                                            mock_exec_cmd_chroot):
        mock_test_path.return_value = False

        self.morphing_tools._yum_clean_all()

        mock_exec_cmd_chroot.assert_called_once_with("yum clean all")

    @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("subscription-manager repos --enable=repo1"),
            mock.call("subscription-manager repos --enable=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.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_clean_all')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, 'pre_packages_install')
    def test_pre_packages_install(self, mock_pre_packages_install,
                                  mock_yum_clean_all, mock_yum_install):
        self.morphing_tools.installed_packages = []

        self.morphing_tools.pre_packages_install(self.package_names)

        mock_pre_packages_install.assert_called_once_with(self.package_names)
        mock_yum_clean_all.assert_called_once()
        mock_yum_install.assert_called_once_with(['grubby'])

    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_clean_all')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, 'pre_packages_install')
    def test_pre_packages_install_has_grubby(
        self, mock_pre_packages_install, mock_yum_clean_all, mock_yum_install):
        self.morphing_tools.installed_packages = ['grubby']

        self.morphing_tools.pre_packages_install(self.package_names)

        mock_pre_packages_install.assert_called_once_with(self.package_names)
        mock_yum_clean_all.assert_called_once()
        mock_yum_install.assert_not_called()

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_set_selinux_autorelabel')
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_configure_cloud_init')
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_run_dracut')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, 'post_packages_install')
    def test_post_packages_install(
            self, mock_post_packages_install, mock__run_dracut,
            mock__configure_cloud_init, mock__set_selinux_autorelabel):

        self.morphing_tools.post_packages_install(self.package_names)

        mock__configure_cloud_init.assert_called_once()
        mock__run_dracut.assert_called_once()
        mock__set_selinux_autorelabel.assert_called_once()
        mock_post_packages_install.assert_called_once_with(self.package_names)

    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_yum_install')
    @mock.patch.object(redhat.BaseRedHatMorphingTools, 'enable_repos')
    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_get_repos_to_enable')
    def test_install_packages(self, mock_get_repos_to_enable,
                              mock_enable_repos, mock_yum_install):
        self.morphing_tools.install_packages(self.package_names)

        mock_get_repos_to_enable.assert_called_once()
        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')
    def test_uninstall_packages(self, mock_yum_uninstall):
        self.morphing_tools.uninstall_packages(self.package_names)

        mock_yum_uninstall.assert_called_once_with(self.package_names)

    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')
    def test__run_dracut(self, mock_exec_cmd_chroot):
        self.morphing_tools._run_dracut()

        mock_exec_cmd_chroot.assert_called_once_with(
            "dracut --regenerate-all -f")

    @mock.patch.object(redhat.BaseRedHatMorphingTools, '_write_config_file')
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_read_config_file')
    def test__set_network_nozeroconf_config(
            self, mock_read_config_file, mock_write_config_file):
        self.morphing_tools._set_network_nozeroconf_config()

        mock_read_config_file.assert_called_once_with(
            "etc/sysconfig/network", check_exists=True)
        mock_write_config_file.assert_called_once_with(
            "etc/sysconfig/network", mock_read_config_file.return_value)

    @mock.patch.object(
        redhat.BaseRedHatMorphingTools, '_get_config_file_content'
    )
    @mock.patch.object(base.BaseLinuxOSMorphingTools, '_write_file_sudo')
    def test__write_config_file(self, mock_write_file_sudo,
                                mock_get_config_file_content):
        self.morphing_tools._write_config_file(
            mock.sentinel.chroot_path, mock.sentinel.config_data)

        mock_get_config_file_content.assert_called_once_with(
            mock.sentinel.config_data)
        mock_write_file_sudo.assert_called_once_with(
            mock.sentinel.chroot_path,
            mock_get_config_file_content.return_value
        )

    def test__get_config_file_content(self):
        config = {
            'key1': 'value1',
            'key2': 'value2'
        }
        result = self.morphing_tools._get_config_file_content(config)

        self.assertEqual(result, 'key1="value1"\nkey2="value2"\n')
