diff --git a/coriolis/tests/osmorphing/__init__.py b/coriolis/tests/osmorphing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/coriolis/tests/osmorphing/osdetect/__init__.py b/coriolis/tests/osmorphing/osdetect/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/coriolis/tests/osmorphing/osdetect/test_amazon.py b/coriolis/tests/osmorphing/osdetect/test_amazon.py new file mode 100644 index 00000000..87730419 --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_amazon.py @@ -0,0 +1,38 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + + +from unittest import mock + +from coriolis.osmorphing.osdetect import amazon +from coriolis.osmorphing.osdetect import base +from coriolis.tests import test_base + + +class AmazonLinuxOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for AmazonLinuxOSDetectTools class.""" + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_get_os_release') + def test_detect_os(self, mock_get_os_release): + mock_get_os_release.return_value = { + "ID": "amzn", + "VERSION": mock.sentinel.version, + "NAME": "Amazon Linux" + } + + expected_info = { + "os_type": amazon.constants.OS_TYPE_LINUX, + "distribution_name": amazon.AMAZON_DISTRO_NAME, + "release_version": mock.sentinel.version, + "friendly_release_name": "Amazon Linux %s" % mock.sentinel.version + } + + amazon_os_detect_tools = amazon.AmazonLinuxOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + result = amazon_os_detect_tools.detect_os() + + mock_get_os_release.assert_called_once_with() + + self.assertEqual(result, expected_info) diff --git a/coriolis/tests/osmorphing/osdetect/test_base.py b/coriolis/tests/osmorphing/osdetect/test_base.py new file mode 100644 index 00000000..df1a3675 --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_base.py @@ -0,0 +1,172 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +import os +from unittest import mock + +from coriolis import exception +from coriolis.osmorphing.osdetect import base +from coriolis.tests import test_base + + +class BaseOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the BaseOSDetectTools class.""" + + @mock.patch.object(base.BaseOSDetectTools, '__abstractmethods__', set()) + def setUp(self): + super(BaseOSDetectToolsTestCase, self).setUp() + self.base_os_detect_tools = base.BaseOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + def test_returned_detected_os_info_fields(self): + self.assertRaises( + NotImplementedError, + self.base_os_detect_tools.returned_detected_os_info_fields + ) + + def test_detect_os(self): + self.assertRaises( + NotImplementedError, + self.base_os_detect_tools.detect_os + ) + + def test_set_environment(self): + self.base_os_detect_tools.set_environment(mock.sentinel.environment) + + self.assertEqual( + self.base_os_detect_tools._environment, mock.sentinel.environment + ) + + +class BaseLinuxOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the BaseLinuxOSDetectTools class.""" + + @mock.patch.object( + base.BaseLinuxOSDetectTools, '__abstractmethods__', set() + ) + def setUp(self): + super(BaseLinuxOSDetectToolsTestCase, self).setUp() + self.chroot_path = '/mock/chroot/path' + self.os_root_dir = '/mock/os/root/dir' + self.base_os_detect = base.BaseLinuxOSDetectTools( + mock.sentinel.conn, self.os_root_dir, + mock.sentinel.operation_timeout) + + def test_returned_detected_os_info_fields(self): + result = self.base_os_detect.returned_detected_os_info_fields() + + self.assertEqual( + result, base.REQUIRED_DETECTED_OS_FIELDS + ) + + @mock.patch.object(base.utils, 'read_ssh_file') + def test__read_file(self, mock_read_ssh_file): + result = self.base_os_detect._read_file(self.chroot_path) + + mocked_full_path = os.path.join( + self.base_os_detect._os_root_dir, self.chroot_path) + + mock_read_ssh_file.assert_called_once_with( + self.base_os_detect._conn, mocked_full_path) + + self.assertEqual(result, mock_read_ssh_file.return_value) + + @mock.patch.object(base.utils, 'read_ssh_ini_config_file') + def test__read_config_file(self, mock_read_ssh_ini_config): + result = self.base_os_detect._read_config_file(self.chroot_path) + + mocked_full_path = os.path.join( + self.base_os_detect._os_root_dir, self.chroot_path) + + mock_read_ssh_ini_config.assert_called_once_with( + self.base_os_detect._conn, mocked_full_path, check_exists=False) + + self.assertEqual(result, mock_read_ssh_ini_config.return_value) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_config_file') + def test__get_os_release(self, mock_read_config_file): + result = self.base_os_detect._get_os_release() + + mock_read_config_file.assert_called_once_with( + "etc/os-release", check_exists=True) + + self.assertEqual(result, mock_read_config_file.return_value) + + @mock.patch.object(base.utils, 'test_ssh_path') + def test__test_path(self, mock_test_ssh_path): + result = self.base_os_detect._test_path(self.chroot_path) + + mocked_full_path = os.path.join( + self.base_os_detect._os_root_dir, self.chroot_path) + mock_test_ssh_path.assert_called_once_with( + self.base_os_detect._conn, mocked_full_path) + + self.assertEqual(result, mock_test_ssh_path.return_value) + + @mock.patch.object(base.utils, 'exec_ssh_cmd') + def test__exec_cmd(self, mock_exec_ssh_cmd): + result = self.base_os_detect._exec_cmd(mock.sentinel.cmd, timeout=120) + + mock_exec_ssh_cmd.assert_called_once_with( + self.base_os_detect._conn, mock.sentinel.cmd, + environment=self.base_os_detect._environment, get_pty=True, + timeout=120) + + self.assertEqual(result, mock_exec_ssh_cmd.return_value) + + @mock.patch.object(base.utils, 'exec_ssh_cmd') + def test__exec_cmd_without_timeout(self, mock_exec_ssh_cmd): + result = self.base_os_detect._exec_cmd(mock.sentinel.cmd) + + mock_exec_ssh_cmd.assert_called_once_with( + self.base_os_detect._conn, mock.sentinel.cmd, + environment=self.base_os_detect._environment, get_pty=True, + timeout=self.base_os_detect._osdetect_operation_timeout) + + self.assertEqual(result, mock_exec_ssh_cmd.return_value) + + @mock.patch.object(base.utils, 'exec_ssh_cmd') + def test__exec_cmd_with_exception(self, mock_exec_ssh_cmd): + mock_exec_ssh_cmd.side_effect = exception.MinionMachineCommandTimeout + + self.assertRaises( + exception.OSMorphingSSHOperationTimeout, + self.base_os_detect._exec_cmd, + mock.sentinel.cmd + ) + + @mock.patch.object(base.utils, 'exec_ssh_cmd_chroot') + def test__exec_cmd_chroot(self, mock_exec_ssh_cmd_chroot): + result = self.base_os_detect._exec_cmd_chroot( + mock.sentinel.cmd, timeout=120) + + mock_exec_ssh_cmd_chroot.assert_called_once_with( + self.base_os_detect._conn, self.base_os_detect._os_root_dir, + mock.sentinel.cmd, environment=self.base_os_detect._environment, + get_pty=True, timeout=120) + + self.assertEqual(result, mock_exec_ssh_cmd_chroot.return_value) + + @mock.patch.object(base.utils, 'exec_ssh_cmd_chroot') + def test__exec_cmd_chroot_without_timeout(self, mock_exec_ssh_cmd_chroot): + result = self.base_os_detect._exec_cmd_chroot(mock.sentinel.cmd) + + mock_exec_ssh_cmd_chroot.assert_called_once_with( + self.base_os_detect._conn, self.base_os_detect._os_root_dir, + mock.sentinel.cmd, environment=self.base_os_detect._environment, + get_pty=True, + timeout=self.base_os_detect._osdetect_operation_timeout) + + self.assertEqual(result, mock_exec_ssh_cmd_chroot.return_value) + + @mock.patch.object(base.utils, 'exec_ssh_cmd_chroot') + def test__exec_cmd_chroot_with_exception(self, mock_exec_ssh_cmd_chroot): + mock_exec_ssh_cmd_chroot.side_effect = [ + exception.MinionMachineCommandTimeout] + + self.assertRaises( + exception.OSMorphingSSHOperationTimeout, + self.base_os_detect._exec_cmd_chroot, + mock.sentinel.cmd + ) diff --git a/coriolis/tests/osmorphing/osdetect/test_centos.py b/coriolis/tests/osmorphing/osdetect/test_centos.py new file mode 100644 index 00000000..8060c1bc --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_centos.py @@ -0,0 +1,76 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +import logging +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import centos +from coriolis.tests import test_base + + +class CentOSOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the CentOSOSDetectTools class.""" + + def setUp(self): + super(CentOSOSDetectToolsTestCase, self).setUp() + self.centos_os_detect_tools = centos.CentOSOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"CentOS Linux release 7.9 (Core)" + + expected_info = { + "os_type": centos.constants.OS_TYPE_LINUX, + "distribution_name": centos.CENTOS_DISTRO_IDENTIFIER, + "release_version": '7.9', + "friendly_release_name": "%s Version %s" % ( + centos.CENTOS_DISTRO_IDENTIFIER, '7.9') + } + + result = self.centos_os_detect_tools.detect_os() + + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") + + self.assertEqual(result, expected_info) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os_centos_stream(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"CentOS Stream release 8.3" + + expected_info = { + "os_type": centos.constants.OS_TYPE_LINUX, + "distribution_name": centos.CENTOS_STREAM_DISTRO_IDENTIFIER, + "release_version": '8.3', + "friendly_release_name": "%s Version %s" % ( + centos.CENTOS_STREAM_DISTRO_IDENTIFIER, '8.3') + } + + result = self.centos_os_detect_tools.detect_os() + + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") + + self.assertEqual(result, expected_info) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os_not_centos(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"dummy release 8.3" + + with self.assertLogs('coriolis.osmorphing.osdetect.centos', + level=logging.DEBUG): + result = self.centos_os_detect_tools.detect_os() + + self.assertEqual(result, {}) + + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") diff --git a/coriolis/tests/osmorphing/osdetect/test_coreos.py b/coriolis/tests/osmorphing/osdetect/test_coreos.py new file mode 100644 index 00000000..935762dc --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_coreos.py @@ -0,0 +1,36 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import coreos +from coriolis.tests import test_base + + +class CoreOSOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the CoreOSOSDetectTools class.""" + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_get_os_release') + def test_detect_os(self, mock_get_os_release): + mock_get_os_release.return_value = { + "ID": "coreos", + "VERSION_ID": mock.sentinel.version + } + + expected_info = { + "os_type": coreos.constants.OS_TYPE_LINUX, + "distribution_name": coreos.COREOS_DISTRO_IDENTIFIER, + "release_version": mock.sentinel.version, + "friendly_release_name": "CoreOS Linux %s" % mock.sentinel.version + } + + coreos_os_detect_tools = coreos.CoreOSOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + result = coreos_os_detect_tools.detect_os() + + mock_get_os_release.assert_called_once_with() + + self.assertEqual(result, expected_info) diff --git a/coriolis/tests/osmorphing/osdetect/test_debian.py b/coriolis/tests/osmorphing/osdetect/test_debian.py new file mode 100644 index 00000000..c1f42fcf --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_debian.py @@ -0,0 +1,88 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import debian +from coriolis.tests import test_base + + +class DebianOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the DebianOSDetectTools class.""" + + def setUp(self): + super(DebianOSDetectToolsTestCase, self).setUp() + self.debian_os_detect_tools = debian.DebianOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_config_file') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os(self, mock_read_file, mock_read_config_file, + mock_test_path): + mock_test_path.return_value = True + mock_read_config_file.return_value = { + 'DISTRIB_ID': 'Debian', + 'DISTRIB_RELEASE': '10' + } + + expected_info = { + "os_type": debian.constants.OS_TYPE_LINUX, + "distribution_name": debian.DEBIAN_DISTRO_IDENTIFIER, + "release_version": '10', + "friendly_release_name": "Debian Linux 10" + } + + result = self.debian_os_detect_tools.detect_os() + + mock_test_path.assert_called_once_with("etc/lsb-release") + mock_read_config_file.assert_called_once_with("etc/lsb-release") + mock_read_file.assert_not_called() + + self.assertEqual(result, expected_info) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_config_file') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os_debian_version( + self, mock_read_file, mock_read_config_file, mock_test_path): + mock_test_path.side_effect = [False, True] + mock_read_file.return_value = b"10\n" + + expected_info = { + "os_type": debian.constants.OS_TYPE_LINUX, + "distribution_name": debian.DEBIAN_DISTRO_IDENTIFIER, + "release_version": '10', + "friendly_release_name": "Debian Linux 10" + } + + result = self.debian_os_detect_tools.detect_os() + + mock_read_config_file.assert_not_called() + mock_test_path.assert_has_calls([ + mock.call("etc/lsb-release"), + mock.call("etc/debian_version") + ]) + mock_read_file.assert_called_once_with("etc/debian_version") + + self.assertEqual(result, expected_info) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_config_file') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + def test_detect_os_no_release(self, mock_test_path, mock_read_file, + mock_read_config_file): + mock_test_path.return_value = False + + result = self.debian_os_detect_tools.detect_os() + + self.assertEqual(result, {}) + + mock_test_path.assert_has_calls([ + mock.call("etc/lsb-release"), + mock.call("etc/debian_version") + ]) + mock_read_file.assert_not_called() + mock_read_config_file.assert_not_called() diff --git a/coriolis/tests/osmorphing/osdetect/test_manager.py b/coriolis/tests/osmorphing/osdetect/test_manager.py new file mode 100644 index 00000000..c8cdc22a --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_manager.py @@ -0,0 +1,205 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis import exception +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import manager +from coriolis.tests import test_base + + +class MockOSDetectTool(base.BaseOSDetectTools): + def returned_detected_os_info_fields(self): + return ['os_type', 'os_version'] + + def detect_os(self): + return { + 'os_type': 'mock_os_type', + 'os_version': 'mock_os_version' + } + + +class MockOSDetectToolNoInfo(base.BaseOSDetectTools): + def returned_detected_os_info_fields(cls): + pass + + def detect_os(self): + pass + + +class MockOSDetectToolMissingFields(MockOSDetectTool): + def returned_detected_os_info_fields(self): + return ['os_type', 'os_version', 'extra_field'] + + def detect_os(self): + return { + 'os_type': 'mock_os_type', + 'os_version': 'mock_os_version' + } + + +class MockOSDetectToolExtraFields(MockOSDetectTool): + def returned_detected_os_info_fields(self): + return ['os_type', 'os_version'] + + def detect_os(self): + return { + 'os_type': 'mock_os_type', + 'os_version': 'mock_os_version', + 'extra_field': 'extra_value' + } + + +class ManagerTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the Coriolis manager module.""" + + def setUp(self): + super(ManagerTestCase, self).setUp() + self.os_type = "linux" + self.os_root_dir = "/root" + self.conn = mock.MagicMock() + + def test__check_custom_os_detect_tools(self): + # Create a mock object that is an instance of BaseOSDetectTools + mock_os_detect_tool = mock.MagicMock(spec=base.BaseOSDetectTools) + + result = manager._check_custom_os_detect_tools([mock_os_detect_tool]) + + self.assertTrue(result) + + def test_check_custom_os_detect_tools_not_list(self): + self.assertRaises(exception.InvalidCustomOSDetectTools, + manager._check_custom_os_detect_tools, "not a list") + + def test_check_custom_os_detect_tools_invalid_type(self): + self.assertRaises(exception.InvalidCustomOSDetectTools, + manager._check_custom_os_detect_tools, [object()]) + + @mock.patch.object(manager, '_check_custom_os_detect_tools') + def test_detect_os_custom_tools(self, mock_check_custom_tools): + mock_os_detect_tool = MockOSDetectTool + + result = manager.detect_os( + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout, + tools_environment=mock.sentinel.tools_environment, + custom_os_detect_tools=[mock_os_detect_tool]) + + mock_check_custom_tools.assert_called_once_with([mock_os_detect_tool]) + + expected_result = { + 'os_type': 'mock_os_type', + 'os_version': 'mock_os_version' + } + + self.assertEqual(result, expected_result) + + @mock.patch.object(manager, '_check_custom_os_detect_tools') + def test_detect_os_windows(self, mock_check_custom_tools): + mock_os_detect_tool = MockOSDetectTool + self.os_type = 'windows' + + result = manager.detect_os( + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout, + custom_os_detect_tools=[mock_os_detect_tool]) + + mock_check_custom_tools.assert_called_once_with([mock_os_detect_tool]) + expected_result = { + 'os_type': 'mock_os_type', + 'os_version': 'mock_os_version' + } + + self.assertEqual(result, expected_result) + + def test_detect_os_invalid_os_type(self): + self.os_type = 'invalid_os_type' + + self.assertRaises(exception.OSDetectToolsNotFound, manager.detect_os, + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout) + + @mock.patch.object(manager, '_check_custom_os_detect_tools') + @mock.patch.object(manager.rocky.RockyLinuxOSDetectTools, 'detect_os') + @mock.patch.object(manager.redhat.RedHatOSDetectTools, 'detect_os') + @mock.patch.object(manager.centos.CentOSOSDetectTools, 'detect_os') + @mock.patch.object(manager.oracle.OracleOSDetectTools, 'detect_os') + def test_detect_os_no_detected_info( + self, mock_oracle_detect_os, mock_centos_detect_os, + mock_redhat_detect_os, mock_rocky_detect_os, + mock_check_custom_tools): + mock_rocky_detect_os.return_value = None + mock_redhat_detect_os.return_value = None + mock_centos_detect_os.return_value = None + mock_oracle_detect_os.return_value = None + mock_os_detect_tool = MockOSDetectToolNoInfo + + self.assertRaises(exception.OSDetectToolsNotFound, manager.detect_os, + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout, + custom_os_detect_tools=[mock_os_detect_tool]) + mock_check_custom_tools.assert_called_once_with([mock_os_detect_tool]) + + @mock.patch.object(manager, '_check_custom_os_detect_tools') + @mock.patch.object(manager.rocky.RockyLinuxOSDetectTools, 'detect_os') + @mock.patch.object(manager.redhat.RedHatOSDetectTools, 'detect_os') + @mock.patch.object(manager.centos.CentOSOSDetectTools, 'detect_os') + @mock.patch.object(manager.oracle.OracleOSDetectTools, 'detect_os') + def test_detect_os_invalid_detected_info( + self, mock_oracle_detect_os, mock_centos_detect_os, + mock_redhat_detect_os, mock_rocky_detect_os, + mock_check_custom_tools): + mock_rocky_detect_os.return_value = None + mock_redhat_detect_os.return_value = None + mock_centos_detect_os.return_value = None + mock_oracle_detect_os.return_value = "invalid_detected_info" + mock_os_detect_tool = MockOSDetectToolNoInfo + + self.assertRaises(exception.InvalidDetectedOSParams, manager.detect_os, + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout, + custom_os_detect_tools=[mock_os_detect_tool]) + mock_check_custom_tools.assert_called_once_with([mock_os_detect_tool]) + + @mock.patch.object(manager, '_check_custom_os_detect_tools') + @mock.patch.object(manager.rocky.RockyLinuxOSDetectTools, 'detect_os') + @mock.patch.object(manager.redhat.RedHatOSDetectTools, 'detect_os') + @mock.patch.object(manager.centos.CentOSOSDetectTools, 'detect_os') + @mock.patch.object(manager.oracle.OracleOSDetectTools, 'detect_os') + def test_detect_os_missing_detected_info_fields( + self, mock_oracle_detect_os, mock_centos_detect_os, + mock_redhat_detect_os, mock_rocky_detect_os, + mock_check_custom_tools): + mock_rocky_detect_os.return_value = None + mock_redhat_detect_os.return_value = None + mock_centos_detect_os.return_value = None + mock_oracle_detect_os.return_value = None + mock_os_detect_tool = MockOSDetectToolMissingFields + + self.assertRaises(exception.InvalidDetectedOSParams, manager.detect_os, + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout, + custom_os_detect_tools=[mock_os_detect_tool]) + mock_check_custom_tools.assert_called_once_with([mock_os_detect_tool]) + + @mock.patch.object(manager, '_check_custom_os_detect_tools') + @mock.patch.object(manager.rocky.RockyLinuxOSDetectTools, 'detect_os') + @mock.patch.object(manager.redhat.RedHatOSDetectTools, 'detect_os') + @mock.patch.object(manager.centos.CentOSOSDetectTools, 'detect_os') + @mock.patch.object(manager.oracle.OracleOSDetectTools, 'detect_os') + def test_detect_os_extra_detected_info_fields( + self, mock_oracle_detect_os, mock_centos_detect_os, + mock_redhat_detect_os, mock_rocky_detect_os, + mock_check_custom_tools): + mock_rocky_detect_os.return_value = None + mock_redhat_detect_os.return_value = None + mock_centos_detect_os.return_value = None + mock_oracle_detect_os.return_value = None + mock_os_detect_tool = MockOSDetectToolExtraFields + + self.assertRaises(exception.InvalidDetectedOSParams, manager.detect_os, + self.conn, self.os_type, self.os_root_dir, + mock.sentinel.operation_timeout, + custom_os_detect_tools=[mock_os_detect_tool]) + mock_check_custom_tools.assert_called_once_with([mock_os_detect_tool]) diff --git a/coriolis/tests/osmorphing/osdetect/test_openwrt.py b/coriolis/tests/osmorphing/osdetect/test_openwrt.py new file mode 100644 index 00000000..788eda3e --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_openwrt.py @@ -0,0 +1,39 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import openwrt +from coriolis.tests import test_base + + +class OpenWRTOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for OpenWRTOSDetectTools class.""" + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_config_file') + def test_detect_os(self, mock_read_config_file): + mock_read_config_file.return_value = { + "DISTRIB_ID": "OpenWrt", + "DISTRIB_DESCRIPTION": "OpenWrt Description", + "DISTRIB_RELEASE": mock.sentinel.version + } + + expected_info = { + "os_type": openwrt.constants.OS_TYPE_LINUX, + "distribution_name": openwrt.OPENWRT_DISTRO_IDENTIFIER, + "release_version": mock.sentinel.version, + "friendly_release_name": "OpenWrt Description Version %s" % ( + mock.sentinel.version) + } + + openwrt_os_detect_tools = openwrt.OpenWRTOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + result = openwrt_os_detect_tools.detect_os() + + mock_read_config_file.assert_called_once_with( + "etc/openwrt_release", check_exists=True) + + self.assertEqual(result, expected_info) diff --git a/coriolis/tests/osmorphing/osdetect/test_oracle.py b/coriolis/tests/osmorphing/osdetect/test_oracle.py new file mode 100644 index 00000000..9386057a --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_oracle.py @@ -0,0 +1,35 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import oracle +from coriolis.tests import test_base + + +class OracleOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the OracleOSDetectTools class.""" + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"Oracle Linux release 8.4" + + expected_info = { + "os_type": oracle.constants.OS_TYPE_LINUX, + "distribution_name": oracle.ORACLE_DISTRO_IDENTIFIER, + "release_version": '8.4', + "friendly_release_name": "Oracle Linux Version 8.4" + } + + oracle_os_detect_tools = oracle.OracleOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + result = oracle_os_detect_tools.detect_os() + mock_test_path.assert_called_once_with("etc/oracle-release") + mock_read_file.assert_called_once_with("etc/oracle-release") + + self.assertEqual(result, expected_info) diff --git a/coriolis/tests/osmorphing/osdetect/test_redhat.py b/coriolis/tests/osmorphing/osdetect/test_redhat.py new file mode 100644 index 00000000..350aaa6a --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_redhat.py @@ -0,0 +1,56 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +import logging +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import redhat +from coriolis.tests import test_base + + +class RedHatOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the RedHatOSDetectTools class.""" + + def setUp(self): + super(RedHatOSDetectToolsTestCase, self).setUp() + self.redhat_os_detect_tools = redhat.RedHatOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = ( + b"Red Hat Enterprise Linux release 8.4 (Ootpa)") + + expected_info = { + "os_type": redhat.constants.OS_TYPE_LINUX, + "distribution_name": redhat.RED_HAT_DISTRO_IDENTIFIER, + "release_version": '8.4', + "friendly_release_name": "%s Version %s" % ( + redhat.RED_HAT_DISTRO_IDENTIFIER, '8.4') + } + + result = self.redhat_os_detect_tools.detect_os() + + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") + + self.assertEqual(result, expected_info) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os_no_redhat(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"CentOS Linux release 8.4 (Ootpa)" + + with self.assertLogs('coriolis.osmorphing.osdetect.redhat', + level=logging.DEBUG): + result = self.redhat_os_detect_tools.detect_os() + + self.assertEqual(result, {}) + + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") diff --git a/coriolis/tests/osmorphing/osdetect/test_rocky.py b/coriolis/tests/osmorphing/osdetect/test_rocky.py new file mode 100644 index 00000000..b5942756 --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_rocky.py @@ -0,0 +1,53 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import rocky +from coriolis.tests import test_base + + +class RockyLinuxOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the RockyLinuxOSDetectTools class.""" + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"Rocky Linux release 8.4" + + expected_info = { + "os_type": rocky.constants.OS_TYPE_LINUX, + "distribution_name": rocky.ROCKY_LINUX_DISTRO_IDENTIFIER, + "release_version": '8.4', + "friendly_release_name": "Rocky Linux Version 8.4" + } + + rocky_os_detect_tools = rocky.RockyLinuxOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + result = rocky_os_detect_tools.detect_os() + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") + + self.assertEqual(result, expected_info) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_test_path') + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_file') + def test_detect_os_no_rocky(self, mock_read_file, mock_test_path): + mock_test_path.return_value = True + mock_read_file.return_value = b"CentOS Linux release 8.4" + + with self.assertLogs('coriolis.osmorphing.osdetect.rocky', + level="DEBUG"): + rocky_os_detect_tools = rocky.RockyLinuxOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + result = rocky_os_detect_tools.detect_os() + + self.assertEqual(result, {}) + + mock_test_path.assert_called_once_with("etc/redhat-release") + mock_read_file.assert_called_once_with("etc/redhat-release") diff --git a/coriolis/tests/osmorphing/osdetect/test_suse.py b/coriolis/tests/osmorphing/osdetect/test_suse.py new file mode 100644 index 00000000..1d19871d --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_suse.py @@ -0,0 +1,96 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import suse +from coriolis.tests import test_base + + +class SUSEOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the SUSEOSDetectTools class.""" + + def setUp(self): + super(SUSEOSDetectToolsTestCase, self).setUp() + + self.suse_os_detect_tools = suse.SUSEOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + @mock.patch.object( + base.BaseLinuxOSDetectTools, 'returned_detected_os_info_fields') + def test_returned_detected_os_info_fields(self, + mock_detected_os_info_fields): + mock_detected_os_info_fields.return_value = [ + "os_type", "distribution_name"] + + result = suse.SUSEOSDetectTools.returned_detected_os_info_fields() + + expected_fields = ["os_type", "distribution_name", + suse.DETECTED_SUSE_RELEASE_FIELD_NAME] + + self.assertEqual(result, expected_fields) + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_get_os_release') + def test_detect_os_sles(self, mock_get_os_release): + mock_get_os_release.return_value = { + "NAME": "SLES", + "VERSION_ID": suse.constants.OS_TYPE_UNKNOWN + } + + expected_info = { + "os_type": suse.constants.OS_TYPE_LINUX, + "distribution_name": suse.SLES_DISTRO_IDENTIFIER, + suse.DETECTED_SUSE_RELEASE_FIELD_NAME: "SLES", + "release_version": suse.constants.OS_TYPE_UNKNOWN, + "friendly_release_name": "SLES %s" % suse.constants.OS_TYPE_UNKNOWN + } + + result = self.suse_os_detect_tools.detect_os() + + self.assertEqual(result, expected_info) + + mock_get_os_release.assert_called_once() + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_get_os_release') + def test_detect_os_opensuse_tumbleweed(self, mock_get_os_release): + mock_get_os_release.return_value = { + "NAME": "openSUSE tumbleweed", + "VERSION_ID": suse.constants.OS_TYPE_UNKNOWN + } + + expected_info = { + "os_type": suse.constants.OS_TYPE_LINUX, + "distribution_name": suse.OPENSUSE_DISTRO_IDENTIFIER, + suse.DETECTED_SUSE_RELEASE_FIELD_NAME: "openSUSE tumbleweed", + "release_version": suse.OPENSUSE_TUMBLEWEED_VERSION_IDENTIFIER, + "friendly_release_name": "openSUSE tumbleweed" + } + + result = self.suse_os_detect_tools.detect_os() + + self.assertEqual(result, expected_info) + + mock_get_os_release.assert_called_once() + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_get_os_release') + def test_detect_os_opensuse(self, mock_get_os_release): + mock_get_os_release.return_value = { + "NAME": "openSUSE test", + "VERSION_ID": "15.3" + } + + expected_info = { + "os_type": suse.constants.OS_TYPE_LINUX, + "distribution_name": suse.OPENSUSE_DISTRO_IDENTIFIER, + suse.DETECTED_SUSE_RELEASE_FIELD_NAME: "openSUSE test", + "release_version": "15.3", + "friendly_release_name": "openSUSE 15.3" + } + + result = self.suse_os_detect_tools.detect_os() + + self.assertEqual(result, expected_info) + + mock_get_os_release.assert_called_once() diff --git a/coriolis/tests/osmorphing/osdetect/test_ubuntu.py b/coriolis/tests/osmorphing/osdetect/test_ubuntu.py new file mode 100644 index 00000000..b9c1085c --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_ubuntu.py @@ -0,0 +1,38 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +from unittest import mock + +from coriolis.osmorphing.osdetect import base +from coriolis.osmorphing.osdetect import ubuntu +from coriolis.tests import test_base + + +class UbuntuOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the UbuntuOSDetectTools class.""" + + @mock.patch.object(base.BaseLinuxOSDetectTools, '_read_config_file') + def test_detect_os(self, mock_read_config_file): + mock_read_config_file.return_value = { + "DISTRIB_ID": "Ubuntu", + "DISTRIB_RELEASE": mock.sentinel.version + } + + expected_info = { + "os_type": ubuntu.constants.OS_TYPE_LINUX, + "distribution_name": ubuntu.UBUNTU_DISTRO_IDENTIFIER, + "release_version": mock.sentinel.version, + "friendly_release_name": "Ubuntu %s" % ( + mock.sentinel.version) + } + + ubuntu_os_detect_tools = ubuntu.UbuntuOSDetectTools( + mock.sentinel.conn, mock.sentinel.os_root_dir, + mock.sentinel.operation_timeout) + + result = ubuntu_os_detect_tools.detect_os() + + mock_read_config_file.assert_called_once_with( + "etc/lsb-release", check_exists=True) + + self.assertEqual(result, expected_info) diff --git a/coriolis/tests/osmorphing/osdetect/test_windows.py b/coriolis/tests/osmorphing/osdetect/test_windows.py new file mode 100644 index 00000000..1c20c9fa --- /dev/null +++ b/coriolis/tests/osmorphing/osdetect/test_windows.py @@ -0,0 +1,202 @@ +# Copyright 2024 Cloudbase Solutions Srl +# All Rights Reserved. + +import logging +import os +from unittest import mock + +import ddt + +from coriolis import exception +from coriolis.osmorphing.osdetect import windows +from coriolis.tests import test_base + + +WIN_VERSION_PS_OUTPUT = """ +CurrentVersion : 6.3 +CurrentMajorVersionNumber : 10 +CurrentMinorVersionNumber : 0 +CurrentBuildNumber : 20348 +InstallationType : Server +ProductName : Windows Server 2022 Datacenter Evaluation +EditionID : ServerDatacenterEval +""" + +WIN_VERSION_PS_OUTPUT_MISSING_FIELDS = """ +CurrentMinorVersionNumber : 0 +CurrentBuildNumber : 20348 +InstallationType : Server +ProductName : Windows Server 2022 Datacenter Evaluation +EditionID : ServerDatacenterEval +""" + +WIN_VERSION_PS_OUTPUT_MISSING_MAJOR_VERSION = """ +CurrentVersion : 6.3 +CurrentMinorVersionNumber : 0 +CurrentBuildNumber : 20348 +InstallationType : Server +ProductName : Windows Server 2022 Datacenter Evaluation +EditionID : ServerDatacenterEval +""" + + +@ddt.ddt +class WindowsOSDetectToolsTestCase(test_base.CoriolisBaseTestCase): + """Test suite for the WindowsOSDetectTools class.""" + + def setUp(self): + super(WindowsOSDetectToolsTestCase, self).setUp() + self.conn = mock.MagicMock() + self.os_root_dir = 'C:\\' + self.conn.EOL = '\n' + + self.windows_os_detect_tools = windows.WindowsOSDetectTools( + self.conn, self.os_root_dir, mock.sentinel.operation_timeout) + + def test_returned_detected_os_info_fields(self): + expected_base_fields = [ + "os_type", + "distribution_name", + "release_version", + "friendly_release_name", + "version_number", + "edition_id", + "installation_type", + "product_name" + ] + + result = ( + windows.WindowsOSDetectTools.returned_detected_os_info_fields() + ) + + self.assertEqual(result, expected_base_fields) + + def test__load_registry_hive(self): + self.windows_os_detect_tools._load_registry_hive( + mock.sentinel.subkey, mock.sentinel.path) + + self.conn.exec_command.assert_called_once_with( + "reg.exe", ["load", mock.sentinel.subkey, mock.sentinel.path]) + + def test__unload_registry_hive(self): + self.windows_os_detect_tools._unload_registry_hive( + mock.sentinel.subkey) + + self.conn.exec_command.assert_called_once_with( + "reg.exe", ["unload", mock.sentinel.subkey]) + + def test__get_ps_fl_value(self): + result = self.windows_os_detect_tools._get_ps_fl_value( + WIN_VERSION_PS_OUTPUT, 'CurrentVersion') + + self.assertEqual(result, '6.3') + + @ddt.data( + { + "ps_output": WIN_VERSION_PS_OUTPUT, + "expected_result": ( + windows.version.LooseVersion("10.0.20348"), + "ServerDatacenterEval", + "Server", + "Windows Server 2022 Datacenter Evaluation" + ), + }, + { + "ps_output": WIN_VERSION_PS_OUTPUT_MISSING_MAJOR_VERSION, + "expected_result": ( + "6.3.20348", + "ServerDatacenterEval", + "Server", + "Windows Server 2022 Datacenter Evaluation" + ) + } + ) + @mock.patch.object(windows.WindowsOSDetectTools, '_load_registry_hive') + @mock.patch.object(windows.WindowsOSDetectTools, '_unload_registry_hive') + @mock.patch.object(windows.uuid, 'uuid4') + def test__get_image_version_info( + self, data, mock_uuid4, mock_unload_registry_hive, + mock_load_registry_hive): + self.conn.exec_ps_command.return_value = ( + data["ps_output"].replace('\n', os.linesep) + ) + + result = self.windows_os_detect_tools._get_image_version_info() + + mock_load_registry_hive.assert_called_once_with( + "HKLM\\%s" % mock_uuid4.return_value, + "%sWindows\\System32\\config\\SOFTWARE" % self.os_root_dir) + + mock_unload_registry_hive.assert_called_once_with( + "HKLM\\%s" % mock_uuid4.return_value) + + self.assertEqual(result, data["expected_result"]) + + @mock.patch.object(windows.WindowsOSDetectTools, '_load_registry_hive') + @mock.patch.object(windows.WindowsOSDetectTools, '_unload_registry_hive') + def test__get_image_version_info_with_exception( + self, mock_unload_registry_hive, + mock_load_registry_hive): + self.conn.exec_ps_command.return_value = ( + WIN_VERSION_PS_OUTPUT_MISSING_FIELDS.replace('\n', os.linesep)) + + mock_unload_registry_hive.assert_not_called() + mock_load_registry_hive.assert_not_called() + + self.assertRaises( + exception.CoriolisException, + self.windows_os_detect_tools._get_image_version_info + ) + + @ddt.data( + { + 'version_number': mock.sentinel.version_number, + 'edition_id': 'server', + 'installation_type': mock.sentinel.installation_type, + 'product_name': mock.sentinel.product_name, + 'distribution_name': windows.WINDOWS_SERVER_IDENTIFIER, + }, + { + 'version_number': mock.sentinel.version_number, + 'edition_id': 'client', + 'installation_type': mock.sentinel.installation_type, + 'product_name': mock.sentinel.product_name, + 'distribution_name': windows.WINDOWS_CLIENT_IDENTIFIER, + } + ) + @mock.patch.object(windows.WindowsOSDetectTools, '_get_image_version_info') + def test_detect_os(self, data, mock_get_image_version_info): + mock_get_image_version_info.return_value = ( + data['version_number'], + data['edition_id'], + data['installation_type'], + data['product_name'] + ) + + expected_result = { + "version_number": data['version_number'], + "edition_id": data['edition_id'], + "installation_type": data['installation_type'], + "product_name": data['product_name'], + "os_type": windows.constants.OS_TYPE_WINDOWS, + "distribution_name": data['distribution_name'], + "release_version": data['product_name'], + "friendly_release_name": "Windows %s" % data['product_name'] + } + + with self.assertLogs('coriolis.osmorphing.osdetect.windows', + level=logging.DEBUG): + result = self.windows_os_detect_tools.detect_os() + + self.assertEqual(result, expected_result) + + @mock.patch.object(windows.WindowsOSDetectTools, '_get_image_version_info') + def test_detect_os_with_exception(self, mock_get_image_version_info): + mock_get_image_version_info.side_effect = exception.CoriolisException + + with self.assertLogs('coriolis.osmorphing.osdetect.windows', + level=logging.DEBUG): + self.assertRaises(exception.CoriolisException, + self.windows_os_detect_tools.detect_os) + + mock_get_image_version_info.assert_called_once_with()