VM Importing/Exporting
This patch adds support for exporting/importing a vm. Change-Id: I9b27bd36753323364f8de05960af08729e37024d
This commit is contained in:
parent
01caadebae
commit
2edc5fa3ff
|
@ -168,3 +168,7 @@ CLUSTER_GROUP_OFFLINE = 1
|
|||
CLUSTER_GROUP_FAILED = 2
|
||||
CLUSTER_GROUP_PARTIAL_ONLINE = 3
|
||||
CLUSTER_GROUP_PENDING = 4
|
||||
|
||||
EXPORT_CONFIG_SNAPSHOTS_ALL = 0
|
||||
EXPORT_CONFIG_NO_SNAPSHOTS = 1
|
||||
EXPORT_CONFIG_ONE_SNAPSHOT = 2
|
||||
|
|
|
@ -24,6 +24,7 @@ from os_win import exceptions
|
|||
from os_win.tests import test_base
|
||||
from os_win.utils.compute import clusterutils
|
||||
from os_win.utils.compute import livemigrationutils
|
||||
from os_win.utils.compute import migrationutils
|
||||
from os_win.utils.compute import rdpconsoleutils
|
||||
from os_win.utils.compute import vmutils
|
||||
from os_win.utils.dns import dnsutils
|
||||
|
@ -146,3 +147,8 @@ class TestHyperVUtilsFactory(test_base.OsWinBaseTestCase):
|
|||
self._check_get_class(
|
||||
expected_class=dnsutils.DNSUtils,
|
||||
class_type='dnsutils')
|
||||
|
||||
def test_get_migrationutils(self):
|
||||
self._check_get_class(
|
||||
expected_class=migrationutils.MigrationUtils,
|
||||
class_type='migrationutils')
|
||||
|
|
|
@ -104,35 +104,24 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
mock.sentinel.ret_val,
|
||||
mock.sentinel.job_path)
|
||||
|
||||
def test_get_planned_vms(self):
|
||||
mock_conn_v2 = mock.MagicMock()
|
||||
mock_conn_v2.Msvm_PlannedComputerSystem.return_value = (
|
||||
mock.sentinel.planned_vms)
|
||||
|
||||
planned_vms = self.liveutils._get_planned_vms(mock_conn_v2,
|
||||
mock.sentinel.vm_name)
|
||||
|
||||
mock_conn_v2.Msvm_PlannedComputerSystem.assert_called_once_with(
|
||||
ElementName=mock.sentinel.vm_name)
|
||||
self.assertEqual(mock.sentinel.planned_vms, planned_vms)
|
||||
|
||||
@ddt.data({'planned_vm': None}, {'planned_vm': mock.sentinel.planned_vm})
|
||||
@ddt.unpack
|
||||
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
||||
'_destroy_planned_vm')
|
||||
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
||||
'_get_planned_vms')
|
||||
def test_destroy_existing_planned_vms(self, mock_get_planned_vms,
|
||||
mock_destroy_planned_vm):
|
||||
mock_planned_vms = [mock.sentinel.planned_vm,
|
||||
mock.sentinel.another_planned_vm]
|
||||
mock_get_planned_vms.return_value = mock_planned_vms
|
||||
'_get_planned_vm')
|
||||
def test_destroy_existing_planned_vm(self, mock_get_planned_vm,
|
||||
mock_destroy_planned_vm, planned_vm):
|
||||
mock_get_planned_vm.return_value = planned_vm
|
||||
|
||||
self.liveutils.destroy_existing_planned_vms(mock.sentinel.vm_name)
|
||||
self.liveutils.destroy_existing_planned_vm(mock.sentinel.vm_name)
|
||||
|
||||
mock_get_planned_vms.assert_called_once_with(self._conn,
|
||||
mock.sentinel.vm_name)
|
||||
mock_destroy_planned_vm.assert_has_calls(
|
||||
[mock.call(mock.sentinel.planned_vm),
|
||||
mock.call(mock.sentinel.another_planned_vm)])
|
||||
mock_get_planned_vm.assert_called_once_with(
|
||||
mock.sentinel.vm_name, self._conn)
|
||||
if planned_vm:
|
||||
mock_destroy_planned_vm.assert_called_once_with(planned_vm)
|
||||
else:
|
||||
self.assertFalse(mock_destroy_planned_vm.called)
|
||||
|
||||
def test_create_planned_vm_helper(self):
|
||||
mock_vm = mock.MagicMock()
|
||||
|
@ -303,19 +292,12 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
MigrationSettingData=mock_vsmsd.GetText_.return_value,
|
||||
NewResourceSettingData=mock.sentinel.FAKE_RASD_PATH)
|
||||
|
||||
def test_live_migrate_multiple_planned_vms(self):
|
||||
mock_vm = self._get_vm()
|
||||
self._conn.Msvm_PlannedComputerSystem.return_value = [
|
||||
mock_vm, mock_vm]
|
||||
|
||||
self.assertRaises(exceptions.OSWinException,
|
||||
self.liveutils.live_migrate_vm,
|
||||
mock.sentinel.vm_name,
|
||||
mock.sentinel.host)
|
||||
|
||||
@ddt.data(True, False)
|
||||
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
||||
'_get_planned_vm')
|
||||
@mock.patch.object(livemigrationutils, 'vmutils')
|
||||
def test_live_migrate_no_planned_vm(self, migrate_disks, mock_vm_utils):
|
||||
def test_live_migrate_no_planned_vm(self, migrate_disks, mock_vm_utils,
|
||||
mock_get_planned_vm):
|
||||
mock_vm_utils_remote = mock_vm_utils.VMUtils.return_value
|
||||
mock_vm = self._get_vm()
|
||||
|
||||
|
@ -333,7 +315,7 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
_get_vhd_setting_data=mock.DEFAULT,
|
||||
_live_migrate_vm=mock.DEFAULT):
|
||||
|
||||
self._conn.Msvm_PlannedComputerSystem.return_value = []
|
||||
mock_get_planned_vm.return_value = None
|
||||
disk_paths = {
|
||||
mock.sentinel.FAKE_IDE_PATH: mock.sentinel.FAKE_SASD_RESOURCE}
|
||||
self.liveutils._get_physical_disk_paths.return_value = disk_paths
|
||||
|
@ -345,7 +327,8 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
self.liveutils.live_migrate_vm(mock.sentinel.vm_name,
|
||||
mock.sentinel.FAKE_HOST,
|
||||
migrate_disks=migrate_disks)
|
||||
|
||||
mock_get_planned_vm.assert_called_once_with(
|
||||
mock.sentinel.vm_name, self._conn)
|
||||
self.liveutils._get_remote_disk_data.assert_called_once_with(
|
||||
mock_vm_utils_remote, disk_paths, mock.sentinel.FAKE_HOST)
|
||||
self.liveutils._create_planned_vm.assert_called_once_with(
|
||||
|
@ -372,7 +355,9 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
mock.sentinel.FAKE_HOST,
|
||||
expected_migr_type)
|
||||
|
||||
def test_live_migrate_single_planned_vm(self):
|
||||
@mock.patch.object(
|
||||
livemigrationutils.LiveMigrationUtils, '_get_planned_vm')
|
||||
def test_live_migrate_single_planned_vm(self, mock_get_planned_vm):
|
||||
mock_vm = self._get_vm()
|
||||
|
||||
mock_migr_svc = self._conn.Msvm_VirtualSystemMigrationService()[0]
|
||||
|
@ -385,7 +370,7 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
_get_vhd_setting_data=mock.DEFAULT,
|
||||
_live_migrate_vm=mock.DEFAULT):
|
||||
|
||||
self._conn.Msvm_PlannedComputerSystem.return_value = [mock_vm]
|
||||
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(
|
||||
|
@ -394,6 +379,8 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
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(vmutils, 'VMUtils')
|
||||
@mock.patch.object(livemigrationutils.LiveMigrationUtils, '_get_vm')
|
||||
|
@ -404,7 +391,7 @@ class LiveMigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
||||
'_create_planned_vm')
|
||||
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
||||
'destroy_existing_planned_vms')
|
||||
'destroy_existing_planned_vm')
|
||||
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
|
||||
'_get_disk_data')
|
||||
def test_create_planned_vm(self, mock_get_disk_data,
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
# Copyright 2016 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 ddt
|
||||
|
||||
import mock
|
||||
|
||||
from os_win import constants
|
||||
from os_win import exceptions
|
||||
from os_win.tests import test_base
|
||||
from os_win.utils.compute import migrationutils
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MigrationUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
"""Unit tests for the Hyper-V MigrationUtils class."""
|
||||
|
||||
_FAKE_VM_NAME = 'fake_vm'
|
||||
|
||||
def setUp(self):
|
||||
super(MigrationUtilsTestCase, self).setUp()
|
||||
self._migrationutils = migrationutils.MigrationUtils()
|
||||
self._migrationutils._vmutils = mock.MagicMock()
|
||||
self._migrationutils._conn_attr = mock.MagicMock()
|
||||
self._migrationutils._jobutils = mock.MagicMock()
|
||||
|
||||
def test_get_export_setting_data(self):
|
||||
mock_vm = self._migrationutils._vmutils._lookup_vm.return_value
|
||||
mock_conn = self._migrationutils._conn
|
||||
mock_exp = mock_conn.Msvm_VirtualSystemExportSettingData
|
||||
mock_exp.return_value = [mock.sentinel.export_setting_data]
|
||||
expected_result = mock.sentinel.export_setting_data
|
||||
|
||||
actual_result = self._migrationutils._get_export_setting_data(
|
||||
self._FAKE_VM_NAME)
|
||||
self.assertEqual(expected_result, actual_result)
|
||||
mock_exp.assert_called_once_with(InstanceID=mock_vm.InstanceID)
|
||||
|
||||
@mock.patch.object(
|
||||
migrationutils.MigrationUtils, '_get_export_setting_data')
|
||||
def test_export_vm(self, mock_get_export_setting_data):
|
||||
mock_vm = self._migrationutils._vmutils._lookup_vm.return_value
|
||||
export_setting_data = mock_get_export_setting_data.return_value
|
||||
mock_svc = self._migrationutils._vs_man_svc
|
||||
mock_svc.ExportSystemDefinition.return_value = (
|
||||
mock.sentinel.job_path, mock.sentinel.ret_val)
|
||||
|
||||
self._migrationutils.export_vm(
|
||||
vm_name=self._FAKE_VM_NAME,
|
||||
export_path=mock.sentinel.fake_export_path)
|
||||
|
||||
self.assertEqual(constants.EXPORT_CONFIG_SNAPSHOTS_ALL,
|
||||
export_setting_data.CopySnapshotConfiguration)
|
||||
self.assertFalse(export_setting_data.CopyVmStorage)
|
||||
self.assertFalse(export_setting_data.CreateVmExportSubdirectory)
|
||||
mock_get_export_setting_data.assert_called_once_with(
|
||||
self._FAKE_VM_NAME)
|
||||
mock_svc.ExportSystemDefinition.assert_called_once_with(
|
||||
ComputerSystem=mock_vm.path_(),
|
||||
ExportDirectory=mock.sentinel.fake_export_path,
|
||||
ExportSettingData=export_setting_data.GetText_(1))
|
||||
self._migrationutils._jobutils.check_ret_val.assert_called_once_with(
|
||||
mock.sentinel.ret_val, mock.sentinel.job_path)
|
||||
|
||||
def test_import_vm_definition(self):
|
||||
mock_svc = self._migrationutils._vs_man_svc
|
||||
mock_svc.ImportSystemDefinition.return_value = (
|
||||
mock.sentinel.ref,
|
||||
mock.sentinel.job_path,
|
||||
mock.sentinel.ret_val)
|
||||
|
||||
self._migrationutils.import_vm_definition(
|
||||
export_config_file_path=mock.sentinel.export_config_file_path,
|
||||
snapshot_folder_path=mock.sentinel.snapshot_folder_path)
|
||||
|
||||
mock_svc.ImportSystemDefinition.assert_called_once_with(
|
||||
False, mock.sentinel.snapshot_folder_path,
|
||||
mock.sentinel.export_config_file_path)
|
||||
self._migrationutils._jobutils.check_ret_val.assert_called_once_with(
|
||||
mock.sentinel.ret_val, mock.sentinel.job_path)
|
||||
|
||||
@mock.patch.object(migrationutils.MigrationUtils, '_get_planned_vm')
|
||||
def test_realize_vm(self, mock_get_planned_vm):
|
||||
mock_get_planned_vm.return_value = mock.MagicMock()
|
||||
self._migrationutils._vs_man_svc.ValidatePlannedSystem.return_value = (
|
||||
mock.sentinel.job_path_ValidatePlannedSystem,
|
||||
mock.sentinel.ret_val_ValidatePlannedSystem)
|
||||
self._migrationutils._vs_man_svc.RealizePlannedSystem.return_value = (
|
||||
mock.sentinel.job_path_RealizePlannedSystem,
|
||||
mock.sentinel.ref_RealizePlannedSystem,
|
||||
mock.sentinel.ret_val_RealizePlannedSystem)
|
||||
|
||||
self._migrationutils.realize_vm(self._FAKE_VM_NAME)
|
||||
|
||||
mock_get_planned_vm.assert_called_once_with(
|
||||
self._FAKE_VM_NAME, fail_if_not_found=True)
|
||||
expected_call = [
|
||||
mock.call(mock.sentinel.ret_val_ValidatePlannedSystem,
|
||||
mock.sentinel.job_path_ValidatePlannedSystem),
|
||||
mock.call(mock.sentinel.ret_val_RealizePlannedSystem,
|
||||
mock.sentinel.job_path_RealizePlannedSystem)]
|
||||
self._migrationutils._jobutils.check_ret_val.has_calls(expected_call)
|
||||
|
||||
@ddt.data([mock.sentinel.planned_vm], [])
|
||||
def test_get_planned_vm(self, planned_vm):
|
||||
planned_computer_system = (
|
||||
self._migrationutils._conn.Msvm_PlannedComputerSystem)
|
||||
planned_computer_system.return_value = planned_vm
|
||||
|
||||
actual_result = self._migrationutils._get_planned_vm(
|
||||
self._FAKE_VM_NAME, fail_if_not_found=False)
|
||||
|
||||
if planned_vm:
|
||||
self.assertEqual(planned_vm[0], actual_result)
|
||||
else:
|
||||
self.assertIsNone(actual_result)
|
||||
planned_computer_system.assert_called_once_with(
|
||||
ElementName=self._FAKE_VM_NAME)
|
||||
|
||||
def test_get_planned_vm_exception(self):
|
||||
planned_computer_system = (
|
||||
self._migrationutils._conn.Msvm_PlannedComputerSystem)
|
||||
planned_computer_system.return_value = None
|
||||
|
||||
self.assertRaises(exceptions.HyperVException,
|
||||
self._migrationutils._get_planned_vm,
|
||||
self._FAKE_VM_NAME, fail_if_not_found=True)
|
||||
|
||||
planned_computer_system.assert_called_once_with(
|
||||
ElementName=self._FAKE_VM_NAME)
|
|
@ -13,6 +13,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ddt
|
||||
|
||||
import mock
|
||||
from six.moves import range # noqa
|
||||
|
||||
|
@ -23,6 +25,7 @@ from os_win.utils import _wqlutils
|
|||
from os_win.utils.compute import vmutils
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
"""Unit tests for the Hyper-V VMUtils class."""
|
||||
|
||||
|
@ -214,26 +217,41 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
self.assertEqual(mock_vm.ConfigurationDataRoot, config_root_dir)
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_vm_disks')
|
||||
def test_get_vm_storage_paths(self, mock_get_vm_disks):
|
||||
self._lookup_vm()
|
||||
@mock.patch.object(vmutils.VMUtils, '_lookup_vm_check')
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_virtual_system_type')
|
||||
def test_get_vm_storage_paths(self, mock_get_virtual_system_type,
|
||||
mock_lookup_vm_check, mock_get_vm_disks):
|
||||
virtual_system_type = mock_get_virtual_system_type.return_value
|
||||
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)
|
||||
storage = self._vmutils.get_vm_storage_paths(
|
||||
self._FAKE_VM_NAME,
|
||||
is_planned_vm=False)
|
||||
(disk_files, volume_drives) = storage
|
||||
|
||||
self.assertEqual([self._FAKE_VHD_PATH], disk_files)
|
||||
self.assertEqual([self._FAKE_VOLUME_DRIVE_PATH], volume_drives)
|
||||
mock_get_virtual_system_type.assert_called_once_with(False)
|
||||
mock_lookup_vm_check.assert_called_once_with(
|
||||
self._FAKE_VM_NAME, virtual_system_type=virtual_system_type)
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_vm_disks')
|
||||
def test_get_vm_disks_by_instance_name(self, mock_get_vm_disks):
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_virtual_system_type')
|
||||
def test_get_vm_disks_by_instance_name(self,
|
||||
mock_get_virtual_system_type,
|
||||
mock_get_vm_disks):
|
||||
virtual_system_type = mock_get_virtual_system_type.return_value
|
||||
self._lookup_vm()
|
||||
mock_get_vm_disks.return_value = mock.sentinel.vm_disks
|
||||
|
||||
vm_disks = self._vmutils.get_vm_disks(self._FAKE_VM_NAME)
|
||||
vm_disks = self._vmutils.get_vm_disks(
|
||||
self._FAKE_VM_NAME, is_planned_vm=False)
|
||||
|
||||
mock_get_virtual_system_type.assert_called_once_with(False)
|
||||
self._vmutils._lookup_vm_check.assert_called_once_with(
|
||||
self._FAKE_VM_NAME)
|
||||
self._FAKE_VM_NAME,
|
||||
virtual_system_type=virtual_system_type)
|
||||
self.assertEqual(mock.sentinel.vm_disks, vm_disks)
|
||||
|
||||
@mock.patch.object(_wqlutils, 'get_element_associated_class')
|
||||
|
@ -280,23 +298,42 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
self.assertRaises(exceptions.HyperVAuthorizationException,
|
||||
self._vmutils.check_admin_permissions)
|
||||
|
||||
@ddt.data(
|
||||
{'configuration_root_dir': mock.sentinel.configuration_root_dir},
|
||||
{'snapshot_dir': mock.sentinel.snapshot_dir, 'is_planned_vm': True},
|
||||
{'is_planned_vm': True}, {})
|
||||
@ddt.unpack
|
||||
@mock.patch.object(vmutils.VMUtils, '_modify_virtual_system')
|
||||
@mock.patch.object(vmutils.VMUtils, '_set_vm_vcpus')
|
||||
@mock.patch.object(vmutils.VMUtils, '_set_vm_memory')
|
||||
def test_update_vm(self, mock_set_mem, mock_set_vcpus):
|
||||
mock_vmsettings = self._lookup_vm()
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_lookup_vm_check')
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_virtual_system_type')
|
||||
def test_update_vm(self, mock_get_virtual_system_type,
|
||||
mock_lookup_vm_check, mock_set_mem, mock_set_vcpus,
|
||||
mock_modify_virtual_system,
|
||||
configuration_root_dir=None,
|
||||
snapshot_dir=None, is_planned_vm=False):
|
||||
mock_vmsettings = mock_lookup_vm_check.return_value
|
||||
virtual_system_type = mock_get_virtual_system_type.return_value
|
||||
self._vmutils.update_vm(
|
||||
mock.sentinel.vm_name, mock.sentinel.memory_mb,
|
||||
mock.sentinel.memory_per_numa, mock.sentinel.vcpus_num,
|
||||
mock.sentinel.vcpus_per_numa, mock.sentinel.limit_cpu_features,
|
||||
mock.sentinel.dynamic_mem_ratio)
|
||||
mock.sentinel.dynamic_mem_ratio, configuration_root_dir,
|
||||
snapshot_dir, is_planned_vm=is_planned_vm)
|
||||
|
||||
mock_get_virtual_system_type.assert_called_once_with(is_planned_vm)
|
||||
mock_lookup_vm_check.assert_called_once_with(
|
||||
mock.sentinel.vm_name, virtual_system_type=virtual_system_type)
|
||||
mock_set_mem.assert_called_once_with(
|
||||
mock_vmsettings, mock.sentinel.memory_mb,
|
||||
mock.sentinel.memory_per_numa, mock.sentinel.dynamic_mem_ratio)
|
||||
mock_set_vcpus.assert_called_once_with(
|
||||
mock_vmsettings, mock.sentinel.vcpus_num,
|
||||
mock.sentinel.vcpus_per_numa, mock.sentinel.limit_cpu_features)
|
||||
if configuration_root_dir or snapshot_dir:
|
||||
mock_modify_virtual_system.assert_called_once_with(
|
||||
mock_vmsettings)
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_set_vm_memory')
|
||||
@mock.patch.object(vmutils.VMUtils, '_create_vm_obj')
|
||||
|
@ -524,9 +561,8 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
getattr(mock_svc, self._DESTROY_SYSTEM).assert_called_with(
|
||||
self._FAKE_VM_PATH)
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_vm_disks')
|
||||
@mock.patch.object(vmutils.VMUtils, 'get_vm_disks')
|
||||
def test_get_vm_physical_disk_mapping(self, mock_get_vm_disks):
|
||||
self._lookup_vm()
|
||||
mock_phys_disk = self._create_mock_disks()[1]
|
||||
|
||||
expected_serial = mock_phys_disk.ElementName
|
||||
|
@ -539,8 +575,11 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
|
||||
mock_get_vm_disks.return_value = ([], [mock_phys_disk])
|
||||
|
||||
result = self._vmutils.get_vm_physical_disk_mapping(self._FAKE_VM_NAME)
|
||||
result = self._vmutils.get_vm_physical_disk_mapping(
|
||||
self._FAKE_VM_NAME, is_planned_vm=False)
|
||||
self.assertEqual(expected_mapping, result)
|
||||
mock_get_vm_disks.assert_called_once_with(
|
||||
self._FAKE_VM_NAME, is_planned_vm=False)
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_wmi_obj')
|
||||
def test_set_disk_host_res(self, mock_get_wmi_obj):
|
||||
|
@ -1232,3 +1271,25 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
def test_vm_has_s3_controller(self, mock_get_vm_generation):
|
||||
self.assertTrue(self._vmutils._vm_has_s3_controller(
|
||||
mock.sentinel.fake_vm_name))
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_mounted_disk_resource_from_path')
|
||||
def test_update_vm_disk_path(self, mock_get_disk_resource_from_path):
|
||||
disk_resource = mock_get_disk_resource_from_path.return_value
|
||||
self._vmutils.update_vm_disk_path(mock.sentinel.disk_path,
|
||||
mock.sentinel.new_path,
|
||||
is_physical=True)
|
||||
|
||||
mock_get_disk_resource_from_path.assert_called_once_with(
|
||||
disk_path=mock.sentinel.disk_path, is_physical=True)
|
||||
self._vmutils._jobutils.modify_virt_resource.assert_called_once_with(
|
||||
disk_resource)
|
||||
self.assertEqual(disk_resource.HostResource, [mock.sentinel.new_path])
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_get_virtual_system_type(self, is_planned_vm):
|
||||
exp_result = (
|
||||
self._vmutils._VIRTUAL_SYSTEM_TYPE_PLANNED if is_planned_vm
|
||||
else self._vmutils._VIRTUAL_SYSTEM_TYPE_REALIZED)
|
||||
actual_result = self._vmutils._get_virtual_system_type(
|
||||
is_planned_vm=is_planned_vm)
|
||||
self.assertEqual(exp_result, actual_result)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import mock
|
||||
|
||||
|
@ -199,3 +200,14 @@ class PathUtilsTestCase(test_base.OsWinBaseTestCase):
|
|||
self.assertEqual(mock.sentinel.temporary_file, tmp_file)
|
||||
self.assertFalse(mock_delete.called)
|
||||
mock_delete.assert_called_once_with(mock.sentinel.temporary_file)
|
||||
|
||||
@mock.patch.object(shutil, 'copytree')
|
||||
@mock.patch('os.path.abspath')
|
||||
def test_copy_dir(self, mock_abspath, mock_copytree):
|
||||
mock_abspath.side_effect = [mock.sentinel.src, mock.sentinel.dest]
|
||||
self._pathutils.copy_dir(mock.sentinel.src, mock.sentinel.dest)
|
||||
|
||||
mock_abspath.has_calls(
|
||||
[mock.call(mock.sentinel.src), mock.call(mock.sentinel.dest)])
|
||||
mock_copytree.assert_called_once_with(mock.sentinel.src,
|
||||
mock.sentinel.dest)
|
||||
|
|
|
@ -20,15 +20,14 @@ from oslo_log import log as logging
|
|||
from os_win._i18n import _, _LE
|
||||
from os_win import exceptions
|
||||
from os_win.utils import _wqlutils
|
||||
from os_win.utils import baseutils
|
||||
from os_win.utils.compute import migrationutils
|
||||
from os_win.utils.compute import vmutils
|
||||
from os_win.utils import jobutils
|
||||
from os_win.utils.storage.initiator import iscsi_wmi_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LiveMigrationUtils(baseutils.BaseUtilsVirt):
|
||||
class LiveMigrationUtils(migrationutils.MigrationUtils):
|
||||
_STORAGE_ALLOC_SETTING_DATA_CLASS = 'Msvm_StorageAllocationSettingData'
|
||||
_CIM_RES_ALLOC_SETTING_DATA_CLASS = 'CIM_ResourceAllocationSettingData'
|
||||
|
||||
|
@ -38,8 +37,6 @@ class LiveMigrationUtils(baseutils.BaseUtilsVirt):
|
|||
|
||||
def __init__(self):
|
||||
super(LiveMigrationUtils, self).__init__()
|
||||
self._vmutils = vmutils.VMUtils()
|
||||
self._jobutils = jobutils.JobUtils()
|
||||
self._iscsi_initiator = iscsi_wmi_utils.ISCSIInitiatorWMIUtils()
|
||||
|
||||
def _get_conn_v2(self, host='localhost'):
|
||||
|
@ -87,12 +84,9 @@ class LiveMigrationUtils(baseutils.BaseUtilsVirt):
|
|||
ret_val) = self._vs_man_svc.DestroySystem(planned_vm.path_())
|
||||
self._jobutils.check_ret_val(ret_val, job_path)
|
||||
|
||||
def _get_planned_vms(self, conn_v2, vm_name):
|
||||
return conn_v2.Msvm_PlannedComputerSystem(ElementName=vm_name)
|
||||
|
||||
def destroy_existing_planned_vms(self, vm_name):
|
||||
planned_vms = self._get_planned_vms(self._compat_conn, vm_name)
|
||||
for planned_vm in planned_vms:
|
||||
def destroy_existing_planned_vm(self, vm_name):
|
||||
planned_vm = self._get_planned_vm(vm_name, self._compat_conn)
|
||||
if planned_vm:
|
||||
self._destroy_planned_vm(planned_vm)
|
||||
|
||||
def _create_planned_vm(self, conn_v2_local, conn_v2_remote,
|
||||
|
@ -238,14 +232,8 @@ class LiveMigrationUtils(baseutils.BaseUtilsVirt):
|
|||
rmt_ip_addr_list = self._get_ip_address_list(conn_v2_remote,
|
||||
dest_host)
|
||||
|
||||
planned_vms = self._get_planned_vms(conn_v2_remote, vm_name)
|
||||
if len(planned_vms) > 1:
|
||||
err_msg = _("Multiple planned VMs were found for VM %(vm_name)s "
|
||||
"on host %(dest_host)s")
|
||||
raise exceptions.OSWinException(
|
||||
err_msg % dict(vm_name=vm_name,
|
||||
dest_host=dest_host))
|
||||
elif not planned_vms:
|
||||
planned_vm = self._get_planned_vm(vm_name, conn_v2_remote)
|
||||
if not planned_vm:
|
||||
# TODO(claudiub): Remove this branch after the livemigrationutils
|
||||
# usage has been updated to create planned VM on the destination
|
||||
# host beforehand.
|
||||
|
@ -262,8 +250,6 @@ class LiveMigrationUtils(baseutils.BaseUtilsVirt):
|
|||
dest_host)
|
||||
self._update_planned_vm_disk_resources(
|
||||
conn_v2_remote, planned_vm, vm_name, disk_paths_remote)
|
||||
else:
|
||||
planned_vm = planned_vms[0]
|
||||
|
||||
if migrate_disks:
|
||||
new_resource_setting_data = self._get_vhd_setting_data(vm)
|
||||
|
@ -285,7 +271,7 @@ class LiveMigrationUtils(baseutils.BaseUtilsVirt):
|
|||
vm = self._get_vm(conn_v2_remote, vm_name)
|
||||
|
||||
# Make sure there are no planned VMs already.
|
||||
self.destroy_existing_planned_vms(vm_name)
|
||||
self.destroy_existing_planned_vm(vm_name)
|
||||
|
||||
ip_addr_list = self._get_ip_address_list(self._compat_conn,
|
||||
dest_host)
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright 2016 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 os_win._i18n import _
|
||||
from os_win import constants
|
||||
from os_win import exceptions
|
||||
from os_win.utils import baseutils
|
||||
from os_win.utils.compute import vmutils
|
||||
from os_win.utils import jobutils
|
||||
|
||||
|
||||
class MigrationUtils(baseutils.BaseUtilsVirt):
|
||||
|
||||
def __init__(self):
|
||||
super(MigrationUtils, self).__init__()
|
||||
self._vmutils = vmutils.VMUtils()
|
||||
self._jobutils = jobutils.JobUtils()
|
||||
|
||||
def _get_export_setting_data(self, vm_name):
|
||||
vm = self._vmutils._lookup_vm(vm_name)
|
||||
export_setting_data = self._conn.Msvm_VirtualSystemExportSettingData(
|
||||
InstanceID=vm.InstanceID)
|
||||
return export_setting_data[0]
|
||||
|
||||
def export_vm(self, vm_name, export_path,
|
||||
copy_snapshots_config=constants.EXPORT_CONFIG_SNAPSHOTS_ALL,
|
||||
copy_vm_storage=False, create_export_subdir=False):
|
||||
vm = self._vmutils._lookup_vm(vm_name)
|
||||
export_setting_data = self._get_export_setting_data(vm_name)
|
||||
|
||||
export_setting_data.CopySnapshotConfiguration = copy_snapshots_config
|
||||
export_setting_data.CopyVmStorage = copy_vm_storage
|
||||
export_setting_data.CreateVmExportSubdirectory = create_export_subdir
|
||||
|
||||
(job_path, ret_val) = self._vs_man_svc.ExportSystemDefinition(
|
||||
ComputerSystem=vm.path_(),
|
||||
ExportDirectory=export_path,
|
||||
ExportSettingData=export_setting_data.GetText_(1))
|
||||
self._jobutils.check_ret_val(ret_val, job_path)
|
||||
|
||||
def import_vm_definition(self, export_config_file_path,
|
||||
snapshot_folder_path,
|
||||
new_uuid=False):
|
||||
(ref, job_path, ret_val) = self._vs_man_svc.ImportSystemDefinition(
|
||||
new_uuid, snapshot_folder_path, export_config_file_path)
|
||||
self._jobutils.check_ret_val(ret_val, job_path)
|
||||
|
||||
def realize_vm(self, vm_name):
|
||||
planned_vm = self._get_planned_vm(vm_name, fail_if_not_found=True)
|
||||
|
||||
if planned_vm:
|
||||
(job_path, ret_val) = (
|
||||
self._vs_man_svc.ValidatePlannedSystem(planned_vm.path_()))
|
||||
self._jobutils.check_ret_val(ret_val, job_path)
|
||||
(job_path, ref, ret_val) = (
|
||||
self._vs_man_svc.RealizePlannedSystem(planned_vm.path_()))
|
||||
self._jobutils.check_ret_val(ret_val, job_path)
|
||||
|
||||
def _get_planned_vm(self, vm_name, conn_v2=None, fail_if_not_found=False):
|
||||
if not conn_v2:
|
||||
conn_v2 = self._conn
|
||||
planned_vm = conn_v2.Msvm_PlannedComputerSystem(ElementName=vm_name)
|
||||
if planned_vm:
|
||||
return planned_vm[0]
|
||||
elif fail_if_not_found:
|
||||
raise exceptions.HyperVException(
|
||||
_('Cannot find planned VM with name: %s') % vm_name)
|
||||
return None
|
|
@ -79,6 +79,7 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
|
||||
_VIRTUAL_SYSTEM_SUBTYPE = 'VirtualSystemSubType'
|
||||
_VIRTUAL_SYSTEM_TYPE_REALIZED = 'Microsoft:Hyper-V:System:Realized'
|
||||
_VIRTUAL_SYSTEM_TYPE_PLANNED = 'Microsoft:Hyper-V:System:Planned'
|
||||
_VIRTUAL_SYSTEM_SUBTYPE_GEN2 = 'Microsoft:Hyper-V:SubType:2'
|
||||
|
||||
_SNAPSHOT_FULL = 2
|
||||
|
@ -190,18 +191,21 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
settings = self.get_vm_summary_info(vm_name)
|
||||
return settings['EnabledState']
|
||||
|
||||
def _lookup_vm_check(self, vm_name, as_vssd=True, for_update=False):
|
||||
vm = self._lookup_vm(vm_name, as_vssd, for_update)
|
||||
def _lookup_vm_check(self, vm_name, as_vssd=True, for_update=False,
|
||||
virtual_system_type=_VIRTUAL_SYSTEM_TYPE_REALIZED):
|
||||
vm = self._lookup_vm(vm_name, as_vssd, for_update,
|
||||
virtual_system_type)
|
||||
if not vm:
|
||||
raise exceptions.HyperVVMNotFoundException(vm_name=vm_name)
|
||||
return vm
|
||||
|
||||
def _lookup_vm(self, vm_name, as_vssd=True, for_update=False):
|
||||
def _lookup_vm(self, vm_name, as_vssd=True, for_update=False,
|
||||
virtual_system_type=_VIRTUAL_SYSTEM_TYPE_REALIZED):
|
||||
if as_vssd:
|
||||
conn = self._compat_conn if for_update else self._conn
|
||||
vms = conn.Msvm_VirtualSystemSettingData(
|
||||
ElementName=vm_name,
|
||||
VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED)
|
||||
VirtualSystemType=virtual_system_type)
|
||||
else:
|
||||
vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
|
||||
n = len(vms)
|
||||
|
@ -267,12 +271,24 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
self._jobutils.modify_virt_resource(procsetting)
|
||||
|
||||
def update_vm(self, vm_name, memory_mb, memory_per_numa_node, vcpus_num,
|
||||
vcpus_per_numa_node, limit_cpu_features, dynamic_mem_ratio):
|
||||
vmsetting = self._lookup_vm_check(vm_name)
|
||||
vcpus_per_numa_node, limit_cpu_features, dynamic_mem_ratio,
|
||||
configuration_root_dir=None, snapshot_dir=None,
|
||||
is_planned_vm=False):
|
||||
virtual_system_type = self._get_virtual_system_type(is_planned_vm)
|
||||
|
||||
vmsetting = self._lookup_vm_check(
|
||||
vm_name, virtual_system_type=virtual_system_type)
|
||||
|
||||
if configuration_root_dir:
|
||||
vmsetting.ConfigurationDataRoot = configuration_root_dir
|
||||
if snapshot_dir:
|
||||
vmsetting.SnapshotDataRoot = snapshot_dir
|
||||
self._set_vm_memory(vmsetting, memory_mb, memory_per_numa_node,
|
||||
dynamic_mem_ratio)
|
||||
self._set_vm_vcpus(vmsetting, vcpus_num, vcpus_per_numa_node,
|
||||
limit_cpu_features)
|
||||
if configuration_root_dir or snapshot_dir:
|
||||
self._modify_virtual_system(vmsetting)
|
||||
|
||||
def check_admin_permissions(self):
|
||||
if not self._compat_conn.Msvm_VirtualSystemManagementService():
|
||||
|
@ -488,9 +504,10 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
diskdrive.ElementName = serial
|
||||
self._jobutils.modify_virt_resource(diskdrive)
|
||||
|
||||
def get_vm_physical_disk_mapping(self, vm_name):
|
||||
def get_vm_physical_disk_mapping(self, vm_name, is_planned_vm=False):
|
||||
mapping = {}
|
||||
physical_disks = self.get_vm_disks(vm_name)[1]
|
||||
physical_disks = (
|
||||
self.get_vm_disks(vm_name, is_planned_vm=is_planned_vm)[1])
|
||||
for diskdrive in physical_disks:
|
||||
mapping[diskdrive.ElementName] = dict(
|
||||
resource_path=diskdrive.path_(),
|
||||
|
@ -599,8 +616,11 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
vmsettings = self._lookup_vm_check(vm_name)
|
||||
return vmsettings.ConfigurationDataRoot
|
||||
|
||||
def get_vm_storage_paths(self, vm_name):
|
||||
vmsettings = self._lookup_vm_check(vm_name)
|
||||
def get_vm_storage_paths(self, vm_name, is_planned_vm=False):
|
||||
virtual_system_type = self._get_virtual_system_type(is_planned_vm)
|
||||
vmsettings = self._lookup_vm_check(
|
||||
vm_name,
|
||||
virtual_system_type=virtual_system_type)
|
||||
(disk_resources, volume_resources) = self._get_vm_disks(vmsettings)
|
||||
|
||||
volume_drives = []
|
||||
|
@ -615,8 +635,10 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
|
||||
return (disk_files, volume_drives)
|
||||
|
||||
def get_vm_disks(self, vm_name):
|
||||
vmsettings = self._lookup_vm_check(vm_name)
|
||||
def get_vm_disks(self, vm_name, is_planned_vm=False):
|
||||
virtual_system_type = self._get_virtual_system_type(is_planned_vm)
|
||||
vmsettings = self._lookup_vm_check(
|
||||
vm_name, virtual_system_type=virtual_system_type)
|
||||
return self._get_vm_disks(vmsettings)
|
||||
|
||||
def _get_vm_disks(self, vmsettings):
|
||||
|
@ -1078,3 +1100,14 @@ class VMUtils(baseutils.BaseUtilsVirt):
|
|||
|
||||
def is_secure_vm(self, instance_name):
|
||||
return False
|
||||
|
||||
def update_vm_disk_path(self, disk_path, new_disk_path, is_physical=True):
|
||||
disk_resource = self._get_mounted_disk_resource_from_path(
|
||||
disk_path=disk_path, is_physical=is_physical)
|
||||
disk_resource.HostResource = [new_disk_path]
|
||||
self._jobutils.modify_virt_resource(disk_resource)
|
||||
|
||||
def _get_virtual_system_type(self, is_planned_vm):
|
||||
return (
|
||||
self._VIRTUAL_SYSTEM_TYPE_PLANNED if is_planned_vm
|
||||
else self._VIRTUAL_SYSTEM_TYPE_REALIZED)
|
||||
|
|
|
@ -60,6 +60,9 @@ class PathUtils(object):
|
|||
def rename(self, src, dest):
|
||||
os.rename(src, dest)
|
||||
|
||||
def copy_dir(self, src, dest):
|
||||
shutil.copytree(src, dest)
|
||||
|
||||
def copyfile(self, src, dest):
|
||||
self.copy(src, dest)
|
||||
|
||||
|
|
|
@ -83,6 +83,11 @@ utils_map = {
|
|||
'min_version': 6.2,
|
||||
'max_version': None,
|
||||
'path': 'os_win.utils.metrics.metricsutils.MetricsUtils'}},
|
||||
'migrationutils': {
|
||||
'MigrationUtils': {
|
||||
'min_version': 6.2,
|
||||
'max_version': None,
|
||||
'path': 'os_win.utils.compute.migrationutils.MigrationUtils'}},
|
||||
'networkutils': {
|
||||
'NetworkUtils': {
|
||||
'min_version': 6.2,
|
||||
|
@ -232,3 +237,7 @@ def get_clusterutils():
|
|||
|
||||
def get_dnsutils():
|
||||
return _get_class(class_type='dnsutils')
|
||||
|
||||
|
||||
def get_migrationutils():
|
||||
return _get_class(class_type='migrationutils')
|
||||
|
|
Loading…
Reference in New Issue