Merge "Added support for new block device format in vmops"
This commit is contained in:
commit
92134b4c18
@ -50,14 +50,7 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self.assertEqual(slot_map[constants.CTRL_TYPE_IDE][1],
|
self.assertEqual(slot_map[constants.CTRL_TYPE_IDE][1],
|
||||||
os_win_const.IDE_CONTROLLER_SLOTS_NUMBER - 1)
|
os_win_const.IDE_CONTROLLER_SLOTS_NUMBER - 1)
|
||||||
|
|
||||||
@mock.patch('nova.virt.configdrive.required_by')
|
@mock.patch.object(block_device_manager.configdrive, 'required_by')
|
||||||
def test_init_controller_slot_counter_gen2(self, mock_cfg_drive_req):
|
|
||||||
slot_map = self._bdman._initialize_controller_slot_counter(
|
|
||||||
mock.sentinel.FAKE_INSTANCE, constants.VM_GEN_2)
|
|
||||||
|
|
||||||
self.assertEqual(slot_map[constants.CTRL_TYPE_SCSI][0],
|
|
||||||
os_win_const.SCSI_CONTROLLER_SLOTS_NUMBER - 1)
|
|
||||||
|
|
||||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||||
'_initialize_controller_slot_counter')
|
'_initialize_controller_slot_counter')
|
||||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||||
@ -66,28 +59,44 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
|
|||||||
'_check_and_update_ephemerals')
|
'_check_and_update_ephemerals')
|
||||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||||
'_check_and_update_volumes')
|
'_check_and_update_volumes')
|
||||||
def test_validate_and_update_bdi(self, mock_check_and_update_vol,
|
def _check_validate_and_update_bdi(self, mock_check_and_update_vol,
|
||||||
mock_check_and_update_eph,
|
mock_check_and_update_eph,
|
||||||
mock_check_and_update_root,
|
mock_check_and_update_root,
|
||||||
mock_init_ctrl_cntr):
|
mock_init_ctrl_cntr,
|
||||||
mock_init_ctrl_cntr.return_value = mock.sentinel.FAKE_SLOT_MAP
|
mock_required_by, available_slots=1):
|
||||||
|
mock_required_by.return_value = True
|
||||||
|
slot_map = {constants.CTRL_TYPE_SCSI: [available_slots]}
|
||||||
|
mock_init_ctrl_cntr.return_value = slot_map
|
||||||
|
|
||||||
self._bdman.validate_and_update_bdi(mock.sentinel.FAKE_INSTANCE,
|
if available_slots:
|
||||||
mock.sentinel.IMAGE_META,
|
self._bdman.validate_and_update_bdi(mock.sentinel.FAKE_INSTANCE,
|
||||||
mock.sentinel.VM_GEN,
|
mock.sentinel.IMAGE_META,
|
||||||
mock.sentinel.BLOCK_DEV_INFO)
|
constants.VM_GEN_2,
|
||||||
|
mock.sentinel.BLOCK_DEV_INFO)
|
||||||
|
else:
|
||||||
|
self.assertRaises(exception.InvalidBDMFormat,
|
||||||
|
self._bdman.validate_and_update_bdi,
|
||||||
|
mock.sentinel.FAKE_INSTANCE,
|
||||||
|
mock.sentinel.IMAGE_META,
|
||||||
|
constants.VM_GEN_2,
|
||||||
|
mock.sentinel.BLOCK_DEV_INFO)
|
||||||
|
|
||||||
mock_init_ctrl_cntr.assert_called_once_with(
|
mock_init_ctrl_cntr.assert_called_once_with(
|
||||||
mock.sentinel.FAKE_INSTANCE, mock.sentinel.VM_GEN)
|
mock.sentinel.FAKE_INSTANCE, constants.VM_GEN_2)
|
||||||
mock_check_and_update_root.assert_called_once_with(
|
mock_check_and_update_root.assert_called_once_with(
|
||||||
mock.sentinel.VM_GEN, mock.sentinel.IMAGE_META,
|
constants.VM_GEN_2, mock.sentinel.IMAGE_META,
|
||||||
mock.sentinel.BLOCK_DEV_INFO, mock.sentinel.FAKE_SLOT_MAP)
|
mock.sentinel.BLOCK_DEV_INFO, slot_map)
|
||||||
mock_check_and_update_eph.assert_called_once_with(
|
mock_check_and_update_eph.assert_called_once_with(
|
||||||
mock.sentinel.VM_GEN, mock.sentinel.BLOCK_DEV_INFO,
|
constants.VM_GEN_2, mock.sentinel.BLOCK_DEV_INFO, slot_map)
|
||||||
mock.sentinel.FAKE_SLOT_MAP)
|
|
||||||
mock_check_and_update_vol.assert_called_once_with(
|
mock_check_and_update_vol.assert_called_once_with(
|
||||||
mock.sentinel.VM_GEN, mock.sentinel.BLOCK_DEV_INFO,
|
constants.VM_GEN_2, mock.sentinel.BLOCK_DEV_INFO, slot_map)
|
||||||
mock.sentinel.FAKE_SLOT_MAP)
|
mock_required_by.assert_called_once_with(mock.sentinel.FAKE_INSTANCE)
|
||||||
|
|
||||||
|
def test_validate_and_update_bdi(self):
|
||||||
|
self._check_validate_and_update_bdi()
|
||||||
|
|
||||||
|
def test_validate_and_update_bdi_insufficient_slots(self):
|
||||||
|
self._check_validate_and_update_bdi(available_slots=0)
|
||||||
|
|
||||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||||
'_get_available_controller_slot')
|
'_get_available_controller_slot')
|
||||||
|
@ -34,6 +34,7 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self._livemigrops = livemigrationops.LiveMigrationOps()
|
self._livemigrops = livemigrationops.LiveMigrationOps()
|
||||||
self._livemigrops._livemigrutils = mock.MagicMock()
|
self._livemigrops._livemigrutils = mock.MagicMock()
|
||||||
self._livemigrops._pathutils = mock.MagicMock()
|
self._livemigrops._pathutils = mock.MagicMock()
|
||||||
|
self._livemigrops._block_dev_man = mock.MagicMock()
|
||||||
|
|
||||||
@mock.patch.object(serialconsoleops.SerialConsoleOps,
|
@mock.patch.object(serialconsoleops.SerialConsoleOps,
|
||||||
'stop_console_handler')
|
'stop_console_handler')
|
||||||
@ -78,22 +79,21 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self._test_live_migration(side_effect=os_win_exc.HyperVException)
|
self._test_live_migration(side_effect=os_win_exc.HyperVException)
|
||||||
|
|
||||||
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps.get_disk_path_mapping')
|
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps.get_disk_path_mapping')
|
||||||
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
|
|
||||||
'.ebs_root_in_block_devices')
|
|
||||||
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
||||||
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
|
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
|
||||||
'.initialize_volumes_connection')
|
'.initialize_volumes_connection')
|
||||||
def _test_pre_live_migration(self, mock_initialize_connection,
|
def _test_pre_live_migration(self, mock_initialize_connection,
|
||||||
mock_get_cached_image,
|
mock_get_cached_image,
|
||||||
mock_ebs_root_in_block_devices,
|
|
||||||
mock_get_disk_path_mapping,
|
mock_get_disk_path_mapping,
|
||||||
phys_disks_attached=True):
|
phys_disks_attached=True):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_instance.image_ref = "fake_image_ref"
|
mock_instance.image_ref = "fake_image_ref"
|
||||||
mock_ebs_root_in_block_devices.return_value = None
|
|
||||||
mock_get_disk_path_mapping.return_value = (
|
mock_get_disk_path_mapping.return_value = (
|
||||||
mock.sentinel.disk_path_mapping if phys_disks_attached
|
mock.sentinel.disk_path_mapping if phys_disks_attached
|
||||||
else None)
|
else None)
|
||||||
|
bdman = self._livemigrops._block_dev_man
|
||||||
|
mock_is_boot_from_vol = bdman.is_boot_from_volume
|
||||||
|
mock_is_boot_from_vol.return_value = None
|
||||||
CONF.set_override('use_cow_images', True)
|
CONF.set_override('use_cow_images', True)
|
||||||
self._livemigrops.pre_live_migration(
|
self._livemigrops.pre_live_migration(
|
||||||
self.context, mock_instance,
|
self.context, mock_instance,
|
||||||
@ -103,7 +103,7 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
check_config = (
|
check_config = (
|
||||||
self._livemigrops._livemigrutils.check_live_migration_config)
|
self._livemigrops._livemigrutils.check_live_migration_config)
|
||||||
check_config.assert_called_once_with()
|
check_config.assert_called_once_with()
|
||||||
mock_ebs_root_in_block_devices.assert_called_once_with(
|
mock_is_boot_from_vol.assert_called_once_with(
|
||||||
mock.sentinel.BLOCK_INFO)
|
mock.sentinel.BLOCK_INFO)
|
||||||
mock_get_cached_image.assert_called_once_with(self.context,
|
mock_get_cached_image.assert_called_once_with(self.context,
|
||||||
mock_instance)
|
mock_instance)
|
||||||
|
@ -23,6 +23,7 @@ from nova import objects
|
|||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit import fake_instance
|
from nova.tests.unit import fake_instance
|
||||||
from nova.tests.unit.virt.hyperv import test_base
|
from nova.tests.unit.virt.hyperv import test_base
|
||||||
|
from nova.virt.hyperv import constants
|
||||||
from nova.virt.hyperv import migrationops
|
from nova.virt.hyperv import migrationops
|
||||||
|
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self._migrationops._pathutils = mock.MagicMock()
|
self._migrationops._pathutils = mock.MagicMock()
|
||||||
self._migrationops._volumeops = mock.MagicMock()
|
self._migrationops._volumeops = mock.MagicMock()
|
||||||
self._migrationops._imagecache = mock.MagicMock()
|
self._migrationops._imagecache = mock.MagicMock()
|
||||||
|
self._migrationops._block_dev_man = mock.MagicMock()
|
||||||
|
|
||||||
def _check_migrate_disk_files(self, host):
|
def _check_migrate_disk_files(self, host):
|
||||||
instance_path = 'fake/instance/path'
|
instance_path = 'fake/instance/path'
|
||||||
@ -223,56 +225,61 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
@mock.patch.object(migrationops.MigrationOps,
|
@mock.patch.object(migrationops.MigrationOps,
|
||||||
'_check_and_attach_config_drive')
|
'_check_and_attach_config_drive')
|
||||||
@mock.patch.object(migrationops.MigrationOps, '_revert_migration_files')
|
@mock.patch.object(migrationops.MigrationOps, '_revert_migration_files')
|
||||||
|
@mock.patch.object(migrationops.MigrationOps, '_check_ephemeral_disks')
|
||||||
@mock.patch.object(objects.ImageMeta, "from_instance")
|
@mock.patch.object(objects.ImageMeta, "from_instance")
|
||||||
def _check_finish_revert_migration(self, mock_image,
|
def _check_finish_revert_migration(self, mock_image,
|
||||||
|
mock_check_eph_disks,
|
||||||
mock_revert_migration_files,
|
mock_revert_migration_files,
|
||||||
mock_check_attach_config_drive,
|
mock_check_attach_config_drive,
|
||||||
boot_from_volume=False):
|
disk_type=constants.DISK):
|
||||||
mock_image.return_value = objects.ImageMeta.from_dict({})
|
mock_image.return_value = objects.ImageMeta.from_dict({})
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_ebs_root_in_block_devices = (
|
root_device = {'type': disk_type}
|
||||||
self._migrationops._volumeops.ebs_root_in_block_devices)
|
block_device_info = {'root_disk': root_device, 'ephemerals': []}
|
||||||
mock_ebs_root_in_block_devices.return_value = boot_from_volume
|
|
||||||
lookup_ephemeral = (
|
|
||||||
self._migrationops._pathutils.lookup_ephemeral_vhd_path)
|
|
||||||
|
|
||||||
self._migrationops.finish_revert_migration(
|
self._migrationops.finish_revert_migration(
|
||||||
context=self.context, instance=mock_instance,
|
context=self.context, instance=mock_instance,
|
||||||
network_info=mock.sentinel.network_info,
|
network_info=mock.sentinel.network_info,
|
||||||
block_device_info=mock.sentinel.block_device,
|
block_device_info=block_device_info,
|
||||||
power_on=True)
|
power_on=True)
|
||||||
|
|
||||||
mock_revert_migration_files.assert_called_once_with(
|
mock_revert_migration_files.assert_called_once_with(
|
||||||
mock_instance.name)
|
mock_instance.name)
|
||||||
mock_ebs_root_in_block_devices.assert_called_once_with(
|
if root_device['type'] == constants.DISK:
|
||||||
mock.sentinel.block_device)
|
|
||||||
if not boot_from_volume:
|
|
||||||
lookup_root_vhd = (
|
lookup_root_vhd = (
|
||||||
self._migrationops._pathutils.lookup_root_vhd_path)
|
self._migrationops._pathutils.lookup_root_vhd_path)
|
||||||
lookup_root_vhd.assert_called_once_with(mock_instance.name)
|
lookup_root_vhd.assert_called_once_with(mock_instance.name)
|
||||||
fake_root_path = lookup_root_vhd.return_value
|
self.assertEqual(lookup_root_vhd.return_value,
|
||||||
else:
|
root_device['path'])
|
||||||
fake_root_path = None
|
|
||||||
|
|
||||||
lookup_ephemeral.assert_called_with(mock_instance.name)
|
|
||||||
get_image_vm_gen = self._migrationops._vmops.get_image_vm_generation
|
get_image_vm_gen = self._migrationops._vmops.get_image_vm_generation
|
||||||
get_image_vm_gen.assert_called_once_with(
|
get_image_vm_gen.assert_called_once_with(
|
||||||
mock_instance.uuid, fake_root_path,
|
mock_instance.uuid, test.MatchType(objects.ImageMeta))
|
||||||
test.MatchType(objects.ImageMeta))
|
|
||||||
self._migrationops._vmops.create_instance.assert_called_once_with(
|
self._migrationops._vmops.create_instance.assert_called_once_with(
|
||||||
mock_instance, mock.sentinel.network_info,
|
mock_instance, mock.sentinel.network_info, root_device,
|
||||||
mock.sentinel.block_device, fake_root_path,
|
block_device_info, get_image_vm_gen.return_value)
|
||||||
lookup_ephemeral.return_value, get_image_vm_gen.return_value)
|
|
||||||
mock_check_attach_config_drive.assert_called_once_with(
|
mock_check_attach_config_drive.assert_called_once_with(
|
||||||
mock_instance, get_image_vm_gen.return_value)
|
mock_instance, get_image_vm_gen.return_value)
|
||||||
self._migrationops._vmops.power_on.assert_called_once_with(
|
self._migrationops._vmops.power_on.assert_called_once_with(
|
||||||
mock_instance)
|
mock_instance)
|
||||||
|
|
||||||
def test_finish_revert_migration_boot_from_volume(self):
|
def test_finish_revert_migration_boot_from_volume(self):
|
||||||
self._check_finish_revert_migration(boot_from_volume=True)
|
self._check_finish_revert_migration(disk_type=constants.VOLUME)
|
||||||
|
|
||||||
def test_finish_revert_migration_not_in_block_device(self):
|
def test_finish_revert_migration_boot_from_disk(self):
|
||||||
self._check_finish_revert_migration()
|
self._check_finish_revert_migration(disk_type=constants.DISK)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.ImageMeta, "from_instance")
|
||||||
|
def test_finish_revert_migration_no_root_vhd(self, mock_image):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
self._migrationops._pathutils.lookup_root_vhd_path.return_value = None
|
||||||
|
bdi = {'root_disk': {'type': constants.DISK},
|
||||||
|
'ephemerals': []}
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exception.DiskNotFound,
|
||||||
|
self._migrationops.finish_revert_migration, self.context,
|
||||||
|
mock_instance, mock.sentinel.network_info, bdi, True)
|
||||||
|
|
||||||
def test_merge_base_vhd(self):
|
def test_merge_base_vhd(self):
|
||||||
fake_diff_vhd_path = 'fake/diff/path'
|
fake_diff_vhd_path = 'fake/diff/path'
|
||||||
@ -362,24 +369,21 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
'_check_and_attach_config_drive')
|
'_check_and_attach_config_drive')
|
||||||
@mock.patch.object(migrationops.MigrationOps, '_check_base_disk')
|
@mock.patch.object(migrationops.MigrationOps, '_check_base_disk')
|
||||||
@mock.patch.object(migrationops.MigrationOps, '_check_resize_vhd')
|
@mock.patch.object(migrationops.MigrationOps, '_check_resize_vhd')
|
||||||
def _check_finish_migration(self, mock_check_resize_vhd,
|
@mock.patch.object(migrationops.MigrationOps, '_check_ephemeral_disks')
|
||||||
|
def _check_finish_migration(self, mock_check_eph_disks,
|
||||||
|
mock_check_resize_vhd,
|
||||||
mock_check_base_disk,
|
mock_check_base_disk,
|
||||||
mock_check_attach_config_drive,
|
mock_check_attach_config_drive,
|
||||||
ephemeral_path=None,
|
disk_type=constants.DISK):
|
||||||
boot_from_volume=False):
|
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_instance.ephemeral_gb = 1
|
mock_instance.ephemeral_gb = 1
|
||||||
mock_vhd_info = mock.MagicMock()
|
root_device = {'type': disk_type}
|
||||||
mock_eph_info = mock.MagicMock()
|
block_device_info = {'root_disk': root_device, 'ephemerals': []}
|
||||||
|
|
||||||
lookup_root_vhd = self._migrationops._pathutils.lookup_root_vhd_path
|
lookup_root_vhd = self._migrationops._pathutils.lookup_root_vhd_path
|
||||||
side_effect = [mock_eph_info] if boot_from_volume else [mock_vhd_info,
|
get_vhd_info = self._migrationops._vhdutils.get_vhd_info
|
||||||
mock_eph_info]
|
mock_vhd_info = get_vhd_info.return_value
|
||||||
mock_ebs_root_in_block_devices = (
|
|
||||||
self._migrationops._volumeops.ebs_root_in_block_devices)
|
|
||||||
mock_ebs_root_in_block_devices.return_value = boot_from_volume
|
|
||||||
self._migrationops._vhdutils.get_vhd_info.side_effect = side_effect
|
|
||||||
look_up_ephem = self._migrationops._pathutils.lookup_ephemeral_vhd_path
|
|
||||||
look_up_ephem.return_value = ephemeral_path
|
|
||||||
expected_check_resize = []
|
expected_check_resize = []
|
||||||
expected_get_info = []
|
expected_get_info = []
|
||||||
|
|
||||||
@ -387,71 +391,131 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
context=self.context, migration=mock.sentinel.migration,
|
context=self.context, migration=mock.sentinel.migration,
|
||||||
instance=mock_instance, disk_info=mock.sentinel.disk_info,
|
instance=mock_instance, disk_info=mock.sentinel.disk_info,
|
||||||
network_info=mock.sentinel.network_info,
|
network_info=mock.sentinel.network_info,
|
||||||
image_meta=mock.sentinel.image_meta, resize_instance=True)
|
image_meta=mock.sentinel.image_meta, resize_instance=True,
|
||||||
|
block_device_info=block_device_info)
|
||||||
|
|
||||||
mock_ebs_root_in_block_devices.assert_called_once_with(None)
|
if root_device['type'] == constants.DISK:
|
||||||
if not boot_from_volume:
|
root_device_path = lookup_root_vhd.return_value
|
||||||
root_vhd_path = lookup_root_vhd.return_value
|
|
||||||
lookup_root_vhd.assert_called_with(mock_instance.name)
|
lookup_root_vhd.assert_called_with(mock_instance.name)
|
||||||
expected_get_info = [mock.call(root_vhd_path)]
|
expected_get_info = [mock.call(root_device_path)]
|
||||||
mock_vhd_info.get.assert_called_once_with("ParentPath")
|
mock_vhd_info.get.assert_called_once_with("ParentPath")
|
||||||
mock_check_base_disk.assert_called_once_with(
|
mock_check_base_disk.assert_called_once_with(
|
||||||
self.context, mock_instance, root_vhd_path,
|
self.context, mock_instance, root_device_path,
|
||||||
mock_vhd_info.get.return_value)
|
mock_vhd_info.get.return_value)
|
||||||
expected_check_resize.append(
|
expected_check_resize.append(
|
||||||
mock.call(root_vhd_path, mock_vhd_info,
|
mock.call(root_device_path, mock_vhd_info,
|
||||||
mock_instance.root_gb * units.Gi))
|
mock_instance.root_gb * units.Gi))
|
||||||
else:
|
|
||||||
root_vhd_path = None
|
|
||||||
|
|
||||||
look_up_ephem.assert_called_once_with(mock_instance.name)
|
ephemerals = block_device_info['ephemerals']
|
||||||
if ephemeral_path is None:
|
mock_check_eph_disks.assert_called_once_with(
|
||||||
create_eph_vhd = self._migrationops._vmops.create_ephemeral_vhd
|
mock_instance, ephemerals, True)
|
||||||
create_eph_vhd.assert_called_once_with(mock_instance)
|
|
||||||
ephemeral_path = create_eph_vhd.return_value
|
|
||||||
else:
|
|
||||||
expected_get_info.append(mock.call(ephemeral_path))
|
|
||||||
expected_check_resize.append(
|
|
||||||
mock.call(ephemeral_path, mock_eph_info,
|
|
||||||
mock_instance.ephemeral_gb * units.Gi))
|
|
||||||
|
|
||||||
mock_check_resize_vhd.assert_has_calls(expected_check_resize)
|
mock_check_resize_vhd.assert_has_calls(expected_check_resize)
|
||||||
self._migrationops._vhdutils.get_vhd_info.assert_has_calls(
|
self._migrationops._vhdutils.get_vhd_info.assert_has_calls(
|
||||||
expected_get_info)
|
expected_get_info)
|
||||||
get_image_vm_gen = self._migrationops._vmops.get_image_vm_generation
|
get_image_vm_gen = self._migrationops._vmops.get_image_vm_generation
|
||||||
get_image_vm_gen.assert_called_once_with(mock_instance.uuid,
|
get_image_vm_gen.assert_called_once_with(mock_instance.uuid,
|
||||||
root_vhd_path,
|
|
||||||
mock.sentinel.image_meta)
|
mock.sentinel.image_meta)
|
||||||
self._migrationops._vmops.create_instance.assert_called_once_with(
|
self._migrationops._vmops.create_instance.assert_called_once_with(
|
||||||
mock_instance, mock.sentinel.network_info, None, root_vhd_path,
|
mock_instance, mock.sentinel.network_info, root_device,
|
||||||
ephemeral_path, get_image_vm_gen.return_value)
|
block_device_info, get_image_vm_gen.return_value)
|
||||||
mock_check_attach_config_drive.assert_called_once_with(
|
mock_check_attach_config_drive.assert_called_once_with(
|
||||||
mock_instance, get_image_vm_gen.return_value)
|
mock_instance, get_image_vm_gen.return_value)
|
||||||
self._migrationops._vmops.power_on.assert_called_once_with(
|
self._migrationops._vmops.power_on.assert_called_once_with(
|
||||||
mock_instance)
|
mock_instance)
|
||||||
|
|
||||||
def test_finish_migration(self):
|
def test_finish_migration(self):
|
||||||
self._check_finish_migration(
|
self._check_finish_migration(disk_type=constants.DISK)
|
||||||
ephemeral_path=mock.sentinel.ephemeral_path)
|
|
||||||
|
|
||||||
def test_finish_migration_boot_from_volume(self):
|
def test_finish_migration_boot_from_volume(self):
|
||||||
self._check_finish_migration(
|
self._check_finish_migration(disk_type=constants.VOLUME)
|
||||||
ephemeral_path=mock.sentinel.ephemeral_path,
|
|
||||||
boot_from_volume=True)
|
|
||||||
|
|
||||||
def test_finish_migration_no_ephemeral(self):
|
|
||||||
self._check_finish_migration()
|
|
||||||
|
|
||||||
def test_finish_migration_no_root(self):
|
def test_finish_migration_no_root(self):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_ebs_root_in_block_devices = (
|
|
||||||
self._migrationops._volumeops.ebs_root_in_block_devices)
|
|
||||||
mock_ebs_root_in_block_devices.return_value = False
|
|
||||||
self._migrationops._pathutils.lookup_root_vhd_path.return_value = None
|
self._migrationops._pathutils.lookup_root_vhd_path.return_value = None
|
||||||
|
bdi = {'root_disk': {'type': constants.DISK},
|
||||||
|
'ephemerals': []}
|
||||||
|
|
||||||
self.assertRaises(exception.DiskNotFound,
|
self.assertRaises(exception.DiskNotFound,
|
||||||
self._migrationops.finish_migration,
|
self._migrationops.finish_migration,
|
||||||
self.context, mock.sentinel.migration,
|
self.context, mock.sentinel.migration,
|
||||||
mock_instance, mock.sentinel.disk_info,
|
mock_instance, mock.sentinel.disk_info,
|
||||||
mock.sentinel.network_info,
|
mock.sentinel.network_info,
|
||||||
mock.sentinel.image_meta, True, None, True)
|
mock.sentinel.image_meta, True, bdi, True)
|
||||||
|
|
||||||
|
@mock.patch.object(migrationops.MigrationOps, '_check_resize_vhd')
|
||||||
|
@mock.patch.object(migrationops.LOG, 'warn')
|
||||||
|
def test_check_ephemeral_disks_multiple_eph_warn(self, mock_warn,
|
||||||
|
mock_check_resize_vhd):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
mock_instance.ephemeral_gb = 3
|
||||||
|
mock_ephemerals = [{'size': 1}, {'size': 1}]
|
||||||
|
|
||||||
|
self._migrationops._check_ephemeral_disks(mock_instance,
|
||||||
|
mock_ephemerals,
|
||||||
|
True)
|
||||||
|
|
||||||
|
mock_warn.assert_called_once_with(
|
||||||
|
"Cannot resize multiple ephemeral disks for instance.",
|
||||||
|
instance=mock_instance)
|
||||||
|
|
||||||
|
def test_check_ephemeral_disks_exception(self):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
mock_ephemerals = [dict()]
|
||||||
|
|
||||||
|
lookup_eph_path = (
|
||||||
|
self._migrationops._pathutils.lookup_ephemeral_vhd_path)
|
||||||
|
lookup_eph_path.return_value = None
|
||||||
|
|
||||||
|
self.assertRaises(exception.DiskNotFound,
|
||||||
|
self._migrationops._check_ephemeral_disks,
|
||||||
|
mock_instance, mock_ephemerals)
|
||||||
|
|
||||||
|
@mock.patch.object(migrationops.MigrationOps, '_check_resize_vhd')
|
||||||
|
def _test_check_ephemeral_disks(self, mock_check_resize_vhd,
|
||||||
|
existing_eph_path=None, new_eph_size=42):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
mock_instance.ephemeral_gb = new_eph_size
|
||||||
|
eph = {}
|
||||||
|
mock_ephemerals = [eph]
|
||||||
|
|
||||||
|
mock_pathutils = self._migrationops._pathutils
|
||||||
|
lookup_eph_path = mock_pathutils.lookup_ephemeral_vhd_path
|
||||||
|
lookup_eph_path.return_value = existing_eph_path
|
||||||
|
mock_get_eph_vhd_path = mock_pathutils.get_ephemeral_vhd_path
|
||||||
|
mock_get_eph_vhd_path.return_value = mock.sentinel.get_path
|
||||||
|
|
||||||
|
mock_vhdutils = self._migrationops._vhdutils
|
||||||
|
mock_get_vhd_format = mock_vhdutils.get_best_supported_vhd_format
|
||||||
|
mock_get_vhd_format.return_value = mock.sentinel.vhd_format
|
||||||
|
|
||||||
|
self._migrationops._check_ephemeral_disks(mock_instance,
|
||||||
|
mock_ephemerals,
|
||||||
|
True)
|
||||||
|
|
||||||
|
self.assertEqual(mock_instance.ephemeral_gb, eph['size'])
|
||||||
|
if not existing_eph_path:
|
||||||
|
mock_vmops = self._migrationops._vmops
|
||||||
|
mock_vmops.create_ephemeral_disk.assert_called_once_with(
|
||||||
|
mock_instance.name, eph)
|
||||||
|
self.assertEqual(mock.sentinel.vhd_format, eph['format'])
|
||||||
|
self.assertEqual(mock.sentinel.get_path, eph['path'])
|
||||||
|
elif new_eph_size:
|
||||||
|
mock_check_resize_vhd.assert_called_once_with(
|
||||||
|
existing_eph_path,
|
||||||
|
self._migrationops._vhdutils.get_vhd_info.return_value,
|
||||||
|
mock_instance.ephemeral_gb * units.Gi)
|
||||||
|
self.assertEqual(existing_eph_path, eph['path'])
|
||||||
|
else:
|
||||||
|
self._migrationops._pathutils.remove.assert_called_once_with(
|
||||||
|
existing_eph_path)
|
||||||
|
|
||||||
|
def test_check_ephemeral_disks_create(self):
|
||||||
|
self._test_check_ephemeral_disks()
|
||||||
|
|
||||||
|
def test_check_ephemeral_disks_resize(self):
|
||||||
|
self._test_check_ephemeral_disks(existing_eph_path=mock.sentinel.path)
|
||||||
|
|
||||||
|
def test_check_ephemeral_disks_remove(self):
|
||||||
|
self._test_check_ephemeral_disks(existing_eph_path=mock.sentinel.path,
|
||||||
|
new_eph_size=0)
|
||||||
|
@ -67,6 +67,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self._vmops._pathutils = mock.MagicMock()
|
self._vmops._pathutils = mock.MagicMock()
|
||||||
self._vmops._hostutils = mock.MagicMock()
|
self._vmops._hostutils = mock.MagicMock()
|
||||||
self._vmops._serial_console_ops = mock.MagicMock()
|
self._vmops._serial_console_ops = mock.MagicMock()
|
||||||
|
self._vmops._block_dev_man = mock.MagicMock()
|
||||||
|
|
||||||
@mock.patch('nova.network.is_neutron')
|
@mock.patch('nova.network.is_neutron')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.importutils.import_object')
|
@mock.patch('nova.virt.hyperv.vmops.importutils.import_object')
|
||||||
@ -151,7 +152,24 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
def test_get_info_exception(self):
|
def test_get_info_exception(self):
|
||||||
self._test_get_info(vm_exists=False)
|
self._test_get_info(vm_exists=False)
|
||||||
|
|
||||||
def _prepare_create_root_vhd_mocks(self, use_cow_images, vhd_format,
|
@mock.patch.object(vmops.VMOps, 'check_vm_image_type')
|
||||||
|
@mock.patch.object(vmops.VMOps, '_create_root_vhd')
|
||||||
|
def test_create_root_device_type_disk(self, mock_create_root_device,
|
||||||
|
mock_check_vm_image_type):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
mock_root_disk_info = {'type': constants.DISK}
|
||||||
|
|
||||||
|
self._vmops._create_root_device(self.context, mock_instance,
|
||||||
|
mock_root_disk_info,
|
||||||
|
mock.sentinel.VM_GEN_1)
|
||||||
|
|
||||||
|
mock_create_root_device.assert_called_once_with(
|
||||||
|
self.context, mock_instance)
|
||||||
|
mock_check_vm_image_type.assert_called_once_with(
|
||||||
|
mock_instance.uuid, mock.sentinel.VM_GEN_1,
|
||||||
|
mock_create_root_device.return_value)
|
||||||
|
|
||||||
|
def _prepare_create_root_device_mocks(self, use_cow_images, vhd_format,
|
||||||
vhd_size):
|
vhd_size):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_instance.root_gb = self.FAKE_SIZE
|
mock_instance.root_gb = self.FAKE_SIZE
|
||||||
@ -169,7 +187,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
||||||
def _test_create_root_vhd_exception(self, mock_get_cached_image,
|
def _test_create_root_vhd_exception(self, mock_get_cached_image,
|
||||||
vhd_format):
|
vhd_format):
|
||||||
mock_instance = self._prepare_create_root_vhd_mocks(
|
mock_instance = self._prepare_create_root_device_mocks(
|
||||||
use_cow_images=False, vhd_format=vhd_format,
|
use_cow_images=False, vhd_format=vhd_format,
|
||||||
vhd_size=(self.FAKE_SIZE + 1))
|
vhd_size=(self.FAKE_SIZE + 1))
|
||||||
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
|
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
|
||||||
@ -188,7 +206,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
|
|
||||||
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
||||||
def _test_create_root_vhd_qcow(self, mock_get_cached_image, vhd_format):
|
def _test_create_root_vhd_qcow(self, mock_get_cached_image, vhd_format):
|
||||||
mock_instance = self._prepare_create_root_vhd_mocks(
|
mock_instance = self._prepare_create_root_device_mocks(
|
||||||
use_cow_images=True, vhd_format=vhd_format,
|
use_cow_images=True, vhd_format=vhd_format,
|
||||||
vhd_size=(self.FAKE_SIZE - 1))
|
vhd_size=(self.FAKE_SIZE - 1))
|
||||||
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
|
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
|
||||||
@ -221,7 +239,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
|
||||||
def _test_create_root_vhd(self, mock_get_cached_image, vhd_format,
|
def _test_create_root_vhd(self, mock_get_cached_image, vhd_format,
|
||||||
is_rescue_vhd=False):
|
is_rescue_vhd=False):
|
||||||
mock_instance = self._prepare_create_root_vhd_mocks(
|
mock_instance = self._prepare_create_root_device_mocks(
|
||||||
use_cow_images=False, vhd_format=vhd_format,
|
use_cow_images=False, vhd_format=vhd_format,
|
||||||
vhd_size=(self.FAKE_SIZE - 1))
|
vhd_size=(self.FAKE_SIZE - 1))
|
||||||
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
|
fake_vhd_path = self.FAKE_ROOT_PATH % vhd_format
|
||||||
@ -292,21 +310,36 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self.assertFalse(self._vmops._is_resize_needed(
|
self.assertFalse(self._vmops._is_resize_needed(
|
||||||
mock.sentinel.FAKE_PATH, self.FAKE_SIZE, self.FAKE_SIZE, inst))
|
mock.sentinel.FAKE_PATH, self.FAKE_SIZE, self.FAKE_SIZE, inst))
|
||||||
|
|
||||||
def test_create_ephemeral_vhd(self):
|
@mock.patch.object(vmops.VMOps, 'create_ephemeral_disk')
|
||||||
|
def test_create_ephemerals(self, mock_create_ephemeral_disk):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_instance.ephemeral_gb = self.FAKE_SIZE
|
|
||||||
best_supported = self._vmops._vhdutils.get_best_supported_vhd_format
|
|
||||||
best_supported.return_value = mock.sentinel.FAKE_FORMAT
|
|
||||||
self._vmops._pathutils.get_ephemeral_vhd_path.return_value = (
|
|
||||||
mock.sentinel.FAKE_PATH)
|
|
||||||
|
|
||||||
response = self._vmops.create_ephemeral_vhd(instance=mock_instance)
|
fake_ephemerals = [dict(), dict()]
|
||||||
|
self._vmops._vhdutils.get_best_supported_vhd_format.return_value = (
|
||||||
|
mock.sentinel.format)
|
||||||
|
self._vmops._pathutils.get_ephemeral_vhd_path.side_effect = [
|
||||||
|
mock.sentinel.FAKE_PATH0, mock.sentinel.FAKE_PATH1]
|
||||||
|
|
||||||
self._vmops._pathutils.get_ephemeral_vhd_path.assert_called_with(
|
self._vmops._create_ephemerals(mock_instance, fake_ephemerals)
|
||||||
mock_instance.name, mock.sentinel.FAKE_FORMAT)
|
|
||||||
self._vmops._vhdutils.create_dynamic_vhd.assert_called_with(
|
self._vmops._pathutils.get_ephemeral_vhd_path.assert_has_calls(
|
||||||
mock.sentinel.FAKE_PATH, mock_instance.ephemeral_gb * units.Gi)
|
[mock.call(mock_instance.name, mock.sentinel.format, 'eph0'),
|
||||||
self.assertEqual(mock.sentinel.FAKE_PATH, response)
|
mock.call(mock_instance.name, mock.sentinel.format, 'eph1')])
|
||||||
|
mock_create_ephemeral_disk.assert_has_calls(
|
||||||
|
[mock.call(mock_instance.name, fake_ephemerals[0]),
|
||||||
|
mock.call(mock_instance.name, fake_ephemerals[1])])
|
||||||
|
|
||||||
|
def test_create_ephemeral_disk(self):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
mock_ephemeral_info = {'path': 'fake_eph_path',
|
||||||
|
'size': 10}
|
||||||
|
|
||||||
|
self._vmops.create_ephemeral_disk(mock_instance.name,
|
||||||
|
mock_ephemeral_info)
|
||||||
|
|
||||||
|
mock_create_dynamic_vhd = self._vmops._vhdutils.create_dynamic_vhd
|
||||||
|
mock_create_dynamic_vhd.assert_called_once_with('fake_eph_path',
|
||||||
|
10 * units.Gi)
|
||||||
|
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.destroy')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps.destroy')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.power_on')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps.power_on')
|
||||||
@ -315,62 +348,59 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
@mock.patch('nova.virt.configdrive.required_by')
|
@mock.patch('nova.virt.configdrive.required_by')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.create_instance')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps.create_instance')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.get_image_vm_generation')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps.get_image_vm_generation')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.create_ephemeral_vhd')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_ephemerals')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_root_vhd')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_root_device')
|
||||||
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps.'
|
|
||||||
'ebs_root_in_block_devices')
|
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps._delete_disk_files')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps._delete_disk_files')
|
||||||
def _test_spawn(self, mock_delete_disk_files,
|
def _test_spawn(self, mock_delete_disk_files, mock_create_root_device,
|
||||||
mock_ebs_root_in_block_devices, mock_create_root_vhd,
|
mock_create_ephemerals, mock_get_image_vm_gen,
|
||||||
mock_create_ephemeral_vhd, mock_get_image_vm_gen,
|
|
||||||
mock_create_instance, mock_configdrive_required,
|
mock_create_instance, mock_configdrive_required,
|
||||||
mock_create_config_drive, mock_attach_config_drive,
|
mock_create_config_drive, mock_attach_config_drive,
|
||||||
mock_power_on, mock_destroy, exists, boot_from_volume,
|
mock_power_on, mock_destroy, exists,
|
||||||
configdrive_required, fail):
|
configdrive_required, fail):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
mock_image_meta = mock.MagicMock()
|
mock_image_meta = mock.MagicMock()
|
||||||
|
root_device_info = mock.sentinel.ROOT_DEV_INFO
|
||||||
fake_root_path = mock_create_root_vhd.return_value
|
|
||||||
fake_root_path = None if boot_from_volume else fake_root_path
|
|
||||||
fake_ephemeral_path = mock_create_ephemeral_vhd.return_value
|
|
||||||
fake_vm_gen = mock_get_image_vm_gen.return_value
|
fake_vm_gen = mock_get_image_vm_gen.return_value
|
||||||
fake_config_drive_path = mock_create_config_drive.return_value
|
fake_config_drive_path = mock_create_config_drive.return_value
|
||||||
|
block_device_info = {'ephemerals': [], 'root_disk': root_device_info}
|
||||||
|
|
||||||
self._vmops._vmutils.vm_exists.return_value = exists
|
self._vmops._vmutils.vm_exists.return_value = exists
|
||||||
mock_ebs_root_in_block_devices.return_value = boot_from_volume
|
|
||||||
mock_create_root_vhd.return_value = fake_root_path
|
|
||||||
mock_configdrive_required.return_value = configdrive_required
|
mock_configdrive_required.return_value = configdrive_required
|
||||||
mock_create_instance.side_effect = fail
|
mock_create_instance.side_effect = fail
|
||||||
if exists:
|
if exists:
|
||||||
self.assertRaises(exception.InstanceExists, self._vmops.spawn,
|
self.assertRaises(exception.InstanceExists, self._vmops.spawn,
|
||||||
self.context, mock_instance, mock_image_meta,
|
self.context, mock_instance, mock_image_meta,
|
||||||
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
||||||
mock.sentinel.INFO, mock.sentinel.DEV_INFO)
|
mock.sentinel.INFO, block_device_info)
|
||||||
elif fail is os_win_exc.HyperVException:
|
elif fail is os_win_exc.HyperVException:
|
||||||
self.assertRaises(os_win_exc.HyperVException, self._vmops.spawn,
|
self.assertRaises(os_win_exc.HyperVException, self._vmops.spawn,
|
||||||
self.context, mock_instance, mock_image_meta,
|
self.context, mock_instance, mock_image_meta,
|
||||||
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
||||||
mock.sentinel.INFO, mock.sentinel.DEV_INFO)
|
mock.sentinel.INFO, block_device_info)
|
||||||
mock_destroy.assert_called_once_with(mock_instance)
|
mock_destroy.assert_called_once_with(mock_instance)
|
||||||
else:
|
else:
|
||||||
self._vmops.spawn(self.context, mock_instance, mock_image_meta,
|
self._vmops.spawn(self.context, mock_instance, mock_image_meta,
|
||||||
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
|
||||||
mock.sentinel.INFO, mock.sentinel.DEV_INFO)
|
mock.sentinel.INFO, block_device_info)
|
||||||
self._vmops._vmutils.vm_exists.assert_called_once_with(
|
self._vmops._vmutils.vm_exists.assert_called_once_with(
|
||||||
mock_instance.name)
|
mock_instance.name)
|
||||||
mock_delete_disk_files.assert_called_once_with(
|
mock_delete_disk_files.assert_called_once_with(
|
||||||
mock_instance.name)
|
mock_instance.name)
|
||||||
mock_ebs_root_in_block_devices.assert_called_once_with(
|
mock_validate_and_update_bdi = (
|
||||||
mock.sentinel.DEV_INFO)
|
self._vmops._block_dev_man.validate_and_update_bdi)
|
||||||
if not boot_from_volume:
|
mock_validate_and_update_bdi.assert_called_once_with(
|
||||||
mock_create_root_vhd.assert_called_once_with(self.context,
|
mock_instance, mock_image_meta, fake_vm_gen, block_device_info)
|
||||||
mock_instance)
|
mock_create_root_device.assert_called_once_with(self.context,
|
||||||
mock_create_ephemeral_vhd.assert_called_once_with(mock_instance)
|
mock_instance,
|
||||||
mock_get_image_vm_gen.assert_called_once_with(
|
root_device_info,
|
||||||
mock_instance.uuid, fake_root_path, mock_image_meta)
|
fake_vm_gen)
|
||||||
|
mock_create_ephemerals.assert_called_once_with(
|
||||||
|
mock_instance, block_device_info['ephemerals'])
|
||||||
|
mock_get_image_vm_gen.assert_called_once_with(mock_instance.uuid,
|
||||||
|
mock_image_meta)
|
||||||
mock_create_instance.assert_called_once_with(
|
mock_create_instance.assert_called_once_with(
|
||||||
mock_instance, mock.sentinel.INFO, mock.sentinel.DEV_INFO,
|
mock_instance, mock.sentinel.INFO, root_device_info,
|
||||||
fake_root_path, fake_ephemeral_path, fake_vm_gen)
|
block_device_info, fake_vm_gen)
|
||||||
mock_configdrive_required.assert_called_once_with(mock_instance)
|
mock_configdrive_required.assert_called_once_with(mock_instance)
|
||||||
if configdrive_required:
|
if configdrive_required:
|
||||||
mock_create_config_drive.assert_called_once_with(
|
mock_create_config_drive.assert_called_once_with(
|
||||||
@ -382,25 +412,17 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock_power_on.assert_called_once_with(mock_instance)
|
mock_power_on.assert_called_once_with(mock_instance)
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
self._test_spawn(exists=False, boot_from_volume=False,
|
self._test_spawn(exists=False, configdrive_required=True, fail=None)
|
||||||
configdrive_required=True, fail=None)
|
|
||||||
|
|
||||||
def test_spawn_instance_exists(self):
|
def test_spawn_instance_exists(self):
|
||||||
self._test_spawn(exists=True, boot_from_volume=False,
|
self._test_spawn(exists=True, configdrive_required=True, fail=None)
|
||||||
configdrive_required=True, fail=None)
|
|
||||||
|
|
||||||
def test_spawn_create_instance_exception(self):
|
def test_spawn_create_instance_exception(self):
|
||||||
self._test_spawn(exists=False, boot_from_volume=False,
|
self._test_spawn(exists=False, configdrive_required=True,
|
||||||
configdrive_required=True,
|
|
||||||
fail=os_win_exc.HyperVException)
|
fail=os_win_exc.HyperVException)
|
||||||
|
|
||||||
def test_spawn_not_required(self):
|
def test_spawn_not_required(self):
|
||||||
self._test_spawn(exists=False, boot_from_volume=False,
|
self._test_spawn(exists=False, configdrive_required=False, fail=None)
|
||||||
configdrive_required=False, fail=None)
|
|
||||||
|
|
||||||
def test_spawn_root_in_block(self):
|
|
||||||
self._test_spawn(exists=False, boot_from_volume=True,
|
|
||||||
configdrive_required=False, fail=None)
|
|
||||||
|
|
||||||
def test_spawn_no_admin_permissions(self):
|
def test_spawn_no_admin_permissions(self):
|
||||||
self._vmops._vmutils.check_admin_permissions.side_effect = (
|
self._vmops._vmutils.check_admin_permissions.side_effect = (
|
||||||
@ -413,19 +435,23 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
|
|
||||||
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
|
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
|
||||||
'.attach_volumes')
|
'.attach_volumes')
|
||||||
@mock.patch.object(vmops.VMOps, '_attach_drive')
|
|
||||||
@mock.patch.object(vmops.VMOps, '_create_vm_com_port_pipes')
|
@mock.patch.object(vmops.VMOps, '_create_vm_com_port_pipes')
|
||||||
|
@mock.patch.object(vmops.VMOps, '_attach_ephemerals')
|
||||||
|
@mock.patch.object(vmops.VMOps, '_attach_root_device')
|
||||||
@mock.patch.object(vmops.VMOps, '_configure_remotefx')
|
@mock.patch.object(vmops.VMOps, '_configure_remotefx')
|
||||||
def _test_create_instance(self, mock_configure_remotefx,
|
def _test_create_instance(self, mock_configure_remotefx,
|
||||||
mock_create_pipes, mock_attach_drive,
|
mock_attach_root_device,
|
||||||
mock_attach_volumes, fake_root_path,
|
mock_attach_ephemerals,
|
||||||
fake_ephemeral_path,
|
mock_create_pipes,
|
||||||
|
mock_attach_volumes,
|
||||||
enable_instance_metrics,
|
enable_instance_metrics,
|
||||||
vm_gen=constants.VM_GEN_1):
|
vm_gen=constants.VM_GEN_1):
|
||||||
mock_vif_driver = mock.MagicMock()
|
mock_vif_driver = mock.MagicMock()
|
||||||
self._vmops._vif_driver = mock_vif_driver
|
self._vmops._vif_driver = mock_vif_driver
|
||||||
self.flags(enable_instance_metrics_collection=enable_instance_metrics,
|
self.flags(enable_instance_metrics_collection=enable_instance_metrics,
|
||||||
group='hyperv')
|
group='hyperv')
|
||||||
|
root_device_info = mock.sentinel.ROOT_DEV_INFO
|
||||||
|
block_device_info = {'ephemerals': [], 'block_device_mapping': []}
|
||||||
fake_network_info = {'id': mock.sentinel.ID,
|
fake_network_info = {'id': mock.sentinel.ID,
|
||||||
'address': mock.sentinel.ADDRESS}
|
'address': mock.sentinel.ADDRESS}
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
@ -436,9 +462,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
|
|
||||||
self._vmops.create_instance(instance=mock_instance,
|
self._vmops.create_instance(instance=mock_instance,
|
||||||
network_info=[fake_network_info],
|
network_info=[fake_network_info],
|
||||||
block_device_info=mock.sentinel.DEV_INFO,
|
root_device=root_device_info,
|
||||||
root_vhd_path=fake_root_path,
|
block_device_info=block_device_info,
|
||||||
eph_vhd_path=fake_ephemeral_path,
|
|
||||||
vm_gen=vm_gen)
|
vm_gen=vm_gen)
|
||||||
self._vmops._vmutils.create_vm.assert_called_once_with(
|
self._vmops._vmutils.create_vm.assert_called_once_with(
|
||||||
mock_instance.name, mock_instance.memory_mb,
|
mock_instance.name, mock_instance.memory_mb,
|
||||||
@ -447,32 +472,15 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
[mock_instance.uuid])
|
[mock_instance.uuid])
|
||||||
|
|
||||||
mock_configure_remotefx.assert_called_once_with(mock_instance, vm_gen)
|
mock_configure_remotefx.assert_called_once_with(mock_instance, vm_gen)
|
||||||
expected = []
|
mock_create_scsi_ctrl = self._vmops._vmutils.create_scsi_controller
|
||||||
ctrl_type = vmops.VM_GENERATIONS_CONTROLLER_TYPES[vm_gen]
|
mock_create_scsi_ctrl.assert_called_once_with(mock_instance.name)
|
||||||
ctrl_disk_addr = 0
|
|
||||||
if fake_root_path:
|
|
||||||
expected.append(mock.call(mock_instance.name, fake_root_path,
|
|
||||||
0, ctrl_disk_addr, ctrl_type,
|
|
||||||
constants.DISK))
|
|
||||||
ctrl_disk_addr = 1
|
|
||||||
if fake_ephemeral_path:
|
|
||||||
expected.append(mock.call(mock_instance.name,
|
|
||||||
fake_ephemeral_path, 0, ctrl_disk_addr,
|
|
||||||
ctrl_type, constants.DISK))
|
|
||||||
mock_attach_drive.has_calls(expected)
|
|
||||||
self._vmops._vmutils.create_scsi_controller.assert_called_once_with(
|
|
||||||
mock_instance.name)
|
|
||||||
|
|
||||||
ebs_root = vm_gen is not constants.VM_GEN_2 and fake_root_path is None
|
mock_attach_root_device.assert_called_once_with(mock_instance.name,
|
||||||
mock_attach_volumes.assert_called_once_with(mock.sentinel.DEV_INFO,
|
root_device_info)
|
||||||
mock_instance.name,
|
mock_attach_ephemerals.assert_called_once_with(mock_instance.name,
|
||||||
ebs_root)
|
block_device_info['ephemerals'])
|
||||||
|
mock_attach_volumes.assert_called_once_with(
|
||||||
expected_port_settings = {
|
block_device_info['block_device_mapping'], mock_instance.name)
|
||||||
constants.DEFAULT_SERIAL_CONSOLE_PORT:
|
|
||||||
constants.SERIAL_PORT_TYPE_RW}
|
|
||||||
mock_create_pipes.assert_called_once_with(
|
|
||||||
mock_instance, expected_port_settings)
|
|
||||||
|
|
||||||
self._vmops._vmutils.create_nic.assert_called_once_with(
|
self._vmops._vmutils.create_nic.assert_called_once_with(
|
||||||
mock_instance.name, mock.sentinel.ID, mock.sentinel.ADDRESS)
|
mock_instance.name, mock.sentinel.ID, mock.sentinel.ADDRESS)
|
||||||
@ -483,39 +491,72 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock_enable.assert_called_once_with(mock_instance.name)
|
mock_enable.assert_called_once_with(mock_instance.name)
|
||||||
|
|
||||||
def test_create_instance(self):
|
def test_create_instance(self):
|
||||||
fake_ephemeral_path = mock.sentinel.FAKE_EPHEMERAL_PATH
|
self._test_create_instance(enable_instance_metrics=True)
|
||||||
self._test_create_instance(fake_root_path=mock.sentinel.FAKE_ROOT_PATH,
|
|
||||||
fake_ephemeral_path=fake_ephemeral_path,
|
|
||||||
enable_instance_metrics=True)
|
|
||||||
|
|
||||||
def test_create_instance_no_root_path(self):
|
|
||||||
fake_ephemeral_path = mock.sentinel.FAKE_EPHEMERAL_PATH
|
|
||||||
self._test_create_instance(fake_root_path=None,
|
|
||||||
fake_ephemeral_path=fake_ephemeral_path,
|
|
||||||
enable_instance_metrics=True)
|
|
||||||
|
|
||||||
def test_create_instance_no_ephemeral_path(self):
|
|
||||||
self._test_create_instance(fake_root_path=mock.sentinel.FAKE_ROOT_PATH,
|
|
||||||
fake_ephemeral_path=None,
|
|
||||||
enable_instance_metrics=True)
|
|
||||||
|
|
||||||
def test_create_instance_no_path(self):
|
|
||||||
self._test_create_instance(fake_root_path=None,
|
|
||||||
fake_ephemeral_path=None,
|
|
||||||
enable_instance_metrics=False)
|
|
||||||
|
|
||||||
def test_create_instance_enable_instance_metrics_false(self):
|
def test_create_instance_enable_instance_metrics_false(self):
|
||||||
fake_ephemeral_path = mock.sentinel.FAKE_EPHEMERAL_PATH
|
self._test_create_instance(enable_instance_metrics=False)
|
||||||
self._test_create_instance(fake_root_path=mock.sentinel.FAKE_ROOT_PATH,
|
|
||||||
fake_ephemeral_path=fake_ephemeral_path,
|
|
||||||
enable_instance_metrics=False)
|
|
||||||
|
|
||||||
def test_create_instance_gen2(self):
|
def test_create_instance_gen2(self):
|
||||||
self._test_create_instance(fake_root_path=None,
|
self._test_create_instance(enable_instance_metrics=False,
|
||||||
fake_ephemeral_path=None,
|
|
||||||
enable_instance_metrics=False,
|
|
||||||
vm_gen=constants.VM_GEN_2)
|
vm_gen=constants.VM_GEN_2)
|
||||||
|
|
||||||
|
@mock.patch.object(vmops.volumeops.VolumeOps, 'attach_volume')
|
||||||
|
def test_attach_root_device_volume(self, mock_attach_volume):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
root_device_info = {'type': constants.VOLUME,
|
||||||
|
'connection_info': mock.sentinel.CONN_INFO,
|
||||||
|
'disk_bus': constants.CTRL_TYPE_IDE}
|
||||||
|
|
||||||
|
self._vmops._attach_root_device(mock_instance.name, root_device_info)
|
||||||
|
|
||||||
|
mock_attach_volume.assert_called_once_with(
|
||||||
|
root_device_info['connection_info'], mock_instance.name,
|
||||||
|
disk_bus=root_device_info['disk_bus'])
|
||||||
|
|
||||||
|
@mock.patch.object(vmops.VMOps, '_attach_drive')
|
||||||
|
def test_attach_root_device_disk(self, mock_attach_drive):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
root_device_info = {'type': constants.DISK,
|
||||||
|
'boot_index': 0,
|
||||||
|
'disk_bus': constants.CTRL_TYPE_IDE,
|
||||||
|
'path': 'fake_path',
|
||||||
|
'drive_addr': 0,
|
||||||
|
'ctrl_disk_addr': 1}
|
||||||
|
|
||||||
|
self._vmops._attach_root_device(mock_instance.name, root_device_info)
|
||||||
|
|
||||||
|
mock_attach_drive.assert_called_once_with(
|
||||||
|
mock_instance.name, root_device_info['path'],
|
||||||
|
root_device_info['drive_addr'], root_device_info['ctrl_disk_addr'],
|
||||||
|
root_device_info['disk_bus'], root_device_info['type'])
|
||||||
|
|
||||||
|
@mock.patch.object(vmops.VMOps, '_attach_drive')
|
||||||
|
def test_attach_ephemerals(self, mock_attach_drive):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
|
||||||
|
ephemerals = [{'path': mock.sentinel.PATH1,
|
||||||
|
'boot_index': 1,
|
||||||
|
'disk_bus': constants.CTRL_TYPE_IDE,
|
||||||
|
'device_type': 'disk',
|
||||||
|
'drive_addr': 0,
|
||||||
|
'ctrl_disk_addr': 1},
|
||||||
|
{'path': mock.sentinel.PATH2,
|
||||||
|
'boot_index': 2,
|
||||||
|
'disk_bus': constants.CTRL_TYPE_SCSI,
|
||||||
|
'device_type': 'disk',
|
||||||
|
'drive_addr': 0,
|
||||||
|
'ctrl_disk_addr': 0},
|
||||||
|
{'path': None}]
|
||||||
|
|
||||||
|
self._vmops._attach_ephemerals(mock_instance.name, ephemerals)
|
||||||
|
|
||||||
|
mock_attach_drive.assert_has_calls(
|
||||||
|
[mock.call(mock_instance.name, mock.sentinel.PATH1, 0,
|
||||||
|
1, constants.CTRL_TYPE_IDE, constants.DISK),
|
||||||
|
mock.call(mock_instance.name, mock.sentinel.PATH2, 0,
|
||||||
|
0, constants.CTRL_TYPE_SCSI, constants.DISK)
|
||||||
|
])
|
||||||
|
|
||||||
def test_attach_drive_vm_to_scsi(self):
|
def test_attach_drive_vm_to_scsi(self):
|
||||||
self._vmops._attach_drive(
|
self._vmops._attach_drive(
|
||||||
mock.sentinel.FAKE_VM_NAME, mock.sentinel.FAKE_PATH,
|
mock.sentinel.FAKE_VM_NAME, mock.sentinel.FAKE_PATH,
|
||||||
@ -545,7 +586,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
constants.IMAGE_PROP_VM_GEN_1, constants.IMAGE_PROP_VM_GEN_2]
|
constants.IMAGE_PROP_VM_GEN_1, constants.IMAGE_PROP_VM_GEN_2]
|
||||||
|
|
||||||
response = self._vmops.get_image_vm_generation(
|
response = self._vmops.get_image_vm_generation(
|
||||||
mock.sentinel.instance_id, mock.sentinel.FAKE_PATH, image_meta)
|
mock.sentinel.instance_id, image_meta)
|
||||||
|
|
||||||
self.assertEqual(constants.VM_GEN_1, response)
|
self.assertEqual(constants.VM_GEN_1, response)
|
||||||
|
|
||||||
@ -555,28 +596,20 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
{"hw_machine_type": constants.IMAGE_PROP_VM_GEN_2}})
|
{"hw_machine_type": constants.IMAGE_PROP_VM_GEN_2}})
|
||||||
self._vmops._hostutils.get_supported_vm_types.return_value = [
|
self._vmops._hostutils.get_supported_vm_types.return_value = [
|
||||||
constants.IMAGE_PROP_VM_GEN_1, constants.IMAGE_PROP_VM_GEN_2]
|
constants.IMAGE_PROP_VM_GEN_1, constants.IMAGE_PROP_VM_GEN_2]
|
||||||
self._vmops._vhdutils.get_vhd_format.return_value = (
|
|
||||||
constants.DISK_FORMAT_VHDX)
|
|
||||||
|
|
||||||
response = self._vmops.get_image_vm_generation(
|
response = self._vmops.get_image_vm_generation(
|
||||||
mock.sentinel.instance_id, mock.sentinel.FAKE_PATH, image_meta)
|
mock.sentinel.instance_id, image_meta)
|
||||||
|
|
||||||
self.assertEqual(constants.VM_GEN_2, response)
|
self.assertEqual(constants.VM_GEN_2, response)
|
||||||
|
|
||||||
def test_get_image_vm_generation_not_vhdx(self):
|
def test_check_vm_image_type_exception(self):
|
||||||
image_meta = objects.ImageMeta.from_dict(
|
|
||||||
{"properties":
|
|
||||||
{'hw_machine_type': constants.IMAGE_PROP_VM_GEN_2}})
|
|
||||||
self._vmops._hostutils.get_supported_vm_types.return_value = [
|
|
||||||
constants.IMAGE_PROP_VM_GEN_1, constants.IMAGE_PROP_VM_GEN_2]
|
|
||||||
self._vmops._vhdutils.get_vhd_format.return_value = (
|
self._vmops._vhdutils.get_vhd_format.return_value = (
|
||||||
constants.DISK_FORMAT_VHD)
|
constants.DISK_FORMAT_VHD)
|
||||||
|
|
||||||
self.assertRaises(exception.InstanceUnacceptable,
|
self.assertRaises(exception.InstanceUnacceptable,
|
||||||
self._vmops.get_image_vm_generation,
|
self._vmops.check_vm_image_type,
|
||||||
mock.sentinel.instance_id,
|
mock.sentinel.instance_id, constants.VM_GEN_2,
|
||||||
mock.sentinel.FAKE_PATH,
|
mock.sentinel.FAKE_PATH)
|
||||||
image_meta)
|
|
||||||
|
|
||||||
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
||||||
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder')
|
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder')
|
||||||
@ -1244,8 +1277,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.sentinel.rescue_password)
|
mock.sentinel.rescue_password)
|
||||||
|
|
||||||
mock_get_image_vm_gen.assert_called_once_with(
|
mock_get_image_vm_gen.assert_called_once_with(
|
||||||
mock_instance.uuid, mock.sentinel.rescue_vhd_path,
|
mock_instance.uuid, mock_image_meta)
|
||||||
mock_image_meta)
|
|
||||||
self._vmops._vmutils.detach_vm_disk.assert_called_once_with(
|
self._vmops._vmutils.detach_vm_disk.assert_called_once_with(
|
||||||
mock_instance.name, mock.sentinel.root_vhd_path,
|
mock_instance.name, mock.sentinel.root_vhd_path,
|
||||||
is_physical=False)
|
is_physical=False)
|
||||||
|
@ -24,6 +24,7 @@ from nova import exception
|
|||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit import fake_block_device
|
from nova.tests.unit import fake_block_device
|
||||||
from nova.tests.unit.virt.hyperv import test_base
|
from nova.tests.unit.virt.hyperv import test_base
|
||||||
|
from nova.virt.hyperv import constants
|
||||||
from nova.virt.hyperv import volumeops
|
from nova.virt.hyperv import volumeops
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -76,13 +77,13 @@ class VolumeOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
def test_attach_volumes(self, mock_attach_volume):
|
def test_attach_volumes(self, mock_attach_volume):
|
||||||
block_device_info = get_fake_block_dev_info()
|
block_device_info = get_fake_block_dev_info()
|
||||||
|
|
||||||
self._volumeops.attach_volumes(block_device_info,
|
self._volumeops.attach_volumes(
|
||||||
mock.sentinel.instance_name,
|
block_device_info['block_device_mapping'],
|
||||||
ebs_root=True)
|
mock.sentinel.instance_name)
|
||||||
|
|
||||||
mock_attach_volume.assert_called_once_with(
|
mock_attach_volume.assert_called_once_with(
|
||||||
block_device_info['block_device_mapping'][0]['connection_info'],
|
block_device_info['block_device_mapping'][0]['connection_info'],
|
||||||
mock.sentinel.instance_name, True)
|
mock.sentinel.instance_name)
|
||||||
|
|
||||||
def test_fix_instance_volume_disk_paths_empty_bdm(self):
|
def test_fix_instance_volume_disk_paths_empty_bdm(self):
|
||||||
self._volumeops.fix_instance_volume_disk_paths(
|
self._volumeops.fix_instance_volume_disk_paths(
|
||||||
@ -144,16 +145,6 @@ class VolumeOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
fake_volume_driver.disconnect_volumes.assert_called_once_with(
|
fake_volume_driver.disconnect_volumes.assert_called_once_with(
|
||||||
block_device_mapping)
|
block_device_mapping)
|
||||||
|
|
||||||
@mock.patch('nova.block_device.volume_in_mapping')
|
|
||||||
def test_ebs_root_in_block_devices(self, mock_vol_in_mapping):
|
|
||||||
block_device_info = get_fake_block_dev_info()
|
|
||||||
|
|
||||||
response = self._volumeops.ebs_root_in_block_devices(block_device_info)
|
|
||||||
|
|
||||||
mock_vol_in_mapping.assert_called_once_with(
|
|
||||||
self._volumeops._default_root_device, block_device_info)
|
|
||||||
self.assertEqual(mock_vol_in_mapping.return_value, response)
|
|
||||||
|
|
||||||
def test_get_volume_connector(self):
|
def test_get_volume_connector(self):
|
||||||
mock_instance = mock.DEFAULT
|
mock_instance = mock.DEFAULT
|
||||||
initiator = self._volumeops._volutils.get_iscsi_initiator.return_value
|
initiator = self._volumeops._volutils.get_iscsi_initiator.return_value
|
||||||
@ -324,7 +315,7 @@ class ISCSIVolumeDriverTestCase(test_base.HyperVBaseTestCase):
|
|||||||
'_get_mounted_disk_from_lun')
|
'_get_mounted_disk_from_lun')
|
||||||
@mock.patch.object(volumeops.ISCSIVolumeDriver, 'login_storage_target')
|
@mock.patch.object(volumeops.ISCSIVolumeDriver, 'login_storage_target')
|
||||||
def _check_attach_volume(self, mock_login_storage_target,
|
def _check_attach_volume(self, mock_login_storage_target,
|
||||||
mock_get_mounted_disk_from_lun, ebs_root):
|
mock_get_mounted_disk_from_lun, disk_bus):
|
||||||
connection_info = get_fake_connection_info()
|
connection_info = get_fake_connection_info()
|
||||||
|
|
||||||
get_ide_path = self._volume_driver._vmutils.get_vm_ide_controller
|
get_ide_path = self._volume_driver._vmutils.get_vm_ide_controller
|
||||||
@ -340,14 +331,14 @@ class ISCSIVolumeDriverTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self._volume_driver.attach_volume(
|
self._volume_driver.attach_volume(
|
||||||
connection_info=connection_info,
|
connection_info=connection_info,
|
||||||
instance_name=mock.sentinel.instance_name,
|
instance_name=mock.sentinel.instance_name,
|
||||||
ebs_root=ebs_root)
|
disk_bus=disk_bus)
|
||||||
|
|
||||||
mock_login_storage_target.assert_called_once_with(connection_info)
|
mock_login_storage_target.assert_called_once_with(connection_info)
|
||||||
mock_get_mounted_disk_from_lun.assert_called_once_with(
|
mock_get_mounted_disk_from_lun.assert_called_once_with(
|
||||||
mock.sentinel.fake_iqn,
|
mock.sentinel.fake_iqn,
|
||||||
mock.sentinel.fake_lun,
|
mock.sentinel.fake_lun,
|
||||||
wait_for_device=True)
|
wait_for_device=True)
|
||||||
if ebs_root:
|
if disk_bus == constants.CTRL_TYPE_IDE:
|
||||||
get_ide_path.assert_called_once_with(
|
get_ide_path.assert_called_once_with(
|
||||||
mock.sentinel.instance_name, 0)
|
mock.sentinel.instance_name, 0)
|
||||||
attach_vol.assert_called_once_with(mock.sentinel.instance_name,
|
attach_vol.assert_called_once_with(mock.sentinel.instance_name,
|
||||||
@ -362,11 +353,11 @@ class ISCSIVolumeDriverTestCase(test_base.HyperVBaseTestCase):
|
|||||||
fake_mounted_disk_path,
|
fake_mounted_disk_path,
|
||||||
serial=mock.sentinel.serial)
|
serial=mock.sentinel.serial)
|
||||||
|
|
||||||
def test_attach_volume_ebs(self):
|
def test_attach_volume_ide(self):
|
||||||
self._check_attach_volume(ebs_root=True)
|
self._check_attach_volume(disk_bus=constants.CTRL_TYPE_IDE)
|
||||||
|
|
||||||
def test_attach_volume(self):
|
def test_attach_volume_scsi(self):
|
||||||
self._check_attach_volume(ebs_root=False)
|
self._check_attach_volume(disk_bus=constants.CTRL_TYPE_SCSI)
|
||||||
|
|
||||||
@mock.patch.object(volumeops.ISCSIVolumeDriver,
|
@mock.patch.object(volumeops.ISCSIVolumeDriver,
|
||||||
'_get_mounted_disk_from_lun')
|
'_get_mounted_disk_from_lun')
|
||||||
@ -491,15 +482,15 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
|
|||||||
@mock.patch.object(volumeops.SMBFSVolumeDriver, 'ensure_share_mounted')
|
@mock.patch.object(volumeops.SMBFSVolumeDriver, 'ensure_share_mounted')
|
||||||
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_get_disk_path')
|
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_get_disk_path')
|
||||||
def _check_attach_volume(self, mock_get_disk_path,
|
def _check_attach_volume(self, mock_get_disk_path,
|
||||||
mock_ensure_share_mounted, ebs_root=False):
|
mock_ensure_share_mounted, disk_bus):
|
||||||
mock_get_disk_path.return_value = mock.sentinel.disk_path
|
mock_get_disk_path.return_value = mock.sentinel.disk_path
|
||||||
|
|
||||||
self._volume_driver.attach_volume(
|
self._volume_driver.attach_volume(
|
||||||
self._FAKE_CONNECTION_INFO,
|
self._FAKE_CONNECTION_INFO,
|
||||||
mock.sentinel.instance_name,
|
mock.sentinel.instance_name,
|
||||||
ebs_root)
|
disk_bus)
|
||||||
|
|
||||||
if ebs_root:
|
if disk_bus == constants.CTRL_TYPE_IDE:
|
||||||
get_vm_ide_controller = (
|
get_vm_ide_controller = (
|
||||||
self._volume_driver._vmutils.get_vm_ide_controller)
|
self._volume_driver._vmutils.get_vm_ide_controller)
|
||||||
get_vm_ide_controller.assert_called_once_with(
|
get_vm_ide_controller.assert_called_once_with(
|
||||||
@ -527,10 +518,10 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
|
|||||||
ctrller_path, slot)
|
ctrller_path, slot)
|
||||||
|
|
||||||
def test_attach_volume_ide(self):
|
def test_attach_volume_ide(self):
|
||||||
self._check_attach_volume(ebs_root=True)
|
self._check_attach_volume(disk_bus=constants.CTRL_TYPE_IDE)
|
||||||
|
|
||||||
def test_attach_volume_scsi(self):
|
def test_attach_volume_scsi(self):
|
||||||
self._check_attach_volume()
|
self._check_attach_volume(disk_bus=constants.CTRL_TYPE_SCSI)
|
||||||
|
|
||||||
@mock.patch.object(volumeops.SMBFSVolumeDriver, 'ensure_share_mounted')
|
@mock.patch.object(volumeops.SMBFSVolumeDriver, 'ensure_share_mounted')
|
||||||
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_get_disk_path')
|
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_get_disk_path')
|
||||||
|
@ -58,8 +58,6 @@ class BlockDeviceInfoManager(object):
|
|||||||
# reserve one slot for the config drive on the second
|
# reserve one slot for the config drive on the second
|
||||||
# controller in case of generation 1 virtual machines
|
# controller in case of generation 1 virtual machines
|
||||||
free_slots_by_device_type[constants.CTRL_TYPE_IDE][1] -= 1
|
free_slots_by_device_type[constants.CTRL_TYPE_IDE][1] -= 1
|
||||||
else:
|
|
||||||
free_slots_by_device_type[constants.CTRL_TYPE_SCSI][0] -= 1
|
|
||||||
return free_slots_by_device_type
|
return free_slots_by_device_type
|
||||||
|
|
||||||
def validate_and_update_bdi(self, instance, image_meta, vm_gen,
|
def validate_and_update_bdi(self, instance, image_meta, vm_gen,
|
||||||
@ -70,6 +68,14 @@ class BlockDeviceInfoManager(object):
|
|||||||
self._check_and_update_ephemerals(vm_gen, block_device_info, slot_map)
|
self._check_and_update_ephemerals(vm_gen, block_device_info, slot_map)
|
||||||
self._check_and_update_volumes(vm_gen, block_device_info, slot_map)
|
self._check_and_update_volumes(vm_gen, block_device_info, slot_map)
|
||||||
|
|
||||||
|
if vm_gen == constants.VM_GEN_2 and configdrive.required_by(instance):
|
||||||
|
# for Generation 2 VMs, the configdrive is attached to the SCSI
|
||||||
|
# controller. Check that there is still a slot available for it.
|
||||||
|
if slot_map[constants.CTRL_TYPE_SCSI][0] == 0:
|
||||||
|
msg = _("There are no more free slots on controller %s for "
|
||||||
|
"configdrive.") % constants.CTRL_TYPE_SCSI
|
||||||
|
raise exception.InvalidBDMFormat(details=msg)
|
||||||
|
|
||||||
def _check_and_update_root_device(self, vm_gen, image_meta,
|
def _check_and_update_root_device(self, vm_gen, image_meta,
|
||||||
block_device_info, slot_map):
|
block_device_info, slot_map):
|
||||||
# either booting from volume, or booting from image/iso
|
# either booting from volume, or booting from image/iso
|
||||||
|
@ -56,6 +56,8 @@ DISK_FORMAT_MAP = {
|
|||||||
DVD_FORMAT.lower(): DVD
|
DVD_FORMAT.lower(): DVD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BDI_DEVICE_TYPE_TO_DRIVE_TYPE = {'disk': DISK}
|
||||||
|
|
||||||
DISK_FORMAT_VHD = "VHD"
|
DISK_FORMAT_VHD = "VHD"
|
||||||
DISK_FORMAT_VHDX = "VHDX"
|
DISK_FORMAT_VHDX = "VHDX"
|
||||||
|
|
||||||
|
@ -128,6 +128,10 @@ class HyperVDriver(driver.ComputeDriver):
|
|||||||
'Windows has been removed in Mitaka.'))
|
'Windows has been removed in Mitaka.'))
|
||||||
raise exception.HypervisorTooOld(version='6.2')
|
raise exception.HypervisorTooOld(version='6.2')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def need_legacy_block_device_info(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def init_host(self, host):
|
def init_host(self, host):
|
||||||
self._serialconsoleops.start_console_handlers()
|
self._serialconsoleops.start_console_handlers()
|
||||||
event_handler = eventhandler.InstanceEventHandler(
|
event_handler = eventhandler.InstanceEventHandler(
|
||||||
|
@ -23,6 +23,7 @@ from oslo_utils import excutils
|
|||||||
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.objects import migrate_data as migrate_data_obj
|
from nova.objects import migrate_data as migrate_data_obj
|
||||||
|
from nova.virt.hyperv import block_device_manager
|
||||||
from nova.virt.hyperv import imagecache
|
from nova.virt.hyperv import imagecache
|
||||||
from nova.virt.hyperv import pathutils
|
from nova.virt.hyperv import pathutils
|
||||||
from nova.virt.hyperv import serialconsoleops
|
from nova.virt.hyperv import serialconsoleops
|
||||||
@ -42,6 +43,7 @@ class LiveMigrationOps(object):
|
|||||||
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
|
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
|
||||||
self._imagecache = imagecache.ImageCache()
|
self._imagecache = imagecache.ImageCache()
|
||||||
self._vmutils = utilsfactory.get_vmutils()
|
self._vmutils = utilsfactory.get_vmutils()
|
||||||
|
self._block_dev_man = block_device_manager.BlockDeviceInfoManager()
|
||||||
|
|
||||||
def live_migration(self, context, instance_ref, dest, post_method,
|
def live_migration(self, context, instance_ref, dest, post_method,
|
||||||
recover_method, block_migration=False,
|
recover_method, block_migration=False,
|
||||||
@ -75,7 +77,7 @@ class LiveMigrationOps(object):
|
|||||||
self._livemigrutils.check_live_migration_config()
|
self._livemigrutils.check_live_migration_config()
|
||||||
|
|
||||||
if CONF.use_cow_images:
|
if CONF.use_cow_images:
|
||||||
boot_from_volume = self._volumeops.ebs_root_in_block_devices(
|
boot_from_volume = self._block_dev_man.is_boot_from_volume(
|
||||||
block_device_info)
|
block_device_info)
|
||||||
if not boot_from_volume and instance.image_ref:
|
if not boot_from_volume and instance.image_ref:
|
||||||
self._imagecache.get_cached_image(context, instance)
|
self._imagecache.get_cached_image(context, instance)
|
||||||
|
@ -24,9 +24,11 @@ from oslo_utils import excutils
|
|||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _, _LE
|
from nova.i18n import _, _LW, _LE
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova.virt import configdrive
|
from nova.virt import configdrive
|
||||||
|
from nova.virt.hyperv import block_device_manager
|
||||||
|
from nova.virt.hyperv import constants
|
||||||
from nova.virt.hyperv import imagecache
|
from nova.virt.hyperv import imagecache
|
||||||
from nova.virt.hyperv import pathutils
|
from nova.virt.hyperv import pathutils
|
||||||
from nova.virt.hyperv import vmops
|
from nova.virt.hyperv import vmops
|
||||||
@ -44,6 +46,7 @@ class MigrationOps(object):
|
|||||||
self._volumeops = volumeops.VolumeOps()
|
self._volumeops = volumeops.VolumeOps()
|
||||||
self._vmops = vmops.VMOps()
|
self._vmops = vmops.VMOps()
|
||||||
self._imagecache = imagecache.ImageCache()
|
self._imagecache = imagecache.ImageCache()
|
||||||
|
self._block_dev_man = block_device_manager.BlockDeviceInfoManager()
|
||||||
|
|
||||||
def _migrate_disk_files(self, instance_name, disk_files, dest):
|
def _migrate_disk_files(self, instance_name, disk_files, dest):
|
||||||
# TODO(mikal): it would be nice if this method took a full instance,
|
# TODO(mikal): it would be nice if this method took a full instance,
|
||||||
@ -167,18 +170,25 @@ class MigrationOps(object):
|
|||||||
instance_name = instance.name
|
instance_name = instance.name
|
||||||
self._revert_migration_files(instance_name)
|
self._revert_migration_files(instance_name)
|
||||||
|
|
||||||
if self._volumeops.ebs_root_in_block_devices(block_device_info):
|
|
||||||
root_vhd_path = None
|
|
||||||
else:
|
|
||||||
root_vhd_path = self._pathutils.lookup_root_vhd_path(instance_name)
|
|
||||||
|
|
||||||
eph_vhd_path = self._pathutils.lookup_ephemeral_vhd_path(instance_name)
|
|
||||||
|
|
||||||
image_meta = objects.ImageMeta.from_instance(instance)
|
image_meta = objects.ImageMeta.from_instance(instance)
|
||||||
vm_gen = self._vmops.get_image_vm_generation(
|
vm_gen = self._vmops.get_image_vm_generation(instance.uuid, image_meta)
|
||||||
instance.uuid, root_vhd_path, image_meta)
|
|
||||||
self._vmops.create_instance(instance, network_info, block_device_info,
|
self._block_dev_man.validate_and_update_bdi(instance, image_meta,
|
||||||
root_vhd_path, eph_vhd_path, vm_gen)
|
vm_gen, block_device_info)
|
||||||
|
root_device = block_device_info['root_disk']
|
||||||
|
|
||||||
|
if root_device['type'] == constants.DISK:
|
||||||
|
root_vhd_path = self._pathutils.lookup_root_vhd_path(instance_name)
|
||||||
|
root_device['path'] = root_vhd_path
|
||||||
|
if not root_vhd_path:
|
||||||
|
base_vhd_path = self._pathutils.get_instance_dir(instance_name)
|
||||||
|
raise exception.DiskNotFound(location=base_vhd_path)
|
||||||
|
|
||||||
|
ephemerals = block_device_info['ephemerals']
|
||||||
|
self._check_ephemeral_disks(instance, ephemerals)
|
||||||
|
|
||||||
|
self._vmops.create_instance(instance, network_info, root_device,
|
||||||
|
block_device_info, vm_gen)
|
||||||
|
|
||||||
self._check_and_attach_config_drive(instance, vm_gen)
|
self._check_and_attach_config_drive(instance, vm_gen)
|
||||||
|
|
||||||
@ -221,8 +231,8 @@ class MigrationOps(object):
|
|||||||
reason=_("Cannot resize the root disk to a smaller size. "
|
reason=_("Cannot resize the root disk to a smaller size. "
|
||||||
"Current size: %(curr_root_gb)s GB. Requested "
|
"Current size: %(curr_root_gb)s GB. Requested "
|
||||||
"size: %(new_root_gb)s GB.") % {
|
"size: %(new_root_gb)s GB.") % {
|
||||||
'curr_root_gb': curr_size,
|
'curr_root_gb': curr_size / units.Gi,
|
||||||
'new_root_gb': new_size})
|
'new_root_gb': new_size / units.Gi})
|
||||||
elif new_size > curr_size:
|
elif new_size > curr_size:
|
||||||
self._resize_vhd(vhd_path, new_size)
|
self._resize_vhd(vhd_path, new_size)
|
||||||
|
|
||||||
@ -260,13 +270,18 @@ class MigrationOps(object):
|
|||||||
LOG.debug("finish_migration called", instance=instance)
|
LOG.debug("finish_migration called", instance=instance)
|
||||||
|
|
||||||
instance_name = instance.name
|
instance_name = instance.name
|
||||||
|
vm_gen = self._vmops.get_image_vm_generation(instance.uuid, image_meta)
|
||||||
|
|
||||||
if self._volumeops.ebs_root_in_block_devices(block_device_info):
|
self._block_dev_man.validate_and_update_bdi(instance, image_meta,
|
||||||
root_vhd_path = None
|
vm_gen, block_device_info)
|
||||||
else:
|
root_device = block_device_info['root_disk']
|
||||||
|
|
||||||
|
if root_device['type'] == constants.DISK:
|
||||||
root_vhd_path = self._pathutils.lookup_root_vhd_path(instance_name)
|
root_vhd_path = self._pathutils.lookup_root_vhd_path(instance_name)
|
||||||
|
root_device['path'] = root_vhd_path
|
||||||
if not root_vhd_path:
|
if not root_vhd_path:
|
||||||
raise exception.DiskNotFound(location=root_vhd_path)
|
base_vhd_path = self._pathutils.get_instance_dir(instance_name)
|
||||||
|
raise exception.DiskNotFound(location=base_vhd_path)
|
||||||
|
|
||||||
root_vhd_info = self._vhdutils.get_vhd_info(root_vhd_path)
|
root_vhd_info = self._vhdutils.get_vhd_info(root_vhd_path)
|
||||||
src_base_disk_path = root_vhd_info.get("ParentPath")
|
src_base_disk_path = root_vhd_info.get("ParentPath")
|
||||||
@ -278,22 +293,57 @@ class MigrationOps(object):
|
|||||||
new_size = instance.root_gb * units.Gi
|
new_size = instance.root_gb * units.Gi
|
||||||
self._check_resize_vhd(root_vhd_path, root_vhd_info, new_size)
|
self._check_resize_vhd(root_vhd_path, root_vhd_info, new_size)
|
||||||
|
|
||||||
eph_vhd_path = self._pathutils.lookup_ephemeral_vhd_path(instance_name)
|
ephemerals = block_device_info['ephemerals']
|
||||||
if resize_instance:
|
self._check_ephemeral_disks(instance, ephemerals, resize_instance)
|
||||||
new_size = instance.get('ephemeral_gb', 0) * units.Gi
|
|
||||||
if not eph_vhd_path:
|
|
||||||
if new_size:
|
|
||||||
eph_vhd_path = self._vmops.create_ephemeral_vhd(instance)
|
|
||||||
else:
|
|
||||||
eph_vhd_info = self._vhdutils.get_vhd_info(eph_vhd_path)
|
|
||||||
self._check_resize_vhd(eph_vhd_path, eph_vhd_info, new_size)
|
|
||||||
|
|
||||||
vm_gen = self._vmops.get_image_vm_generation(
|
self._vmops.create_instance(instance, network_info, root_device,
|
||||||
instance.uuid, root_vhd_path, image_meta)
|
block_device_info, vm_gen)
|
||||||
self._vmops.create_instance(instance, network_info, block_device_info,
|
|
||||||
root_vhd_path, eph_vhd_path, vm_gen)
|
|
||||||
|
|
||||||
self._check_and_attach_config_drive(instance, vm_gen)
|
self._check_and_attach_config_drive(instance, vm_gen)
|
||||||
|
|
||||||
if power_on:
|
if power_on:
|
||||||
self._vmops.power_on(instance)
|
self._vmops.power_on(instance)
|
||||||
|
|
||||||
|
def _check_ephemeral_disks(self, instance, ephemerals,
|
||||||
|
resize_instance=False):
|
||||||
|
instance_name = instance.name
|
||||||
|
new_eph_gb = instance.get('ephemeral_gb', 0)
|
||||||
|
|
||||||
|
if len(ephemerals) == 1:
|
||||||
|
# NOTE(claudiub): Resize only if there is one ephemeral. If there
|
||||||
|
# are more than 1, resizing them can be problematic. This behaviour
|
||||||
|
# also exists in the libvirt driver and it has to be addressed in
|
||||||
|
# the future.
|
||||||
|
ephemerals[0]['size'] = new_eph_gb
|
||||||
|
elif sum(eph['size'] for eph in ephemerals) != new_eph_gb:
|
||||||
|
# New ephemeral size is different from the original ephemeral size
|
||||||
|
# and there are multiple ephemerals.
|
||||||
|
LOG.warn(_LW("Cannot resize multiple ephemeral disks for "
|
||||||
|
"instance."), instance=instance)
|
||||||
|
|
||||||
|
for index, eph in enumerate(ephemerals):
|
||||||
|
eph_name = "eph%s" % index
|
||||||
|
existing_eph_path = self._pathutils.lookup_ephemeral_vhd_path(
|
||||||
|
instance_name, eph_name)
|
||||||
|
|
||||||
|
if not existing_eph_path:
|
||||||
|
eph['format'] = self._vhdutils.get_best_supported_vhd_format()
|
||||||
|
eph['path'] = self._pathutils.get_ephemeral_vhd_path(
|
||||||
|
instance_name, eph['format'], eph_name)
|
||||||
|
if not resize_instance:
|
||||||
|
# ephemerals should have existed.
|
||||||
|
raise exception.DiskNotFound(location=eph['path'])
|
||||||
|
|
||||||
|
if eph['size']:
|
||||||
|
# create ephemerals
|
||||||
|
self._vmops.create_ephemeral_disk(instance.name, eph)
|
||||||
|
elif eph['size'] > 0:
|
||||||
|
# ephemerals exist. resize them.
|
||||||
|
eph['path'] = existing_eph_path
|
||||||
|
eph_vhd_info = self._vhdutils.get_vhd_info(eph['path'])
|
||||||
|
self._check_resize_vhd(
|
||||||
|
eph['path'], eph_vhd_info, eph['size'] * units.Gi)
|
||||||
|
else:
|
||||||
|
# ephemeral new size is 0, remove it.
|
||||||
|
self._pathutils.remove(existing_eph_path)
|
||||||
|
eph['path'] = None
|
||||||
|
@ -106,9 +106,10 @@ class PathUtils(pathutils.PathUtils):
|
|||||||
break
|
break
|
||||||
return configdrive_path
|
return configdrive_path
|
||||||
|
|
||||||
def lookup_ephemeral_vhd_path(self, instance_name):
|
def lookup_ephemeral_vhd_path(self, instance_name, eph_name):
|
||||||
return self._lookup_vhd_path(instance_name,
|
return self._lookup_vhd_path(instance_name,
|
||||||
self.get_ephemeral_vhd_path)
|
self.get_ephemeral_vhd_path,
|
||||||
|
eph_name)
|
||||||
|
|
||||||
def get_root_vhd_path(self, instance_name, format_ext, rescue=False):
|
def get_root_vhd_path(self, instance_name, format_ext, rescue=False):
|
||||||
instance_path = self.get_instance_dir(instance_name)
|
instance_path = self.get_instance_dir(instance_name)
|
||||||
@ -127,9 +128,9 @@ class PathUtils(pathutils.PathUtils):
|
|||||||
return os.path.join(instance_path,
|
return os.path.join(instance_path,
|
||||||
configdrive_image_name + '.' + format_ext.lower())
|
configdrive_image_name + '.' + format_ext.lower())
|
||||||
|
|
||||||
def get_ephemeral_vhd_path(self, instance_name, format_ext):
|
def get_ephemeral_vhd_path(self, instance_name, format_ext, eph_name):
|
||||||
instance_path = self.get_instance_dir(instance_name)
|
instance_path = self.get_instance_dir(instance_name)
|
||||||
return os.path.join(instance_path, 'ephemeral.' + format_ext.lower())
|
return os.path.join(instance_path, eph_name + '.' + format_ext.lower())
|
||||||
|
|
||||||
def get_base_vhd_dir(self):
|
def get_base_vhd_dir(self):
|
||||||
return self._get_instances_sub_dir('_base')
|
return self._get_instances_sub_dir('_base')
|
||||||
|
@ -42,6 +42,7 @@ from nova.i18n import _, _LI, _LE, _LW
|
|||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.virt import configdrive
|
from nova.virt import configdrive
|
||||||
from nova.virt import hardware
|
from nova.virt import hardware
|
||||||
|
from nova.virt.hyperv import block_device_manager
|
||||||
from nova.virt.hyperv import constants
|
from nova.virt.hyperv import constants
|
||||||
from nova.virt.hyperv import imagecache
|
from nova.virt.hyperv import imagecache
|
||||||
from nova.virt.hyperv import pathutils
|
from nova.virt.hyperv import pathutils
|
||||||
@ -107,6 +108,8 @@ class VMOps(object):
|
|||||||
self._volumeops = volumeops.VolumeOps()
|
self._volumeops = volumeops.VolumeOps()
|
||||||
self._imagecache = imagecache.ImageCache()
|
self._imagecache = imagecache.ImageCache()
|
||||||
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
|
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
|
||||||
|
self._block_dev_man = (
|
||||||
|
block_device_manager.BlockDeviceInfoManager())
|
||||||
self._vif_driver = None
|
self._vif_driver = None
|
||||||
self._load_vif_driver_class()
|
self._load_vif_driver_class()
|
||||||
|
|
||||||
@ -157,6 +160,13 @@ class VMOps(object):
|
|||||||
num_cpu=info['NumberOfProcessors'],
|
num_cpu=info['NumberOfProcessors'],
|
||||||
cpu_time_ns=info['UpTime'])
|
cpu_time_ns=info['UpTime'])
|
||||||
|
|
||||||
|
def _create_root_device(self, context, instance, root_disk_info, vm_gen):
|
||||||
|
path = None
|
||||||
|
if root_disk_info['type'] == constants.DISK:
|
||||||
|
path = self._create_root_vhd(context, instance)
|
||||||
|
self.check_vm_image_type(instance.uuid, vm_gen, path)
|
||||||
|
root_disk_info['path'] = path
|
||||||
|
|
||||||
def _create_root_vhd(self, context, instance, rescue_image_id=None):
|
def _create_root_vhd(self, context, instance, rescue_image_id=None):
|
||||||
is_rescue_vhd = rescue_image_id is not None
|
is_rescue_vhd = rescue_image_id is not None
|
||||||
|
|
||||||
@ -223,15 +233,17 @@ class VMOps(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def create_ephemeral_vhd(self, instance):
|
def _create_ephemerals(self, instance, ephemerals):
|
||||||
eph_vhd_size = instance.get('ephemeral_gb', 0) * units.Gi
|
for index, eph in enumerate(ephemerals):
|
||||||
if eph_vhd_size:
|
eph['format'] = self._vhdutils.get_best_supported_vhd_format()
|
||||||
vhd_format = self._vhdutils.get_best_supported_vhd_format()
|
eph_name = "eph%s" % index
|
||||||
|
eph['path'] = self._pathutils.get_ephemeral_vhd_path(
|
||||||
|
instance.name, eph['format'], eph_name)
|
||||||
|
self.create_ephemeral_disk(instance.name, eph)
|
||||||
|
|
||||||
eph_vhd_path = self._pathutils.get_ephemeral_vhd_path(
|
def create_ephemeral_disk(self, instance_name, eph_info):
|
||||||
instance.name, vhd_format)
|
self._vhdutils.create_dynamic_vhd(eph_info['path'],
|
||||||
self._vhdutils.create_dynamic_vhd(eph_vhd_path, eph_vhd_size)
|
eph_info['size'] * units.Gi)
|
||||||
return eph_vhd_path
|
|
||||||
|
|
||||||
@check_admin_permissions
|
@check_admin_permissions
|
||||||
def spawn(self, context, instance, image_meta, injected_files,
|
def spawn(self, context, instance, image_meta, injected_files,
|
||||||
@ -246,19 +258,17 @@ class VMOps(object):
|
|||||||
# Make sure we're starting with a clean slate.
|
# Make sure we're starting with a clean slate.
|
||||||
self._delete_disk_files(instance_name)
|
self._delete_disk_files(instance_name)
|
||||||
|
|
||||||
if self._volumeops.ebs_root_in_block_devices(block_device_info):
|
vm_gen = self.get_image_vm_generation(instance.uuid, image_meta)
|
||||||
root_vhd_path = None
|
|
||||||
else:
|
|
||||||
root_vhd_path = self._create_root_vhd(context, instance)
|
|
||||||
|
|
||||||
eph_vhd_path = self.create_ephemeral_vhd(instance)
|
self._block_dev_man.validate_and_update_bdi(
|
||||||
vm_gen = self.get_image_vm_generation(
|
instance, image_meta, vm_gen, block_device_info)
|
||||||
instance.uuid, root_vhd_path, image_meta)
|
root_device = block_device_info['root_disk']
|
||||||
|
self._create_root_device(context, instance, root_device, vm_gen)
|
||||||
|
self._create_ephemerals(instance, block_device_info['ephemerals'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.create_instance(instance, network_info, block_device_info,
|
self.create_instance(instance, network_info, root_device,
|
||||||
root_vhd_path, eph_vhd_path,
|
block_device_info, vm_gen)
|
||||||
vm_gen)
|
|
||||||
|
|
||||||
if configdrive.required_by(instance):
|
if configdrive.required_by(instance):
|
||||||
configdrive_path = self._create_config_drive(instance,
|
configdrive_path = self._create_config_drive(instance,
|
||||||
@ -273,8 +283,8 @@ class VMOps(object):
|
|||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self.destroy(instance)
|
self.destroy(instance)
|
||||||
|
|
||||||
def create_instance(self, instance, network_info, block_device_info,
|
def create_instance(self, instance, network_info, root_device,
|
||||||
root_vhd_path, eph_vhd_path, vm_gen):
|
block_device_info, vm_gen):
|
||||||
instance_name = instance.name
|
instance_name = instance.name
|
||||||
instance_path = os.path.join(CONF.instances_path, instance_name)
|
instance_path = os.path.join(CONF.instances_path, instance_name)
|
||||||
|
|
||||||
@ -290,24 +300,10 @@ class VMOps(object):
|
|||||||
self._configure_remotefx(instance, vm_gen)
|
self._configure_remotefx(instance, vm_gen)
|
||||||
|
|
||||||
self._vmutils.create_scsi_controller(instance_name)
|
self._vmutils.create_scsi_controller(instance_name)
|
||||||
controller_type = VM_GENERATIONS_CONTROLLER_TYPES[vm_gen]
|
self._attach_root_device(instance_name, root_device)
|
||||||
|
self._attach_ephemerals(instance_name, block_device_info['ephemerals'])
|
||||||
ctrl_disk_addr = 0
|
self._volumeops.attach_volumes(
|
||||||
if root_vhd_path:
|
block_device_info['block_device_mapping'], instance_name)
|
||||||
self._attach_drive(instance_name, root_vhd_path, 0, ctrl_disk_addr,
|
|
||||||
controller_type)
|
|
||||||
|
|
||||||
ctrl_disk_addr = 1
|
|
||||||
if eph_vhd_path:
|
|
||||||
self._attach_drive(instance_name, eph_vhd_path, 0, ctrl_disk_addr,
|
|
||||||
controller_type)
|
|
||||||
|
|
||||||
# If ebs_root is False, the first volume will be attached to SCSI
|
|
||||||
# controller. Generation 2 VMs only has a SCSI controller.
|
|
||||||
ebs_root = vm_gen is not constants.VM_GEN_2 and root_vhd_path is None
|
|
||||||
self._volumeops.attach_volumes(block_device_info,
|
|
||||||
instance_name,
|
|
||||||
ebs_root)
|
|
||||||
|
|
||||||
# For the moment, we use COM port 1 when getting the serial console
|
# For the moment, we use COM port 1 when getting the serial console
|
||||||
# log as well as interactive sessions. In the future, the way in which
|
# log as well as interactive sessions. In the future, the way in which
|
||||||
@ -368,6 +364,29 @@ class VMOps(object):
|
|||||||
remotefx_max_resolution,
|
remotefx_max_resolution,
|
||||||
vram_bytes)
|
vram_bytes)
|
||||||
|
|
||||||
|
def _attach_root_device(self, instance_name, root_dev_info):
|
||||||
|
if root_dev_info['type'] == constants.VOLUME:
|
||||||
|
self._volumeops.attach_volume(root_dev_info['connection_info'],
|
||||||
|
instance_name,
|
||||||
|
disk_bus=root_dev_info['disk_bus'])
|
||||||
|
else:
|
||||||
|
self._attach_drive(instance_name, root_dev_info['path'],
|
||||||
|
root_dev_info['drive_addr'],
|
||||||
|
root_dev_info['ctrl_disk_addr'],
|
||||||
|
root_dev_info['disk_bus'],
|
||||||
|
root_dev_info['type'])
|
||||||
|
|
||||||
|
def _attach_ephemerals(self, instance_name, ephemerals):
|
||||||
|
for eph in ephemerals:
|
||||||
|
# if an ephemeral doesn't have a path, it might have been removed
|
||||||
|
# during resize.
|
||||||
|
if eph.get('path'):
|
||||||
|
self._attach_drive(
|
||||||
|
instance_name, eph['path'], eph['drive_addr'],
|
||||||
|
eph['ctrl_disk_addr'], eph['disk_bus'],
|
||||||
|
constants.BDI_DEVICE_TYPE_TO_DRIVE_TYPE[
|
||||||
|
eph['device_type']])
|
||||||
|
|
||||||
def _attach_drive(self, instance_name, path, drive_addr, ctrl_disk_addr,
|
def _attach_drive(self, instance_name, path, drive_addr, ctrl_disk_addr,
|
||||||
controller_type, drive_type=constants.DISK):
|
controller_type, drive_type=constants.DISK):
|
||||||
if controller_type == constants.CTRL_TYPE_SCSI:
|
if controller_type == constants.CTRL_TYPE_SCSI:
|
||||||
@ -376,18 +395,19 @@ class VMOps(object):
|
|||||||
self._vmutils.attach_ide_drive(instance_name, path, drive_addr,
|
self._vmutils.attach_ide_drive(instance_name, path, drive_addr,
|
||||||
ctrl_disk_addr, drive_type)
|
ctrl_disk_addr, drive_type)
|
||||||
|
|
||||||
def get_image_vm_generation(self, instance_id, root_vhd_path, image_meta):
|
def get_image_vm_generation(self, instance_id, image_meta):
|
||||||
default_vm_gen = self._hostutils.get_default_vm_generation()
|
default_vm_gen = self._hostutils.get_default_vm_generation()
|
||||||
image_prop_vm = image_meta.properties.get(
|
image_prop_vm = image_meta.properties.get('hw_machine_type',
|
||||||
'hw_machine_type', default_vm_gen)
|
default_vm_gen)
|
||||||
if image_prop_vm not in self._hostutils.get_supported_vm_types():
|
if image_prop_vm not in self._hostutils.get_supported_vm_types():
|
||||||
reason = _LE('Requested VM Generation %s is not supported on '
|
reason = _LE('Requested VM Generation %s is not supported on '
|
||||||
'this OS.') % image_prop_vm
|
'this OS.') % image_prop_vm
|
||||||
raise exception.InstanceUnacceptable(instance_id=instance_id,
|
raise exception.InstanceUnacceptable(instance_id=instance_id,
|
||||||
reason=reason)
|
reason=reason)
|
||||||
|
|
||||||
vm_gen = VM_GENERATIONS[image_prop_vm]
|
return VM_GENERATIONS[image_prop_vm]
|
||||||
|
|
||||||
|
def check_vm_image_type(self, instance_id, vm_gen, root_vhd_path):
|
||||||
if (vm_gen != constants.VM_GEN_1 and root_vhd_path and
|
if (vm_gen != constants.VM_GEN_1 and root_vhd_path and
|
||||||
self._vhdutils.get_vhd_format(
|
self._vhdutils.get_vhd_format(
|
||||||
root_vhd_path) == constants.DISK_FORMAT_VHD):
|
root_vhd_path) == constants.DISK_FORMAT_VHD):
|
||||||
@ -396,8 +416,6 @@ class VMOps(object):
|
|||||||
raise exception.InstanceUnacceptable(instance_id=instance_id,
|
raise exception.InstanceUnacceptable(instance_id=instance_id,
|
||||||
reason=reason)
|
reason=reason)
|
||||||
|
|
||||||
return vm_gen
|
|
||||||
|
|
||||||
def _create_config_drive(self, instance, injected_files, admin_password,
|
def _create_config_drive(self, instance, injected_files, admin_password,
|
||||||
network_info, rescue=False):
|
network_info, rescue=False):
|
||||||
if CONF.config_drive_format != 'iso9660':
|
if CONF.config_drive_format != 'iso9660':
|
||||||
@ -748,7 +766,6 @@ class VMOps(object):
|
|||||||
context, instance, rescue_image_id=rescue_image_id)
|
context, instance, rescue_image_id=rescue_image_id)
|
||||||
|
|
||||||
rescue_vm_gen = self.get_image_vm_generation(instance.uuid,
|
rescue_vm_gen = self.get_image_vm_generation(instance.uuid,
|
||||||
rescue_vhd_path,
|
|
||||||
image_meta)
|
image_meta)
|
||||||
vm_gen = self._vmutils.get_vm_generation(instance.name)
|
vm_gen = self._vmutils.get_vm_generation(instance.name)
|
||||||
if rescue_vm_gen != vm_gen:
|
if rescue_vm_gen != vm_gen:
|
||||||
|
@ -28,12 +28,12 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
|
|
||||||
from nova import block_device
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _, _LE, _LW
|
from nova.i18n import _, _LE, _LW
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.virt import driver
|
from nova.virt import driver
|
||||||
|
from nova.virt.hyperv import constants
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -59,14 +59,8 @@ class VolumeOps(object):
|
|||||||
raise exception.VolumeDriverNotFound(driver_type=driver_type)
|
raise exception.VolumeDriverNotFound(driver_type=driver_type)
|
||||||
return self.volume_drivers[driver_type]
|
return self.volume_drivers[driver_type]
|
||||||
|
|
||||||
def attach_volumes(self, block_device_info, instance_name, ebs_root):
|
def attach_volumes(self, volumes, instance_name):
|
||||||
mapping = driver.block_device_info_get_mapping(block_device_info)
|
for vol in volumes:
|
||||||
|
|
||||||
if ebs_root:
|
|
||||||
self.attach_volume(mapping[0]['connection_info'],
|
|
||||||
instance_name, True)
|
|
||||||
mapping = mapping[1:]
|
|
||||||
for vol in mapping:
|
|
||||||
self.attach_volume(vol['connection_info'], instance_name)
|
self.attach_volume(vol['connection_info'], instance_name)
|
||||||
|
|
||||||
def disconnect_volumes(self, block_device_info):
|
def disconnect_volumes(self, block_device_info):
|
||||||
@ -77,24 +71,18 @@ class VolumeOps(object):
|
|||||||
volume_driver = self._get_volume_driver(driver_type)
|
volume_driver = self._get_volume_driver(driver_type)
|
||||||
volume_driver.disconnect_volumes(block_device_mapping)
|
volume_driver.disconnect_volumes(block_device_mapping)
|
||||||
|
|
||||||
def attach_volume(self, connection_info, instance_name, ebs_root=False):
|
def attach_volume(self, connection_info, instance_name,
|
||||||
|
disk_bus=constants.CTRL_TYPE_SCSI):
|
||||||
volume_driver = self._get_volume_driver(
|
volume_driver = self._get_volume_driver(
|
||||||
connection_info=connection_info)
|
connection_info=connection_info)
|
||||||
volume_driver.attach_volume(connection_info, instance_name, ebs_root)
|
volume_driver.attach_volume(connection_info, instance_name,
|
||||||
|
disk_bus=disk_bus)
|
||||||
|
|
||||||
def detach_volume(self, connection_info, instance_name):
|
def detach_volume(self, connection_info, instance_name):
|
||||||
volume_driver = self._get_volume_driver(
|
volume_driver = self._get_volume_driver(
|
||||||
connection_info=connection_info)
|
connection_info=connection_info)
|
||||||
volume_driver.detach_volume(connection_info, instance_name)
|
volume_driver.detach_volume(connection_info, instance_name)
|
||||||
|
|
||||||
def ebs_root_in_block_devices(self, block_device_info):
|
|
||||||
if block_device_info:
|
|
||||||
root_device = block_device_info.get('root_device_name')
|
|
||||||
if not root_device:
|
|
||||||
root_device = self._default_root_device
|
|
||||||
return block_device.volume_in_mapping(root_device,
|
|
||||||
block_device_info)
|
|
||||||
|
|
||||||
def fix_instance_volume_disk_paths(self, instance_name, block_device_info):
|
def fix_instance_volume_disk_paths(self, instance_name, block_device_info):
|
||||||
# Mapping containing the current disk paths for each volume.
|
# Mapping containing the current disk paths for each volume.
|
||||||
actual_disk_mapping = self.get_disk_path_mapping(block_device_info)
|
actual_disk_mapping = self.get_disk_path_mapping(block_device_info)
|
||||||
@ -230,7 +218,8 @@ class ISCSIVolumeDriver(object):
|
|||||||
return self._get_mounted_disk_from_lun(target_iqn, target_lun,
|
return self._get_mounted_disk_from_lun(target_iqn, target_lun,
|
||||||
wait_for_device=True)
|
wait_for_device=True)
|
||||||
|
|
||||||
def attach_volume(self, connection_info, instance_name, ebs_root=False):
|
def attach_volume(self, connection_info, instance_name,
|
||||||
|
disk_bus=constants.CTRL_TYPE_SCSI):
|
||||||
"""Attach a volume to the SCSI controller or to the IDE controller if
|
"""Attach a volume to the SCSI controller or to the IDE controller if
|
||||||
ebs_root is True
|
ebs_root is True
|
||||||
"""
|
"""
|
||||||
@ -246,7 +235,7 @@ class ISCSIVolumeDriver(object):
|
|||||||
mounted_disk_path = self.get_mounted_disk_path_from_volume(
|
mounted_disk_path = self.get_mounted_disk_path_from_volume(
|
||||||
connection_info)
|
connection_info)
|
||||||
|
|
||||||
if ebs_root:
|
if disk_bus == constants.CTRL_TYPE_IDE:
|
||||||
# Find the IDE controller for the vm.
|
# Find the IDE controller for the vm.
|
||||||
ctrller_path = self._vmutils.get_vm_ide_controller(
|
ctrller_path = self._vmutils.get_vm_ide_controller(
|
||||||
instance_name, 0)
|
instance_name, 0)
|
||||||
@ -359,13 +348,14 @@ class SMBFSVolumeDriver(object):
|
|||||||
return self._get_disk_path(connection_info)
|
return self._get_disk_path(connection_info)
|
||||||
|
|
||||||
@export_path_synchronized
|
@export_path_synchronized
|
||||||
def attach_volume(self, connection_info, instance_name, ebs_root=False):
|
def attach_volume(self, connection_info, instance_name,
|
||||||
|
disk_bus=constants.CTRL_TYPE_SCSI):
|
||||||
self.ensure_share_mounted(connection_info)
|
self.ensure_share_mounted(connection_info)
|
||||||
|
|
||||||
disk_path = self._get_disk_path(connection_info)
|
disk_path = self._get_disk_path(connection_info)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if ebs_root:
|
if disk_bus == constants.CTRL_TYPE_IDE:
|
||||||
ctrller_path = self._vmutils.get_vm_ide_controller(
|
ctrller_path = self._vmutils.get_vm_ide_controller(
|
||||||
instance_name, 0)
|
instance_name, 0)
|
||||||
slot = 0
|
slot = 0
|
||||||
|
Loading…
Reference in New Issue
Block a user