From cbb0662ab2a75406e40d1348a89d1dae8f2c1411 Mon Sep 17 00:00:00 2001 From: Claudiu Belu Date: Mon, 10 Aug 2015 11:50:34 +0300 Subject: [PATCH] Hyper-V: removes *Utils modules and unit tests Since HyperVDriver now uses the os-windows library, the *Utils classes are no longer used. Implements: blueprint add-os-win-library Change-Id: I19adf9d8ff885c99a77b9efbbbe7cda03391d852 --- .../unit/virt/hyperv/test_basevolumeutils.py | 188 ---- nova/tests/unit/virt/hyperv/test_hostutils.py | 141 --- .../unit/virt/hyperv/test_hostutilsv2.py | 30 - nova/tests/unit/virt/hyperv/test_ioutils.py | 61 -- .../virt/hyperv/test_livemigrationutils.py | 301 ------ .../unit/virt/hyperv/test_networkutils.py | 82 -- .../unit/virt/hyperv/test_networkutilsv2.py | 45 - .../unit/virt/hyperv/test_rdpconsoleutils.py | 28 - .../virt/hyperv/test_rdpconsoleutilsv2.py | 37 - .../unit/virt/hyperv/test_utilsfactory.py | 56 -- nova/tests/unit/virt/hyperv/test_vhdutils.py | 288 ------ .../tests/unit/virt/hyperv/test_vhdutilsv2.py | 244 ----- nova/tests/unit/virt/hyperv/test_vmutils.py | 918 ------------------ nova/tests/unit/virt/hyperv/test_vmutilsv2.py | 294 ------ .../unit/virt/hyperv/test_volumeutils.py | 164 ---- .../unit/virt/hyperv/test_volumeutilsv2.py | 164 ---- nova/virt/hyperv/basevolumeutils.py | 149 --- nova/virt/hyperv/constants.py | 19 - nova/virt/hyperv/hostutils.py | 120 --- nova/virt/hyperv/hostutilsv2.py | 34 - nova/virt/hyperv/ioutils.py | 73 -- nova/virt/hyperv/livemigrationutils.py | 252 ----- nova/virt/hyperv/networkutils.py | 68 -- nova/virt/hyperv/networkutilsv2.py | 63 -- nova/virt/hyperv/rdpconsoleutils.py | 21 - nova/virt/hyperv/rdpconsoleutilsv2.py | 31 - nova/virt/hyperv/utilsfactory.py | 80 -- nova/virt/hyperv/vhdutils.py | 212 ---- nova/virt/hyperv/vhdutilsv2.py | 247 ----- nova/virt/hyperv/vmutils.py | 870 ----------------- nova/virt/hyperv/vmutilsv2.py | 346 ------- nova/virt/hyperv/volumeutils.py | 122 --- nova/virt/hyperv/volumeutilsv2.py | 132 --- tests-py3.txt | 13 - 34 files changed, 5893 deletions(-) delete mode 100644 nova/tests/unit/virt/hyperv/test_basevolumeutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_hostutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_hostutilsv2.py delete mode 100644 nova/tests/unit/virt/hyperv/test_ioutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_livemigrationutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_networkutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_networkutilsv2.py delete mode 100644 nova/tests/unit/virt/hyperv/test_rdpconsoleutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_rdpconsoleutilsv2.py delete mode 100644 nova/tests/unit/virt/hyperv/test_utilsfactory.py delete mode 100644 nova/tests/unit/virt/hyperv/test_vhdutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_vhdutilsv2.py delete mode 100644 nova/tests/unit/virt/hyperv/test_vmutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_vmutilsv2.py delete mode 100644 nova/tests/unit/virt/hyperv/test_volumeutils.py delete mode 100644 nova/tests/unit/virt/hyperv/test_volumeutilsv2.py delete mode 100644 nova/virt/hyperv/basevolumeutils.py delete mode 100644 nova/virt/hyperv/hostutils.py delete mode 100644 nova/virt/hyperv/hostutilsv2.py delete mode 100644 nova/virt/hyperv/ioutils.py delete mode 100644 nova/virt/hyperv/livemigrationutils.py delete mode 100644 nova/virt/hyperv/networkutils.py delete mode 100644 nova/virt/hyperv/networkutilsv2.py delete mode 100644 nova/virt/hyperv/rdpconsoleutils.py delete mode 100644 nova/virt/hyperv/rdpconsoleutilsv2.py delete mode 100644 nova/virt/hyperv/utilsfactory.py delete mode 100644 nova/virt/hyperv/vhdutils.py delete mode 100644 nova/virt/hyperv/vhdutilsv2.py delete mode 100644 nova/virt/hyperv/vmutils.py delete mode 100644 nova/virt/hyperv/vmutilsv2.py delete mode 100644 nova/virt/hyperv/volumeutils.py delete mode 100644 nova/virt/hyperv/volumeutilsv2.py diff --git a/nova/tests/unit/virt/hyperv/test_basevolumeutils.py b/nova/tests/unit/virt/hyperv/test_basevolumeutils.py deleted file mode 100644 index 75c24afa1b3a..000000000000 --- a/nova/tests/unit/virt/hyperv/test_basevolumeutils.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova import test -from nova.virt.hyperv import basevolumeutils - - -def _exception_thrower(): - raise Exception("Testing exception handling.") - - -class BaseVolumeUtilsTestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V BaseVolumeUtils class.""" - - _FAKE_COMPUTER_NAME = "fake_computer_name" - _FAKE_DOMAIN_NAME = "fake_domain_name" - _FAKE_INITIATOR_NAME = "fake_initiator_name" - _FAKE_INITIATOR_IQN_NAME = "iqn.1991-05.com.microsoft:fake_computer_name" - _FAKE_DISK_PATH = 'fake_path DeviceID="123\\\\2"' - _FAKE_MOUNT_DEVICE = '/dev/fake/mount' - _FAKE_DEVICE_NAME = '/dev/fake/path' - _FAKE_SWAP = {'device_name': _FAKE_DISK_PATH} - - def setUp(self): - self._volutils = basevolumeutils.BaseVolumeUtils() - self._volutils._conn_wmi = mock.MagicMock() - self._volutils._conn_cimv2 = mock.MagicMock() - - super(BaseVolumeUtilsTestCase, self).setUp() - - def test_get_iscsi_initiator_ok(self): - self._check_get_iscsi_initiator( - mock.MagicMock(return_value=mock.sentinel.FAKE_KEY), - self._FAKE_INITIATOR_NAME) - - def test_get_iscsi_initiator_exception(self): - initiator_name = "%(iqn)s.%(domain)s" % { - 'iqn': self._FAKE_INITIATOR_IQN_NAME, - 'domain': self._FAKE_DOMAIN_NAME - } - - self._check_get_iscsi_initiator(_exception_thrower, initiator_name) - - def _check_get_iscsi_initiator(self, winreg_method, expected): - mock_computer = mock.MagicMock() - mock_computer.name = self._FAKE_COMPUTER_NAME - mock_computer.Domain = self._FAKE_DOMAIN_NAME - self._volutils._conn_cimv2.Win32_ComputerSystem.return_value = [ - mock_computer] - - with mock.patch.object(basevolumeutils, - '_winreg', create=True) as mock_winreg: - mock_winreg.OpenKey = winreg_method - mock_winreg.QueryValueEx = mock.MagicMock(return_value=[expected]) - - initiator_name = self._volutils.get_iscsi_initiator() - self.assertEqual(expected, initiator_name) - - @mock.patch.object(basevolumeutils, 'driver') - def test_volume_in_mapping(self, mock_driver): - mock_driver.block_device_info_get_mapping.return_value = [ - {'mount_device': self._FAKE_MOUNT_DEVICE}] - mock_driver.block_device_info_get_swap = mock.MagicMock( - return_value=self._FAKE_SWAP) - mock_driver.block_device_info_get_ephemerals = mock.MagicMock( - return_value=[{'device_name': self._FAKE_DEVICE_NAME}]) - - mock_driver.swap_is_usable = mock.MagicMock(return_value=True) - - self.assertTrue(self._volutils.volume_in_mapping( - self._FAKE_MOUNT_DEVICE, mock.sentinel.FAKE_BLOCK_DEVICE_INFO)) - - def test_get_drive_number_from_disk_path(self): - fake_disk_path = ( - '\\\\WIN-I5BTVHOIFGK\\root\\virtualization\\v2:Msvm_DiskDrive.' - 'CreationClassName="Msvm_DiskDrive",DeviceID="Microsoft:353B3BE8-' - '310C-4cf4-839E-4E1B14616136\\\\1",SystemCreationClassName=' - '"Msvm_ComputerSystem",SystemName="WIN-I5BTVHOIFGK"') - expected_disk_number = 1 - - ret_val = self._volutils._get_drive_number_from_disk_path( - fake_disk_path) - - self.assertEqual(expected_disk_number, ret_val) - - def test_get_drive_number_not_found(self): - fake_disk_path = 'fake_disk_path' - - ret_val = self._volutils._get_drive_number_from_disk_path( - fake_disk_path) - - self.assertFalse(ret_val) - - @mock.patch.object(basevolumeutils.BaseVolumeUtils, - "_get_drive_number_from_disk_path") - def test_get_session_id_from_mounted_disk(self, mock_get_session_id): - mock_get_session_id.return_value = mock.sentinel.FAKE_DEVICE_NUMBER - mock_initiator_session = self._create_initiator_session() - mock_ses_class = self._volutils._conn_wmi.MSiSCSIInitiator_SessionClass - mock_ses_class.return_value = [mock_initiator_session] - - session_id = self._volutils.get_session_id_from_mounted_disk( - self._FAKE_DISK_PATH) - - self.assertEqual(mock.sentinel.FAKE_SESSION_ID, session_id) - - def test_get_devices_for_target(self): - init_session = self._create_initiator_session() - mock_ses_class = self._volutils._conn_wmi.MSiSCSIInitiator_SessionClass - mock_ses_class.return_value = [init_session] - devices = self._volutils._get_devices_for_target( - mock.sentinel.FAKE_IQN) - - self.assertEqual(init_session.Devices, devices) - - def test_get_devices_for_target_not_found(self): - mock_ses_class = self._volutils._conn_wmi.MSiSCSIInitiator_SessionClass - mock_ses_class.return_value = [] - devices = self._volutils._get_devices_for_target( - mock.sentinel.FAKE_IQN) - - self.assertEqual(0, len(devices)) - - @mock.patch.object(basevolumeutils.BaseVolumeUtils, - '_get_devices_for_target') - def test_get_device_number_for_target(self, fake_get_devices): - init_session = self._create_initiator_session() - fake_get_devices.return_value = init_session.Devices - mock_ses_class = self._volutils._conn_wmi.MSiSCSIInitiator_SessionClass - mock_ses_class.return_value = [init_session] - device_number = self._volutils.get_device_number_for_target( - mock.sentinel.FAKE_IQN, mock.sentinel.FAKE_LUN) - - self.assertEqual(mock.sentinel.FAKE_DEVICE_NUMBER, device_number) - - @mock.patch.object(basevolumeutils.BaseVolumeUtils, - '_get_devices_for_target') - def test_get_target_lun_count(self, fake_get_devices): - init_session = self._create_initiator_session() - # Only disk devices are being counted. - disk_device = mock.Mock(DeviceType=self._volutils._FILE_DEVICE_DISK) - init_session.Devices.append(disk_device) - fake_get_devices.return_value = init_session.Devices - - lun_count = self._volutils.get_target_lun_count( - mock.sentinel.FAKE_IQN) - - self.assertEqual(1, lun_count) - - @mock.patch.object(basevolumeutils.BaseVolumeUtils, - "_get_drive_number_from_disk_path") - def test_get_target_from_disk_path(self, mock_get_session_id): - mock_get_session_id.return_value = mock.sentinel.FAKE_DEVICE_NUMBER - init_sess = self._create_initiator_session() - mock_ses_class = self._volutils._conn_wmi.MSiSCSIInitiator_SessionClass - mock_ses_class.return_value = [init_sess] - - (target_name, scsi_lun) = self._volutils.get_target_from_disk_path( - self._FAKE_DISK_PATH) - - self.assertEqual(mock.sentinel.FAKE_TARGET_NAME, target_name) - self.assertEqual(mock.sentinel.FAKE_LUN, scsi_lun) - - def _create_initiator_session(self): - device = mock.MagicMock() - device.ScsiLun = mock.sentinel.FAKE_LUN - device.DeviceNumber = mock.sentinel.FAKE_DEVICE_NUMBER - device.TargetName = mock.sentinel.FAKE_TARGET_NAME - init_session = mock.MagicMock() - init_session.Devices = [device] - init_session.SessionId = mock.sentinel.FAKE_SESSION_ID - - return init_session diff --git a/nova/tests/unit/virt/hyperv/test_hostutils.py b/nova/tests/unit/virt/hyperv/test_hostutils.py deleted file mode 100644 index 0d6b641cf7a6..000000000000 --- a/nova/tests/unit/virt/hyperv/test_hostutils.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova import test -from nova.virt.hyperv import constants -from nova.virt.hyperv import hostutils - - -class FakeCPUSpec(object): - """Fake CPU Spec for unit tests.""" - - Architecture = mock.sentinel.cpu_arch - Name = mock.sentinel.cpu_name - Manufacturer = mock.sentinel.cpu_man - NumberOfCores = mock.sentinel.cpu_cores - NumberOfLogicalProcessors = mock.sentinel.cpu_procs - - -class HostUtilsTestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V hostutils class.""" - - _FAKE_MEMORY_TOTAL = 1024 - _FAKE_MEMORY_FREE = 512 - _FAKE_DISK_SIZE = 1024 - _FAKE_DISK_FREE = 512 - _FAKE_VERSION_GOOD = '6.2.0' - _FAKE_VERSION_BAD = '6.1.9' - - def setUp(self): - self._hostutils = hostutils.HostUtils() - self._hostutils._conn_cimv2 = mock.MagicMock() - - super(HostUtilsTestCase, self).setUp() - - @mock.patch('nova.virt.hyperv.hostutils.ctypes') - def test_get_host_tick_count64(self, mock_ctypes): - tick_count64 = "100" - mock_ctypes.windll.kernel32.GetTickCount64.return_value = tick_count64 - response = self._hostutils.get_host_tick_count64() - self.assertEqual(tick_count64, response) - - def test_get_cpus_info(self): - cpu = mock.MagicMock(spec=FakeCPUSpec) - self._hostutils._conn_cimv2.query.return_value = [cpu] - cpu_list = self._hostutils.get_cpus_info() - self.assertEqual([cpu._mock_children], cpu_list) - - def test_get_memory_info(self): - memory = mock.MagicMock() - type(memory).TotalVisibleMemorySize = mock.PropertyMock( - return_value=self._FAKE_MEMORY_TOTAL) - type(memory).FreePhysicalMemory = mock.PropertyMock( - return_value=self._FAKE_MEMORY_FREE) - - self._hostutils._conn_cimv2.query.return_value = [memory] - total_memory, free_memory = self._hostutils.get_memory_info() - - self.assertEqual(self._FAKE_MEMORY_TOTAL, total_memory) - self.assertEqual(self._FAKE_MEMORY_FREE, free_memory) - - def test_get_volume_info(self): - disk = mock.MagicMock() - type(disk).Size = mock.PropertyMock(return_value=self._FAKE_DISK_SIZE) - type(disk).FreeSpace = mock.PropertyMock( - return_value=self._FAKE_DISK_FREE) - - self._hostutils._conn_cimv2.query.return_value = [disk] - (total_memory, free_memory) = self._hostutils.get_volume_info( - mock.sentinel.FAKE_DRIVE) - - self.assertEqual(self._FAKE_DISK_SIZE, total_memory) - self.assertEqual(self._FAKE_DISK_FREE, free_memory) - - def test_check_min_windows_version_true(self): - self._test_check_min_windows_version(self._FAKE_VERSION_GOOD, True) - - def test_check_min_windows_version_false(self): - self._test_check_min_windows_version(self._FAKE_VERSION_BAD, False) - - def _test_check_min_windows_version(self, version, expected): - os = mock.MagicMock() - os.Version = version - self._hostutils._conn_cimv2.Win32_OperatingSystem.return_value = [os] - self.assertEqual(expected, - self._hostutils.check_min_windows_version(6, 2)) - - def _test_host_power_action(self, action): - fake_win32 = mock.MagicMock() - fake_win32.Win32Shutdown = mock.MagicMock() - - self._hostutils._conn_cimv2.Win32_OperatingSystem.return_value = [ - fake_win32] - - if action == constants.HOST_POWER_ACTION_SHUTDOWN: - self._hostutils.host_power_action(action) - fake_win32.Win32Shutdown.assert_called_with( - self._hostutils._HOST_FORCED_SHUTDOWN) - elif action == constants.HOST_POWER_ACTION_REBOOT: - self._hostutils.host_power_action(action) - fake_win32.Win32Shutdown.assert_called_with( - self._hostutils._HOST_FORCED_REBOOT) - else: - self.assertRaises(NotImplementedError, - self._hostutils.host_power_action, action) - - def test_host_shutdown(self): - self._test_host_power_action(constants.HOST_POWER_ACTION_SHUTDOWN) - - def test_host_reboot(self): - self._test_host_power_action(constants.HOST_POWER_ACTION_REBOOT) - - def test_host_startup(self): - self._test_host_power_action(constants.HOST_POWER_ACTION_STARTUP) - - def test_get_supported_vm_types_2012_r2(self): - with mock.patch.object(self._hostutils, - 'check_min_windows_version') as mock_check_win: - mock_check_win.return_value = True - result = self._hostutils.get_supported_vm_types() - self.assertEqual([constants.IMAGE_PROP_VM_GEN_1, - constants.IMAGE_PROP_VM_GEN_2], result) - - def test_get_supported_vm_types(self): - with mock.patch.object(self._hostutils, - 'check_min_windows_version') as mock_check_win: - mock_check_win.return_value = False - result = self._hostutils.get_supported_vm_types() - self.assertEqual([constants.IMAGE_PROP_VM_GEN_1], result) diff --git a/nova/tests/unit/virt/hyperv/test_hostutilsv2.py b/nova/tests/unit/virt/hyperv/test_hostutilsv2.py deleted file mode 100644 index b2e426bf71b7..000000000000 --- a/nova/tests/unit/virt/hyperv/test_hostutilsv2.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2015 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova import test -from nova.virt.hyperv import hostutilsv2 - - -class HostUtilsV2TestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V hostutilsv2 class.""" - - def setUp(self): - self._hostutils = hostutilsv2.HostUtilsV2() - self._hostutils._conn_cimv2 = mock.MagicMock() - self._hostutils._conn_virt = mock.MagicMock() - - super(HostUtilsV2TestCase, self).setUp() diff --git a/nova/tests/unit/virt/hyperv/test_ioutils.py b/nova/tests/unit/virt/hyperv/test_ioutils.py deleted file mode 100644 index b412ed4331fa..000000000000 --- a/nova/tests/unit/virt/hyperv/test_ioutils.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License.import mock - -import mock - -import os - -from nova import test -from nova.virt.hyperv import ioutils - - -class IOThreadTestCase(test.NoDBTestCase): - _FAKE_SRC = r'fake_source_file' - _FAKE_DEST = r'fake_dest_file' - _FAKE_MAX_BYTES = 1 - - def setUp(self): - self._iothread = ioutils.IOThread( - self._FAKE_SRC, self._FAKE_DEST, self._FAKE_MAX_BYTES) - super(IOThreadTestCase, self).setUp() - - @mock.patch('__builtin__.open') - @mock.patch('os.rename') - @mock.patch('os.path.exists') - @mock.patch('os.remove') - def test_copy(self, fake_remove, fake_exists, fake_rename, fake_open): - fake_data = 'a' - fake_src = mock.Mock() - fake_dest = mock.Mock() - - fake_src.read.return_value = fake_data - fake_dest.tell.return_value = 0 - fake_exists.return_value = True - - mock_context_manager = mock.MagicMock() - fake_open.return_value = mock_context_manager - mock_context_manager.__enter__.side_effect = [fake_src, fake_dest] - self._iothread._stopped.isSet = mock.Mock(side_effect=[False, True]) - - self._iothread._copy() - - fake_dest.seek.assert_called_once_with(0, os.SEEK_END) - fake_dest.write.assert_called_once_with(fake_data) - fake_dest.close.assert_called_once_with() - fake_rename.assert_called_once_with( - self._iothread._dest, self._iothread._dest_archive) - fake_remove.assert_called_once_with( - self._iothread._dest_archive) - self.assertEqual(3, fake_open.call_count) diff --git a/nova/tests/unit/virt/hyperv/test_livemigrationutils.py b/nova/tests/unit/virt/hyperv/test_livemigrationutils.py deleted file mode 100644 index 65e2bbb0d7e8..000000000000 --- a/nova/tests/unit/virt/hyperv/test_livemigrationutils.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova import exception -from nova import test -from nova.virt.hyperv import livemigrationutils -from nova.virt.hyperv import vmutils - - -class LiveMigrationUtilsTestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V LiveMigrationUtils class.""" - - _FAKE_VM_NAME = 'fake_vm_name' - _FAKE_RET_VAL = 0 - - _RESOURCE_TYPE_VHD = 31 - _RESOURCE_TYPE_DISK = 17 - _RESOURCE_SUB_TYPE_VHD = 'Microsoft:Hyper-V:Virtual Hard Disk' - _RESOURCE_SUB_TYPE_DISK = 'Microsoft:Hyper-V:Physical Disk Drive' - - def setUp(self): - self.liveutils = livemigrationutils.LiveMigrationUtils() - self.liveutils._vmutils = mock.MagicMock() - self.liveutils._volutils = mock.MagicMock() - - self._conn = mock.MagicMock() - self.liveutils._get_conn_v2 = mock.MagicMock(return_value=self._conn) - - super(LiveMigrationUtilsTestCase, self).setUp() - - def test_check_live_migration_config(self): - mock_migr_svc = self._conn.Msvm_VirtualSystemMigrationService()[0] - - vsmssd = mock.MagicMock() - vsmssd.EnableVirtualSystemMigration = True - mock_migr_svc.associators.return_value = [vsmssd] - mock_migr_svc.MigrationServiceListenerIPAdressList.return_value = [ - mock.sentinel.FAKE_HOST] - - self.liveutils.check_live_migration_config() - self.assertTrue(mock_migr_svc.associators.called) - - def test_get_vm(self): - expected_vm = mock.MagicMock() - mock_conn_v2 = mock.MagicMock() - mock_conn_v2.Msvm_ComputerSystem.return_value = [expected_vm] - - found_vm = self.liveutils._get_vm(mock_conn_v2, self._FAKE_VM_NAME) - - self.assertEqual(expected_vm, found_vm) - - def test_get_vm_duplicate(self): - mock_vm = mock.MagicMock() - mock_conn_v2 = mock.MagicMock() - mock_conn_v2.Msvm_ComputerSystem.return_value = [mock_vm, mock_vm] - - self.assertRaises(vmutils.HyperVException, self.liveutils._get_vm, - mock_conn_v2, self._FAKE_VM_NAME) - - def test_get_vm_not_found(self): - mock_conn_v2 = mock.MagicMock() - mock_conn_v2.Msvm_ComputerSystem.return_value = [] - - self.assertRaises(exception.InstanceNotFound, self.liveutils._get_vm, - mock_conn_v2, self._FAKE_VM_NAME) - - @mock.patch.object(livemigrationutils.LiveMigrationUtils, - '_destroy_planned_vm') - def test_check_existing_planned_vm_found(self, mock_destroy_planned_vm): - mock_vm = mock.MagicMock() - mock_v2 = mock.MagicMock() - mock_v2.Msvm_PlannedComputerSystem.return_value = [mock_vm] - self.liveutils._check_existing_planned_vm(mock_v2, mock_vm) - - mock_destroy_planned_vm.assert_called_once_with(mock_v2, mock_vm) - - @mock.patch.object(livemigrationutils.LiveMigrationUtils, - '_destroy_planned_vm') - def test_check_existing_planned_vm_none(self, mock_destroy_planned_vm): - mock_v2 = mock.MagicMock() - mock_v2.Msvm_PlannedComputerSystem.return_value = [] - self.liveutils._check_existing_planned_vm(mock_v2, mock.MagicMock()) - - self.assertFalse(mock_destroy_planned_vm.called) - - def test_create_remote_planned_vm(self): - mock_vsmsd = self._conn.query()[0] - mock_vm = mock.MagicMock() - mock_v2 = mock.MagicMock() - mock_v2.Msvm_PlannedComputerSystem.return_value = [mock_vm] - - migr_svc = self._conn.Msvm_VirtualSystemMigrationService()[0] - migr_svc.MigrateVirtualSystemToHost.return_value = ( - self._FAKE_RET_VAL, mock.sentinel.FAKE_JOB_PATH) - - resulted_vm = self.liveutils._create_remote_planned_vm( - self._conn, mock_v2, mock_vm, [mock.sentinel.FAKE_REMOTE_IP_ADDR], - mock.sentinel.FAKE_HOST) - - self.assertEqual(mock_vm, resulted_vm) - - migr_svc.MigrateVirtualSystemToHost.assert_called_once_with( - ComputerSystem=mock_vm.path_.return_value, - DestinationHost=mock.sentinel.FAKE_HOST, - MigrationSettingData=mock_vsmsd.GetText_.return_value) - - def test_get_physical_disk_paths(self): - ide_path = {mock.sentinel.IDE_PATH: mock.sentinel.IDE_HOST_RESOURCE} - scsi_path = {mock.sentinel.SCSI_PATH: mock.sentinel.SCSI_HOST_RESOURCE} - ide_ctrl = self.liveutils._vmutils.get_vm_ide_controller.return_value - scsi_ctrl = self.liveutils._vmutils.get_vm_scsi_controller.return_value - mock_get_controller_paths = ( - self.liveutils._vmutils.get_controller_volume_paths) - - mock_get_controller_paths.side_effect = [ide_path, scsi_path] - - result = self.liveutils._get_physical_disk_paths(mock.sentinel.VM_NAME) - - expected = dict(ide_path) - expected.update(scsi_path) - self.assertDictContainsSubset(expected, result) - calls = [mock.call(ide_ctrl), mock.call(scsi_ctrl)] - mock_get_controller_paths.assert_has_calls(calls) - - def test_get_physical_disk_paths_no_ide(self): - scsi_path = {mock.sentinel.SCSI_PATH: mock.sentinel.SCSI_HOST_RESOURCE} - scsi_ctrl = self.liveutils._vmutils.get_vm_scsi_controller.return_value - mock_get_controller_paths = ( - self.liveutils._vmutils.get_controller_volume_paths) - - self.liveutils._vmutils.get_vm_ide_controller.return_value = None - mock_get_controller_paths.return_value = scsi_path - - result = self.liveutils._get_physical_disk_paths(mock.sentinel.VM_NAME) - - self.assertEqual(scsi_path, result) - mock_get_controller_paths.assert_called_once_with(scsi_ctrl) - - @mock.patch.object(livemigrationutils.volumeutilsv2, 'VolumeUtilsV2') - def test_get_remote_disk_data(self, mock_vol_utils_class): - mock_vol_utils_remote = mock_vol_utils_class.return_value - mock_vm_utils = mock.MagicMock() - disk_paths = { - mock.sentinel.FAKE_RASD_PATH: mock.sentinel.FAKE_DISK_PATH} - self.liveutils._volutils.get_target_from_disk_path.return_value = ( - mock.sentinel.FAKE_IQN, mock.sentinel.FAKE_LUN) - mock_vol_utils_remote.get_device_number_for_target.return_value = ( - mock.sentinel.FAKE_DEV_NUM) - mock_vm_utils.get_mounted_disk_by_drive_number.return_value = ( - mock.sentinel.FAKE_DISK_PATH) - - disk_paths = self.liveutils._get_remote_disk_data( - mock_vm_utils, disk_paths, mock.sentinel.FAKE_HOST) - - self.liveutils._volutils.get_target_from_disk_path.assert_called_with( - mock.sentinel.FAKE_DISK_PATH) - mock_vol_utils_remote.get_device_number_for_target.assert_called_with( - mock.sentinel.FAKE_IQN, mock.sentinel.FAKE_LUN) - mock_vm_utils.get_mounted_disk_by_drive_number.assert_called_once_with( - mock.sentinel.FAKE_DEV_NUM) - - self.assertEqual( - {mock.sentinel.FAKE_RASD_PATH: mock.sentinel.FAKE_DISK_PATH}, - disk_paths) - - def test_update_planned_vm_disk_resources(self): - mock_vm_utils = mock.MagicMock() - - self._prepare_vm_mocks(self._RESOURCE_TYPE_DISK, - self._RESOURCE_SUB_TYPE_DISK) - mock_vm = self._conn.Msvm_ComputerSystem.return_value[0] - sasd = mock_vm.associators()[0].associators()[0] - - mock_vsmsvc = self._conn.Msvm_VirtualSystemManagementService()[0] - - self.liveutils._update_planned_vm_disk_resources( - mock_vm_utils, self._conn, mock_vm, mock.sentinel.FAKE_VM_NAME, - {sasd.path.return_value.RelPath: mock.sentinel.FAKE_RASD_PATH}) - - mock_vsmsvc.ModifyResourceSettings.assert_called_once_with( - ResourceSettings=[sasd.GetText_.return_value]) - - def test_get_vhd_setting_data(self): - self._prepare_vm_mocks(self._RESOURCE_TYPE_VHD, - self._RESOURCE_SUB_TYPE_VHD) - mock_vm = self._conn.Msvm_ComputerSystem.return_value[0] - mock_sasd = mock_vm.associators()[0].associators()[0] - - vhd_sds = self.liveutils._get_vhd_setting_data(mock_vm) - self.assertEqual([mock_sasd.GetText_.return_value], vhd_sds) - - def test_live_migrate_vm_helper(self): - mock_conn_local = mock.MagicMock() - mock_vm = mock.MagicMock() - mock_vsmsd = mock_conn_local.query()[0] - - mock_vsmsvc = mock_conn_local.Msvm_VirtualSystemMigrationService()[0] - mock_vsmsvc.MigrateVirtualSystemToHost.return_value = ( - self._FAKE_RET_VAL, mock.sentinel.FAKE_JOB_PATH) - - self.liveutils._live_migrate_vm( - mock_conn_local, mock_vm, None, - [mock.sentinel.FAKE_REMOTE_IP_ADDR], - mock.sentinel.FAKE_RASD_PATH, mock.sentinel.FAKE_HOST) - - mock_vsmsvc.MigrateVirtualSystemToHost.assert_called_once_with( - ComputerSystem=mock_vm.path_.return_value, - DestinationHost=mock.sentinel.FAKE_HOST, - MigrationSettingData=mock_vsmsd.GetText_.return_value, - NewResourceSettingData=mock.sentinel.FAKE_RASD_PATH) - - @mock.patch.object(livemigrationutils, 'vmutilsv2') - def test_live_migrate_vm(self, mock_vm_utils): - mock_vm_utils_remote = mock_vm_utils.VMUtilsV2.return_value - mock_vm = self._get_vm() - - mock_migr_svc = self._conn.Msvm_VirtualSystemMigrationService()[0] - mock_migr_svc.MigrationServiceListenerIPAddressList = [ - mock.sentinel.FAKE_REMOTE_IP_ADDR] - - # patches, call and assertions. - with mock.patch.multiple( - self.liveutils, - _destroy_planned_vm=mock.DEFAULT, - _get_physical_disk_paths=mock.DEFAULT, - _get_remote_disk_data=mock.DEFAULT, - _create_remote_planned_vm=mock.DEFAULT, - _update_planned_vm_disk_resources=mock.DEFAULT, - _get_vhd_setting_data=mock.DEFAULT, - _live_migrate_vm=mock.DEFAULT): - - disk_paths = { - mock.sentinel.FAKE_IDE_PATH: mock.sentinel.FAKE_SASD_RESOURCE} - self.liveutils._get_physical_disk_paths.return_value = disk_paths - - mock_disk_paths = [mock.sentinel.FAKE_DISK_PATH] - self.liveutils._get_remote_disk_data.return_value = ( - mock_disk_paths) - - self.liveutils._create_remote_planned_vm.return_value = mock_vm - - self.liveutils.live_migrate_vm(mock.sentinel.FAKE_VM_NAME, - mock.sentinel.FAKE_HOST) - - self.liveutils._get_remote_disk_data.assert_called_once_with( - mock_vm_utils_remote, disk_paths, mock.sentinel.FAKE_HOST) - - self.liveutils._create_remote_planned_vm.assert_called_once_with( - self._conn, self._conn, mock_vm, - [mock.sentinel.FAKE_REMOTE_IP_ADDR], mock.sentinel.FAKE_HOST) - - mocked_method = self.liveutils._update_planned_vm_disk_resources - mocked_method.assert_called_once_with( - mock_vm_utils_remote, self._conn, mock_vm, - mock.sentinel.FAKE_VM_NAME, mock_disk_paths) - - self.liveutils._live_migrate_vm.assert_called_once_with( - self._conn, mock_vm, mock_vm, - [mock.sentinel.FAKE_REMOTE_IP_ADDR], - self.liveutils._get_vhd_setting_data.return_value, - mock.sentinel.FAKE_HOST) - - def _prepare_vm_mocks(self, resource_type, resource_sub_type): - mock_vm_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - vm = self._get_vm() - self._conn.Msvm_PlannedComputerSystem.return_value = [vm] - mock_vm_svc.DestroySystem.return_value = (mock.sentinel.FAKE_JOB_PATH, - self._FAKE_RET_VAL) - mock_vm_svc.ModifyResourceSettings.return_value = ( - None, mock.sentinel.FAKE_JOB_PATH, self._FAKE_RET_VAL) - - sasd = mock.MagicMock() - other_sasd = mock.MagicMock() - sasd.ResourceType = resource_type - sasd.ResourceSubType = resource_sub_type - sasd.HostResource = [mock.sentinel.FAKE_SASD_RESOURCE] - sasd.path.return_value.RelPath = mock.sentinel.FAKE_DISK_PATH - - vm_settings = mock.MagicMock() - vm.associators.return_value = [vm_settings] - vm_settings.associators.return_value = [sasd, other_sasd] - - def _get_vm(self): - mock_vm = mock.MagicMock() - self._conn.Msvm_ComputerSystem.return_value = [mock_vm] - mock_vm.path_.return_value = mock.sentinel.FAKE_VM_PATH - return mock_vm diff --git a/nova/tests/unit/virt/hyperv/test_networkutils.py b/nova/tests/unit/virt/hyperv/test_networkutils.py deleted file mode 100644 index 281df29833ed..000000000000 --- a/nova/tests/unit/virt/hyperv/test_networkutils.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova import test -from nova.virt.hyperv import networkutils -from nova.virt.hyperv import vmutils - - -class NetworkUtilsTestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V NetworkUtils class.""" - - _FAKE_PORT = {'Name': mock.sentinel.FAKE_PORT_NAME} - _FAKE_RET_VALUE = 0 - - _MSVM_VIRTUAL_SWITCH = 'Msvm_VirtualSwitch' - - def setUp(self): - self._networkutils = networkutils.NetworkUtils() - self._networkutils._conn = mock.MagicMock() - - super(NetworkUtilsTestCase, self).setUp() - - def test_get_external_vswitch(self): - mock_vswitch = mock.MagicMock() - mock_vswitch.path_.return_value = mock.sentinel.FAKE_VSWITCH_PATH - getattr(self._networkutils._conn, - self._MSVM_VIRTUAL_SWITCH).return_value = [mock_vswitch] - - switch_path = self._networkutils.get_external_vswitch( - mock.sentinel.FAKE_VSWITCH_NAME) - - self.assertEqual(mock.sentinel.FAKE_VSWITCH_PATH, switch_path) - - def test_get_external_vswitch_not_found(self): - self._networkutils._conn.Msvm_VirtualEthernetSwitch.return_value = [] - - self.assertRaises(vmutils.HyperVException, - self._networkutils.get_external_vswitch, - mock.sentinel.FAKE_VSWITCH_NAME) - - def test_get_external_vswitch_no_name(self): - mock_vswitch = mock.MagicMock() - mock_vswitch.path_.return_value = mock.sentinel.FAKE_VSWITCH_PATH - - mock_ext_port = self._networkutils._conn.Msvm_ExternalEthernetPort()[0] - self._prepare_external_port(mock_vswitch, mock_ext_port) - - switch_path = self._networkutils.get_external_vswitch(None) - self.assertEqual(mock.sentinel.FAKE_VSWITCH_PATH, switch_path) - - def _prepare_external_port(self, mock_vswitch, mock_ext_port): - mock_lep = mock_ext_port.associators()[0] - mock_lep.associators.return_value = [mock_vswitch] - - def test_create_vswitch_port(self): - svc = self._networkutils._conn.Msvm_VirtualSwitchManagementService()[0] - svc.CreateSwitchPort.return_value = ( - self._FAKE_PORT, self._FAKE_RET_VALUE) - - port = self._networkutils.create_vswitch_port( - mock.sentinel.FAKE_VSWITCH_PATH, mock.sentinel.FAKE_PORT_NAME) - - svc.CreateSwitchPort.assert_called_once_with( - Name=mock.ANY, FriendlyName=mock.sentinel.FAKE_PORT_NAME, - ScopeOfResidence="", VirtualSwitch=mock.sentinel.FAKE_VSWITCH_PATH) - self.assertEqual(self._FAKE_PORT, port) - - def test_vswitch_port_needed(self): - self.assertTrue(self._networkutils.vswitch_port_needed()) diff --git a/nova/tests/unit/virt/hyperv/test_networkutilsv2.py b/nova/tests/unit/virt/hyperv/test_networkutilsv2.py deleted file mode 100644 index 1038e8868270..000000000000 --- a/nova/tests/unit/virt/hyperv/test_networkutilsv2.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova.tests.unit.virt.hyperv import test_networkutils -from nova.virt.hyperv import networkutilsv2 - - -class NetworkUtilsV2TestCase(test_networkutils.NetworkUtilsTestCase): - """Unit tests for the Hyper-V NetworkUtilsV2 class.""" - - _MSVM_VIRTUAL_SWITCH = 'Msvm_VirtualEthernetSwitch' - - def setUp(self): - super(NetworkUtilsV2TestCase, self).setUp() - self._networkutils = networkutilsv2.NetworkUtilsV2() - self._networkutils._conn = mock.MagicMock() - - def _prepare_external_port(self, mock_vswitch, mock_ext_port): - mock_lep = mock_ext_port.associators()[0] - mock_lep1 = mock_lep.associators()[0] - mock_esw = mock_lep1.associators()[0] - mock_esw.associators.return_value = [mock_vswitch] - - def test_create_vswitch_port(self): - self.assertRaises( - NotImplementedError, - self._networkutils.create_vswitch_port, - mock.sentinel.FAKE_VSWITCH_PATH, - mock.sentinel.FAKE_PORT_NAME) - - def test_vswitch_port_needed(self): - self.assertFalse(self._networkutils.vswitch_port_needed()) diff --git a/nova/tests/unit/virt/hyperv/test_rdpconsoleutils.py b/nova/tests/unit/virt/hyperv/test_rdpconsoleutils.py deleted file mode 100644 index 98d4484b61d1..000000000000 --- a/nova/tests/unit/virt/hyperv/test_rdpconsoleutils.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from nova import test -from nova.virt.hyperv import rdpconsoleutils - - -class RDPConsoleUtilsTestCase(test.NoDBTestCase): - def setUp(self): - self._rdpconsoleutils = rdpconsoleutils.RDPConsoleUtils() - super(RDPConsoleUtilsTestCase, self).setUp() - - def test_get_rdp_console_port(self): - listener_port = self._rdpconsoleutils.get_rdp_console_port() - - self.assertEqual(self._rdpconsoleutils._DEFAULT_HYPERV_RDP_PORT, - listener_port) diff --git a/nova/tests/unit/virt/hyperv/test_rdpconsoleutilsv2.py b/nova/tests/unit/virt/hyperv/test_rdpconsoleutilsv2.py deleted file mode 100644 index bcdfaf92f088..000000000000 --- a/nova/tests/unit/virt/hyperv/test_rdpconsoleutilsv2.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova import test -from nova.virt.hyperv import rdpconsoleutilsv2 - - -class RDPConsoleUtilsV2TestCase(test.NoDBTestCase): - _FAKE_RDP_PORT = 1000 - - def setUp(self): - self._rdpconsoleutils = rdpconsoleutilsv2.RDPConsoleUtilsV2() - self._rdpconsoleutils._conn = mock.MagicMock() - - super(RDPConsoleUtilsV2TestCase, self).setUp() - - def test_get_rdp_console_port(self): - conn = self._rdpconsoleutils._conn - mock_rdp_setting_data = conn.Msvm_TerminalServiceSettingData()[0] - mock_rdp_setting_data.ListenerPort = self._FAKE_RDP_PORT - - listener_port = self._rdpconsoleutils.get_rdp_console_port() - - self.assertEqual(self._FAKE_RDP_PORT, listener_port) diff --git a/nova/tests/unit/virt/hyperv/test_utilsfactory.py b/nova/tests/unit/virt/hyperv/test_utilsfactory.py deleted file mode 100644 index b13913bd19c6..000000000000 --- a/nova/tests/unit/virt/hyperv/test_utilsfactory.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2014 Cloudbase Solutions SRL -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Unit tests for the Hyper-V utils factory. -""" - -import mock -from oslo_config import cfg - -from nova import test -from nova.virt.hyperv import utilsfactory -from nova.virt.hyperv import volumeutils -from nova.virt.hyperv import volumeutilsv2 - -CONF = cfg.CONF - - -class TestHyperVUtilsFactory(test.NoDBTestCase): - def test_get_volumeutils_v2(self): - self._test_returned_class(expected_class=volumeutilsv2.VolumeUtilsV2, - os_supports_v2=True) - - def test_get_volumeutils_v1(self): - self._test_returned_class(expected_class=volumeutils.VolumeUtils) - - def test_get_volumeutils_force_v1_and_not_min_version(self): - self._test_returned_class(expected_class=volumeutils.VolumeUtils, - force_v1=True) - - @mock.patch.object(utilsfactory, 'CONF') - def _test_returned_class(self, mock_CONF, expected_class, force_v1=False, - os_supports_v2=False): - # NOTE(claudiub): temporary change, in order for unit tests to pass. - # force_hyperv_utils_v1 CONF flag does not exist anymore. - # utilsfactory and its test cases will be removed next commit. - mock_CONF.hyperv.force_volumeutils_v1 = force_v1 - with mock.patch.object( - utilsfactory.utils, - 'check_min_windows_version') as mock_check_min_windows_version: - mock_check_min_windows_version.return_value = os_supports_v2 - - actual_class = type(utilsfactory.get_volumeutils()) - self.assertEqual(actual_class, expected_class) diff --git a/nova/tests/unit/virt/hyperv/test_vhdutils.py b/nova/tests/unit/virt/hyperv/test_vhdutils.py deleted file mode 100644 index 659dd5ef2220..000000000000 --- a/nova/tests/unit/virt/hyperv/test_vhdutils.py +++ /dev/null @@ -1,288 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from oslo_utils import units - -from nova import test -from nova.virt.hyperv import constants -from nova.virt.hyperv import vhdutils -from nova.virt.hyperv import vmutils - - -class VHDUtilsBaseTestCase(test.NoDBTestCase): - "Base Class unit test classes of Hyper-V VHD Utils classes." - - _FAKE_VHD_PATH = "C:\\fake_path.vhdx" - _FAKE_PARENT_PATH = "C:\\fake_parent_path.vhdx" - _FAKE_FORMAT = 3 - _FAKE_TYPE = 3 - _FAKE_MAX_INTERNAL_SIZE = units.Gi - _FAKE_DYNAMIC_BLK_SIZE = 2097152 - _FAKE_BAD_TYPE = 5 - - _FAKE_JOB_PATH = 'fake_job_path' - _FAKE_RET_VAL = 0 - _FAKE_VHD_INFO_XML = ( - """ - -33554432 - - -Virtual Hard Disk Setting Data - - -Setting Data for a Virtual Hard Disk. - - -fake_path.vhdx - - -%(format)s - - -52794B89-AC06-4349-AC57-486CAAD52F69 - - -4096 - - -%(max_internal_size)s - - -%(parent_path)s - - -%(path)s - - -4096 - - -%(type)s - -""" % {'path': _FAKE_VHD_PATH, - 'parent_path': _FAKE_PARENT_PATH, - 'format': _FAKE_FORMAT, - 'max_internal_size': _FAKE_MAX_INTERNAL_SIZE, - 'type': _FAKE_TYPE}) - - -class VHDUtilsTestCase(VHDUtilsBaseTestCase): - """Unit tests for the Hyper-V VHDUtils class.""" - - def setUp(self): - super(VHDUtilsTestCase, self).setUp() - self._vhdutils = vhdutils.VHDUtils() - self._vhdutils._conn = mock.MagicMock() - self._vhdutils._vmutils = mock.MagicMock() - - self._fake_vhd_info = { - 'ParentPath': self._FAKE_PARENT_PATH, - 'MaxInternalSize': self._FAKE_MAX_INTERNAL_SIZE, - 'Type': self._FAKE_TYPE} - - def test_validate_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.ValidateVirtualHardDisk.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.validate_vhd(self._FAKE_VHD_PATH) - mock_img_svc.ValidateVirtualHardDisk.assert_called_once_with( - Path=self._FAKE_VHD_PATH) - - def test_get_vhd_info(self): - self._mock_get_vhd_info() - vhd_info = self._vhdutils.get_vhd_info(self._FAKE_VHD_PATH) - self.assertEqual(self._fake_vhd_info, vhd_info) - - def _mock_get_vhd_info(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.GetVirtualHardDiskInfo.return_value = ( - self._FAKE_VHD_INFO_XML, self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - def test_create_dynamic_vhd(self): - self._vhdutils.get_vhd_info = mock.MagicMock( - return_value={'Format': self._FAKE_FORMAT}) - - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.CreateDynamicVirtualHardDisk.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.create_dynamic_vhd(self._FAKE_VHD_PATH, - self._FAKE_MAX_INTERNAL_SIZE, - constants.DISK_FORMAT_VHD) - - mock_img_svc.CreateDynamicVirtualHardDisk.assert_called_once_with( - Path=self._FAKE_VHD_PATH, - MaxInternalSize=self._FAKE_MAX_INTERNAL_SIZE) - self._vhdutils._vmutils.check_ret_val.assert_called_once_with( - self._FAKE_RET_VAL, self._FAKE_JOB_PATH) - - def test_reconnect_parent_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.ReconnectParentVirtualHardDisk.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.reconnect_parent_vhd(self._FAKE_VHD_PATH, - self._FAKE_PARENT_PATH) - mock_img_svc.ReconnectParentVirtualHardDisk.assert_called_once_with( - ChildPath=self._FAKE_VHD_PATH, - ParentPath=self._FAKE_PARENT_PATH, - Force=True) - self._vhdutils._vmutils.check_ret_val.assert_called_once_with( - self._FAKE_RET_VAL, self._FAKE_JOB_PATH) - - def test_merge_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.MergeVirtualHardDisk.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.merge_vhd(self._FAKE_VHD_PATH, self._FAKE_VHD_PATH) - - mock_img_svc.MergeVirtualHardDisk.assert_called_once_with( - SourcePath=self._FAKE_VHD_PATH, - DestinationPath=self._FAKE_VHD_PATH) - self._vhdutils._vmutils.check_ret_val.assert_called_once_with( - self._FAKE_RET_VAL, self._FAKE_JOB_PATH) - - def test_resize_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.ExpandVirtualHardDisk.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.get_internal_vhd_size_by_file_size = mock.MagicMock( - return_value=self._FAKE_MAX_INTERNAL_SIZE) - - self._vhdutils.resize_vhd(self._FAKE_VHD_PATH, - self._FAKE_MAX_INTERNAL_SIZE) - - mock_img_svc.ExpandVirtualHardDisk.assert_called_once_with( - Path=self._FAKE_VHD_PATH, - MaxInternalSize=self._FAKE_MAX_INTERNAL_SIZE) - self._vhdutils._vmutils.check_ret_val.assert_called_once_with( - self._FAKE_RET_VAL, self._FAKE_JOB_PATH) - - def _mocked_get_internal_vhd_size(self, root_vhd_size, vhd_type): - mock_get_vhd_info = mock.MagicMock(return_value={'Type': vhd_type}) - mock_get_blk_size = mock.MagicMock( - return_value=self._FAKE_DYNAMIC_BLK_SIZE) - with mock.patch.multiple(self._vhdutils, - get_vhd_info=mock_get_vhd_info, - _get_vhd_dynamic_blk_size=mock_get_blk_size): - - return self._vhdutils.get_internal_vhd_size_by_file_size( - None, root_vhd_size) - - def test_create_differencing_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.CreateDifferencingVirtualHardDisk.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.create_differencing_vhd(self._FAKE_VHD_PATH, - self._FAKE_PARENT_PATH) - - mock_img_svc.CreateDifferencingVirtualHardDisk.assert_called_once_with( - Path=self._FAKE_VHD_PATH, - ParentPath=self._FAKE_PARENT_PATH) - - def test_get_internal_vhd_size_by_file_size_fixed(self): - root_vhd_size = 1 * 1024 ** 3 - real_size = self._mocked_get_internal_vhd_size( - root_vhd_size, constants.VHD_TYPE_FIXED) - - expected_vhd_size = 1 * 1024 ** 3 - 512 - self.assertEqual(expected_vhd_size, real_size) - - def test_get_internal_vhd_size_by_file_size_dynamic(self): - root_vhd_size = 20 * 1024 ** 3 - real_size = self._mocked_get_internal_vhd_size( - root_vhd_size, constants.VHD_TYPE_DYNAMIC) - - expected_vhd_size = 20 * 1024 ** 3 - 43008 - self.assertEqual(expected_vhd_size, real_size) - - def test_get_internal_vhd_size_by_file_size_differencing(self): - # For differencing images, the internal size of the parent vhd - # is returned - vhdutil = vhdutils.VHDUtils() - root_vhd_size = 20 * 1024 ** 3 - vhdutil.get_vhd_info = mock.MagicMock() - vhdutil.get_vhd_parent_path = mock.MagicMock() - vhdutil.get_vhd_parent_path.return_value = self._FAKE_VHD_PATH - vhdutil.get_vhd_info.side_effect = [ - {'Type': 4}, {'Type': constants.VHD_TYPE_DYNAMIC}] - - vhdutil._get_vhd_dynamic_blk_size = mock.MagicMock() - vhdutil._get_vhd_dynamic_blk_size.return_value = 2097152 - - real_size = vhdutil.get_internal_vhd_size_by_file_size(None, - root_vhd_size) - expected_vhd_size = 20 * 1024 ** 3 - 43008 - self.assertEqual(expected_vhd_size, real_size) - - def test_get_vhd_format_vhdx(self): - with mock.patch('nova.virt.hyperv.vhdutils.open', - mock.mock_open(read_data=vhdutils.VHDX_SIGNATURE), - create=True): - - format = self._vhdutils.get_vhd_format(self._FAKE_VHD_PATH) - - self.assertEqual(constants.DISK_FORMAT_VHDX, format) - - def test_get_vhd_format_vhd(self): - with mock.patch('nova.virt.hyperv.vhdutils.open', - mock.mock_open(), - create=True) as mock_open: - f = mock_open.return_value - f.tell.return_value = 1024 - readdata = ['notthesig', vhdutils.VHD_SIGNATURE] - - def read(*args): - for content in readdata: - yield content - - f.read.side_effect = read() - - format = self._vhdutils.get_vhd_format(self._FAKE_VHD_PATH) - - self.assertEqual(constants.DISK_FORMAT_VHD, format) - - def test_get_vhd_format_invalid_format(self): - with mock.patch('nova.virt.hyperv.vhdutils.open', - mock.mock_open(read_data='invalid'), - create=True) as mock_open: - f = mock_open.return_value - f.tell.return_value = 1024 - - self.assertRaises(vmutils.HyperVException, - self._vhdutils.get_vhd_format, - self._FAKE_VHD_PATH) - - def test_get_vhd_format_zero_length_file(self): - with mock.patch('nova.virt.hyperv.vhdutils.open', - mock.mock_open(read_data=''), - create=True) as mock_open: - f = mock_open.return_value - f.tell.return_value = 0 - - self.assertRaises(vmutils.HyperVException, - self._vhdutils.get_vhd_format, - self._FAKE_VHD_PATH) - - f.seek.assert_called_once_with(0, 2) - - def test_get_supported_vhd_format(self): - fmt = self._vhdutils.get_best_supported_vhd_format() - self.assertEqual(constants.DISK_FORMAT_VHD, fmt) diff --git a/nova/tests/unit/virt/hyperv/test_vhdutilsv2.py b/nova/tests/unit/virt/hyperv/test_vhdutilsv2.py deleted file mode 100644 index 765d1ddb785b..000000000000 --- a/nova/tests/unit/virt/hyperv/test_vhdutilsv2.py +++ /dev/null @@ -1,244 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova.tests.unit.virt.hyperv import test_vhdutils -from nova.virt.hyperv import constants -from nova.virt.hyperv import vhdutilsv2 -from nova.virt.hyperv import vmutils - - -class VHDUtilsV2TestCase(test_vhdutils.VHDUtilsBaseTestCase): - """Unit tests for the Hyper-V VHDUtilsV2 class.""" - - _FAKE_BLOCK_SIZE = 33554432 - _FAKE_LOG_SIZE = 1048576 - _FAKE_LOGICAL_SECTOR_SIZE = 4096 - _FAKE_METADATA_SIZE = 1048576 - _FAKE_PHYSICAL_SECTOR_SIZE = 4096 - - def setUp(self): - super(VHDUtilsV2TestCase, self).setUp() - self._vhdutils = vhdutilsv2.VHDUtilsV2() - self._vhdutils._conn = mock.MagicMock() - self._vhdutils._vmutils = mock.MagicMock() - - self._fake_file_handle = mock.MagicMock() - - self._fake_vhd_info = { - 'Path': self._FAKE_VHD_PATH, - 'ParentPath': self._FAKE_PARENT_PATH, - 'Format': self._FAKE_FORMAT, - 'MaxInternalSize': self._FAKE_MAX_INTERNAL_SIZE, - 'Type': self._FAKE_TYPE, - 'BlockSize': self._FAKE_BLOCK_SIZE, - 'LogicalSectorSize': self._FAKE_LOGICAL_SECTOR_SIZE, - 'PhysicalSectorSize': self._FAKE_PHYSICAL_SECTOR_SIZE} - - def _mock_get_vhd_info(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.GetVirtualHardDiskSettingData.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL, self._FAKE_VHD_INFO_XML) - - def test_get_vhd_info(self): - self._mock_get_vhd_info() - vhd_info = self._vhdutils.get_vhd_info(self._FAKE_VHD_PATH) - - self.assertEqual(self._FAKE_VHD_PATH, vhd_info['Path']) - self.assertEqual(self._FAKE_PARENT_PATH, vhd_info['ParentPath']) - self.assertEqual(self._FAKE_FORMAT, vhd_info['Format']) - self.assertEqual(self._FAKE_MAX_INTERNAL_SIZE, - vhd_info['MaxInternalSize']) - self.assertEqual(self._FAKE_TYPE, vhd_info['Type']) - - def test_get_vhd_info_no_parent(self): - fake_vhd_xml_no_parent = self._FAKE_VHD_INFO_XML.replace( - self._FAKE_PARENT_PATH, "") - - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.GetVirtualHardDiskSettingData.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL, fake_vhd_xml_no_parent) - - vhd_info = self._vhdutils.get_vhd_info(self._FAKE_VHD_PATH) - - self.assertEqual(self._FAKE_VHD_PATH, vhd_info['Path']) - self.assertIsNone(vhd_info['ParentPath']) - self.assertEqual(self._FAKE_FORMAT, vhd_info['Format']) - self.assertEqual(self._FAKE_MAX_INTERNAL_SIZE, - vhd_info['MaxInternalSize']) - self.assertEqual(self._FAKE_TYPE, vhd_info['Type']) - - def test_create_dynamic_vhd(self): - self._vhdutils.get_vhd_info = mock.MagicMock( - return_value={'Format': self._FAKE_FORMAT}) - - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.CreateVirtualHardDisk.return_value = (self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - - self._vhdutils.create_dynamic_vhd(self._FAKE_VHD_PATH, - self._FAKE_MAX_INTERNAL_SIZE, - constants.DISK_FORMAT_VHDX) - - self.assertTrue(mock_img_svc.CreateVirtualHardDisk.called) - - def test_create_differencing_vhd(self): - self._vhdutils.get_vhd_info = mock.MagicMock( - return_value={'ParentPath': self._FAKE_PARENT_PATH, - 'Format': self._FAKE_FORMAT}) - - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.CreateVirtualHardDisk.return_value = (self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - - self._vhdutils.create_differencing_vhd(self._FAKE_VHD_PATH, - self._FAKE_PARENT_PATH) - - self.assertTrue(mock_img_svc.CreateVirtualHardDisk.called) - - def test_reconnect_parent_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - fake_new_parent_path = 'fake_new_parent_path' - - self._vhdutils._get_vhd_info_xml = mock.MagicMock( - return_value=self._FAKE_VHD_INFO_XML) - - mock_img_svc.SetVirtualHardDiskSettingData.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vhdutils.reconnect_parent_vhd(self._FAKE_VHD_PATH, - fake_new_parent_path) - - expected_virt_disk_data = self._FAKE_VHD_INFO_XML.replace( - self._FAKE_PARENT_PATH, fake_new_parent_path) - mock_img_svc.SetVirtualHardDiskSettingData.assert_called_once_with( - VirtualDiskSettingData=expected_virt_disk_data) - - def test_reconnect_parent_vhd_exception(self): - # Test that reconnect_parent_vhd raises an exception if the - # vhd info XML does not contain the ParentPath property. - fake_vhd_info_xml = self._FAKE_VHD_INFO_XML.replace('ParentPath', - 'FakeParentPath') - self._vhdutils._get_vhd_info_xml = mock.Mock( - return_value=fake_vhd_info_xml) - - self.assertRaises(vmutils.HyperVException, - self._vhdutils.reconnect_parent_vhd, - self._FAKE_VHD_PATH, - mock.sentinel.new_parent_path) - - def test_resize_vhd(self): - mock_img_svc = self._vhdutils._conn.Msvm_ImageManagementService()[0] - mock_img_svc.ResizeVirtualHardDisk.return_value = (self._FAKE_JOB_PATH, - self._FAKE_RET_VAL) - self._vhdutils.get_internal_vhd_size_by_file_size = mock.MagicMock( - return_value=self._FAKE_MAX_INTERNAL_SIZE) - - self._vhdutils.resize_vhd(self._FAKE_VHD_PATH, - self._FAKE_MAX_INTERNAL_SIZE) - - mock_img_svc.ResizeVirtualHardDisk.assert_called_once_with( - Path=self._FAKE_VHD_PATH, - MaxInternalSize=self._FAKE_MAX_INTERNAL_SIZE) - - self.mock_get = self._vhdutils.get_internal_vhd_size_by_file_size - self.mock_get.assert_called_once_with(self._FAKE_VHD_PATH, - self._FAKE_MAX_INTERNAL_SIZE) - - def _test_get_vhdx_internal_size(self, vhd_type): - self._vhdutils.get_vhd_info = mock.MagicMock() - self._vhdutils.get_vhd_parent_path = mock.Mock( - return_value=self._FAKE_PARENT_PATH) - - if vhd_type == 4: - self._vhdutils.get_vhd_info.side_effect = [ - {'Type': vhd_type}, self._fake_vhd_info] - else: - self._vhdutils.get_vhd_info.return_value = self._fake_vhd_info - - @mock.patch('nova.virt.hyperv.vhdutils.VHDUtils.get_vhd_format') - def test_get_vhdx_internal_size(self, mock_get_vhd_format): - mock_get_vhd_format.return_value = constants.DISK_FORMAT_VHDX - self._mock_get_vhd_info() - self._vhdutils._get_vhdx_log_size = mock.MagicMock( - return_value=self._FAKE_LOG_SIZE) - self._vhdutils._get_vhdx_metadata_size_and_offset = mock.MagicMock( - return_value=(self._FAKE_METADATA_SIZE, 1024)) - self._vhdutils._get_vhdx_block_size = mock.MagicMock( - return_value=self._FAKE_BLOCK_SIZE) - - file_mock = mock.MagicMock() - with mock.patch('__builtin__.open', file_mock): - internal_size = ( - self._vhdutils.get_internal_vhd_size_by_file_size( - self._FAKE_VHD_PATH, self._FAKE_MAX_INTERNAL_SIZE)) - - self.assertEqual(self._FAKE_MAX_INTERNAL_SIZE - self._FAKE_BLOCK_SIZE, - internal_size) - - def test_get_vhdx_internal_size_dynamic(self): - self._test_get_vhdx_internal_size(3) - - def test_get_vhdx_internal_size_differencing(self): - self._test_get_vhdx_internal_size(4) - - def test_get_vhdx_current_header(self): - VHDX_HEADER_OFFSETS = [64 * 1024, 128 * 1024] - fake_sequence_numbers = ['\x01\x00\x00\x00\x00\x00\x00\x00', - '\x02\x00\x00\x00\x00\x00\x00\x00'] - self._fake_file_handle.read = mock.MagicMock( - side_effect=fake_sequence_numbers) - - offset = self._vhdutils._get_vhdx_current_header_offset( - self._fake_file_handle) - self.assertEqual(offset, VHDX_HEADER_OFFSETS[1]) - - def test_get_vhdx_metadata_size(self): - fake_metadata_offset = '\x01\x00\x00\x00\x00\x00\x00\x00' - fake_metadata_size = '\x01\x00\x00\x00' - self._fake_file_handle.read = mock.MagicMock( - side_effect=[fake_metadata_offset, fake_metadata_size]) - - metadata_size, metadata_offset = ( - self._vhdutils._get_vhdx_metadata_size_and_offset( - self._fake_file_handle)) - self.assertEqual(metadata_size, 1) - self.assertEqual(metadata_offset, 1) - - def test_get_block_size(self): - self._vhdutils._get_vhdx_metadata_size_and_offset = mock.MagicMock( - return_value=(self._FAKE_METADATA_SIZE, 1024)) - fake_block_size = '\x01\x00\x00\x00' - self._fake_file_handle.read = mock.MagicMock( - return_value=fake_block_size) - - block_size = self._vhdutils._get_vhdx_block_size( - self._fake_file_handle) - self.assertEqual(block_size, 1) - - def test_get_log_size(self): - fake_current_header_offset = 64 * 1024 - self._vhdutils._get_vhdx_current_header_offset = mock.MagicMock( - return_value=fake_current_header_offset) - fake_log_size = '\x01\x00\x00\x00' - self._fake_file_handle.read = mock.MagicMock( - return_value=fake_log_size) - - log_size = self._vhdutils._get_vhdx_log_size(self._fake_file_handle) - self.assertEqual(log_size, 1) - - def test_get_supported_vhd_format(self): - fmt = self._vhdutils.get_best_supported_vhd_format() - self.assertEqual(constants.DISK_FORMAT_VHDX, fmt) diff --git a/nova/tests/unit/virt/hyperv/test_vmutils.py b/nova/tests/unit/virt/hyperv/test_vmutils.py deleted file mode 100644 index eac39c17057d..000000000000 --- a/nova/tests/unit/virt/hyperv/test_vmutils.py +++ /dev/null @@ -1,918 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from six.moves import range - -from nova import exception -from nova import test -from nova.virt.hyperv import constants -from nova.virt.hyperv import vmutils - - -class VMUtilsTestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V VMUtils class.""" - - _FAKE_VM_NAME = 'fake_vm' - _FAKE_MEMORY_MB = 2 - _FAKE_VCPUS_NUM = 4 - _FAKE_JOB_PATH = 'fake_job_path' - _FAKE_RET_VAL = 0 - _FAKE_RET_VAL_BAD = -1 - _FAKE_PATH = "fake_path" - _FAKE_CTRL_PATH = 'fake_ctrl_path' - _FAKE_CTRL_ADDR = 0 - _FAKE_DRIVE_ADDR = 0 - _FAKE_MOUNTED_DISK_PATH = 'fake_mounted_disk_path' - _FAKE_VM_PATH = "fake_vm_path" - _FAKE_VHD_PATH = "fake_vhd_path" - _FAKE_DVD_PATH = "fake_dvd_path" - _FAKE_VOLUME_DRIVE_PATH = "fake_volume_drive_path" - _FAKE_VM_UUID = "04e79212-39bc-4065-933c-50f6d48a57f6" - _FAKE_INSTANCE = {"name": _FAKE_VM_NAME, - "uuid": _FAKE_VM_UUID} - _FAKE_SNAPSHOT_PATH = "fake_snapshot_path" - _FAKE_RES_DATA = "fake_res_data" - _FAKE_HOST_RESOURCE = "fake_host_resource" - _FAKE_CLASS = "FakeClass" - _FAKE_RES_PATH = "fake_res_path" - _FAKE_RES_NAME = 'fake_res_name' - _FAKE_ADDRESS = "fake_address" - _FAKE_JOB_STATUS_DONE = 7 - _FAKE_JOB_STATUS_BAD = -1 - _FAKE_JOB_DESCRIPTION = "fake_job_description" - _FAKE_ERROR = "fake_error" - _FAKE_ELAPSED_TIME = 0 - _CONCRETE_JOB = "Msvm_ConcreteJob" - _FAKE_DYNAMIC_MEMORY_RATIO = 1.0 - - _FAKE_SUMMARY_INFO = {'NumberOfProcessors': 4, - 'EnabledState': 2, - 'MemoryUsage': 2, - 'UpTime': 1} - - _DEFINE_SYSTEM = 'DefineVirtualSystem' - _DESTROY_SYSTEM = 'DestroyVirtualSystem' - _DESTROY_SNAPSHOT = 'RemoveVirtualSystemSnapshot' - _ADD_RESOURCE = 'AddVirtualSystemResources' - _REMOVE_RESOURCE = 'RemoveVirtualSystemResources' - _SETTING_TYPE = 'SettingType' - _VM_GEN = constants.VM_GEN_1 - - _VIRTUAL_SYSTEM_TYPE_REALIZED = 3 - - def setUp(self): - self._vmutils = vmutils.VMUtils() - self._vmutils._conn = mock.MagicMock() - - super(VMUtilsTestCase, self).setUp() - - def test_enable_vm_metrics_collection(self): - self.assertRaises(NotImplementedError, - self._vmutils.enable_vm_metrics_collection, - self._FAKE_VM_NAME) - - def test_get_vm_summary_info(self): - self._lookup_vm() - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - - mock_summary = mock.MagicMock() - mock_svc.GetSummaryInformation.return_value = (self._FAKE_RET_VAL, - [mock_summary]) - - for (key, val) in self._FAKE_SUMMARY_INFO.items(): - setattr(mock_summary, key, val) - - summary = self._vmutils.get_vm_summary_info(self._FAKE_VM_NAME) - self.assertEqual(self._FAKE_SUMMARY_INFO, summary) - - def _lookup_vm(self): - mock_vm = mock.MagicMock() - self._vmutils._lookup_vm_check = mock.MagicMock( - return_value=mock_vm) - mock_vm.path_.return_value = self._FAKE_VM_PATH - return mock_vm - - def test_lookup_vm_ok(self): - mock_vm = mock.MagicMock() - self._vmutils._conn.Msvm_ComputerSystem.return_value = [mock_vm] - vm = self._vmutils._lookup_vm_check(self._FAKE_VM_NAME) - self.assertEqual(mock_vm, vm) - - def test_lookup_vm_multiple(self): - mockvm = mock.MagicMock() - self._vmutils._conn.Msvm_ComputerSystem.return_value = [mockvm, mockvm] - self.assertRaises(vmutils.HyperVException, - self._vmutils._lookup_vm_check, - self._FAKE_VM_NAME) - - def test_lookup_vm_none(self): - self._vmutils._conn.Msvm_ComputerSystem.return_value = [] - self.assertRaises(exception.InstanceNotFound, - self._vmutils._lookup_vm_check, - self._FAKE_VM_NAME) - - def test_set_vm_memory_static(self): - self._test_set_vm_memory_dynamic(1.0) - - def test_set_vm_memory_dynamic(self): - self._test_set_vm_memory_dynamic(2.0) - - def _test_set_vm_memory_dynamic(self, dynamic_memory_ratio): - mock_vm = self._lookup_vm() - - mock_s = self._vmutils._conn.Msvm_VirtualSystemSettingData()[0] - mock_s.SystemType = 3 - - mock_vmsetting = mock.MagicMock() - mock_vmsetting.associators.return_value = [mock_s] - - self._vmutils._modify_virt_resource = mock.MagicMock() - - self._vmutils._set_vm_memory(mock_vm, mock_vmsetting, - self._FAKE_MEMORY_MB, - dynamic_memory_ratio) - - self._vmutils._modify_virt_resource.assert_called_with( - mock_s, self._FAKE_VM_PATH) - - if dynamic_memory_ratio > 1: - self.assertTrue(mock_s.DynamicMemoryEnabled) - else: - self.assertFalse(mock_s.DynamicMemoryEnabled) - - def test_soft_shutdown_vm(self): - mock_vm = self._lookup_vm() - mock_shutdown = mock.MagicMock() - mock_shutdown.InitiateShutdown.return_value = (self._FAKE_RET_VAL, ) - mock_vm.associators.return_value = [mock_shutdown] - - with mock.patch.object(self._vmutils, 'check_ret_val') as mock_check: - self._vmutils.soft_shutdown_vm(self._FAKE_VM_NAME) - - mock_shutdown.InitiateShutdown.assert_called_once_with( - Force=False, Reason=mock.ANY) - mock_check.assert_called_once_with(self._FAKE_RET_VAL, None) - - def test_soft_shutdown_vm_no_component(self): - mock_vm = self._lookup_vm() - mock_vm.associators.return_value = [] - - with mock.patch.object(self._vmutils, 'check_ret_val') as mock_check: - self._vmutils.soft_shutdown_vm(self._FAKE_VM_NAME) - self.assertFalse(mock_check.called) - - @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_vm_disks') - def test_get_vm_storage_paths(self, mock_get_vm_disks): - self._lookup_vm() - mock_rasds = self._create_mock_disks() - mock_get_vm_disks.return_value = ([mock_rasds[0]], [mock_rasds[1]]) - - storage = self._vmutils.get_vm_storage_paths(self._FAKE_VM_NAME) - (disk_files, volume_drives) = storage - - self.assertEqual([self._FAKE_VHD_PATH], disk_files) - self.assertEqual([self._FAKE_VOLUME_DRIVE_PATH], volume_drives) - - def test_get_vm_disks(self): - mock_vm = self._lookup_vm() - mock_vmsettings = [mock.MagicMock()] - mock_vm.associators.return_value = mock_vmsettings - - mock_rasds = self._create_mock_disks() - mock_vmsettings[0].associators.return_value = mock_rasds - - (disks, volumes) = self._vmutils._get_vm_disks(mock_vm) - - mock_vm.associators.assert_called_with( - wmi_result_class=self._vmutils._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - mock_vmsettings[0].associators.assert_called_with( - wmi_result_class=self._vmutils._RESOURCE_ALLOC_SETTING_DATA_CLASS) - self.assertEqual([mock_rasds[0]], disks) - self.assertEqual([mock_rasds[1]], volumes) - - def _create_mock_disks(self): - mock_rasd1 = mock.MagicMock() - mock_rasd1.ResourceSubType = self._vmutils._HARD_DISK_RES_SUB_TYPE - mock_rasd1.HostResource = [self._FAKE_VHD_PATH] - mock_rasd1.Connection = [self._FAKE_VHD_PATH] - mock_rasd1.Parent = self._FAKE_CTRL_PATH - mock_rasd1.Address = self._FAKE_ADDRESS - mock_rasd1.HostResource = [self._FAKE_VHD_PATH] - - mock_rasd2 = mock.MagicMock() - mock_rasd2.ResourceSubType = self._vmutils._PHYS_DISK_RES_SUB_TYPE - mock_rasd2.HostResource = [self._FAKE_VOLUME_DRIVE_PATH] - - return [mock_rasd1, mock_rasd2] - - @mock.patch.object(vmutils.VMUtils, '_set_vm_vcpus') - @mock.patch.object(vmutils.VMUtils, '_set_vm_memory') - @mock.patch.object(vmutils.VMUtils, '_get_wmi_obj') - def test_create_vm(self, mock_get_wmi_obj, mock_set_mem, mock_set_vcpus): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - getattr(mock_svc, self._DEFINE_SYSTEM).return_value = ( - None, self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - mock_vm = mock_get_wmi_obj.return_value - self._vmutils._conn.Msvm_ComputerSystem.return_value = [mock_vm] - - mock_s = mock.MagicMock() - setattr(mock_s, - self._SETTING_TYPE, - self._VIRTUAL_SYSTEM_TYPE_REALIZED) - mock_vm.associators.return_value = [mock_s] - - self._vmutils.create_vm(self._FAKE_VM_NAME, self._FAKE_MEMORY_MB, - self._FAKE_VCPUS_NUM, False, - self._FAKE_DYNAMIC_MEMORY_RATIO, - self._VM_GEN, - mock.sentinel.instance_path) - - self.assertTrue(getattr(mock_svc, self._DEFINE_SYSTEM).called) - mock_set_mem.assert_called_with(mock_vm, mock_s, self._FAKE_MEMORY_MB, - self._FAKE_DYNAMIC_MEMORY_RATIO) - - mock_set_vcpus.assert_called_with(mock_vm, mock_s, - self._FAKE_VCPUS_NUM, - False) - - def test_get_vm_scsi_controller(self): - self._prepare_get_vm_controller(self._vmutils._SCSI_CTRL_RES_SUB_TYPE) - path = self._vmutils.get_vm_scsi_controller(self._FAKE_VM_NAME) - self.assertEqual(self._FAKE_RES_PATH, path) - - @mock.patch("nova.virt.hyperv.vmutils.VMUtils.get_attached_disks") - def test_get_free_controller_slot(self, mock_get_attached_disks): - with mock.patch.object(self._vmutils, - '_get_disk_resource_address') as mock_get_addr: - mock_get_addr.return_value = 3 - mock_get_attached_disks.return_value = [mock.sentinel.disk] - - response = self._vmutils.get_free_controller_slot( - self._FAKE_CTRL_PATH) - - mock_get_attached_disks.assert_called_once_with( - self._FAKE_CTRL_PATH) - - self.assertEqual(response, 0) - - def test_get_free_controller_slot_exception(self): - mock_get_address = mock.Mock() - mock_get_address.side_effect = range( - constants.SCSI_CONTROLLER_SLOTS_NUMBER) - - mock_get_attached_disks = mock.Mock() - mock_get_attached_disks.return_value = ( - [mock.sentinel.drive] * constants.SCSI_CONTROLLER_SLOTS_NUMBER) - - with mock.patch.multiple(self._vmutils, - get_attached_disks=mock_get_attached_disks, - _get_disk_resource_address=mock_get_address): - self.assertRaises(vmutils.HyperVException, - self._vmutils.get_free_controller_slot, - mock.sentinel.scsi_controller_path) - - def test_get_vm_ide_controller(self): - self._prepare_get_vm_controller(self._vmutils._IDE_CTRL_RES_SUB_TYPE) - path = self._vmutils.get_vm_ide_controller(self._FAKE_VM_NAME, - self._FAKE_ADDRESS) - self.assertEqual(self._FAKE_RES_PATH, path) - - def test_get_vm_ide_controller_none(self): - self._prepare_get_vm_controller(self._vmutils._IDE_CTRL_RES_SUB_TYPE) - path = self._vmutils.get_vm_ide_controller( - mock.sentinel.FAKE_VM_NAME, mock.sentinel.FAKE_NOT_FOUND_ADDR) - self.assertNotEqual(self._FAKE_RES_PATH, path) - - def _prepare_get_vm_controller(self, resource_sub_type): - mock_vm = self._lookup_vm() - mock_vm_settings = mock.MagicMock() - mock_rasds = mock.MagicMock() - mock_rasds.path_.return_value = self._FAKE_RES_PATH - mock_rasds.ResourceSubType = resource_sub_type - mock_rasds.Address = self._FAKE_ADDRESS - mock_vm_settings.associators.return_value = [mock_rasds] - mock_vm.associators.return_value = [mock_vm_settings] - - def _prepare_resources(self, mock_path, mock_subtype, mock_vm_settings): - mock_rasds = mock_vm_settings.associators.return_value[0] - mock_rasds.path_.return_value = mock_path - mock_rasds.ResourceSubType = mock_subtype - return mock_rasds - - @mock.patch("nova.virt.hyperv.vmutils.VMUtils.get_free_controller_slot") - @mock.patch("nova.virt.hyperv.vmutils.VMUtils._get_vm_scsi_controller") - def test_attach_scsi_drive(self, mock_get_vm_scsi_controller, - mock_get_free_controller_slot): - mock_vm = self._lookup_vm() - mock_get_vm_scsi_controller.return_value = self._FAKE_CTRL_PATH - mock_get_free_controller_slot.return_value = self._FAKE_DRIVE_ADDR - - with mock.patch.object(self._vmutils, - 'attach_drive') as mock_attach_drive: - self._vmutils.attach_scsi_drive(mock_vm, self._FAKE_PATH, - constants.DISK) - - mock_get_vm_scsi_controller.assert_called_once_with(mock_vm) - mock_get_free_controller_slot.assert_called_once_with( - self._FAKE_CTRL_PATH) - mock_attach_drive.assert_called_once_with( - mock_vm, self._FAKE_PATH, self._FAKE_CTRL_PATH, - self._FAKE_DRIVE_ADDR, constants.DISK) - - @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') - @mock.patch.object(vmutils.VMUtils, '_get_vm_ide_controller') - def test_attach_ide_drive(self, mock_get_ide_ctrl, mock_get_new_rsd): - mock_vm = self._lookup_vm() - mock_rsd = mock_get_new_rsd.return_value - - with mock.patch.object(self._vmutils, - '_add_virt_resource') as mock_add_virt_res: - self._vmutils.attach_ide_drive(self._FAKE_VM_NAME, - self._FAKE_CTRL_PATH, - self._FAKE_CTRL_ADDR, - self._FAKE_DRIVE_ADDR) - - mock_add_virt_res.assert_called_with(mock_rsd, - mock_vm.path_.return_value) - - mock_get_ide_ctrl.assert_called_with(mock_vm, self._FAKE_CTRL_ADDR) - self.assertTrue(mock_get_new_rsd.called) - - @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') - def test_create_scsi_controller(self, mock_get_new_rsd): - mock_vm = self._lookup_vm() - with mock.patch.object(self._vmutils, - '_add_virt_resource') as mock_add_virt_res: - self._vmutils.create_scsi_controller(self._FAKE_VM_NAME) - - mock_add_virt_res.assert_called_with(mock_get_new_rsd.return_value, - mock_vm.path_.return_value) - - @mock.patch.object(vmutils.VMUtils, '_get_new_resource_setting_data') - def test_attach_volume_to_controller(self, mock_get_new_rsd): - mock_vm = self._lookup_vm() - with mock.patch.object(self._vmutils, - '_add_virt_resource') as mock_add_virt_res: - self._vmutils.attach_volume_to_controller( - self._FAKE_VM_NAME, self._FAKE_CTRL_PATH, self._FAKE_CTRL_ADDR, - self._FAKE_MOUNTED_DISK_PATH) - - mock_add_virt_res.assert_called_with(mock_get_new_rsd.return_value, - mock_vm.path_.return_value) - - @mock.patch.object(vmutils.VMUtils, '_modify_virt_resource') - @mock.patch.object(vmutils.VMUtils, '_get_nic_data_by_name') - def test_set_nic_connection(self, mock_get_nic_conn, mock_modify_virt_res): - self._lookup_vm() - mock_nic = mock_get_nic_conn.return_value - self._vmutils.set_nic_connection(self._FAKE_VM_NAME, None, None) - - mock_modify_virt_res.assert_called_with(mock_nic, self._FAKE_VM_PATH) - - @mock.patch.object(vmutils.VMUtils, '_get_new_setting_data') - def test_create_nic(self, mock_get_new_virt_res): - self._lookup_vm() - mock_nic = mock_get_new_virt_res.return_value - - with mock.patch.object(self._vmutils, - '_add_virt_resource') as mock_add_virt_res: - self._vmutils.create_nic( - self._FAKE_VM_NAME, self._FAKE_RES_NAME, self._FAKE_ADDRESS) - - mock_add_virt_res.assert_called_with(mock_nic, self._FAKE_VM_PATH) - - @mock.patch.object(vmutils.VMUtils, '_get_nic_data_by_name') - def test_destroy_nic(self, mock_get_nic_data_by_name): - self._lookup_vm() - fake_nic_data = mock_get_nic_data_by_name.return_value - with mock.patch.object(self._vmutils, - '_remove_virt_resource') as mock_rem_virt_res: - self._vmutils.destroy_nic(self._FAKE_VM_NAME, - mock.sentinel.FAKE_NIC_NAME) - mock_rem_virt_res.assert_called_once_with(fake_nic_data, - self._FAKE_VM_PATH) - - def test_set_vm_state(self): - mock_vm = self._lookup_vm() - mock_vm.RequestStateChange.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vmutils.set_vm_state(self._FAKE_VM_NAME, - constants.HYPERV_VM_STATE_ENABLED) - mock_vm.RequestStateChange.assert_called_with( - constants.HYPERV_VM_STATE_ENABLED) - - def test_destroy_vm(self): - self._lookup_vm() - - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - getattr(mock_svc, self._DESTROY_SYSTEM).return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vmutils.destroy_vm(self._FAKE_VM_NAME) - - getattr(mock_svc, self._DESTROY_SYSTEM).assert_called_with( - self._FAKE_VM_PATH) - - @mock.patch.object(vmutils.VMUtils, '_wait_for_job') - def test_check_ret_val_ok(self, mock_wait_for_job): - self._vmutils.check_ret_val(constants.WMI_JOB_STATUS_STARTED, - self._FAKE_JOB_PATH) - mock_wait_for_job.assert_called_once_with(self._FAKE_JOB_PATH) - - def test_check_ret_val_exception(self): - self.assertRaises(vmutils.HyperVException, - self._vmutils.check_ret_val, - self._FAKE_RET_VAL_BAD, - self._FAKE_JOB_PATH) - - def test_wait_for_job_done(self): - mockjob = self._prepare_wait_for_job(constants.WMI_JOB_STATE_COMPLETED) - job = self._vmutils._wait_for_job(self._FAKE_JOB_PATH) - self.assertEqual(mockjob, job) - - def test_wait_for_job_killed(self): - mockjob = self._prepare_wait_for_job(constants.JOB_STATE_KILLED) - job = self._vmutils._wait_for_job(self._FAKE_JOB_PATH) - self.assertEqual(mockjob, job) - - def test_wait_for_job_exception_concrete_job(self): - mock_job = self._prepare_wait_for_job() - mock_job.path.return_value.Class = self._CONCRETE_JOB - self.assertRaises(vmutils.HyperVException, - self._vmutils._wait_for_job, - self._FAKE_JOB_PATH) - - def test_wait_for_job_exception_with_error(self): - mock_job = self._prepare_wait_for_job() - mock_job.GetError.return_value = (self._FAKE_ERROR, self._FAKE_RET_VAL) - self.assertRaises(vmutils.HyperVException, - self._vmutils._wait_for_job, - self._FAKE_JOB_PATH) - - def test_wait_for_job_exception_no_error(self): - mock_job = self._prepare_wait_for_job() - mock_job.GetError.return_value = (None, None) - self.assertRaises(vmutils.HyperVException, - self._vmutils._wait_for_job, - self._FAKE_JOB_PATH) - - def _prepare_wait_for_job(self, state=_FAKE_JOB_STATUS_BAD): - mock_job = mock.MagicMock() - mock_job.JobState = state - mock_job.Description = self._FAKE_JOB_DESCRIPTION - mock_job.ElapsedTime = self._FAKE_ELAPSED_TIME - - self._vmutils._get_wmi_obj = mock.MagicMock(return_value=mock_job) - return mock_job - - def test_add_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - getattr(mock_svc, self._ADD_RESOURCE).return_value = ( - self._FAKE_JOB_PATH, mock.MagicMock(), self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA - - self._vmutils._add_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - self._assert_add_resources(mock_svc) - - def test_modify_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.ModifyVirtualSystemResources.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA - - self._vmutils._modify_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - - mock_svc.ModifyVirtualSystemResources.assert_called_with( - ResourceSettingData=[self._FAKE_RES_DATA], - ComputerSystem=self._FAKE_VM_PATH) - - def test_remove_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - getattr(mock_svc, self._REMOVE_RESOURCE).return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.path_.return_value = self._FAKE_RES_PATH - - self._vmutils._remove_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - self._assert_remove_resources(mock_svc) - - def test_set_disk_host_resource(self): - self._lookup_vm() - mock_rasds = self._create_mock_disks() - - self._vmutils._get_vm_disks = mock.MagicMock( - return_value=([mock_rasds[0]], [mock_rasds[1]])) - self._vmutils._modify_virt_resource = mock.MagicMock() - self._vmutils._get_disk_resource_address = mock.MagicMock( - return_value=self._FAKE_ADDRESS) - - self._vmutils.set_disk_host_resource( - self._FAKE_VM_NAME, - self._FAKE_CTRL_PATH, - self._FAKE_ADDRESS, - mock.sentinel.fake_new_mounted_disk_path) - self._vmutils._get_disk_resource_address.assert_called_with( - mock_rasds[0]) - self._vmutils._modify_virt_resource.assert_called_with( - mock_rasds[0], self._FAKE_VM_PATH) - self.assertEqual( - mock.sentinel.fake_new_mounted_disk_path, - mock_rasds[0].HostResource[0]) - - @mock.patch.object(vmutils, 'wmi', create=True) - @mock.patch.object(vmutils.VMUtils, 'check_ret_val') - def test_take_vm_snapshot(self, mock_check_ret_val, mock_wmi): - self._lookup_vm() - - mock_svc = self._get_snapshot_service() - mock_svc.CreateVirtualSystemSnapshot.return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL, mock.MagicMock()) - - self._vmutils.take_vm_snapshot(self._FAKE_VM_NAME) - - mock_svc.CreateVirtualSystemSnapshot.assert_called_with( - self._FAKE_VM_PATH) - - mock_check_ret_val.assert_called_once_with(self._FAKE_RET_VAL, - self._FAKE_JOB_PATH) - - def test_remove_vm_snapshot(self): - mock_svc = self._get_snapshot_service() - getattr(mock_svc, self._DESTROY_SNAPSHOT).return_value = ( - self._FAKE_JOB_PATH, self._FAKE_RET_VAL) - - self._vmutils.remove_vm_snapshot(self._FAKE_SNAPSHOT_PATH) - getattr(mock_svc, self._DESTROY_SNAPSHOT).assert_called_with( - self._FAKE_SNAPSHOT_PATH) - - def test_detach_vm_disk(self): - self._lookup_vm() - mock_disk = self._prepare_mock_disk() - - with mock.patch.object(self._vmutils, - '_remove_virt_resource') as mock_rm_virt_res: - self._vmutils.detach_vm_disk(self._FAKE_VM_NAME, - self._FAKE_HOST_RESOURCE) - - mock_rm_virt_res.assert_called_with(mock_disk, self._FAKE_VM_PATH) - - def _test_get_mounted_disk_resource_from_path(self, is_physical): - mock_disk_1 = mock.MagicMock() - mock_disk_2 = mock.MagicMock() - conn_attr = (self._vmutils._PHYS_DISK_CONNECTION_ATTR if is_physical - else self._vmutils._VIRT_DISK_CONNECTION_ATTR) - setattr(mock_disk_2, conn_attr, [self._FAKE_MOUNTED_DISK_PATH]) - self._vmutils._conn.query.return_value = [mock_disk_1, mock_disk_2] - - mounted_disk = self._vmutils._get_mounted_disk_resource_from_path( - self._FAKE_MOUNTED_DISK_PATH, is_physical) - - self.assertEqual(mock_disk_2, mounted_disk) - - def test_get_physical_mounted_disk_resource_from_path(self): - self._test_get_mounted_disk_resource_from_path(is_physical=True) - - def test_get_virtual_mounted_disk_resource_from_path(self): - self._test_get_mounted_disk_resource_from_path(is_physical=False) - - def test_get_controller_volume_paths(self): - self._prepare_mock_disk() - mock_disks = {self._FAKE_RES_PATH: self._FAKE_HOST_RESOURCE} - disks = self._vmutils.get_controller_volume_paths(self._FAKE_RES_PATH) - self.assertEqual(mock_disks, disks) - - def _prepare_mock_disk(self): - mock_disk = mock.MagicMock() - mock_disk.HostResource = [self._FAKE_HOST_RESOURCE] - mock_disk.path.return_value.RelPath = self._FAKE_RES_PATH - mock_disk.ResourceSubType = self._vmutils._HARD_DISK_RES_SUB_TYPE - self._vmutils._conn.query.return_value = [mock_disk] - - return mock_disk - - def _get_snapshot_service(self): - return self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - - def _assert_add_resources(self, mock_svc): - getattr(mock_svc, self._ADD_RESOURCE).assert_called_with( - [self._FAKE_RES_DATA], self._FAKE_VM_PATH) - - def _assert_remove_resources(self, mock_svc): - getattr(mock_svc, self._REMOVE_RESOURCE).assert_called_with( - [self._FAKE_RES_PATH], self._FAKE_VM_PATH) - - def test_get_active_instances(self): - fake_vm = mock.MagicMock() - - type(fake_vm).ElementName = mock.PropertyMock( - side_effect=['active_vm', 'inactive_vm']) - type(fake_vm).EnabledState = mock.PropertyMock( - side_effect=[constants.HYPERV_VM_STATE_ENABLED, - constants.HYPERV_VM_STATE_DISABLED]) - self._vmutils.list_instances = mock.MagicMock( - return_value=[mock.sentinel.fake_vm_name] * 2) - self._vmutils._lookup_vm = mock.MagicMock(side_effect=[fake_vm] * 2) - active_instances = self._vmutils.get_active_instances() - - self.assertEqual(['active_vm'], active_instances) - - def _test_get_vm_serial_port_connection(self, new_connection=None): - old_serial_connection = 'old_serial_connection' - - mock_vm = self._lookup_vm() - mock_vmsettings = [mock.MagicMock()] - mock_vm.associators.return_value = mock_vmsettings - - fake_serial_port = mock.MagicMock() - - fake_serial_port.ResourceSubType = ( - self._vmutils._SERIAL_PORT_RES_SUB_TYPE) - fake_serial_port.Connection = [old_serial_connection] - mock_rasds = [fake_serial_port] - mock_vmsettings[0].associators.return_value = mock_rasds - self._vmutils._modify_virt_resource = mock.MagicMock() - fake_modify = self._vmutils._modify_virt_resource - - ret_val = self._vmutils.get_vm_serial_port_connection( - self._FAKE_VM_NAME, update_connection=new_connection) - - mock_vmsettings[0].associators.assert_called_once_with( - wmi_result_class=self._vmutils._SERIAL_PORT_SETTING_DATA_CLASS) - - if new_connection: - self.assertEqual(new_connection, ret_val) - fake_modify.assert_called_once_with(fake_serial_port, - mock_vm.path_()) - else: - self.assertEqual(old_serial_connection, ret_val) - - def test_set_vm_serial_port_connection(self): - self._test_get_vm_serial_port_connection('new_serial_connection') - - def test_get_vm_serial_port_connection(self): - self._test_get_vm_serial_port_connection() - - def test_list_instance_notes(self): - vs = mock.MagicMock() - attrs = {'ElementName': 'fake_name', - 'Notes': '4f54fb69-d3a2-45b7-bb9b-b6e6b3d893b3'} - vs.configure_mock(**attrs) - vs2 = mock.MagicMock(ElementName='fake_name2', Notes=None) - self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs, - vs2] - response = self._vmutils.list_instance_notes() - - self.assertEqual([(attrs['ElementName'], [attrs['Notes']])], response) - self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( - ['ElementName', 'Notes'], - SettingType=self._vmutils._VIRTUAL_SYSTEM_CURRENT_SETTINGS) - - @mock.patch('nova.virt.hyperv.vmutils.VMUtils.check_ret_val') - def test_modify_virtual_system(self, mock_check_ret_val): - mock_vs_man_svc = mock.MagicMock() - mock_vmsetting = mock.MagicMock() - fake_path = 'fake path' - fake_job_path = 'fake job path' - fake_ret_val = 'fake return value' - - mock_vs_man_svc.ModifyVirtualSystem.return_value = (0, fake_job_path, - fake_ret_val) - - self._vmutils._modify_virtual_system(vs_man_svc=mock_vs_man_svc, - vm_path=fake_path, - vmsetting=mock_vmsetting) - - mock_vs_man_svc.ModifyVirtualSystem.assert_called_once_with( - ComputerSystem=fake_path, - SystemSettingData=mock_vmsetting.GetText_(1)) - mock_check_ret_val.assert_called_once_with(fake_ret_val, fake_job_path) - - @mock.patch('nova.virt.hyperv.vmutils.VMUtils.check_ret_val') - @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_wmi_obj') - @mock.patch('nova.virt.hyperv.vmutils.VMUtils._modify_virtual_system') - @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_vm_setting_data') - def test_create_vm_obj(self, mock_get_vm_setting_data, - mock_modify_virtual_system, - mock_get_wmi_obj, mock_check_ret_val): - mock_vs_man_svc = mock.MagicMock() - mock_vs_gs_data = mock.MagicMock() - fake_vm_path = 'fake vm path' - fake_job_path = 'fake job path' - fake_ret_val = 'fake return value' - _conn = self._vmutils._conn.Msvm_VirtualSystemGlobalSettingData - - _conn.new.return_value = mock_vs_gs_data - mock_vs_man_svc.DefineVirtualSystem.return_value = (fake_vm_path, - fake_job_path, - fake_ret_val) - - response = self._vmutils._create_vm_obj( - vs_man_svc=mock_vs_man_svc, - vm_name='fake vm', vm_gen='fake vm gen', - notes='fake notes', dynamic_memory_ratio=1.0, - instance_path=mock.sentinel.instance_path) - - _conn.new.assert_called_once_with() - self.assertEqual(mock_vs_gs_data.ElementName, 'fake vm') - mock_vs_man_svc.DefineVirtualSystem.assert_called_once_with( - [], None, mock_vs_gs_data.GetText_(1)) - mock_check_ret_val.assert_called_once_with(fake_ret_val, fake_job_path) - self.assertEqual(mock.sentinel.instance_path, - mock_vs_gs_data.ExternalDataRoot) - self.assertEqual(mock.sentinel.instance_path, - mock_vs_gs_data.SnapshotDataRoot) - - mock_get_wmi_obj.assert_called_with(fake_vm_path) - mock_get_vm_setting_data.assert_called_once_with(mock_get_wmi_obj()) - mock_modify_virtual_system.assert_called_once_with( - mock_vs_man_svc, fake_vm_path, mock_get_vm_setting_data()) - - self.assertEqual(mock_get_vm_setting_data().Notes, - '\n'.join('fake notes')) - self.assertEqual(response, mock_get_wmi_obj()) - - def test_list_instances(self): - vs = mock.MagicMock() - attrs = {'ElementName': 'fake_name'} - vs.configure_mock(**attrs) - self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs] - response = self._vmutils.list_instances() - - self.assertEqual([(attrs['ElementName'])], response) - self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( - ['ElementName'], - SettingType=self._vmutils._VIRTUAL_SYSTEM_CURRENT_SETTINGS) - - @mock.patch.object(vmutils.VMUtils, "_clone_wmi_obj") - def _test_check_clone_wmi_obj(self, mock_clone_wmi_obj, clone_objects): - mock_obj = mock.MagicMock() - self._vmutils._clone_wmi_objs = clone_objects - - response = self._vmutils._check_clone_wmi_obj(class_name="fakeClass", - obj=mock_obj) - if not clone_objects: - self.assertEqual(mock_obj, response) - else: - mock_clone_wmi_obj.assert_called_once_with("fakeClass", mock_obj) - self.assertEqual(mock_clone_wmi_obj.return_value, response) - - def test_check_clone_wmi_obj_true(self): - self._test_check_clone_wmi_obj(clone_objects=True) - - def test_check_clone_wmi_obj_false(self): - self._test_check_clone_wmi_obj(clone_objects=False) - - def test_clone_wmi_obj(self): - mock_obj = mock.MagicMock() - mock_value = mock.MagicMock() - mock_value.Value = mock.sentinel.fake_value - mock_obj._properties = [mock.sentinel.property] - mock_obj.Properties_.Item.return_value = mock_value - - response = self._vmutils._clone_wmi_obj( - class_name="FakeClass", obj=mock_obj) - - compare = self._vmutils._conn.FakeClass.new() - self.assertEqual(mock.sentinel.fake_value, - compare.Properties_.Item().Value) - self.assertEqual(compare, response) - - def test_get_attached_disks(self): - mock_scsi_ctrl_path = mock.MagicMock() - expected_query = ("SELECT * FROM %(class_name)s " - "WHERE (ResourceSubType='%(res_sub_type)s' OR " - "ResourceSubType='%(res_sub_type_virt)s')" - " AND Parent='%(parent)s'" % - {"class_name": - self._vmutils._RESOURCE_ALLOC_SETTING_DATA_CLASS, - "res_sub_type": - self._vmutils._PHYS_DISK_RES_SUB_TYPE, - "res_sub_type_virt": - self._vmutils._DISK_DRIVE_RES_SUB_TYPE, - "parent": - mock_scsi_ctrl_path.replace("'", "''")}) - expected_disks = self._vmutils._conn.query.return_value - - ret_disks = self._vmutils.get_attached_disks(mock_scsi_ctrl_path) - - self._vmutils._conn.query.assert_called_once_with(expected_query) - self.assertEqual(expected_disks, ret_disks) - - def _get_fake_instance_notes(self): - return self._FAKE_VM_UUID - - def test_instance_notes(self): - self._lookup_vm() - mock_vm_settings = mock.Mock() - mock_vm_settings.Notes = self._get_fake_instance_notes() - self._vmutils._get_vm_setting_data = mock.Mock( - return_value=mock_vm_settings) - - notes = self._vmutils._get_instance_notes(mock.sentinel.vm_name) - - self.assertEqual(notes[0], self._FAKE_VM_UUID) - - def test_get_event_wql_query(self): - cls = self._vmutils._COMPUTER_SYSTEM_CLASS - field = self._vmutils._VM_ENABLED_STATE_PROP - timeframe = 10 - filtered_states = [constants.HYPERV_VM_STATE_ENABLED, - constants.HYPERV_VM_STATE_DISABLED] - - expected_checks = ' OR '.join( - ["TargetInstance.%s = '%s'" % (field, state) - for state in filtered_states]) - expected_query = ( - "SELECT %(field)s, TargetInstance " - "FROM __InstanceModificationEvent " - "WITHIN %(timeframe)s " - "WHERE TargetInstance ISA '%(class)s' " - "AND TargetInstance.%(field)s != " - "PreviousInstance.%(field)s " - "AND (%(checks)s)" % - {'class': cls, - 'field': field, - 'timeframe': timeframe, - 'checks': expected_checks}) - - query = self._vmutils._get_event_wql_query( - cls=cls, field=field, timeframe=timeframe, - filtered_states=filtered_states) - self.assertEqual(expected_query, query) - - def test_get_vm_power_state_change_listener(self): - with mock.patch.object(self._vmutils, - '_get_event_wql_query') as mock_get_query: - listener = self._vmutils.get_vm_power_state_change_listener( - mock.sentinel.timeframe, - mock.sentinel.filtered_states) - - mock_get_query.assert_called_once_with( - cls=self._vmutils._COMPUTER_SYSTEM_CLASS, - field=self._vmutils._VM_ENABLED_STATE_PROP, - timeframe=mock.sentinel.timeframe, - filtered_states=mock.sentinel.filtered_states) - watcher = self._vmutils._conn.Msvm_ComputerSystem.watch_for - watcher.assert_called_once_with( - raw_wql=mock_get_query.return_value, - fields=[self._vmutils._VM_ENABLED_STATE_PROP]) - - self.assertEqual(watcher.return_value, listener) - - def test_get_vm_generation_gen1(self): - ret = self._vmutils.get_vm_generation(mock.sentinel.FAKE_VM_NAME) - self.assertEqual(constants.VM_GEN_1, ret) - - def test_stop_vm_jobs(self): - mock_vm = self._lookup_vm() - - mock_job1 = mock.MagicMock(Cancellable=True) - mock_job2 = mock.MagicMock(Cancellable=True) - mock_job3 = mock.MagicMock(Cancellable=True) - - mock_job1.JobState = 2 - mock_job2.JobState = 3 - mock_job3.JobState = constants.JOB_STATE_KILLED - - mock_vm_jobs = [mock_job1, mock_job2, mock_job3] - - mock_vm.associators.return_value = mock_vm_jobs - - self._vmutils.stop_vm_jobs(mock.sentinel.FAKE_VM_NAME) - - mock_job1.RequestStateChange.assert_called_once_with( - self._vmutils._KILL_JOB_STATE_CHANGE_REQUEST) - mock_job2.RequestStateChange.assert_called_once_with( - self._vmutils._KILL_JOB_STATE_CHANGE_REQUEST) - self.assertFalse(mock_job3.RequestStateChange.called) - - def test_is_job_completed_true(self): - job = mock.MagicMock(JobState=constants.JOB_STATE_COMPLETED) - - self.assertTrue(self._vmutils._is_job_completed(job)) - - def test_is_job_completed_false(self): - job = mock.MagicMock(JobState=constants.WMI_JOB_STATE_RUNNING) - - self.assertFalse(self._vmutils._is_job_completed(job)) diff --git a/nova/tests/unit/virt/hyperv/test_vmutilsv2.py b/nova/tests/unit/virt/hyperv/test_vmutilsv2.py deleted file mode 100644 index 4d8b5ff65b08..000000000000 --- a/nova/tests/unit/virt/hyperv/test_vmutilsv2.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from nova.tests.unit.virt.hyperv import test_vmutils -from nova.virt.hyperv import constants -from nova.virt.hyperv import vmutilsv2 - - -class VMUtilsV2TestCase(test_vmutils.VMUtilsTestCase): - """Unit tests for the Hyper-V VMUtilsV2 class.""" - - _DEFINE_SYSTEM = 'DefineSystem' - _DESTROY_SYSTEM = 'DestroySystem' - _DESTROY_SNAPSHOT = 'DestroySnapshot' - - _ADD_RESOURCE = 'AddResourceSettings' - _REMOVE_RESOURCE = 'RemoveResourceSettings' - _SETTING_TYPE = 'VirtualSystemType' - _VM_GEN = constants.VM_GEN_2 - - _VIRTUAL_SYSTEM_TYPE_REALIZED = 'Microsoft:Hyper-V:System:Realized' - - def setUp(self): - super(VMUtilsV2TestCase, self).setUp() - self._vmutils = vmutilsv2.VMUtilsV2() - self._vmutils._conn = mock.MagicMock() - - @mock.patch('nova.virt.hyperv.hostutils.HostUtils' - '.check_min_windows_version') - @mock.patch.object(vmutilsv2, 'sys') - def test_serial_port_setting_data_win_version_10(self, mock_sys, - mock_check_version): - mock_sys.platform = 'win32' - mock_check_version.return_value = True - _vmutils = vmutilsv2.VMUtilsV2() - - self.assertEqual("Msvm_SerialPortSettingData", - _vmutils._SERIAL_PORT_SETTING_DATA_CLASS) - - def test_create_vm(self): - super(VMUtilsV2TestCase, self).test_create_vm() - mock_vssd = self._vmutils._conn.Msvm_VirtualSystemSettingData.new() - self.assertEqual(self._vmutils._VIRTUAL_SYSTEM_SUBTYPE_GEN2, - mock_vssd.VirtualSystemSubType) - self.assertFalse(mock_vssd.SecureBootEnabled) - - def test_modify_virt_resource(self): - mock_svc = self._vmutils._conn.Msvm_VirtualSystemManagementService()[0] - mock_svc.ModifyResourceSettings.return_value = (self._FAKE_JOB_PATH, - mock.MagicMock(), - self._FAKE_RET_VAL) - mock_res_setting_data = mock.MagicMock() - mock_res_setting_data.GetText_.return_value = self._FAKE_RES_DATA - - self._vmutils._modify_virt_resource(mock_res_setting_data, - self._FAKE_VM_PATH) - - mock_svc.ModifyResourceSettings.assert_called_with( - ResourceSettings=[self._FAKE_RES_DATA]) - - @mock.patch.object(vmutilsv2, 'wmi', create=True) - @mock.patch.object(vmutilsv2.VMUtilsV2, 'check_ret_val') - def test_take_vm_snapshot(self, mock_check_ret_val, mock_wmi): - self._lookup_vm() - - mock_svc = self._get_snapshot_service() - mock_svc.CreateSnapshot.return_value = (self._FAKE_JOB_PATH, - mock.MagicMock(), - self._FAKE_RET_VAL) - - self._vmutils.take_vm_snapshot(self._FAKE_VM_NAME) - - mock_svc.CreateSnapshot.assert_called_with( - AffectedSystem=self._FAKE_VM_PATH, - SnapshotType=self._vmutils._SNAPSHOT_FULL) - - mock_check_ret_val.assert_called_once_with(self._FAKE_RET_VAL, - self._FAKE_JOB_PATH) - - @mock.patch.object(vmutilsv2.VMUtilsV2, '_add_virt_resource') - @mock.patch.object(vmutilsv2.VMUtilsV2, '_get_new_setting_data') - @mock.patch.object(vmutilsv2.VMUtilsV2, '_get_nic_data_by_name') - def test_set_nic_connection(self, mock_get_nic_data, mock_get_new_sd, - mock_add_virt_res): - self._lookup_vm() - fake_eth_port = mock_get_new_sd.return_value - - self._vmutils.set_nic_connection(self._FAKE_VM_NAME, None, None) - mock_add_virt_res.assert_called_with(fake_eth_port, self._FAKE_VM_PATH) - - @mock.patch('nova.virt.hyperv.vmutils.VMUtils._get_vm_disks') - def test_enable_vm_metrics_collection(self, mock_get_vm_disks): - self._lookup_vm() - mock_svc = self._vmutils._conn.Msvm_MetricService()[0] - - metric_def = mock.MagicMock() - mock_disk = mock.MagicMock() - mock_disk.path_.return_value = self._FAKE_RES_PATH - mock_get_vm_disks.return_value = ([mock_disk], [mock_disk]) - - fake_metric_def_paths = ['fake_0', 'fake_0', None] - fake_metric_resource_paths = [self._FAKE_VM_PATH, - self._FAKE_VM_PATH, - self._FAKE_RES_PATH] - - metric_def.path_.side_effect = fake_metric_def_paths - self._vmutils._conn.CIM_BaseMetricDefinition.return_value = [ - metric_def] - - self._vmutils.enable_vm_metrics_collection(self._FAKE_VM_NAME) - - calls = [mock.call(Name=def_name) - for def_name in [self._vmutils._METRIC_AGGR_CPU_AVG, - self._vmutils._METRIC_AGGR_MEMORY_AVG]] - self._vmutils._conn.CIM_BaseMetricDefinition.assert_has_calls(calls) - - calls = [] - for i in range(len(fake_metric_def_paths)): - calls.append(mock.call( - Subject=fake_metric_resource_paths[i], - Definition=fake_metric_def_paths[i], - MetricCollectionEnabled=self._vmutils._METRIC_ENABLED)) - - mock_svc.ControlMetrics.assert_has_calls(calls, any_order=True) - - def _get_snapshot_service(self): - return self._vmutils._conn.Msvm_VirtualSystemSnapshotService()[0] - - def _assert_add_resources(self, mock_svc): - getattr(mock_svc, self._ADD_RESOURCE).assert_called_with( - self._FAKE_VM_PATH, [self._FAKE_RES_DATA]) - - def _assert_remove_resources(self, mock_svc): - getattr(mock_svc, self._REMOVE_RESOURCE).assert_called_with( - [self._FAKE_RES_PATH]) - - def test_list_instance_notes(self): - vs = mock.MagicMock() - attrs = {'ElementName': 'fake_name', - 'Notes': ['4f54fb69-d3a2-45b7-bb9b-b6e6b3d893b3']} - vs.configure_mock(**attrs) - vs2 = mock.MagicMock(ElementName='fake_name2', Notes=None) - self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs, - vs2] - response = self._vmutils.list_instance_notes() - - self.assertEqual([(attrs['ElementName'], attrs['Notes'])], response) - self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( - ['ElementName', 'Notes'], - VirtualSystemType=self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED) - - def _get_fake_instance_notes(self): - return [self._FAKE_VM_UUID] - - @mock.patch('nova.virt.hyperv.vmutilsv2.VMUtilsV2.check_ret_val') - @mock.patch('nova.virt.hyperv.vmutilsv2.VMUtilsV2._get_wmi_obj') - def _test_create_vm_obj(self, mock_get_wmi_obj, mock_check_ret_val, - vm_path, dynamic_memory_ratio=1.0): - mock_vs_man_svc = mock.MagicMock() - mock_vs_data = mock.MagicMock() - mock_job = mock.MagicMock() - fake_job_path = 'fake job path' - fake_ret_val = 'fake return value' - fake_vm_name = 'fake_vm_name' - _conn = self._vmutils._conn.Msvm_VirtualSystemSettingData - - mock_check_ret_val.return_value = mock_job - _conn.new.return_value = mock_vs_data - mock_vs_man_svc.DefineSystem.return_value = (fake_job_path, - vm_path, - fake_ret_val) - mock_job.associators.return_value = ['fake vm path'] - - response = self._vmutils._create_vm_obj( - vs_man_svc=mock_vs_man_svc, - vm_name=fake_vm_name, - vm_gen='fake vm gen', - notes='fake notes', - dynamic_memory_ratio=dynamic_memory_ratio, - instance_path=mock.sentinel.instance_path) - - if not vm_path: - mock_job.associators.assert_called_once_with( - self._vmutils._AFFECTED_JOB_ELEMENT_CLASS) - - _conn.new.assert_called_once_with() - self.assertEqual(mock_vs_data.ElementName, fake_vm_name) - mock_vs_man_svc.DefineSystem.assert_called_once_with( - ResourceSettings=[], ReferenceConfiguration=None, - SystemSettings=mock_vs_data.GetText_(1)) - mock_check_ret_val.assert_called_once_with(fake_ret_val, fake_job_path) - - if dynamic_memory_ratio > 1: - self.assertFalse(mock_vs_data.VirtualNumaEnabled) - - mock_get_wmi_obj.assert_called_with('fake vm path') - - self.assertEqual(mock_vs_data.Notes, 'fake notes') - self.assertEqual(mock.sentinel.instance_path, - mock_vs_data.ConfigurationDataRoot) - self.assertEqual(mock.sentinel.instance_path, mock_vs_data.LogDataRoot) - self.assertEqual(mock.sentinel.instance_path, - mock_vs_data.SnapshotDataRoot) - self.assertEqual(mock.sentinel.instance_path, - mock_vs_data.SuspendDataRoot) - self.assertEqual(mock.sentinel.instance_path, - mock_vs_data.SwapFileDataRoot) - self.assertEqual(response, mock_get_wmi_obj()) - - def test_create_vm_obj(self): - self._test_create_vm_obj(vm_path='fake vm path') - - def test_create_vm_obj_no_vm_path(self): - self._test_create_vm_obj(vm_path=None) - - def test_create_vm_obj_dynamic_memory(self): - self._test_create_vm_obj(vm_path=None, dynamic_memory_ratio=1.1) - - def test_list_instances(self): - vs = mock.MagicMock() - attrs = {'ElementName': 'fake_name'} - vs.configure_mock(**attrs) - self._vmutils._conn.Msvm_VirtualSystemSettingData.return_value = [vs] - response = self._vmutils.list_instances() - - self.assertEqual([(attrs['ElementName'])], response) - self._vmutils._conn.Msvm_VirtualSystemSettingData.assert_called_with( - ['ElementName'], - VirtualSystemType=self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED) - - def test_get_attached_disks(self): - mock_scsi_ctrl_path = mock.MagicMock() - expected_query = ("SELECT * FROM %(class_name)s " - "WHERE (ResourceSubType='%(res_sub_type)s' OR " - "ResourceSubType='%(res_sub_type_virt)s' OR " - "ResourceSubType='%(res_sub_type_dvd)s') AND " - "Parent = '%(parent)s'" % - {"class_name": - self._vmutils._RESOURCE_ALLOC_SETTING_DATA_CLASS, - "res_sub_type": - self._vmutils._PHYS_DISK_RES_SUB_TYPE, - "res_sub_type_virt": - self._vmutils._DISK_DRIVE_RES_SUB_TYPE, - "res_sub_type_dvd": - self._vmutils._DVD_DRIVE_RES_SUB_TYPE, - "parent": mock_scsi_ctrl_path.replace("'", "''")}) - expected_disks = self._vmutils._conn.query.return_value - - ret_disks = self._vmutils.get_attached_disks(mock_scsi_ctrl_path) - - self._vmutils._conn.query.assert_called_once_with(expected_query) - self.assertEqual(expected_disks, ret_disks) - - def test_get_vm_dvd_disk_paths(self): - mock_vm = self._lookup_vm() - mock_sasd1 = mock.MagicMock( - ResourceSubType=self._vmutils._DVD_DISK_RES_SUB_TYPE, - HostResource=[mock.sentinel.FAKE_DVD_PATH1]) - mock_settings = mock.MagicMock() - mock_settings.associators.return_value = [mock_sasd1] - mock_vm.associators.return_value = [mock_settings] - - ret_val = self._vmutils.get_vm_dvd_disk_paths(self._FAKE_VM_NAME) - self.assertEqual(mock.sentinel.FAKE_DVD_PATH1, ret_val[0]) - - @mock.patch.object(vmutilsv2.VMUtilsV2, '_get_vm_setting_data') - def _test_get_vm_generation(self, vm_gen, mock_get_vm_setting_data): - self._lookup_vm() - vm_gen_string = "Microsoft:Hyper-V:SubType:" + str(vm_gen) - mock_vssd = mock.MagicMock(VirtualSystemSubType=vm_gen_string) - mock_get_vm_setting_data.return_value = mock_vssd - - ret = self._vmutils.get_vm_generation(mock.sentinel.FAKE_VM_NAME) - - self.assertEqual(vm_gen, ret) - - def test_get_vm_generation_gen1(self): - self._test_get_vm_generation(constants.VM_GEN_1) - - def test_get_vm_generation_gen2(self): - self._test_get_vm_generation(constants.VM_GEN_2) diff --git a/nova/tests/unit/virt/hyperv/test_volumeutils.py b/nova/tests/unit/virt/hyperv/test_volumeutils.py deleted file mode 100644 index 0a3f661cc2a1..000000000000 --- a/nova/tests/unit/virt/hyperv/test_volumeutils.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from oslo_config import cfg - -from nova.tests.unit.virt.hyperv import test_basevolumeutils -from nova.virt.hyperv import vmutils -from nova.virt.hyperv import volumeutils - -CONF = cfg.CONF -CONF.import_opt('volume_attach_retry_count', 'nova.virt.hyperv.volumeops', - 'hyperv') - - -class VolumeUtilsTestCase(test_basevolumeutils.BaseVolumeUtilsTestCase): - """Unit tests for the Hyper-V VolumeUtils class.""" - - _FAKE_PORTAL_ADDR = '10.1.1.1' - _FAKE_PORTAL_PORT = '3260' - _FAKE_LUN = 0 - _FAKE_TARGET = 'iqn.2010-10.org.openstack:fake_target' - - _FAKE_STDOUT_VALUE = 'The operation completed successfully' - - def setUp(self): - super(VolumeUtilsTestCase, self).setUp() - self._volutils = volumeutils.VolumeUtils() - self._volutils._conn_wmi = mock.MagicMock() - self._volutils._conn_cimv2 = mock.MagicMock() - self.flags(volume_attach_retry_count=4, group='hyperv') - self.flags(volume_attach_retry_interval=0, group='hyperv') - - def _test_login_target_portal(self, portal_connected): - fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR, - self._FAKE_PORTAL_PORT) - - self._volutils.execute = mock.MagicMock() - if portal_connected: - exec_output = 'Address and Socket: %s %s' % ( - self._FAKE_PORTAL_ADDR, self._FAKE_PORTAL_PORT) - else: - exec_output = '' - - self._volutils.execute.return_value = exec_output - - self._volutils._login_target_portal(fake_portal) - - call_list = self._volutils.execute.call_args_list - all_call_args = [arg for call in call_list for arg in call[0]] - - if portal_connected: - self.assertIn('RefreshTargetPortal', all_call_args) - else: - self.assertIn('AddTargetPortal', all_call_args) - - def test_login_connected_portal(self): - self._test_login_target_portal(True) - - def test_login_new_portal(self): - self._test_login_target_portal(False) - - def _test_login_target(self, target_connected=False, raise_exception=False, - use_chap=False): - fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR, - self._FAKE_PORTAL_PORT) - self._volutils.execute = mock.MagicMock() - self._volutils._login_target_portal = mock.MagicMock() - - if use_chap: - username, password = (mock.sentinel.username, - mock.sentinel.password) - else: - username, password = None, None - - if target_connected: - self._volutils.execute.return_value = self._FAKE_TARGET - elif raise_exception: - self._volutils.execute.return_value = '' - else: - self._volutils.execute.side_effect = ( - ['', '', '', self._FAKE_TARGET, '']) - - if raise_exception: - self.assertRaises(vmutils.HyperVException, - self._volutils.login_storage_target, - self._FAKE_LUN, self._FAKE_TARGET, - fake_portal, username, password) - else: - self._volutils.login_storage_target(self._FAKE_LUN, - self._FAKE_TARGET, - fake_portal, - username, password) - - if target_connected: - call_list = self._volutils.execute.call_args_list - all_call_args = [arg for call in call_list for arg in call[0]] - self.assertNotIn('qlogintarget', all_call_args) - else: - self._volutils.execute.assert_any_call( - 'iscsicli.exe', 'qlogintarget', - self._FAKE_TARGET, username, password) - - def test_login_connected_target(self): - self._test_login_target(target_connected=True) - - def test_login_disconncted_target(self): - self._test_login_target() - - def test_login_target_exception(self): - self._test_login_target(raise_exception=True) - - def test_login_target_using_chap(self): - self._test_login_target(use_chap=True) - - def _test_execute_wrapper(self, raise_exception): - fake_cmd = ('iscsicli.exe', 'ListTargetPortals') - - if raise_exception: - output = 'fake error' - else: - output = 'The operation completed successfully' - - with mock.patch('nova.utils.execute') as fake_execute: - fake_execute.return_value = (output, None) - - if raise_exception: - self.assertRaises(vmutils.HyperVException, - self._volutils.execute, - *fake_cmd) - else: - ret_val = self._volutils.execute(*fake_cmd) - self.assertEqual(output, ret_val) - - def test_execute_raise_exception(self): - self._test_execute_wrapper(True) - - def test_execute_exception(self): - self._test_execute_wrapper(False) - - @mock.patch.object(volumeutils, 'utils') - def test_logout_storage_target(self, mock_utils): - mock_utils.execute.return_value = (self._FAKE_STDOUT_VALUE, - mock.sentinel.FAKE_STDERR_VALUE) - session = mock.MagicMock() - session.SessionId = mock.sentinel.FAKE_SESSION_ID - self._volutils._conn_wmi.query.return_value = [session] - - self._volutils.logout_storage_target(mock.sentinel.FAKE_IQN) - mock_utils.execute.assert_called_once_with( - 'iscsicli.exe', 'logouttarget', mock.sentinel.FAKE_SESSION_ID) diff --git a/nova/tests/unit/virt/hyperv/test_volumeutilsv2.py b/nova/tests/unit/virt/hyperv/test_volumeutilsv2.py deleted file mode 100644 index 5c73f0ce6a30..000000000000 --- a/nova/tests/unit/virt/hyperv/test_volumeutilsv2.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from oslo_config import cfg - -from nova import test -from nova.virt.hyperv import vmutils -from nova.virt.hyperv import volumeutilsv2 - -CONF = cfg.CONF -CONF.import_opt('volume_attach_retry_count', 'nova.virt.hyperv.volumeops', - 'hyperv') - - -class VolumeUtilsV2TestCase(test.NoDBTestCase): - """Unit tests for the Hyper-V VolumeUtilsV2 class.""" - - _FAKE_PORTAL_ADDR = '10.1.1.1' - _FAKE_PORTAL_PORT = '3260' - _FAKE_LUN = 0 - _FAKE_TARGET = 'iqn.2010-10.org.openstack:fake_target' - - def setUp(self): - super(VolumeUtilsV2TestCase, self).setUp() - self._volutilsv2 = volumeutilsv2.VolumeUtilsV2() - self._volutilsv2._conn_storage = mock.MagicMock() - self._volutilsv2._conn_wmi = mock.MagicMock() - self.flags(volume_attach_retry_count=4, group='hyperv') - self.flags(volume_attach_retry_interval=0, group='hyperv') - - def _test_login_target_portal(self, portal_connected): - fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR, - self._FAKE_PORTAL_PORT) - fake_portal_object = mock.MagicMock() - _query = self._volutilsv2._conn_storage.query - self._volutilsv2._conn_storage.MSFT_iSCSITargetPortal = ( - fake_portal_object) - - if portal_connected: - _query.return_value = [fake_portal_object] - else: - _query.return_value = None - - self._volutilsv2._login_target_portal(fake_portal) - - if portal_connected: - fake_portal_object.Update.assert_called_once_with() - else: - fake_portal_object.New.assert_called_once_with( - TargetPortalAddress=self._FAKE_PORTAL_ADDR, - TargetPortalPortNumber=self._FAKE_PORTAL_PORT) - - def test_login_connected_portal(self): - self._test_login_target_portal(True) - - def test_login_new_portal(self): - self._test_login_target_portal(False) - - def _test_login_target(self, target_connected=False, raise_exception=False, - use_chap=False): - fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR, - self._FAKE_PORTAL_PORT) - - fake_target_object = mock.MagicMock() - - if target_connected: - fake_target_object.IsConnected = True - elif not raise_exception: - type(fake_target_object).IsConnected = mock.PropertyMock( - side_effect=[False, True]) - else: - fake_target_object.IsConnected = False - - _query = self._volutilsv2._conn_storage.query - _query.return_value = [fake_target_object] - - self._volutilsv2._conn_storage.MSFT_iSCSITarget = ( - fake_target_object) - - if use_chap: - username, password = (mock.sentinel.username, - mock.sentinel.password) - auth = { - 'AuthenticationType': self._volutilsv2._CHAP_AUTH_TYPE, - 'ChapUsername': username, - 'ChapSecret': password, - } - else: - username, password = None, None - auth = {} - - if raise_exception: - self.assertRaises(vmutils.HyperVException, - self._volutilsv2.login_storage_target, - self._FAKE_LUN, self._FAKE_TARGET, fake_portal) - else: - self._volutilsv2.login_storage_target(self._FAKE_LUN, - self._FAKE_TARGET, - fake_portal, - username, password) - - if target_connected: - fake_target_object.Update.assert_called_with() - else: - fake_target_object.Connect.assert_called_once_with( - IsPersistent=True, NodeAddress=self._FAKE_TARGET, **auth) - - def test_login_connected_target(self): - self._test_login_target(target_connected=True) - - def test_login_disconncted_target(self): - self._test_login_target() - - def test_login_target_exception(self): - self._test_login_target(raise_exception=True) - - def test_login_target_using_chap(self): - self._test_login_target(use_chap=True) - - def test_logout_storage_target(self): - mock_msft_target = self._volutilsv2._conn_storage.MSFT_iSCSITarget - mock_msft_session = self._volutilsv2._conn_storage.MSFT_iSCSISession - - mock_target = mock.MagicMock() - mock_target.IsConnected = True - mock_msft_target.return_value = [mock_target] - - mock_session = mock.MagicMock() - mock_session.IsPersistent = True - mock_msft_session.return_value = [mock_session] - - self._volutilsv2.logout_storage_target(self._FAKE_TARGET) - - mock_msft_target.assert_called_once_with(NodeAddress=self._FAKE_TARGET) - mock_msft_session.assert_called_once_with( - TargetNodeAddress=self._FAKE_TARGET) - - mock_session.Unregister.assert_called_once_with() - mock_target.Disconnect.assert_called_once_with() - - @mock.patch.object(volumeutilsv2.VolumeUtilsV2, 'logout_storage_target') - def test_execute_log_out(self, mock_logout_target): - sess_class = self._volutilsv2._conn_wmi.MSiSCSIInitiator_SessionClass - - mock_session = mock.MagicMock() - sess_class.return_value = [mock_session] - - self._volutilsv2.execute_log_out(mock.sentinel.FAKE_SESSION_ID) - - sess_class.assert_called_once_with( - SessionId=mock.sentinel.FAKE_SESSION_ID) - mock_logout_target.assert_called_once_with(mock_session.TargetName) diff --git a/nova/virt/hyperv/basevolumeutils.py b/nova/virt/hyperv/basevolumeutils.py deleted file mode 100644 index 5fcb4facebf4..000000000000 --- a/nova/virt/hyperv/basevolumeutils.py +++ /dev/null @@ -1,149 +0,0 @@ -# -# Copyright 2012 Pedro Navarro Perez -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Helper methods for operations related to the management of volumes, -and storage repositories -""" - -import abc -import re -import sys - -if sys.platform == 'win32': - import _winreg - import wmi - -from oslo_log import log as logging - -from nova import block_device -from nova.i18n import _LI -from nova.virt import driver - -LOG = logging.getLogger(__name__) - - -class BaseVolumeUtils(object): - _FILE_DEVICE_DISK = 7 - - def __init__(self, host='.'): - if sys.platform == 'win32': - self._conn_wmi = wmi.WMI(moniker='//%s/root/wmi' % host) - self._conn_cimv2 = wmi.WMI(moniker='//%s/root/cimv2' % host) - self._drive_number_regex = re.compile(r'DeviceID=\"[^,]*\\(\d+)\"') - - @abc.abstractmethod - def login_storage_target(self, target_lun, target_iqn, target_portal): - pass - - @abc.abstractmethod - def logout_storage_target(self, target_iqn): - pass - - @abc.abstractmethod - def execute_log_out(self, session_id): - pass - - def get_iscsi_initiator(self): - """Get iscsi initiator name for this machine.""" - - computer_system = self._conn_cimv2.Win32_ComputerSystem()[0] - hostname = computer_system.name - keypath = ("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\" - "iSCSI\\Discovery") - try: - key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keypath, 0, - _winreg.KEY_ALL_ACCESS) - temp = _winreg.QueryValueEx(key, 'DefaultInitiatorName') - initiator_name = str(temp[0]) - _winreg.CloseKey(key) - except Exception: - LOG.info(_LI("The ISCSI initiator name can't be found. " - "Choosing the default one")) - initiator_name = "iqn.1991-05.com.microsoft:" + hostname.lower() - if computer_system.PartofDomain: - initiator_name += '.' + computer_system.Domain.lower() - return initiator_name - - def volume_in_mapping(self, mount_device, block_device_info): - block_device_list = [block_device.strip_dev(vol['mount_device']) - for vol in - driver.block_device_info_get_mapping( - block_device_info)] - swap = driver.block_device_info_get_swap(block_device_info) - if driver.swap_is_usable(swap): - block_device_list.append( - block_device.strip_dev(swap['device_name'])) - block_device_list += [block_device.strip_dev( - ephemeral['device_name']) - for ephemeral in - driver.block_device_info_get_ephemerals(block_device_info)] - - LOG.debug("block_device_list %s", block_device_list) - return block_device.strip_dev(mount_device) in block_device_list - - def _get_drive_number_from_disk_path(self, disk_path): - drive_number = self._drive_number_regex.findall(disk_path) - if drive_number: - return int(drive_number[0]) - - def get_session_id_from_mounted_disk(self, physical_drive_path): - drive_number = self._get_drive_number_from_disk_path( - physical_drive_path) - if not drive_number: - return None - - initiator_sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass() - for initiator_session in initiator_sessions: - devices = initiator_session.Devices - for device in devices: - device_number = device.DeviceNumber - if device_number == drive_number: - return initiator_session.SessionId - - def _get_devices_for_target(self, target_iqn): - initiator_sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass( - TargetName=target_iqn) - if not initiator_sessions: - return [] - - return initiator_sessions[0].Devices - - def get_device_number_for_target(self, target_iqn, target_lun): - devices = self._get_devices_for_target(target_iqn) - - for device in devices: - if device.ScsiLun == target_lun: - return device.DeviceNumber - - def get_target_lun_count(self, target_iqn): - devices = self._get_devices_for_target(target_iqn) - disk_devices = [device for device in devices - if device.DeviceType == self._FILE_DEVICE_DISK] - return len(disk_devices) - - def get_target_from_disk_path(self, disk_path): - initiator_sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass() - drive_number = self._get_drive_number_from_disk_path(disk_path) - if not drive_number: - return None - - for initiator_session in initiator_sessions: - devices = initiator_session.Devices - for device in devices: - if device.DeviceNumber == drive_number: - return (device.TargetName, device.ScsiLun) diff --git a/nova/virt/hyperv/constants.py b/nova/virt/hyperv/constants.py index 3e079775723d..00e883867623 100644 --- a/nova/virt/hyperv/constants.py +++ b/nova/virt/hyperv/constants.py @@ -60,15 +60,6 @@ PROCESSOR_FEATURE = { 17: 'xsave', } -WMI_JOB_STATUS_STARTED = 4096 -WMI_JOB_STATE_RUNNING = 4 -WMI_JOB_STATE_COMPLETED = 7 - -VM_SUMMARY_NUM_PROCS = 4 -VM_SUMMARY_ENABLED_STATE = 100 -VM_SUMMARY_MEMORY_USAGE = 103 -VM_SUMMARY_UPTIME = 105 - CTRL_TYPE_IDE = "IDE" CTRL_TYPE_SCSI = "SCSI" @@ -85,11 +76,6 @@ DISK_FORMAT_MAP = { DISK_FORMAT_VHD = "VHD" DISK_FORMAT_VHDX = "VHDX" -VHD_TYPE_FIXED = 2 -VHD_TYPE_DYNAMIC = 3 - -SCSI_CONTROLLER_SLOTS_NUMBER = 64 - HOST_POWER_ACTION_SHUTDOWN = "shutdown" HOST_POWER_ACTION_REBOOT = "reboot" HOST_POWER_ACTION_STARTUP = "startup" @@ -99,8 +85,3 @@ IMAGE_PROP_VM_GEN_2 = "hyperv-gen2" VM_GEN_1 = 1 VM_GEN_2 = 2 - -JOB_STATE_COMPLETED = 7 -JOB_STATE_TERMINATED = 8 -JOB_STATE_KILLED = 9 -JOB_STATE_COMPLETED_WITH_WARNINGS = 32768 diff --git a/nova/virt/hyperv/hostutils.py b/nova/virt/hyperv/hostutils.py deleted file mode 100644 index 7f073e6a9cb2..000000000000 --- a/nova/virt/hyperv/hostutils.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import ctypes -import socket -import sys - -if sys.platform == 'win32': - import wmi - -from nova.i18n import _ -from nova.virt.hyperv import constants - - -class HostUtils(object): - - _HOST_FORCED_REBOOT = 6 - _HOST_FORCED_SHUTDOWN = 12 - _DEFAULT_VM_GENERATION = constants.IMAGE_PROP_VM_GEN_1 - - def __init__(self): - if sys.platform == 'win32': - self._conn_cimv2 = wmi.WMI(privileges=["Shutdown"]) - self._init_wmi_virt_conn() - - def _init_wmi_virt_conn(self): - self._conn_virt = None - - def get_cpus_info(self): - cpus = self._conn_cimv2.query("SELECT * FROM Win32_Processor " - "WHERE ProcessorType = 3") - cpus_list = [] - for cpu in cpus: - cpu_info = {'Architecture': cpu.Architecture, - 'Name': cpu.Name, - 'Manufacturer': cpu.Manufacturer, - 'NumberOfCores': cpu.NumberOfCores, - 'NumberOfLogicalProcessors': - cpu.NumberOfLogicalProcessors} - cpus_list.append(cpu_info) - return cpus_list - - def is_cpu_feature_present(self, feature_key): - return ctypes.windll.kernel32.IsProcessorFeaturePresent(feature_key) - - def get_memory_info(self): - """Returns a tuple with total visible memory and free physical memory - expressed in kB. - """ - mem_info = self._conn_cimv2.query("SELECT TotalVisibleMemorySize, " - "FreePhysicalMemory " - "FROM win32_operatingsystem")[0] - return (long(mem_info.TotalVisibleMemorySize), - long(mem_info.FreePhysicalMemory)) - - def get_volume_info(self, drive): - """Returns a tuple with total size and free space - expressed in bytes. - """ - logical_disk = self._conn_cimv2.query("SELECT Size, FreeSpace " - "FROM win32_logicaldisk " - "WHERE DeviceID='%s'" - % drive)[0] - return (long(logical_disk.Size), long(logical_disk.FreeSpace)) - - def check_min_windows_version(self, major, minor, build=0): - version_str = self.get_windows_version() - return map(int, version_str.split('.')) >= [major, minor, build] - - def get_windows_version(self): - return self._conn_cimv2.Win32_OperatingSystem()[0].Version - - def get_local_ips(self): - addr_info = socket.getaddrinfo(socket.gethostname(), None, 0, 0, 0) - # Returns IPv4 and IPv6 addresses, ordered by protocol family - addr_info.sort() - return [a[4][0] for a in addr_info] - - def get_host_tick_count64(self): - return ctypes.windll.kernel32.GetTickCount64() - - def host_power_action(self, action): - win32_os = self._conn_cimv2.Win32_OperatingSystem()[0] - - if action == constants.HOST_POWER_ACTION_SHUTDOWN: - win32_os.Win32Shutdown(self._HOST_FORCED_SHUTDOWN) - elif action == constants.HOST_POWER_ACTION_REBOOT: - win32_os.Win32Shutdown(self._HOST_FORCED_REBOOT) - else: - raise NotImplementedError( - _("Host %(action)s is not supported by the Hyper-V driver") % - {"action": action}) - - def get_supported_vm_types(self): - """Get the supported Hyper-V VM generations. - Hyper-V Generation 2 VMs are supported in Windows 8.1, - Windows Server / Hyper-V Server 2012 R2 or newer. - - :returns: array of supported VM generations (ex. ['hyperv-gen1']) - """ - if self.check_min_windows_version(6, 3): - return [constants.IMAGE_PROP_VM_GEN_1, - constants.IMAGE_PROP_VM_GEN_2] - else: - return [constants.IMAGE_PROP_VM_GEN_1] - - def get_default_vm_generation(self): - return self._DEFAULT_VM_GENERATION diff --git a/nova/virt/hyperv/hostutilsv2.py b/nova/virt/hyperv/hostutilsv2.py deleted file mode 100644 index 7bed7a579771..000000000000 --- a/nova/virt/hyperv/hostutilsv2.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2015 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -if sys.platform == 'win32': - import wmi - -from nova.virt.hyperv import hostutils - - -class HostUtilsV2(hostutils.HostUtils): - - FEATURE_RDS_VIRTUALIZATION = 322 - - def __init__(self): - super(HostUtilsV2, self).__init__() - self._init_wmi_virt_conn() - - def _init_wmi_virt_conn(self): - if sys.platform == 'win32': - self._conn_virt = wmi.WMI(moniker='//./root/virtualization/v2') diff --git a/nova/virt/hyperv/ioutils.py b/nova/virt/hyperv/ioutils.py deleted file mode 100644 index e28deb84ff49..000000000000 --- a/nova/virt/hyperv/ioutils.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2014 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import errno -import os - -from eventlet import patcher -from oslo_log import log as logging - -from nova.i18n import _LE - -LOG = logging.getLogger(__name__) - -native_threading = patcher.original('threading') - - -class IOThread(native_threading.Thread): - def __init__(self, src, dest, max_bytes): - super(IOThread, self).__init__() - self.setDaemon(True) - self._src = src - self._dest = dest - self._dest_archive = dest + '.1' - self._max_bytes = max_bytes - self._stopped = native_threading.Event() - - def run(self): - try: - self._copy() - except IOError as err: - self._stopped.set() - # Invalid argument error means that the vm console pipe was closed, - # probably the vm was stopped. The worker can stop it's execution. - if err.errno != errno.EINVAL: - LOG.error(_LE("Error writing vm console log file from " - "serial console pipe. Error: %s") % err) - - def _copy(self): - with open(self._src, 'rb') as src: - with open(self._dest, 'ab', 0) as dest: - dest.seek(0, os.SEEK_END) - log_size = dest.tell() - while (not self._stopped.isSet()): - # Read one byte at a time to avoid blocking. - data = src.read(1) - dest.write(data) - log_size += len(data) - if (log_size >= self._max_bytes): - dest.close() - if os.path.exists(self._dest_archive): - os.remove(self._dest_archive) - os.rename(self._dest, self._dest_archive) - dest = open(self._dest, 'ab', 0) - log_size = 0 - - def join(self): - self._stopped.set() - super(IOThread, self).join() - - def is_active(self): - return not self._stopped.isSet() diff --git a/nova/virt/hyperv/livemigrationutils.py b/nova/virt/hyperv/livemigrationutils.py deleted file mode 100644 index 8dbcef3002c5..000000000000 --- a/nova/virt/hyperv/livemigrationutils.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -if sys.platform == 'win32': - import wmi - -from oslo_log import log as logging - -from nova import exception -from nova.i18n import _, _LE -from nova.virt.hyperv import vmutils -from nova.virt.hyperv import vmutilsv2 -from nova.virt.hyperv import volumeutilsv2 - -LOG = logging.getLogger(__name__) - - -class LiveMigrationUtils(object): - - def __init__(self): - self._vmutils = vmutilsv2.VMUtilsV2() - self._volutils = volumeutilsv2.VolumeUtilsV2() - - def _get_conn_v2(self, host='localhost'): - try: - return wmi.WMI(moniker='//%s/root/virtualization/v2' % host) - except wmi.x_wmi as ex: - LOG.exception(_LE('Get version 2 connection error')) - if ex.com_error.hresult == -2147217394: - msg = (_('Live migration is not supported on target host "%s"') - % host) - elif ex.com_error.hresult == -2147023174: - msg = (_('Target live migration host "%s" is unreachable') - % host) - else: - msg = _('Live migration failed: %s') % ex.message - raise vmutils.HyperVException(msg) - - def check_live_migration_config(self): - conn_v2 = self._get_conn_v2() - migration_svc = conn_v2.Msvm_VirtualSystemMigrationService()[0] - vsmssds = migration_svc.associators( - wmi_association_class='Msvm_ElementSettingData', - wmi_result_class='Msvm_VirtualSystemMigrationServiceSettingData') - vsmssd = vsmssds[0] - if not vsmssd.EnableVirtualSystemMigration: - raise vmutils.HyperVException( - _('Live migration is not enabled on this host')) - if not migration_svc.MigrationServiceListenerIPAddressList: - raise vmutils.HyperVException( - _('Live migration networks are not configured on this host')) - - def _get_vm(self, conn_v2, vm_name): - vms = conn_v2.Msvm_ComputerSystem(ElementName=vm_name) - n = len(vms) - if not n: - raise exception.InstanceNotFound(_('VM not found: %s') % vm_name) - elif n > 1: - raise vmutils.HyperVException(_('Duplicate VM name found: %s') - % vm_name) - return vms[0] - - def _destroy_planned_vm(self, conn_v2_remote, planned_vm): - LOG.debug("Destroying existing remote planned VM: %s", - planned_vm.ElementName) - vs_man_svc = conn_v2_remote.Msvm_VirtualSystemManagementService()[0] - (job_path, ret_val) = vs_man_svc.DestroySystem(planned_vm.path_()) - self._vmutils.check_ret_val(ret_val, job_path) - - def _check_existing_planned_vm(self, conn_v2_remote, vm): - # Make sure that there's not yet a remote planned VM on the target - # host for this VM - planned_vms = conn_v2_remote.Msvm_PlannedComputerSystem(Name=vm.Name) - if planned_vms: - self._destroy_planned_vm(conn_v2_remote, planned_vms[0]) - - def _create_remote_planned_vm(self, conn_v2_local, conn_v2_remote, - vm, rmt_ip_addr_list, dest_host): - # Staged - vsmsd = conn_v2_local.query("select * from " - "Msvm_VirtualSystemMigrationSettingData " - "where MigrationType = 32770")[0] - vsmsd.DestinationIPAddressList = rmt_ip_addr_list - migration_setting_data = vsmsd.GetText_(1) - - LOG.debug("Creating remote planned VM for VM: %s", - vm.ElementName) - migr_svc = conn_v2_local.Msvm_VirtualSystemMigrationService()[0] - (job_path, ret_val) = migr_svc.MigrateVirtualSystemToHost( - ComputerSystem=vm.path_(), - DestinationHost=dest_host, - MigrationSettingData=migration_setting_data) - self._vmutils.check_ret_val(ret_val, job_path) - - return conn_v2_remote.Msvm_PlannedComputerSystem(Name=vm.Name)[0] - - def _get_physical_disk_paths(self, vm_name): - ide_ctrl_path = self._vmutils.get_vm_ide_controller(vm_name, 0) - if ide_ctrl_path: - ide_paths = self._vmutils.get_controller_volume_paths( - ide_ctrl_path) - else: - ide_paths = {} - - scsi_ctrl_path = self._vmutils.get_vm_scsi_controller(vm_name) - scsi_paths = self._vmutils.get_controller_volume_paths(scsi_ctrl_path) - - return dict(ide_paths.items() + scsi_paths.items()) - - def _get_remote_disk_data(self, vmutils_remote, disk_paths, dest_host): - volutils_remote = volumeutilsv2.VolumeUtilsV2(dest_host) - - disk_paths_remote = {} - for (rasd_rel_path, disk_path) in disk_paths.items(): - target = self._volutils.get_target_from_disk_path(disk_path) - if target: - (target_iqn, target_lun) = target - - dev_num = volutils_remote.get_device_number_for_target( - target_iqn, target_lun) - disk_path_remote = ( - vmutils_remote.get_mounted_disk_by_drive_number(dev_num)) - - disk_paths_remote[rasd_rel_path] = disk_path_remote - else: - LOG.debug("Could not retrieve iSCSI target " - "from disk path: %s", disk_path) - - return disk_paths_remote - - def _update_planned_vm_disk_resources(self, vmutils_remote, conn_v2_remote, - planned_vm, vm_name, - disk_paths_remote): - vm_settings = planned_vm.associators( - wmi_association_class='Msvm_SettingsDefineState', - wmi_result_class='Msvm_VirtualSystemSettingData')[0] - - updated_resource_setting_data = [] - sasds = vm_settings.associators( - wmi_association_class='Msvm_VirtualSystemSettingDataComponent') - for sasd in sasds: - if (sasd.ResourceType == 17 and sasd.ResourceSubType == - "Microsoft:Hyper-V:Physical Disk Drive" and - sasd.HostResource): - # Replace the local disk target with the correct remote one - old_disk_path = sasd.HostResource[0] - new_disk_path = disk_paths_remote.pop(sasd.path().RelPath) - - LOG.debug("Replacing host resource " - "%(old_disk_path)s with " - "%(new_disk_path)s on planned VM %(vm_name)s", - {'old_disk_path': old_disk_path, - 'new_disk_path': new_disk_path, - 'vm_name': vm_name}) - sasd.HostResource = [new_disk_path] - updated_resource_setting_data.append(sasd.GetText_(1)) - - LOG.debug("Updating remote planned VM disk paths for VM: %s", - vm_name) - vsmsvc = conn_v2_remote.Msvm_VirtualSystemManagementService()[0] - (res_settings, job_path, ret_val) = vsmsvc.ModifyResourceSettings( - ResourceSettings=updated_resource_setting_data) - vmutils_remote.check_ret_val(ret_val, job_path) - - def _get_vhd_setting_data(self, vm): - vm_settings = vm.associators( - wmi_association_class='Msvm_SettingsDefineState', - wmi_result_class='Msvm_VirtualSystemSettingData')[0] - - new_resource_setting_data = [] - sasds = vm_settings.associators( - wmi_association_class='Msvm_VirtualSystemSettingDataComponent', - wmi_result_class='Msvm_StorageAllocationSettingData') - for sasd in sasds: - if (sasd.ResourceType == 31 and sasd.ResourceSubType == - "Microsoft:Hyper-V:Virtual Hard Disk"): - new_resource_setting_data.append(sasd.GetText_(1)) - return new_resource_setting_data - - def _live_migrate_vm(self, conn_v2_local, vm, planned_vm, rmt_ip_addr_list, - new_resource_setting_data, dest_host): - # VirtualSystemAndStorage - vsmsd = conn_v2_local.query("select * from " - "Msvm_VirtualSystemMigrationSettingData " - "where MigrationType = 32771")[0] - vsmsd.DestinationIPAddressList = rmt_ip_addr_list - if planned_vm: - vsmsd.DestinationPlannedVirtualSystemId = planned_vm.Name - migration_setting_data = vsmsd.GetText_(1) - - migr_svc = conn_v2_local.Msvm_VirtualSystemMigrationService()[0] - - LOG.debug("Starting live migration for VM: %s", vm.ElementName) - (job_path, ret_val) = migr_svc.MigrateVirtualSystemToHost( - ComputerSystem=vm.path_(), - DestinationHost=dest_host, - MigrationSettingData=migration_setting_data, - NewResourceSettingData=new_resource_setting_data) - self._vmutils.check_ret_val(ret_val, job_path) - - def _get_remote_ip_address_list(self, conn_v2_remote, dest_host): - LOG.debug("Getting live migration networks for remote host: %s", - dest_host) - migr_svc_rmt = conn_v2_remote.Msvm_VirtualSystemMigrationService()[0] - return migr_svc_rmt.MigrationServiceListenerIPAddressList - - def live_migrate_vm(self, vm_name, dest_host): - self.check_live_migration_config() - - conn_v2_local = self._get_conn_v2() - conn_v2_remote = self._get_conn_v2(dest_host) - - vm = self._get_vm(conn_v2_local, vm_name) - self._check_existing_planned_vm(conn_v2_remote, vm) - - rmt_ip_addr_list = self._get_remote_ip_address_list(conn_v2_remote, - dest_host) - - planned_vm = None - disk_paths = self._get_physical_disk_paths(vm_name) - if disk_paths: - vmutils_remote = vmutilsv2.VMUtilsV2(dest_host) - disk_paths_remote = self._get_remote_disk_data(vmutils_remote, - disk_paths, - dest_host) - - planned_vm = self._create_remote_planned_vm(conn_v2_local, - conn_v2_remote, - vm, rmt_ip_addr_list, - dest_host) - - self._update_planned_vm_disk_resources(vmutils_remote, - conn_v2_remote, planned_vm, - vm_name, disk_paths_remote) - - new_resource_setting_data = self._get_vhd_setting_data(vm) - self._live_migrate_vm(conn_v2_local, vm, planned_vm, rmt_ip_addr_list, - new_resource_setting_data, dest_host) diff --git a/nova/virt/hyperv/networkutils.py b/nova/virt/hyperv/networkutils.py deleted file mode 100644 index 07ad489187a0..000000000000 --- a/nova/virt/hyperv/networkutils.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utility class for network related operations. -""" - -import sys -import uuid - -if sys.platform == 'win32': - import wmi - -from nova.i18n import _ -from nova.virt.hyperv import vmutils - - -class NetworkUtils(object): - def __init__(self): - if sys.platform == 'win32': - self._conn = wmi.WMI(moniker='//./root/virtualization') - - def get_external_vswitch(self, vswitch_name): - if vswitch_name: - vswitches = self._conn.Msvm_VirtualSwitch(ElementName=vswitch_name) - else: - # Find the vswitch that is connected to the first physical nic. - ext_port = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0] - port = ext_port.associators(wmi_result_class='Msvm_SwitchPort')[0] - vswitches = port.associators(wmi_result_class='Msvm_VirtualSwitch') - - if not len(vswitches): - raise vmutils.HyperVException(_('vswitch "%s" not found') - % vswitch_name) - return vswitches[0].path_() - - def create_vswitch_port(self, vswitch_path, port_name): - switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] - # Create a port on the vswitch. - (new_port, ret_val) = switch_svc.CreateSwitchPort( - Name=str(uuid.uuid4()), - FriendlyName=port_name, - ScopeOfResidence="", - VirtualSwitch=vswitch_path) - if ret_val != 0: - raise vmutils.HyperVException(_("Failed to create vswitch port " - "%(port_name)s on switch " - "%(vswitch_path)s") % - {'port_name': port_name, - 'vswitch_path': vswitch_path}) - return new_port - - def vswitch_port_needed(self): - # NOTE(alexpilotti): In WMI V2 the vswitch_path is set in the VM - # setting data without the need for a vswitch port. - return True diff --git a/nova/virt/hyperv/networkutilsv2.py b/nova/virt/hyperv/networkutilsv2.py deleted file mode 100644 index 558f7c44cd8b..000000000000 --- a/nova/virt/hyperv/networkutilsv2.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utility class for network related operations. -Based on the "root/virtualization/v2" namespace available starting with -Hyper-V Server / Windows Server 2012. -""" - -import sys - -if sys.platform == 'win32': - import wmi - -from nova.i18n import _ -from nova.virt.hyperv import networkutils -from nova.virt.hyperv import vmutils - - -class NetworkUtilsV2(networkutils.NetworkUtils): - def __init__(self): - if sys.platform == 'win32': - self._conn = wmi.WMI(moniker='//./root/virtualization/v2') - - def get_external_vswitch(self, vswitch_name): - if vswitch_name: - vswitches = self._conn.Msvm_VirtualEthernetSwitch( - ElementName=vswitch_name) - if not len(vswitches): - raise vmutils.HyperVException(_('vswitch "%s" not found') - % vswitch_name) - else: - # Find the vswitch that is connected to the first physical nic. - ext_port = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0] - lep = ext_port.associators(wmi_result_class='Msvm_LANEndpoint')[0] - lep1 = lep.associators(wmi_result_class='Msvm_LANEndpoint')[0] - esw = lep1.associators( - wmi_result_class='Msvm_EthernetSwitchPort')[0] - vswitches = esw.associators( - wmi_result_class='Msvm_VirtualEthernetSwitch') - - if not len(vswitches): - raise vmutils.HyperVException(_('No external vswitch found')) - - return vswitches[0].path_() - - def create_vswitch_port(self, vswitch_path, port_name): - raise NotImplementedError() - - def vswitch_port_needed(self): - return False diff --git a/nova/virt/hyperv/rdpconsoleutils.py b/nova/virt/hyperv/rdpconsoleutils.py deleted file mode 100644 index c897ea8c73d2..000000000000 --- a/nova/virt/hyperv/rdpconsoleutils.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -class RDPConsoleUtils(object): - _DEFAULT_HYPERV_RDP_PORT = 2179 - - def get_rdp_console_port(self): - return self._DEFAULT_HYPERV_RDP_PORT diff --git a/nova/virt/hyperv/rdpconsoleutilsv2.py b/nova/virt/hyperv/rdpconsoleutilsv2.py deleted file mode 100644 index 4c173e227809..000000000000 --- a/nova/virt/hyperv/rdpconsoleutilsv2.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -from nova.virt.hyperv import rdpconsoleutils - -if sys.platform == 'win32': - import wmi - - -class RDPConsoleUtilsV2(rdpconsoleutils.RDPConsoleUtils): - def __init__(self): - if sys.platform == 'win32': - self._conn = wmi.WMI(moniker='//./root/virtualization/v2') - - def get_rdp_console_port(self): - rdp_setting_data = self._conn.Msvm_TerminalServiceSettingData()[0] - return rdp_setting_data.ListenerPort diff --git a/nova/virt/hyperv/utilsfactory.py b/nova/virt/hyperv/utilsfactory.py deleted file mode 100644 index 96c612777ed6..000000000000 --- a/nova/virt/hyperv/utilsfactory.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg -from oslo_log import log as logging - -from nova.virt.hyperv import hostutils -from nova.virt.hyperv import hostutilsv2 -from nova.virt.hyperv import livemigrationutils -from nova.virt.hyperv import networkutilsv2 -from nova.virt.hyperv import pathutils -from nova.virt.hyperv import rdpconsoleutilsv2 -from nova.virt.hyperv import vhdutilsv2 -from nova.virt.hyperv import vmutilsv2 -from nova.virt.hyperv import volumeutils -from nova.virt.hyperv import volumeutilsv2 - -CONF = cfg.CONF - -LOG = logging.getLogger(__name__) -CONF.import_group('hyperv', 'os_win.utilsfactory') - -utils = hostutils.HostUtils() - - -def _get_class(v1_class, v2_class, force_v1_flag=False): - # V2 classes are supported starting from Hyper-V Server 2012 and - # Windows Server 2012 (kernel version 6.2) - if not force_v1_flag and utils.check_min_windows_version(6, 2): - cls = v2_class - else: - cls = v1_class - LOG.debug("Loading class: %(module_name)s.%(class_name)s", - {'module_name': cls.__module__, 'class_name': cls.__name__}) - return cls - - -def get_vmutils(host='.'): - return vmutilsv2.VMUtilsV2(host) - - -def get_vhdutils(): - return vhdutilsv2.VHDUtilsV2() - - -def get_networkutils(): - return networkutilsv2.NetworkUtilsV2() - - -def get_hostutils(): - return hostutilsv2.HostUtilsV2() - - -def get_pathutils(): - return pathutils.PathUtils() - - -def get_volumeutils(): - return _get_class(volumeutils.VolumeUtils, volumeutilsv2.VolumeUtilsV2, - CONF.hyperv.force_volumeutils_v1)() - - -def get_livemigrationutils(): - return livemigrationutils.LiveMigrationUtils() - - -def get_rdpconsoleutils(): - return rdpconsoleutilsv2.RDPConsoleUtilsV2() diff --git a/nova/virt/hyperv/vhdutils.py b/nova/virt/hyperv/vhdutils.py deleted file mode 100644 index e5ac1311b79e..000000000000 --- a/nova/virt/hyperv/vhdutils.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utility class for VHD related operations. - -Official VHD format specs can be retrieved at: -http://technet.microsoft.com/en-us/library/bb676673.aspx -See "Download the Specifications Without Registering" - -Official VHDX format specs can be retrieved at: -http://www.microsoft.com/en-us/download/details.aspx?id=34750 -""" -import struct -import sys - -if sys.platform == 'win32': - import wmi - -from xml.etree import ElementTree - -from nova.i18n import _ -from nova.virt.hyperv import constants -from nova.virt.hyperv import vmutils - - -VHD_HEADER_SIZE_FIX = 512 -VHD_BAT_ENTRY_SIZE = 4 -VHD_DYNAMIC_DISK_HEADER_SIZE = 1024 -VHD_HEADER_SIZE_DYNAMIC = 512 -VHD_FOOTER_SIZE_DYNAMIC = 512 -VHD_BLK_SIZE_OFFSET = 544 - -VHD_SIGNATURE = 'conectix' -VHDX_SIGNATURE = 'vhdxfile' - - -class VHDUtils(object): - - def __init__(self): - self._vmutils = vmutils.VMUtils() - if sys.platform == 'win32': - self._conn = wmi.WMI(moniker='//./root/virtualization') - - def validate_vhd(self, vhd_path): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - - (job_path, ret_val) = image_man_svc.ValidateVirtualHardDisk( - Path=vhd_path) - self._vmutils.check_ret_val(ret_val, job_path) - - def create_dynamic_vhd(self, path, max_internal_size, format): - if format != constants.DISK_FORMAT_VHD: - raise vmutils.HyperVException(_("Unsupported disk format: %s") % - format) - - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - - (job_path, ret_val) = image_man_svc.CreateDynamicVirtualHardDisk( - Path=path, MaxInternalSize=max_internal_size) - self._vmutils.check_ret_val(ret_val, job_path) - - def create_differencing_vhd(self, path, parent_path): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - - (job_path, ret_val) = image_man_svc.CreateDifferencingVirtualHardDisk( - Path=path, ParentPath=parent_path) - self._vmutils.check_ret_val(ret_val, job_path) - - def reconnect_parent_vhd(self, child_vhd_path, parent_vhd_path): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - - (job_path, ret_val) = image_man_svc.ReconnectParentVirtualHardDisk( - ChildPath=child_vhd_path, - ParentPath=parent_vhd_path, - Force=True) - self._vmutils.check_ret_val(ret_val, job_path) - - def merge_vhd(self, src_vhd_path, dest_vhd_path): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - - (job_path, ret_val) = image_man_svc.MergeVirtualHardDisk( - SourcePath=src_vhd_path, - DestinationPath=dest_vhd_path) - self._vmutils.check_ret_val(ret_val, job_path) - - def _get_resize_method(self): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - return image_man_svc.ExpandVirtualHardDisk - - def resize_vhd(self, vhd_path, new_max_size, is_file_max_size=True): - if is_file_max_size: - new_internal_max_size = self.get_internal_vhd_size_by_file_size( - vhd_path, new_max_size) - else: - new_internal_max_size = new_max_size - - resize = self._get_resize_method() - - (job_path, ret_val) = resize( - Path=vhd_path, MaxInternalSize=new_internal_max_size) - self._vmutils.check_ret_val(ret_val, job_path) - - def get_internal_vhd_size_by_file_size(self, vhd_path, new_vhd_file_size): - """Fixed VHD size = Data Block size + 512 bytes - | Dynamic_VHD_size = Dynamic Disk Header - | + Copy of hard disk footer - | + Hard Disk Footer - | + Data Block - | + BAT - | Dynamic Disk header fields - | Copy of hard disk footer (512 bytes) - | Dynamic Disk Header (1024 bytes) - | BAT (Block Allocation table) - | Data Block 1 - | Data Block 2 - | Data Block n - | Hard Disk Footer (512 bytes) - | Default block size is 2M - | BAT entry size is 4byte - """ - base_vhd_info = self.get_vhd_info(vhd_path) - vhd_type = base_vhd_info['Type'] - - if vhd_type == constants.VHD_TYPE_FIXED: - vhd_header_size = VHD_HEADER_SIZE_FIX - return new_vhd_file_size - vhd_header_size - elif vhd_type == constants.VHD_TYPE_DYNAMIC: - bs = self._get_vhd_dynamic_blk_size(vhd_path) - bes = VHD_BAT_ENTRY_SIZE - ddhs = VHD_DYNAMIC_DISK_HEADER_SIZE - hs = VHD_HEADER_SIZE_DYNAMIC - fs = VHD_FOOTER_SIZE_DYNAMIC - - max_internal_size = (new_vhd_file_size - - (hs + ddhs + fs)) * bs / (bes + bs) - return max_internal_size - else: - vhd_parent = self.get_vhd_parent_path(vhd_path) - return self.get_internal_vhd_size_by_file_size(vhd_parent, - new_vhd_file_size) - - def _get_vhd_dynamic_blk_size(self, vhd_path): - blk_size_offset = VHD_BLK_SIZE_OFFSET - try: - with open(vhd_path, "rb") as f: - f.seek(blk_size_offset) - version = f.read(4) - except IOError: - raise vmutils.HyperVException(_("Unable to obtain block size from" - " VHD %(vhd_path)s") % - {"vhd_path": vhd_path}) - return struct.unpack('>i', version)[0] - - def get_vhd_parent_path(self, vhd_path): - return self.get_vhd_info(vhd_path).get("ParentPath") - - def get_vhd_info(self, vhd_path): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - - (vhd_info, - job_path, - ret_val) = image_man_svc.GetVirtualHardDiskInfo(vhd_path) - self._vmutils.check_ret_val(ret_val, job_path) - - vhd_info_dict = {} - - et = ElementTree.fromstring(vhd_info) - for item in et.findall("PROPERTY"): - name = item.attrib["NAME"] - value_text = item.find("VALUE").text - if name == "ParentPath": - vhd_info_dict[name] = value_text - elif name in ["FileSize", "MaxInternalSize"]: - vhd_info_dict[name] = long(value_text) - elif name in ["InSavedState", "InUse"]: - vhd_info_dict[name] = bool(value_text) - elif name == "Type": - vhd_info_dict[name] = int(value_text) - - return vhd_info_dict - - def get_vhd_format(self, path): - with open(path, 'rb') as f: - # Read header - if f.read(8) == VHDX_SIGNATURE: - return constants.DISK_FORMAT_VHDX - - # Read footer - f.seek(0, 2) - file_size = f.tell() - if file_size >= 512: - f.seek(-512, 2) - if f.read(8) == VHD_SIGNATURE: - return constants.DISK_FORMAT_VHD - - raise vmutils.HyperVException(_('Unsupported virtual disk format')) - - def get_best_supported_vhd_format(self): - return constants.DISK_FORMAT_VHD diff --git a/nova/virt/hyperv/vhdutilsv2.py b/nova/virt/hyperv/vhdutilsv2.py deleted file mode 100644 index 07f267d42a76..000000000000 --- a/nova/virt/hyperv/vhdutilsv2.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Utility class for VHD related operations. -Based on the "root/virtualization/v2" namespace available starting with -Hyper-V Server / Windows Server 2012. -""" -import struct -import sys - -if sys.platform == 'win32': - import wmi - -from xml.etree import ElementTree - -from oslo_utils import units - -from nova.i18n import _ -from nova.virt.hyperv import constants -from nova.virt.hyperv import vhdutils -from nova.virt.hyperv import vmutils -from nova.virt.hyperv import vmutilsv2 - - -VHDX_BAT_ENTRY_SIZE = 8 -VHDX_HEADER_OFFSETS = [64 * units.Ki, 128 * units.Ki] -VHDX_HEADER_SECTION_SIZE = units.Mi -VHDX_LOG_LENGTH_OFFSET = 68 -VHDX_METADATA_SIZE_OFFSET = 64 -VHDX_REGION_TABLE_OFFSET = 192 * units.Ki -VHDX_BS_METADATA_ENTRY_OFFSET = 48 - - -class VHDUtilsV2(vhdutils.VHDUtils): - - _VHD_TYPE_DYNAMIC = 3 - _VHD_TYPE_DIFFERENCING = 4 - - _vhd_format_map = { - constants.DISK_FORMAT_VHD: 2, - constants.DISK_FORMAT_VHDX: 3, - } - - def __init__(self): - self._vmutils = vmutilsv2.VMUtilsV2() - if sys.platform == 'win32': - self._conn = wmi.WMI(moniker='//./root/virtualization/v2') - - def create_dynamic_vhd(self, path, max_internal_size, format): - vhd_format = self._vhd_format_map.get(format) - if not vhd_format: - raise vmutils.HyperVException(_("Unsupported disk format: %s") % - format) - - self._create_vhd(self._VHD_TYPE_DYNAMIC, vhd_format, path, - max_internal_size=max_internal_size) - - def create_differencing_vhd(self, path, parent_path): - # Although this method can take a size argument in case of VHDX - # images, avoid it as the underlying Win32 is currently not - # resizing the disk properly. This can be reconsidered once the - # Win32 issue is fixed. - parent_vhd_info = self.get_vhd_info(parent_path) - self._create_vhd(self._VHD_TYPE_DIFFERENCING, - parent_vhd_info["Format"], - path, parent_path=parent_path) - - def _create_vhd(self, vhd_type, format, path, max_internal_size=None, - parent_path=None): - vhd_info = self._conn.Msvm_VirtualHardDiskSettingData.new() - - vhd_info.Type = vhd_type - vhd_info.Format = format - vhd_info.Path = path - vhd_info.ParentPath = parent_path - - if max_internal_size: - vhd_info.MaxInternalSize = max_internal_size - - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - (job_path, ret_val) = image_man_svc.CreateVirtualHardDisk( - VirtualDiskSettingData=vhd_info.GetText_(1)) - self._vmutils.check_ret_val(ret_val, job_path) - - def reconnect_parent_vhd(self, child_vhd_path, parent_vhd_path): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - vhd_info_xml = self._get_vhd_info_xml(image_man_svc, child_vhd_path) - - et = ElementTree.fromstring(vhd_info_xml) - item = et.find(".//PROPERTY[@NAME='ParentPath']/VALUE") - if item is not None: - item.text = parent_vhd_path - else: - msg = (_("Failed to reconnect image %(child_vhd_path)s to " - "parent %(parent_vhd_path)s. The child image has no " - "parent path property.") % - {'child_vhd_path': child_vhd_path, - 'parent_vhd_path': parent_vhd_path}) - raise vmutils.HyperVException(msg) - - vhd_info_xml = ElementTree.tostring(et) - - (job_path, ret_val) = image_man_svc.SetVirtualHardDiskSettingData( - VirtualDiskSettingData=vhd_info_xml) - - self._vmutils.check_ret_val(ret_val, job_path) - - def _get_resize_method(self): - image_man_svc = self._conn.Msvm_ImageManagementService()[0] - return image_man_svc.ResizeVirtualHardDisk - - def get_internal_vhd_size_by_file_size(self, vhd_path, - new_vhd_file_size): - """Get internal size of a VHD according to new VHD file size. - - VHDX Size = Header (1MB) + Log + Metadata Region + BAT + Payload Blocks - - The chunk size is the maximum number of bytes described by a SB - block. - - Chunk size = 2^{23} * LogicalSectorSize - - :param str vhd_path: VHD file path - :param new_vhd_file_size: Size of the new VHD file. - :return: Internal VHD size according to new VHD file size. - """ - vhd_format = self.get_vhd_format(vhd_path) - if vhd_format == constants.DISK_FORMAT_VHD: - return super(VHDUtilsV2, - self).get_internal_vhd_size_by_file_size( - vhd_path, new_vhd_file_size) - else: - vhd_info = self.get_vhd_info(vhd_path) - vhd_type = vhd_info['Type'] - if vhd_type == self._VHD_TYPE_DIFFERENCING: - vhd_parent = self.get_vhd_parent_path(vhd_path) - return self.get_internal_vhd_size_by_file_size(vhd_parent, - new_vhd_file_size) - else: - try: - with open(vhd_path, 'rb') as f: - hs = VHDX_HEADER_SECTION_SIZE - bes = VHDX_BAT_ENTRY_SIZE - - lss = vhd_info['LogicalSectorSize'] - bs = self._get_vhdx_block_size(f) - ls = self._get_vhdx_log_size(f) - ms = self._get_vhdx_metadata_size_and_offset(f)[0] - - chunk_ratio = (1 << 23) * lss / bs - size = new_vhd_file_size - - max_internal_size = (bs * chunk_ratio * (size - hs - - ls - ms - bes - bes / chunk_ratio) / (bs * - chunk_ratio + bes * chunk_ratio + bes)) - - return max_internal_size - (max_internal_size % bs) - - except IOError as ex: - raise vmutils.HyperVException(_("Unable to obtain " - "internal size from VHDX: " - "%(vhd_path)s. Exception: " - "%(ex)s") % - {"vhd_path": vhd_path, - "ex": ex}) - - def _get_vhdx_current_header_offset(self, vhdx_file): - sequence_numbers = [] - for offset in VHDX_HEADER_OFFSETS: - vhdx_file.seek(offset + 8) - sequence_numbers.append(struct.unpack(' 1: - raise HyperVException(_('Duplicate VM name found: %s') % vm_name) - else: - return vms[0] - - def vm_exists(self, vm_name): - return self._lookup_vm(vm_name) is not None - - def get_vm_id(self, vm_name): - vm = self._lookup_vm_check(vm_name) - return vm.Name - - def _get_vm_setting_data(self, vm): - vmsettings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - # Avoid snapshots - return [s for s in vmsettings if s.SettingType == 3][0] - - def _set_vm_memory(self, vm, vmsetting, memory_mb, dynamic_memory_ratio): - mem_settings = vmsetting.associators( - wmi_result_class=self._MEMORY_SETTING_DATA_CLASS)[0] - - max_mem = long(memory_mb) - mem_settings.Limit = max_mem - - if dynamic_memory_ratio > 1: - mem_settings.DynamicMemoryEnabled = True - # Must be a multiple of 2 - reserved_mem = min( - long(max_mem / dynamic_memory_ratio) >> 1 << 1, - max_mem) - else: - mem_settings.DynamicMemoryEnabled = False - reserved_mem = max_mem - - mem_settings.Reservation = reserved_mem - # Start with the minimum memory - mem_settings.VirtualQuantity = reserved_mem - - self._modify_virt_resource(mem_settings, vm.path_()) - - def _set_vm_vcpus(self, vm, vmsetting, vcpus_num, limit_cpu_features): - procsetting = vmsetting.associators( - wmi_result_class=self._PROCESSOR_SETTING_DATA_CLASS)[0] - vcpus = long(vcpus_num) - procsetting.VirtualQuantity = vcpus - procsetting.Reservation = vcpus - procsetting.Limit = 100000 # static assignment to 100% - procsetting.LimitProcessorFeatures = limit_cpu_features - - self._modify_virt_resource(procsetting, vm.path_()) - - def update_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features, - dynamic_memory_ratio): - vm = self._lookup_vm_check(vm_name) - vmsetting = self._get_vm_setting_data(vm) - self._set_vm_memory(vm, vmsetting, memory_mb, dynamic_memory_ratio) - self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features) - - def check_admin_permissions(self): - if not self._conn.Msvm_VirtualSystemManagementService(): - msg = _("The Windows account running nova-compute on this Hyper-V" - " host doesn't have the required permissions to create or" - " operate the virtual machine.") - raise HyperVAuthorizationException(msg) - - def create_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features, - dynamic_memory_ratio, vm_gen, instance_path, notes=None): - """Creates a VM.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - - LOG.debug('Creating VM %s', vm_name) - vm = self._create_vm_obj(vs_man_svc, vm_name, vm_gen, notes, - dynamic_memory_ratio, instance_path) - - vmsetting = self._get_vm_setting_data(vm) - - LOG.debug('Setting memory for vm %s', vm_name) - self._set_vm_memory(vm, vmsetting, memory_mb, dynamic_memory_ratio) - - LOG.debug('Set vCPUs for vm %s', vm_name) - self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features) - - def _create_vm_obj(self, vs_man_svc, vm_name, vm_gen, notes, - dynamic_memory_ratio, instance_path): - vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new() - vs_gs_data.ElementName = vm_name - # Don't start automatically on host boot - vs_gs_data.AutomaticStartupAction = self._AUTOMATIC_STARTUP_ACTION_NONE - vs_gs_data.ExternalDataRoot = instance_path - vs_gs_data.SnapshotDataRoot = instance_path - - (vm_path, - job_path, - ret_val) = vs_man_svc.DefineVirtualSystem([], None, - vs_gs_data.GetText_(1)) - self.check_ret_val(ret_val, job_path) - - vm = self._get_wmi_obj(vm_path) - - if notes: - vmsetting = self._get_vm_setting_data(vm) - vmsetting.Notes = '\n'.join(notes) - self._modify_virtual_system(vs_man_svc, vm_path, vmsetting) - - return self._get_wmi_obj(vm_path) - - def _modify_virtual_system(self, vs_man_svc, vm_path, vmsetting): - (job_path, ret_val) = vs_man_svc.ModifyVirtualSystem( - ComputerSystem=vm_path, - SystemSettingData=vmsetting.GetText_(1))[1:] - self.check_ret_val(ret_val, job_path) - - def get_vm_scsi_controller(self, vm_name): - vm = self._lookup_vm_check(vm_name) - return self._get_vm_scsi_controller(vm) - - def _get_vm_scsi_controller(self, vm): - vmsettings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - rasds = vmsettings[0].associators( - wmi_result_class=self._RESOURCE_ALLOC_SETTING_DATA_CLASS) - res = [r for r in rasds - if r.ResourceSubType == self._SCSI_CTRL_RES_SUB_TYPE][0] - return res.path_() - - def _get_vm_ide_controller(self, vm, ctrller_addr): - vmsettings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - rasds = vmsettings[0].associators( - wmi_result_class=self._RESOURCE_ALLOC_SETTING_DATA_CLASS) - ide_ctrls = [r for r in rasds - if r.ResourceSubType == self._IDE_CTRL_RES_SUB_TYPE - and r.Address == str(ctrller_addr)] - - return ide_ctrls[0].path_() if ide_ctrls else None - - def get_vm_ide_controller(self, vm_name, ctrller_addr): - vm = self._lookup_vm_check(vm_name) - return self._get_vm_ide_controller(vm, ctrller_addr) - - def get_attached_disks(self, scsi_controller_path): - volumes = self._conn.query( - self._get_attached_disks_query_string(scsi_controller_path)) - return volumes - - def _get_attached_disks_query_string(self, scsi_controller_path): - return ("SELECT * FROM %(class_name)s WHERE (" - "ResourceSubType='%(res_sub_type)s' OR " - "ResourceSubType='%(res_sub_type_virt)s') AND " - "Parent='%(parent)s'" % { - 'class_name': self._RESOURCE_ALLOC_SETTING_DATA_CLASS, - 'res_sub_type': self._PHYS_DISK_RES_SUB_TYPE, - 'res_sub_type_virt': self._DISK_DRIVE_RES_SUB_TYPE, - 'parent': scsi_controller_path.replace("'", "''")}) - - def _get_new_setting_data(self, class_name): - obj = self._conn.query("SELECT * FROM %s WHERE InstanceID " - "LIKE '%%\\Default'" % class_name)[0] - return self._check_clone_wmi_obj(class_name, obj) - - def _get_new_resource_setting_data(self, resource_sub_type, - class_name=None): - if class_name is None: - class_name = self._RESOURCE_ALLOC_SETTING_DATA_CLASS - obj = self._conn.query("SELECT * FROM %(class_name)s " - "WHERE ResourceSubType = " - "'%(res_sub_type)s' AND " - "InstanceID LIKE '%%\\Default'" % - {"class_name": class_name, - "res_sub_type": resource_sub_type})[0] - return self._check_clone_wmi_obj(class_name, obj) - - def _check_clone_wmi_obj(self, class_name, obj): - if self._clone_wmi_objs: - return self._clone_wmi_obj(class_name, obj) - else: - return obj - - def _clone_wmi_obj(self, class_name, obj): - wmi_class = getattr(self._conn, class_name) - new_obj = wmi_class.new() - # Copy the properties from the original. - for prop in obj._properties: - value = obj.Properties_.Item(prop).Value - new_obj.Properties_.Item(prop).Value = value - return new_obj - - def attach_scsi_drive(self, vm_name, path, drive_type=constants.DISK): - vm = self._lookup_vm_check(vm_name) - ctrller_path = self._get_vm_scsi_controller(vm) - drive_addr = self.get_free_controller_slot(ctrller_path) - self.attach_drive(vm_name, path, ctrller_path, drive_addr, drive_type) - - def attach_ide_drive(self, vm_name, path, ctrller_addr, drive_addr, - drive_type=constants.DISK): - vm = self._lookup_vm_check(vm_name) - ctrller_path = self._get_vm_ide_controller(vm, ctrller_addr) - self.attach_drive(vm_name, path, ctrller_path, drive_addr, drive_type) - - def attach_drive(self, vm_name, path, ctrller_path, drive_addr, - drive_type=constants.DISK): - """Create a drive and attach it to the vm.""" - - vm = self._lookup_vm_check(vm_name) - - if drive_type == constants.DISK: - res_sub_type = self._DISK_DRIVE_RES_SUB_TYPE - elif drive_type == constants.DVD: - res_sub_type = self._DVD_DRIVE_RES_SUB_TYPE - - drive = self._get_new_resource_setting_data(res_sub_type) - - # Set the ctrller as parent. - drive.Parent = ctrller_path - drive.Address = drive_addr - # Add the cloned disk drive object to the vm. - new_resources = self._add_virt_resource(drive, vm.path_()) - drive_path = new_resources[0] - - if drive_type == constants.DISK: - res_sub_type = self._HARD_DISK_RES_SUB_TYPE - elif drive_type == constants.DVD: - res_sub_type = self._DVD_DISK_RES_SUB_TYPE - - res = self._get_new_resource_setting_data(res_sub_type) - # Set the new drive as the parent. - res.Parent = drive_path - res.Connection = [path] - - # Add the new vhd object as a virtual hard disk to the vm. - self._add_virt_resource(res, vm.path_()) - - def create_scsi_controller(self, vm_name): - """Create an iscsi controller ready to mount volumes.""" - - vm = self._lookup_vm_check(vm_name) - scsicontrl = self._get_new_resource_setting_data( - self._SCSI_CTRL_RES_SUB_TYPE) - - scsicontrl.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}'] - self._add_virt_resource(scsicontrl, vm.path_()) - - def attach_volume_to_controller(self, vm_name, controller_path, address, - mounted_disk_path): - """Attach a volume to a controller.""" - - vm = self._lookup_vm_check(vm_name) - - diskdrive = self._get_new_resource_setting_data( - self._PHYS_DISK_RES_SUB_TYPE) - - diskdrive.Address = address - diskdrive.Parent = controller_path - diskdrive.HostResource = [mounted_disk_path] - self._add_virt_resource(diskdrive, vm.path_()) - - def _get_disk_resource_address(self, disk_resource): - return disk_resource.Address - - def set_disk_host_resource(self, vm_name, controller_path, address, - mounted_disk_path): - disk_found = False - vm = self._lookup_vm_check(vm_name) - (disk_resources, volume_resources) = self._get_vm_disks(vm) - for disk_resource in disk_resources + volume_resources: - if (disk_resource.Parent == controller_path and - self._get_disk_resource_address(disk_resource) == - str(address)): - if (disk_resource.HostResource and - disk_resource.HostResource[0] != mounted_disk_path): - LOG.debug('Updating disk host resource "%(old)s" to ' - '"%(new)s"' % - {'old': disk_resource.HostResource[0], - 'new': mounted_disk_path}) - disk_resource.HostResource = [mounted_disk_path] - self._modify_virt_resource(disk_resource, vm.path_()) - disk_found = True - break - if not disk_found: - LOG.warning(_LW('Disk not found on controller ' - '"%(controller_path)s" with ' - 'address "%(address)s"'), - {'controller_path': controller_path, - 'address': address}) - - def set_nic_connection(self, vm_name, nic_name, vswitch_conn_data): - nic_data = self._get_nic_data_by_name(nic_name) - nic_data.Connection = [vswitch_conn_data] - - vm = self._lookup_vm_check(vm_name) - self._modify_virt_resource(nic_data, vm.path_()) - - def destroy_nic(self, vm_name, nic_name): - """Destroys the NIC with the given nic_name from the given VM. - - :param vm_name: The name of the VM which has the NIC to be destroyed. - :param nic_name: The NIC's ElementName. - """ - nic_data = self._get_nic_data_by_name(nic_name) - - vm = self._lookup_vm_check(vm_name) - self._remove_virt_resource(nic_data, vm.path_()) - - def _get_nic_data_by_name(self, name): - return self._conn.Msvm_SyntheticEthernetPortSettingData( - ElementName=name)[0] - - def create_nic(self, vm_name, nic_name, mac_address): - """Create a (synthetic) nic and attach it to the vm.""" - # Create a new nic - new_nic_data = self._get_new_setting_data( - self._SYNTHETIC_ETHERNET_PORT_SETTING_DATA_CLASS) - - # Configure the nic - new_nic_data.ElementName = nic_name - new_nic_data.Address = mac_address.replace(':', '') - new_nic_data.StaticMacAddress = 'True' - new_nic_data.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}'] - - # Add the new nic to the vm - vm = self._lookup_vm_check(vm_name) - - self._add_virt_resource(new_nic_data, vm.path_()) - - def soft_shutdown_vm(self, vm_name): - vm = self._lookup_vm_check(vm_name) - shutdown_component = vm.associators( - wmi_result_class=self._SHUTDOWN_COMPONENT) - - if not shutdown_component: - # If no shutdown_component is found, it means the VM is already - # in a shutdown state. - return - - reason = 'Soft shutdown requested by OpenStack Nova.' - (ret_val, ) = shutdown_component[0].InitiateShutdown(Force=False, - Reason=reason) - self.check_ret_val(ret_val, None) - - def set_vm_state(self, vm_name, req_state): - """Set the desired state of the VM.""" - vm = self._lookup_vm_check(vm_name) - (job_path, - ret_val) = vm.RequestStateChange(self._vm_power_states_map[req_state]) - # Invalid state for current operation (32775) typically means that - # the VM is already in the state requested - self.check_ret_val(ret_val, job_path, [0, 32775]) - LOG.debug("Successfully changed vm state of %(vm_name)s " - "to %(req_state)s", - {'vm_name': vm_name, 'req_state': req_state}) - - def _get_disk_resource_disk_path(self, disk_resource): - return disk_resource.Connection - - def get_vm_storage_paths(self, vm_name): - vm = self._lookup_vm_check(vm_name) - (disk_resources, volume_resources) = self._get_vm_disks(vm) - - volume_drives = [] - for volume_resource in volume_resources: - drive_path = volume_resource.HostResource[0] - volume_drives.append(drive_path) - - disk_files = [] - for disk_resource in disk_resources: - disk_files.extend( - [c for c in self._get_disk_resource_disk_path(disk_resource)]) - - return (disk_files, volume_drives) - - def _get_vm_disks(self, vm): - vmsettings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - rasds = vmsettings[0].associators( - wmi_result_class=self._STORAGE_ALLOC_SETTING_DATA_CLASS) - disk_resources = [r for r in rasds if - r.ResourceSubType in - [self._HARD_DISK_RES_SUB_TYPE, - self._DVD_DISK_RES_SUB_TYPE]] - - if (self._RESOURCE_ALLOC_SETTING_DATA_CLASS != - self._STORAGE_ALLOC_SETTING_DATA_CLASS): - rasds = vmsettings[0].associators( - wmi_result_class=self._RESOURCE_ALLOC_SETTING_DATA_CLASS) - - volume_resources = [r for r in rasds if - r.ResourceSubType == self._PHYS_DISK_RES_SUB_TYPE] - - return (disk_resources, volume_resources) - - def destroy_vm(self, vm_name): - vm = self._lookup_vm_check(vm_name) - - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - # Remove the VM. Does not destroy disks. - (job_path, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) - self.check_ret_val(ret_val, job_path) - - def check_ret_val(self, ret_val, job_path, success_values=[0]): - if ret_val == constants.WMI_JOB_STATUS_STARTED: - return self._wait_for_job(job_path) - elif ret_val not in success_values: - raise HyperVException(_('Operation failed with return value: %s') - % ret_val) - - def _wait_for_job(self, job_path): - """Poll WMI job state and wait for completion.""" - job = self._get_wmi_obj(job_path) - - while job.JobState == constants.WMI_JOB_STATE_RUNNING: - time.sleep(0.1) - job = self._get_wmi_obj(job_path) - if job.JobState == constants.JOB_STATE_KILLED: - LOG.debug("WMI job killed with status %s.", job.JobState) - return job - - if job.JobState != constants.WMI_JOB_STATE_COMPLETED: - job_state = job.JobState - if job.path().Class == "Msvm_ConcreteJob": - err_sum_desc = job.ErrorSummaryDescription - err_desc = job.ErrorDescription - err_code = job.ErrorCode - raise HyperVException(_("WMI job failed with status " - "%(job_state)d. Error details: " - "%(err_sum_desc)s - %(err_desc)s - " - "Error code: %(err_code)d") % - {'job_state': job_state, - 'err_sum_desc': err_sum_desc, - 'err_desc': err_desc, - 'err_code': err_code}) - else: - (error, ret_val) = job.GetError() - if not ret_val and error: - raise HyperVException(_("WMI job failed with status " - "%(job_state)d. Error details: " - "%(error)s") % - {'job_state': job_state, - 'error': error}) - else: - raise HyperVException(_("WMI job failed with status " - "%d. No error " - "description available") % - job_state) - desc = job.Description - elap = job.ElapsedTime - LOG.debug("WMI job succeeded: %(desc)s, Elapsed=%(elap)s", - {'desc': desc, 'elap': elap}) - return job - - def _get_wmi_obj(self, path): - return wmi.WMI(moniker=path.replace('\\', '/')) - - def _add_virt_resource(self, res_setting_data, vm_path): - """Adds a new resource to the VM.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - res_xml = [res_setting_data.GetText_(1)] - (job_path, - new_resources, - ret_val) = vs_man_svc.AddVirtualSystemResources(res_xml, vm_path) - self.check_ret_val(ret_val, job_path) - return new_resources - - def _modify_virt_resource(self, res_setting_data, vm_path): - """Updates a VM resource.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, ret_val) = vs_man_svc.ModifyVirtualSystemResources( - ResourceSettingData=[res_setting_data.GetText_(1)], - ComputerSystem=vm_path) - self.check_ret_val(ret_val, job_path) - - def _remove_virt_resource(self, res_setting_data, vm_path): - """Removes a VM resource.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - res_path = [res_setting_data.path_()] - (job_path, ret_val) = vs_man_svc.RemoveVirtualSystemResources(res_path, - vm_path) - self.check_ret_val(ret_val, job_path) - - def take_vm_snapshot(self, vm_name): - vm = self._lookup_vm_check(vm_name) - - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - - (job_path, ret_val, - snp_setting_data) = vs_man_svc.CreateVirtualSystemSnapshot(vm.path_()) - self.check_ret_val(ret_val, job_path) - - job_wmi_path = job_path.replace('\\', '/') - job = wmi.WMI(moniker=job_wmi_path) - snp_setting_data = job.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS)[0] - return snp_setting_data.path_() - - def remove_vm_snapshot(self, snapshot_path): - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - - (job_path, ret_val) = vs_man_svc.RemoveVirtualSystemSnapshot( - snapshot_path) - self.check_ret_val(ret_val, job_path) - - def detach_vm_disk(self, vm_name, disk_path, is_physical=True): - vm = self._lookup_vm_check(vm_name) - disk_resource = self._get_mounted_disk_resource_from_path(disk_path, - is_physical) - - if disk_resource: - parent = self._conn.query("SELECT * FROM " - "Msvm_ResourceAllocationSettingData " - "WHERE __PATH = '%s'" % - disk_resource.Parent)[0] - - self._remove_virt_resource(disk_resource, vm.path_()) - if not is_physical: - self._remove_virt_resource(parent, vm.path_()) - - def _get_mounted_disk_resource_from_path(self, disk_path, is_physical): - if is_physical: - class_name = self._RESOURCE_ALLOC_SETTING_DATA_CLASS - res_sub_type = self._PHYS_DISK_RES_SUB_TYPE - conn_attr = self._PHYS_DISK_CONNECTION_ATTR - else: - class_name = self._STORAGE_ALLOC_SETTING_DATA_CLASS - res_sub_type = self._HARD_DISK_RES_SUB_TYPE - conn_attr = self._VIRT_DISK_CONNECTION_ATTR - - disk_resources = self._conn.query("SELECT * FROM %(class_name)s " - "WHERE ResourceSubType = " - "'%(res_sub_type)s'" % - {"class_name": class_name, - "res_sub_type": res_sub_type}) - - for disk_resource in disk_resources: - conn = getattr(disk_resource, conn_attr, None) - if conn and conn[0].lower() == disk_path.lower(): - return disk_resource - - def get_mounted_disk_by_drive_number(self, device_number): - mounted_disks = self._conn.query("SELECT * FROM Msvm_DiskDrive " - "WHERE DriveNumber=" + - str(device_number)) - if len(mounted_disks): - return mounted_disks[0].path_() - - def get_controller_volume_paths(self, controller_path): - disks = self._conn.query("SELECT * FROM %(class_name)s " - "WHERE ResourceSubType = '%(res_sub_type)s' " - "AND Parent='%(parent)s'" % - {"class_name": - self._RESOURCE_ALLOC_SETTING_DATA_CLASS, - "res_sub_type": - self._PHYS_DISK_RES_SUB_TYPE, - "parent": - controller_path}) - disk_data = {} - for disk in disks: - if disk.HostResource: - disk_data[disk.path().RelPath] = disk.HostResource[0] - return disk_data - - def get_free_controller_slot(self, scsi_controller_path): - attached_disks = self.get_attached_disks(scsi_controller_path) - used_slots = [int(self._get_disk_resource_address(disk)) - for disk in attached_disks] - - for slot in range(constants.SCSI_CONTROLLER_SLOTS_NUMBER): - if slot not in used_slots: - return slot - raise HyperVException(_("Exceeded the maximum number of slots")) - - def enable_vm_metrics_collection(self, vm_name): - raise NotImplementedError(_("Metrics collection is not supported on " - "this version of Hyper-V")) - - def get_vm_serial_port_connection(self, vm_name, update_connection=None): - vm = self._lookup_vm_check(vm_name) - - vmsettings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - rasds = vmsettings[0].associators( - wmi_result_class=self._SERIAL_PORT_SETTING_DATA_CLASS) - serial_port = ( - [r for r in rasds if - r.ResourceSubType == self._SERIAL_PORT_RES_SUB_TYPE][0]) - - if update_connection: - serial_port.Connection = [update_connection] - self._modify_virt_resource(serial_port, vm.path_()) - - if len(serial_port.Connection) > 0: - return serial_port.Connection[0] - - def get_active_instances(self): - """Return the names of all the active instances known to Hyper-V.""" - vm_names = self.list_instances() - vms = [self._lookup_vm(vm_name) for vm_name in vm_names] - active_vm_names = [v.ElementName for v in vms - if v.EnabledState == constants.HYPERV_VM_STATE_ENABLED] - - return active_vm_names - - def get_vm_power_state_change_listener(self, timeframe, filtered_states): - field = self._VM_ENABLED_STATE_PROP - query = self._get_event_wql_query(cls=self._COMPUTER_SYSTEM_CLASS, - field=field, - timeframe=timeframe, - filtered_states=filtered_states) - return self._conn.Msvm_ComputerSystem.watch_for(raw_wql=query, - fields=[field]) - - def _get_event_wql_query(self, cls, field, - timeframe, filtered_states=None): - """Return a WQL query used for polling WMI events. - - :param cls: the WMI class polled for events - :param field: the field checked - :param timeframe: check for events that occurred in - the specified timeframe - :param filtered_states: only catch events triggered when a WMI - object transitioned into one of those - states. - """ - query = ("SELECT %(field)s, TargetInstance " - "FROM __InstanceModificationEvent " - "WITHIN %(timeframe)s " - "WHERE TargetInstance ISA '%(class)s' " - "AND TargetInstance.%(field)s != " - "PreviousInstance.%(field)s" % - {'class': cls, - 'field': field, - 'timeframe': timeframe}) - if filtered_states: - checks = ["TargetInstance.%s = '%s'" % (field, state) - for state in filtered_states] - query += " AND (%s)" % " OR ".join(checks) - return query - - def _get_instance_notes(self, vm_name): - vm = self._lookup_vm_check(vm_name) - vmsettings = self._get_vm_setting_data(vm) - return [note for note in vmsettings.Notes.split('\n') if note] - - def get_instance_uuid(self, vm_name): - instance_notes = self._get_instance_notes(vm_name) - if instance_notes and uuidutils.is_uuid_like(instance_notes[0]): - return instance_notes[0] - - def get_vm_power_state(self, vm_enabled_state): - return self._enabled_states_map.get(vm_enabled_state, - constants.HYPERV_VM_STATE_OTHER) - - def get_vm_generation(self, vm_name): - return constants.VM_GEN_1 - - def stop_vm_jobs(self, vm_name): - vm = self._lookup_vm_check(vm_name) - vm_jobs = vm.associators(wmi_result_class=self._CONCRETE_JOB_CLASS) - - for job in vm_jobs: - if job and job.Cancellable and not self._is_job_completed(job): - - job.RequestStateChange(self._KILL_JOB_STATE_CHANGE_REQUEST) - - return vm_jobs - - def _is_job_completed(self, job): - return job.JobState in self._completed_job_states diff --git a/nova/virt/hyperv/vmutilsv2.py b/nova/virt/hyperv/vmutilsv2.py deleted file mode 100644 index b647b8fb4a1e..000000000000 --- a/nova/virt/hyperv/vmutilsv2.py +++ /dev/null @@ -1,346 +0,0 @@ -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Utility class for VM related operations. -Based on the "root/virtualization/v2" namespace available starting with -Hyper-V Server / Windows Server 2012. -""" - -import sys -import uuid - -if sys.platform == 'win32': - import wmi - -from oslo_config import cfg -from oslo_log import log as logging - -from nova.virt.hyperv import constants -from nova.virt.hyperv import hostutils -from nova.virt.hyperv import vmutils - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class VMUtilsV2(vmutils.VMUtils): - - _PHYS_DISK_RES_SUB_TYPE = 'Microsoft:Hyper-V:Physical Disk Drive' - _DISK_DRIVE_RES_SUB_TYPE = 'Microsoft:Hyper-V:Synthetic Disk Drive' - _DVD_DRIVE_RES_SUB_TYPE = 'Microsoft:Hyper-V:Synthetic DVD Drive' - _SCSI_RES_SUBTYPE = 'Microsoft:Hyper-V:Synthetic SCSI Controller' - _HARD_DISK_RES_SUB_TYPE = 'Microsoft:Hyper-V:Virtual Hard Disk' - _DVD_DISK_RES_SUB_TYPE = 'Microsoft:Hyper-V:Virtual CD/DVD Disk' - _IDE_CTRL_RES_SUB_TYPE = 'Microsoft:Hyper-V:Emulated IDE Controller' - _SCSI_CTRL_RES_SUB_TYPE = 'Microsoft:Hyper-V:Synthetic SCSI Controller' - _SERIAL_PORT_RES_SUB_TYPE = 'Microsoft:Hyper-V:Serial Port' - - _VIRTUAL_SYSTEM_SUBTYPE = 'VirtualSystemSubType' - _VIRTUAL_SYSTEM_TYPE_REALIZED = 'Microsoft:Hyper-V:System:Realized' - _VIRTUAL_SYSTEM_SUBTYPE_GEN2 = 'Microsoft:Hyper-V:SubType:2' - - _SNAPSHOT_FULL = 2 - - _METRIC_AGGR_CPU_AVG = 'Aggregated Average CPU Utilization' - _METRIC_AGGR_MEMORY_AVG = 'Aggregated Average Memory Utilization' - _METRIC_ENABLED = 2 - - _STORAGE_ALLOC_SETTING_DATA_CLASS = 'Msvm_StorageAllocationSettingData' - _ETHERNET_PORT_ALLOCATION_SETTING_DATA_CLASS = \ - 'Msvm_EthernetPortAllocationSettingData' - - _VIRT_DISK_CONNECTION_ATTR = "HostResource" - - _AUTOMATIC_STARTUP_ACTION_NONE = 2 - - _vm_power_states_map = {constants.HYPERV_VM_STATE_ENABLED: 2, - constants.HYPERV_VM_STATE_DISABLED: 3, - constants.HYPERV_VM_STATE_SHUTTING_DOWN: 4, - constants.HYPERV_VM_STATE_REBOOT: 11, - constants.HYPERV_VM_STATE_PAUSED: 9, - constants.HYPERV_VM_STATE_SUSPENDED: 6} - - def __init__(self, host='.'): - if sys.platform == 'win32': - # A separate WMI class for VM serial ports has been introduced - # in Windows 10 / Windows Server 2016 - if hostutils.HostUtils().check_min_windows_version(10, 0): - self._SERIAL_PORT_SETTING_DATA_CLASS = ( - "Msvm_SerialPortSettingData") - super(VMUtilsV2, self).__init__(host) - - def _init_hyperv_wmi_conn(self, host): - self._conn = wmi.WMI(moniker='//%s/root/virtualization/v2' % host) - - def list_instance_notes(self): - instance_notes = [] - - for vs in self._conn.Msvm_VirtualSystemSettingData( - ['ElementName', 'Notes'], - VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED): - if vs.Notes is not None: - instance_notes.append( - (vs.ElementName, [v for v in vs.Notes if v])) - - return instance_notes - - def list_instances(self): - """Return the names of all the instances known to Hyper-V.""" - return [v.ElementName for v in - self._conn.Msvm_VirtualSystemSettingData( - ['ElementName'], - VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED)] - - def _create_vm_obj(self, vs_man_svc, vm_name, vm_gen, notes, - dynamic_memory_ratio, instance_path): - vs_data = self._conn.Msvm_VirtualSystemSettingData.new() - vs_data.ElementName = vm_name - vs_data.Notes = notes - # Don't start automatically on host boot - vs_data.AutomaticStartupAction = self._AUTOMATIC_STARTUP_ACTION_NONE - - # vNUMA and dynamic memory are mutually exclusive - if dynamic_memory_ratio > 1: - vs_data.VirtualNumaEnabled = False - - if vm_gen == constants.VM_GEN_2: - vs_data.VirtualSystemSubType = self._VIRTUAL_SYSTEM_SUBTYPE_GEN2 - vs_data.SecureBootEnabled = False - - # Created VMs must have their *DataRoot paths in the same location as - # the instances' path. - vs_data.ConfigurationDataRoot = instance_path - vs_data.LogDataRoot = instance_path - vs_data.SnapshotDataRoot = instance_path - vs_data.SuspendDataRoot = instance_path - vs_data.SwapFileDataRoot = instance_path - - (job_path, - vm_path, - ret_val) = vs_man_svc.DefineSystem(ResourceSettings=[], - ReferenceConfiguration=None, - SystemSettings=vs_data.GetText_(1)) - job = self.check_ret_val(ret_val, job_path) - if not vm_path and job: - vm_path = job.associators(self._AFFECTED_JOB_ELEMENT_CLASS)[0] - return self._get_wmi_obj(vm_path) - - def _get_vm_setting_data(self, vm): - vmsettings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS) - # Avoid snapshots - return [s for s in vmsettings if - s.VirtualSystemType == self._VIRTUAL_SYSTEM_TYPE_REALIZED][0] - - def _get_attached_disks_query_string(self, scsi_controller_path): - # DVD Drives can be attached to SCSI as well, if the VM Generation is 2 - return ("SELECT * FROM Msvm_ResourceAllocationSettingData WHERE (" - "ResourceSubType='%(res_sub_type)s' OR " - "ResourceSubType='%(res_sub_type_virt)s' OR " - "ResourceSubType='%(res_sub_type_dvd)s') AND " - "Parent = '%(parent)s'" % { - 'res_sub_type': self._PHYS_DISK_RES_SUB_TYPE, - 'res_sub_type_virt': self._DISK_DRIVE_RES_SUB_TYPE, - 'res_sub_type_dvd': self._DVD_DRIVE_RES_SUB_TYPE, - 'parent': scsi_controller_path.replace("'", "''")}) - - def attach_drive(self, vm_name, path, ctrller_path, drive_addr, - drive_type=constants.DISK): - """Create a drive and attach it to the vm.""" - - vm = self._lookup_vm_check(vm_name) - - if drive_type == constants.DISK: - res_sub_type = self._DISK_DRIVE_RES_SUB_TYPE - elif drive_type == constants.DVD: - res_sub_type = self._DVD_DRIVE_RES_SUB_TYPE - - drive = self._get_new_resource_setting_data(res_sub_type) - - # Set the ctrller as parent. - drive.Parent = ctrller_path - drive.Address = drive_addr - drive.AddressOnParent = drive_addr - # Add the cloned disk drive object to the vm. - new_resources = self._add_virt_resource(drive, vm.path_()) - drive_path = new_resources[0] - - if drive_type == constants.DISK: - res_sub_type = self._HARD_DISK_RES_SUB_TYPE - elif drive_type == constants.DVD: - res_sub_type = self._DVD_DISK_RES_SUB_TYPE - - res = self._get_new_resource_setting_data( - res_sub_type, self._STORAGE_ALLOC_SETTING_DATA_CLASS) - - res.Parent = drive_path - res.HostResource = [path] - - self._add_virt_resource(res, vm.path_()) - - def attach_volume_to_controller(self, vm_name, controller_path, address, - mounted_disk_path): - """Attach a volume to a controller.""" - - vm = self._lookup_vm_check(vm_name) - - diskdrive = self._get_new_resource_setting_data( - self._PHYS_DISK_RES_SUB_TYPE) - - diskdrive.AddressOnParent = address - diskdrive.Parent = controller_path - diskdrive.HostResource = [mounted_disk_path] - - self._add_virt_resource(diskdrive, vm.path_()) - - def _get_disk_resource_address(self, disk_resource): - return disk_resource.AddressOnParent - - def create_scsi_controller(self, vm_name): - """Create an iscsi controller ready to mount volumes.""" - scsicontrl = self._get_new_resource_setting_data( - self._SCSI_RES_SUBTYPE) - - scsicontrl.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}'] - - vm = self._lookup_vm_check(vm_name) - self._add_virt_resource(scsicontrl, vm.path_()) - - def _get_disk_resource_disk_path(self, disk_resource): - return disk_resource.HostResource - - def destroy_vm(self, vm_name): - vm = self._lookup_vm_check(vm_name) - - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - # Remove the VM. It does not destroy any associated virtual disk. - (job_path, ret_val) = vs_man_svc.DestroySystem(vm.path_()) - self.check_ret_val(ret_val, job_path) - - def _add_virt_resource(self, res_setting_data, vm_path): - """Adds a new resource to the VM.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - res_xml = [res_setting_data.GetText_(1)] - (job_path, - new_resources, - ret_val) = vs_man_svc.AddResourceSettings(vm_path, res_xml) - self.check_ret_val(ret_val, job_path) - return new_resources - - def _modify_virt_resource(self, res_setting_data, vm_path): - """Updates a VM resource.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job_path, - out_res_setting_data, - ret_val) = vs_man_svc.ModifyResourceSettings( - ResourceSettings=[res_setting_data.GetText_(1)]) - self.check_ret_val(ret_val, job_path) - - def _remove_virt_resource(self, res_setting_data, vm_path): - """Removes a VM resource.""" - vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - res_path = [res_setting_data.path_()] - (job_path, ret_val) = vs_man_svc.RemoveResourceSettings(res_path) - self.check_ret_val(ret_val, job_path) - - def get_vm_state(self, vm_name): - settings = self.get_vm_summary_info(vm_name) - return settings['EnabledState'] - - def take_vm_snapshot(self, vm_name): - vm = self._lookup_vm_check(vm_name) - vs_snap_svc = self._conn.Msvm_VirtualSystemSnapshotService()[0] - - (job_path, snp_setting_data, ret_val) = vs_snap_svc.CreateSnapshot( - AffectedSystem=vm.path_(), - SnapshotType=self._SNAPSHOT_FULL) - self.check_ret_val(ret_val, job_path) - - job_wmi_path = job_path.replace('\\', '/') - job = wmi.WMI(moniker=job_wmi_path) - snp_setting_data = job.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS)[0] - - return snp_setting_data.path_() - - def remove_vm_snapshot(self, snapshot_path): - vs_snap_svc = self._conn.Msvm_VirtualSystemSnapshotService()[0] - (job_path, ret_val) = vs_snap_svc.DestroySnapshot(snapshot_path) - self.check_ret_val(ret_val, job_path) - - def set_nic_connection(self, vm_name, nic_name, vswitch_conn_data): - nic_data = self._get_nic_data_by_name(nic_name) - - eth_port_data = self._get_new_setting_data( - self._ETHERNET_PORT_ALLOCATION_SETTING_DATA_CLASS) - - eth_port_data.HostResource = [vswitch_conn_data] - eth_port_data.Parent = nic_data.path_() - - vm = self._lookup_vm_check(vm_name) - self._add_virt_resource(eth_port_data, vm.path_()) - - def enable_vm_metrics_collection(self, vm_name): - metric_names = [self._METRIC_AGGR_CPU_AVG, - self._METRIC_AGGR_MEMORY_AVG] - - vm = self._lookup_vm_check(vm_name) - metric_svc = self._conn.Msvm_MetricService()[0] - (disks, volumes) = self._get_vm_disks(vm) - filtered_disks = [d for d in disks if - d.ResourceSubType is not self._DVD_DISK_RES_SUB_TYPE] - - # enable metrics for disk. - for disk in filtered_disks: - self._enable_metrics(metric_svc, disk) - - for metric_name in metric_names: - metric_def = self._conn.CIM_BaseMetricDefinition(Name=metric_name) - if not metric_def: - LOG.debug("Metric not found: %s", metric_name) - else: - self._enable_metrics(metric_svc, vm, metric_def[0].path_()) - - def _enable_metrics(self, metric_svc, element, definition_path=None): - metric_svc.ControlMetrics( - Subject=element.path_(), - Definition=definition_path, - MetricCollectionEnabled=self._METRIC_ENABLED) - - def get_vm_dvd_disk_paths(self, vm_name): - vm = self._lookup_vm_check(vm_name) - - settings = vm.associators( - wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA_CLASS)[0] - sasds = settings.associators( - wmi_result_class=self._STORAGE_ALLOC_SETTING_DATA_CLASS) - - dvd_paths = [sasd.HostResource[0] for sasd in sasds - if sasd.ResourceSubType == self._DVD_DISK_RES_SUB_TYPE] - - return dvd_paths - - def _get_instance_notes(self, vm_name): - vm = self._lookup_vm_check(vm_name) - vmsettings = self._get_vm_setting_data(vm) - return [note for note in vmsettings.Notes if note] - - def get_vm_generation(self, vm_name): - vm = self._lookup_vm_check(vm_name) - vssd = self._get_vm_setting_data(vm) - if hasattr(vssd, self._VIRTUAL_SYSTEM_SUBTYPE): - # expected format: 'Microsoft:Hyper-V:SubType:2' - return int(vssd.VirtualSystemSubType.split(':')[-1]) - return constants.VM_GEN_1 diff --git a/nova/virt/hyperv/volumeutils.py b/nova/virt/hyperv/volumeutils.py deleted file mode 100644 index fa5f2ff93e61..000000000000 --- a/nova/virt/hyperv/volumeutils.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright 2012 Pedro Navarro Perez -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Helper methods for operations related to the management of volumes, -and storage repositories - -Official Microsoft iSCSI Initiator and iSCSI command line interface -documentation can be retrieved at: -http://www.microsoft.com/en-us/download/details.aspx?id=34750 -""" - -import re -import time - -from oslo_config import cfg -from oslo_log import log as logging -from six.moves import range - -from nova.i18n import _ -from nova import utils -from nova.virt.hyperv import basevolumeutils -from nova.virt.hyperv import vmutils - -LOG = logging.getLogger(__name__) - -CONF = cfg.CONF - - -class VolumeUtils(basevolumeutils.BaseVolumeUtils): - - def __init__(self): - super(VolumeUtils, self).__init__() - - def execute(self, *args, **kwargs): - stdout_value, stderr_value = utils.execute(*args, **kwargs) - if stdout_value.find('The operation completed successfully') == -1: - raise vmutils.HyperVException(_('An error has occurred when ' - 'calling the iscsi initiator: %s') - % stdout_value) - return stdout_value - - def _login_target_portal(self, target_portal): - (target_address, - target_port) = utils.parse_server_string(target_portal) - - output = self.execute('iscsicli.exe', 'ListTargetPortals') - pattern = r'Address and Socket *: (.*)' - portals = [addr.split() for addr in re.findall(pattern, output)] - LOG.debug("Ensuring connection to portal: %s" % target_portal) - if [target_address, str(target_port)] in portals: - self.execute('iscsicli.exe', 'RefreshTargetPortal', - target_address, target_port) - else: - # Adding target portal to iscsi initiator. Sending targets - self.execute('iscsicli.exe', 'AddTargetPortal', - target_address, target_port, - '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', - '*', '*') - - def login_storage_target(self, target_lun, target_iqn, target_portal, - auth_username=None, auth_password=None): - """Ensure that the target is logged in.""" - - self._login_target_portal(target_portal) - # Listing targets - self.execute('iscsicli.exe', 'ListTargets') - - retry_count = CONF.hyperv.volume_attach_retry_count - - # If the target is not connected, at least two iterations are needed: - # one for performing the login and another one for checking if the - # target was logged in successfully. - if retry_count < 2: - retry_count = 2 - - for attempt in range(retry_count): - try: - session_info = self.execute('iscsicli.exe', 'SessionList') - if session_info.find(target_iqn) == -1: - # Sending login - self.execute('iscsicli.exe', 'qlogintarget', target_iqn, - auth_username, auth_password) - else: - return - except vmutils.HyperVException as exc: - LOG.debug("Attempt %(attempt)d to connect to target " - "%(target_iqn)s failed. Retrying. " - "Exceptipn: %(exc)s ", - {'target_iqn': target_iqn, - 'exc': exc, - 'attempt': attempt}) - time.sleep(CONF.hyperv.volume_attach_retry_interval) - - raise vmutils.HyperVException(_('Failed to login target %s') % - target_iqn) - - def logout_storage_target(self, target_iqn): - """Logs out storage target through its session id.""" - - sessions = self._conn_wmi.query("SELECT * FROM " - "MSiSCSIInitiator_SessionClass " - "WHERE TargetName='%s'" % target_iqn) - for session in sessions: - self.execute_log_out(session.SessionId) - - def execute_log_out(self, session_id): - """Executes log out of the session described by its session ID.""" - self.execute('iscsicli.exe', 'logouttarget', session_id) diff --git a/nova/virt/hyperv/volumeutilsv2.py b/nova/virt/hyperv/volumeutilsv2.py deleted file mode 100644 index 0a29e01d4867..000000000000 --- a/nova/virt/hyperv/volumeutilsv2.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright 2012 Pedro Navarro Perez -# Copyright 2013 Cloudbase Solutions Srl -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Helper methods for operations related to the management of volumes -and storage repositories on Windows Server 2012 and above -""" -import sys -import time - -if sys.platform == 'win32': - import wmi - -from oslo_config import cfg -from oslo_log import log as logging -from six.moves import range - -from nova.i18n import _ -from nova import utils -from nova.virt.hyperv import basevolumeutils -from nova.virt.hyperv import vmutils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils): - _CHAP_AUTH_TYPE = 'ONEWAYCHAP' - - def __init__(self, host='.'): - super(VolumeUtilsV2, self).__init__(host) - - storage_namespace = '//%s/root/microsoft/windows/storage' % host - if sys.platform == 'win32': - self._conn_storage = wmi.WMI(moniker=storage_namespace) - - def _login_target_portal(self, target_portal): - (target_address, - target_port) = utils.parse_server_string(target_portal) - - # Checking if the portal is already connected. - portal = self._conn_storage.query("SELECT * FROM " - "MSFT_iSCSITargetPortal " - "WHERE TargetPortalAddress='%s' " - "AND TargetPortalPortNumber='%s'" - % (target_address, target_port)) - if portal: - portal[0].Update() - else: - # Adding target portal to iscsi initiator. Sending targets - portal = self._conn_storage.MSFT_iSCSITargetPortal - portal.New(TargetPortalAddress=target_address, - TargetPortalPortNumber=target_port) - - def login_storage_target(self, target_lun, target_iqn, target_portal, - auth_username=None, auth_password=None): - """Ensure that the target is logged in.""" - - self._login_target_portal(target_portal) - - retry_count = CONF.hyperv.volume_attach_retry_count - - # If the target is not connected, at least two iterations are needed: - # one for performing the login and another one for checking if the - # target was logged in successfully. - if retry_count < 2: - retry_count = 2 - - for attempt in range(retry_count): - target = self._conn_storage.query("SELECT * FROM MSFT_iSCSITarget " - "WHERE NodeAddress='%s' " % - target_iqn) - - if target and target[0].IsConnected: - if attempt == 0: - # The target was already connected but an update may be - # required - target[0].Update() - return - try: - target = self._conn_storage.MSFT_iSCSITarget - auth = {} - if auth_username and auth_password: - auth['AuthenticationType'] = self._CHAP_AUTH_TYPE - auth['ChapUsername'] = auth_username - auth['ChapSecret'] = auth_password - target.Connect(NodeAddress=target_iqn, - IsPersistent=True, **auth) - time.sleep(CONF.hyperv.volume_attach_retry_interval) - except wmi.x_wmi as exc: - LOG.debug("Attempt %(attempt)d to connect to target " - "%(target_iqn)s failed. Retrying. " - "WMI exception: %(exc)s " % - {'target_iqn': target_iqn, - 'exc': exc, - 'attempt': attempt}) - raise vmutils.HyperVException(_('Failed to login target %s') % - target_iqn) - - def logout_storage_target(self, target_iqn): - """Logs out storage target through its session id.""" - targets = self._conn_storage.MSFT_iSCSITarget(NodeAddress=target_iqn) - if targets: - target = targets[0] - if target.IsConnected: - sessions = self._conn_storage.MSFT_iSCSISession( - TargetNodeAddress=target_iqn) - - for session in sessions: - if session.IsPersistent: - session.Unregister() - - target.Disconnect() - - def execute_log_out(self, session_id): - sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass( - SessionId=session_id) - if sessions: - self.logout_storage_target(sessions[0].TargetName) diff --git a/tests-py3.txt b/tests-py3.txt index 4ede0b456b81..b70da7e0a49a 100644 --- a/tests-py3.txt +++ b/tests-py3.txt @@ -241,29 +241,16 @@ nova.tests.unit.virt.disk.mount.test_nbd.NbdTestCase nova.tests.unit.virt.hyperv.test_driver.HyperVDriverTestCase nova.tests.unit.virt.hyperv.test_eventhandler.EventHandlerTestCase nova.tests.unit.virt.hyperv.test_hostops.HostOpsTestCase -nova.tests.unit.virt.hyperv.test_hostutils.HostUtilsTestCase -nova.tests.unit.virt.hyperv.test_ioutils.IOThreadTestCase nova.tests.unit.virt.hyperv.test_livemigrationops.LiveMigrationOpsTestCase -nova.tests.unit.virt.hyperv.test_livemigrationutils.LiveMigrationUtilsTestCase nova.tests.unit.virt.hyperv.test_migrationops.MigrationOpsTestCase -nova.tests.unit.virt.hyperv.test_networkutils.NetworkUtilsTestCase -nova.tests.unit.virt.hyperv.test_networkutilsv2.NetworkUtilsV2TestCase nova.tests.unit.virt.hyperv.test_pathutils.PathUtilsTestCase nova.tests.unit.virt.hyperv.test_rdpconsoleops.RDPConsoleOpsTestCase -nova.tests.unit.virt.hyperv.test_rdpconsoleutilsv2.RDPConsoleUtilsV2TestCase nova.tests.unit.virt.hyperv.test_snapshotops.SnapshotOpsTestCase -nova.tests.unit.virt.hyperv.test_utilsfactory.TestHyperVUtilsFactory -nova.tests.unit.virt.hyperv.test_vhdutils.VHDUtilsTestCase -nova.tests.unit.virt.hyperv.test_vhdutilsv2.VHDUtilsV2TestCase nova.tests.unit.virt.hyperv.test_vif.HyperVNovaNetworkVIFDriverTestCase nova.tests.unit.virt.hyperv.test_vmops.VMOpsTestCase -nova.tests.unit.virt.hyperv.test_vmutils.VMUtilsTestCase -nova.tests.unit.virt.hyperv.test_vmutilsv2.VMUtilsV2TestCase nova.tests.unit.virt.hyperv.test_volumeops.ISCSIVolumeDriverTestCase nova.tests.unit.virt.hyperv.test_volumeops.SMBFSVolumeDriverTestCase nova.tests.unit.virt.hyperv.test_volumeops.VolumeOpsTestCase -nova.tests.unit.virt.hyperv.test_volumeutils.VolumeUtilsTestCase -nova.tests.unit.virt.hyperv.test_volumeutilsv2.VolumeUtilsV2TestCase nova.tests.unit.virt.ironic.test_driver.IronicDriverTestCase nova.tests.unit.virt.ironic.test_patcher.IronicDriverFieldsTestCase nova.tests.unit.virt.libvirt.storage.test_lvm.LvmTestCase