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()