313 lines
13 KiB
Python
313 lines
13 KiB
Python
# 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 platform
|
|
|
|
import ddt
|
|
import mock
|
|
|
|
from os_win import exceptions
|
|
from os_win.tests.unit import test_base
|
|
from os_win.utils import _wqlutils
|
|
from os_win.utils.compute import livemigrationutils
|
|
from os_win.utils.compute import vmutils
|
|
from os_win.utils import jobutils
|
|
|
|
|
|
@ddt.ddt
|
|
class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|
"""Unit tests for the Hyper-V LiveMigrationUtils class."""
|
|
|
|
_autospec_classes = [
|
|
vmutils.VMUtils,
|
|
jobutils.JobUtils,
|
|
]
|
|
|
|
_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):
|
|
super(LiveMigrationUtilsTestCase, self).setUp()
|
|
self.liveutils = livemigrationutils.LiveMigrationUtils()
|
|
self._conn = mock.MagicMock()
|
|
self.liveutils._conn_attr = self._conn
|
|
|
|
self.liveutils._get_wmi_obj = mock.MagicMock(return_value=self._conn)
|
|
self.liveutils._conn_v2 = self._conn
|
|
|
|
def test_get_conn_v2(self):
|
|
self.liveutils._get_wmi_obj.side_effect = exceptions.x_wmi(
|
|
com_error=mock.Mock())
|
|
|
|
self.assertRaises(exceptions.HyperVException,
|
|
self.liveutils._get_conn_v2, '.')
|
|
|
|
self.liveutils._get_wmi_obj.assert_called_once_with(
|
|
self.liveutils._wmi_namespace % '.', compatibility_mode=True)
|
|
|
|
def test_check_live_migration_config(self):
|
|
mock_migr_svc = (
|
|
self._conn.Msvm_VirtualSystemMigrationService.return_value[0])
|
|
conn_vsmssd = self._conn.Msvm_VirtualSystemMigrationServiceSettingData
|
|
|
|
vsmssd = mock.MagicMock()
|
|
vsmssd.EnableVirtualSystemMigration = True
|
|
conn_vsmssd.return_value = [vsmssd]
|
|
mock_migr_svc.MigrationServiceListenerIPAdressList.return_value = [
|
|
mock.sentinel.FAKE_HOST]
|
|
|
|
self.liveutils.check_live_migration_config()
|
|
conn_vsmssd.assert_called_once_with()
|
|
self._conn.Msvm_VirtualSystemMigrationService.assert_called_once_with()
|
|
|
|
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(exceptions.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(exceptions.HyperVVMNotFoundException,
|
|
self.liveutils._get_vm,
|
|
mock_conn_v2, self._FAKE_VM_NAME)
|
|
|
|
def test_create_planned_vm_helper(self):
|
|
mock_vm = mock.MagicMock()
|
|
mock_v2 = mock.MagicMock()
|
|
mock_vsmsd_cls = mock_v2.Msvm_VirtualSystemMigrationSettingData
|
|
mock_vsmsd = mock_vsmsd_cls.return_value[0]
|
|
self._conn.Msvm_PlannedComputerSystem.return_value = [mock_vm]
|
|
|
|
migr_svc = mock_v2.Msvm_VirtualSystemMigrationService()[0]
|
|
migr_svc.MigrateVirtualSystemToHost.return_value = (
|
|
self._FAKE_RET_VAL, mock.sentinel.FAKE_JOB_PATH)
|
|
|
|
resulted_vm = self.liveutils._create_planned_vm(
|
|
self._conn, mock_v2, mock_vm, [mock.sentinel.FAKE_REMOTE_IP_ADDR],
|
|
mock.sentinel.FAKE_HOST)
|
|
|
|
self.assertEqual(mock_vm, resulted_vm)
|
|
|
|
mock_vsmsd_cls.assert_called_once_with(
|
|
MigrationType=self.liveutils._MIGRATION_TYPE_STAGED)
|
|
migr_svc.MigrateVirtualSystemToHost.assert_called_once_with(
|
|
ComputerSystem=mock_vm.path_.return_value,
|
|
DestinationHost=mock.sentinel.FAKE_HOST,
|
|
MigrationSettingData=mock_vsmsd.GetText_.return_value)
|
|
self.liveutils._jobutils.check_ret_val.assert_called_once_with(
|
|
mock.sentinel.FAKE_JOB_PATH,
|
|
self._FAKE_RET_VAL)
|
|
|
|
def test_get_disk_data(self):
|
|
mock_vmutils_remote = mock.MagicMock()
|
|
mock_disk = mock.MagicMock()
|
|
mock_disk_path_mapping = {
|
|
mock.sentinel.serial: mock.sentinel.disk_path}
|
|
|
|
mock_disk.path.return_value.RelPath = mock.sentinel.rel_path
|
|
mock_vmutils_remote.get_vm_disks.return_value = [
|
|
None, [mock_disk]]
|
|
mock_disk.ElementName = mock.sentinel.serial
|
|
|
|
resulted_disk_paths = self.liveutils._get_disk_data(
|
|
self._FAKE_VM_NAME, mock_vmutils_remote, mock_disk_path_mapping)
|
|
|
|
mock_vmutils_remote.get_vm_disks.assert_called_once_with(
|
|
self._FAKE_VM_NAME)
|
|
mock_disk.path.assert_called_once_with()
|
|
expected_disk_paths = {mock.sentinel.rel_path: mock.sentinel.disk_path}
|
|
self.assertEqual(expected_disk_paths, resulted_disk_paths)
|
|
|
|
@mock.patch.object(_wqlutils, 'get_element_associated_class')
|
|
def test_update_planned_vm_disk_resources(self,
|
|
mock_get_elem_associated_class):
|
|
self._prepare_vm_mocks(self._RESOURCE_TYPE_DISK,
|
|
self._RESOURCE_SUB_TYPE_DISK,
|
|
mock_get_elem_associated_class)
|
|
mock_vm = mock.Mock(Name='fake_name')
|
|
sasd = mock_get_elem_associated_class.return_value[0]
|
|
|
|
mock_vsmsvc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
|
|
self.liveutils._update_planned_vm_disk_resources(
|
|
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])
|
|
mock_get_elem_associated_class.assert_called_once_with(
|
|
self._conn, self.liveutils._CIM_RES_ALLOC_SETTING_DATA_CLASS,
|
|
element_uuid=mock_vm.Name)
|
|
|
|
@mock.patch.object(_wqlutils, 'get_element_associated_class')
|
|
def test_get_vhd_setting_data(self, mock_get_elem_associated_class):
|
|
self._prepare_vm_mocks(self._RESOURCE_TYPE_VHD,
|
|
self._RESOURCE_SUB_TYPE_VHD,
|
|
mock_get_elem_associated_class)
|
|
mock_vm = mock.Mock(Name='fake_vm_name')
|
|
mock_sasd = mock_get_elem_associated_class.return_value[0]
|
|
|
|
vhd_sds = self.liveutils._get_vhd_setting_data(mock_vm)
|
|
self.assertEqual([mock_sasd.GetText_.return_value], vhd_sds)
|
|
mock_get_elem_associated_class.assert_called_once_with(
|
|
self._conn, self.liveutils._STORAGE_ALLOC_SETTING_DATA_CLASS,
|
|
element_uuid=mock_vm.Name)
|
|
|
|
def test_live_migrate_vm_helper(self):
|
|
mock_conn_local = mock.MagicMock()
|
|
mock_vm = mock.MagicMock()
|
|
mock_vsmsd_cls = (
|
|
mock_conn_local.Msvm_VirtualSystemMigrationSettingData)
|
|
mock_vsmsd = mock_vsmsd_cls.return_value[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.sentinel.migration_type)
|
|
|
|
mock_vsmsd_cls.assert_called_once_with(
|
|
MigrationType=mock.sentinel.migration_type)
|
|
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.LiveMigrationUtils, '_live_migrate_vm')
|
|
@mock.patch.object(
|
|
livemigrationutils.LiveMigrationUtils, '_get_vhd_setting_data')
|
|
@mock.patch.object(
|
|
livemigrationutils.LiveMigrationUtils, '_get_planned_vm')
|
|
def test_live_migrate_single_planned_vm(self, mock_get_planned_vm,
|
|
mock_get_vhd_sd,
|
|
mock_live_migrate_vm):
|
|
mock_vm = self._get_vm()
|
|
|
|
mock_migr_svc = self._conn.Msvm_VirtualSystemMigrationService()[0]
|
|
mock_migr_svc.MigrationServiceListenerIPAddressList = [
|
|
mock.sentinel.FAKE_REMOTE_IP_ADDR]
|
|
|
|
mock_get_planned_vm.return_value = mock_vm
|
|
self.liveutils.live_migrate_vm(mock.sentinel.vm_name,
|
|
mock.sentinel.FAKE_HOST)
|
|
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,
|
|
self.liveutils._MIGRATION_TYPE_VIRTUAL_SYSTEM_AND_STORAGE)
|
|
mock_get_planned_vm.assert_called_once_with(
|
|
mock.sentinel.vm_name, self._conn)
|
|
|
|
@mock.patch.object(livemigrationutils.LiveMigrationUtils, '_get_vm')
|
|
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
|
'_get_ip_address_list')
|
|
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
|
'_update_planned_vm_disk_resources')
|
|
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
|
'_create_planned_vm')
|
|
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
|
'destroy_existing_planned_vm')
|
|
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
|
'_get_disk_data')
|
|
def test_create_planned_vm(self, mock_get_disk_data,
|
|
mock_destroy_existing_planned_vm,
|
|
mock_create_planned_vm,
|
|
mock_update_planned_vm_disk_resources,
|
|
mock_get_ip_address_list, mock_get_vm):
|
|
dest_host = platform.node()
|
|
mock_vm = mock.MagicMock()
|
|
mock_get_vm.return_value = mock_vm
|
|
mock_conn_v2 = mock.MagicMock()
|
|
self.liveutils._get_wmi_obj.return_value = mock_conn_v2
|
|
|
|
mock_get_disk_data.return_value = mock.sentinel.disk_data
|
|
mock_get_ip_address_list.return_value = mock.sentinel.ip_address_list
|
|
|
|
mock_vsmsvc = self._conn.Msvm_VirtualSystemManagementService()[0]
|
|
mock_vsmsvc.ModifyResourceSettings.return_value = (
|
|
mock.sentinel.res_setting,
|
|
mock.sentinel.job_path,
|
|
self._FAKE_RET_VAL)
|
|
|
|
self.liveutils.create_planned_vm(mock.sentinel.vm_name,
|
|
mock.sentinel.host,
|
|
mock.sentinel.disk_path_mapping)
|
|
|
|
mock_destroy_existing_planned_vm.assert_called_once_with(
|
|
mock.sentinel.vm_name)
|
|
mock_get_ip_address_list.assert_called_once_with(self._conn, dest_host)
|
|
mock_get_disk_data.assert_called_once_with(
|
|
mock.sentinel.vm_name,
|
|
vmutils.VMUtils.return_value,
|
|
mock.sentinel.disk_path_mapping)
|
|
mock_create_planned_vm.assert_called_once_with(
|
|
self._conn, mock_conn_v2, mock_vm,
|
|
mock.sentinel.ip_address_list, dest_host)
|
|
mock_update_planned_vm_disk_resources.assert_called_once_with(
|
|
self._conn, mock_create_planned_vm.return_value,
|
|
mock.sentinel.vm_name, mock.sentinel.disk_data)
|
|
|
|
def _prepare_vm_mocks(self, resource_type, resource_sub_type,
|
|
mock_get_elem_associated_class):
|
|
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
|
|
|
|
mock_get_elem_associated_class.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
|
|
mock_vm.Name = self._FAKE_VM_NAME
|
|
return mock_vm
|