Merge "Added support for new block device format in vmops"

This commit is contained in:
Jenkins 2016-07-11 17:55:27 +00:00 committed by Gerrit Code Review
commit 92134b4c18
13 changed files with 528 additions and 360 deletions

View File

@ -50,14 +50,7 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
self.assertEqual(slot_map[constants.CTRL_TYPE_IDE][1],
os_win_const.IDE_CONTROLLER_SLOTS_NUMBER - 1)
@mock.patch('nova.virt.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.configdrive, 'required_by')
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
'_initialize_controller_slot_counter')
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
@ -66,28 +59,44 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
'_check_and_update_ephemerals')
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
'_check_and_update_volumes')
def test_validate_and_update_bdi(self, mock_check_and_update_vol,
mock_check_and_update_eph,
mock_check_and_update_root,
mock_init_ctrl_cntr):
mock_init_ctrl_cntr.return_value = mock.sentinel.FAKE_SLOT_MAP
def _check_validate_and_update_bdi(self, mock_check_and_update_vol,
mock_check_and_update_eph,
mock_check_and_update_root,
mock_init_ctrl_cntr,
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,
mock.sentinel.IMAGE_META,
mock.sentinel.VM_GEN,
mock.sentinel.BLOCK_DEV_INFO)
if available_slots:
self._bdman.validate_and_update_bdi(mock.sentinel.FAKE_INSTANCE,
mock.sentinel.IMAGE_META,
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.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.sentinel.VM_GEN, mock.sentinel.IMAGE_META,
mock.sentinel.BLOCK_DEV_INFO, mock.sentinel.FAKE_SLOT_MAP)
constants.VM_GEN_2, mock.sentinel.IMAGE_META,
mock.sentinel.BLOCK_DEV_INFO, slot_map)
mock_check_and_update_eph.assert_called_once_with(
mock.sentinel.VM_GEN, mock.sentinel.BLOCK_DEV_INFO,
mock.sentinel.FAKE_SLOT_MAP)
constants.VM_GEN_2, mock.sentinel.BLOCK_DEV_INFO, slot_map)
mock_check_and_update_vol.assert_called_once_with(
mock.sentinel.VM_GEN, mock.sentinel.BLOCK_DEV_INFO,
mock.sentinel.FAKE_SLOT_MAP)
constants.VM_GEN_2, mock.sentinel.BLOCK_DEV_INFO, 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,
'_get_available_controller_slot')

View File

@ -34,6 +34,7 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
self._livemigrops = livemigrationops.LiveMigrationOps()
self._livemigrops._livemigrutils = mock.MagicMock()
self._livemigrops._pathutils = mock.MagicMock()
self._livemigrops._block_dev_man = mock.MagicMock()
@mock.patch.object(serialconsoleops.SerialConsoleOps,
'stop_console_handler')
@ -78,22 +79,21 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
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'
'.ebs_root_in_block_devices')
@mock.patch('nova.virt.hyperv.imagecache.ImageCache.get_cached_image')
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
'.initialize_volumes_connection')
def _test_pre_live_migration(self, mock_initialize_connection,
mock_get_cached_image,
mock_ebs_root_in_block_devices,
mock_get_disk_path_mapping,
phys_disks_attached=True):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_instance.image_ref = "fake_image_ref"
mock_ebs_root_in_block_devices.return_value = None
mock_get_disk_path_mapping.return_value = (
mock.sentinel.disk_path_mapping if phys_disks_attached
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)
self._livemigrops.pre_live_migration(
self.context, mock_instance,
@ -103,7 +103,7 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
check_config = (
self._livemigrops._livemigrutils.check_live_migration_config)
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_get_cached_image.assert_called_once_with(self.context,
mock_instance)

View File

@ -23,6 +23,7 @@ from nova import objects
from nova import test
from nova.tests.unit import fake_instance
from nova.tests.unit.virt.hyperv import test_base
from nova.virt.hyperv import constants
from nova.virt.hyperv import migrationops
@ -46,6 +47,7 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
self._migrationops._pathutils = mock.MagicMock()
self._migrationops._volumeops = mock.MagicMock()
self._migrationops._imagecache = mock.MagicMock()
self._migrationops._block_dev_man = mock.MagicMock()
def _check_migrate_disk_files(self, host):
instance_path = 'fake/instance/path'
@ -223,56 +225,61 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(migrationops.MigrationOps,
'_check_and_attach_config_drive')
@mock.patch.object(migrationops.MigrationOps, '_revert_migration_files')
@mock.patch.object(migrationops.MigrationOps, '_check_ephemeral_disks')
@mock.patch.object(objects.ImageMeta, "from_instance")
def _check_finish_revert_migration(self, mock_image,
mock_check_eph_disks,
mock_revert_migration_files,
mock_check_attach_config_drive,
boot_from_volume=False):
disk_type=constants.DISK):
mock_image.return_value = objects.ImageMeta.from_dict({})
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 = boot_from_volume
lookup_ephemeral = (
self._migrationops._pathutils.lookup_ephemeral_vhd_path)
root_device = {'type': disk_type}
block_device_info = {'root_disk': root_device, 'ephemerals': []}
self._migrationops.finish_revert_migration(
context=self.context, instance=mock_instance,
network_info=mock.sentinel.network_info,
block_device_info=mock.sentinel.block_device,
block_device_info=block_device_info,
power_on=True)
mock_revert_migration_files.assert_called_once_with(
mock_instance.name)
mock_ebs_root_in_block_devices.assert_called_once_with(
mock.sentinel.block_device)
if not boot_from_volume:
if root_device['type'] == constants.DISK:
lookup_root_vhd = (
self._migrationops._pathutils.lookup_root_vhd_path)
lookup_root_vhd.assert_called_once_with(mock_instance.name)
fake_root_path = lookup_root_vhd.return_value
else:
fake_root_path = None
self.assertEqual(lookup_root_vhd.return_value,
root_device['path'])
lookup_ephemeral.assert_called_with(mock_instance.name)
get_image_vm_gen = self._migrationops._vmops.get_image_vm_generation
get_image_vm_gen.assert_called_once_with(
mock_instance.uuid, fake_root_path,
test.MatchType(objects.ImageMeta))
mock_instance.uuid, test.MatchType(objects.ImageMeta))
self._migrationops._vmops.create_instance.assert_called_once_with(
mock_instance, mock.sentinel.network_info,
mock.sentinel.block_device, fake_root_path,
lookup_ephemeral.return_value, get_image_vm_gen.return_value)
mock_instance, mock.sentinel.network_info, root_device,
block_device_info, get_image_vm_gen.return_value)
mock_check_attach_config_drive.assert_called_once_with(
mock_instance, get_image_vm_gen.return_value)
self._migrationops._vmops.power_on.assert_called_once_with(
mock_instance)
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):
self._check_finish_revert_migration()
def test_finish_revert_migration_boot_from_disk(self):
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):
fake_diff_vhd_path = 'fake/diff/path'
@ -362,24 +369,21 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
'_check_and_attach_config_drive')
@mock.patch.object(migrationops.MigrationOps, '_check_base_disk')
@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_attach_config_drive,
ephemeral_path=None,
boot_from_volume=False):
disk_type=constants.DISK):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_instance.ephemeral_gb = 1
mock_vhd_info = mock.MagicMock()
mock_eph_info = mock.MagicMock()
root_device = {'type': disk_type}
block_device_info = {'root_disk': root_device, 'ephemerals': []}
lookup_root_vhd = self._migrationops._pathutils.lookup_root_vhd_path
side_effect = [mock_eph_info] if boot_from_volume else [mock_vhd_info,
mock_eph_info]
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
get_vhd_info = self._migrationops._vhdutils.get_vhd_info
mock_vhd_info = get_vhd_info.return_value
expected_check_resize = []
expected_get_info = []
@ -387,71 +391,131 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
context=self.context, migration=mock.sentinel.migration,
instance=mock_instance, disk_info=mock.sentinel.disk_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 not boot_from_volume:
root_vhd_path = lookup_root_vhd.return_value
if root_device['type'] == constants.DISK:
root_device_path = lookup_root_vhd.return_value
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_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)
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))
else:
root_vhd_path = None
look_up_ephem.assert_called_once_with(mock_instance.name)
if ephemeral_path is None:
create_eph_vhd = self._migrationops._vmops.create_ephemeral_vhd
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))
ephemerals = block_device_info['ephemerals']
mock_check_eph_disks.assert_called_once_with(
mock_instance, ephemerals, True)
mock_check_resize_vhd.assert_has_calls(expected_check_resize)
self._migrationops._vhdutils.get_vhd_info.assert_has_calls(
expected_get_info)
get_image_vm_gen = self._migrationops._vmops.get_image_vm_generation
get_image_vm_gen.assert_called_once_with(mock_instance.uuid,
root_vhd_path,
mock.sentinel.image_meta)
self._migrationops._vmops.create_instance.assert_called_once_with(
mock_instance, mock.sentinel.network_info, None, root_vhd_path,
ephemeral_path, get_image_vm_gen.return_value)
mock_instance, mock.sentinel.network_info, root_device,
block_device_info, get_image_vm_gen.return_value)
mock_check_attach_config_drive.assert_called_once_with(
mock_instance, get_image_vm_gen.return_value)
self._migrationops._vmops.power_on.assert_called_once_with(
mock_instance)
def test_finish_migration(self):
self._check_finish_migration(
ephemeral_path=mock.sentinel.ephemeral_path)
self._check_finish_migration(disk_type=constants.DISK)
def test_finish_migration_boot_from_volume(self):
self._check_finish_migration(
ephemeral_path=mock.sentinel.ephemeral_path,
boot_from_volume=True)
def test_finish_migration_no_ephemeral(self):
self._check_finish_migration()
self._check_finish_migration(disk_type=constants.VOLUME)
def test_finish_migration_no_root(self):
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
bdi = {'root_disk': {'type': constants.DISK},
'ephemerals': []}
self.assertRaises(exception.DiskNotFound,
self._migrationops.finish_migration,
self.context, mock.sentinel.migration,
mock_instance, mock.sentinel.disk_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)

View File

@ -67,6 +67,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
self._vmops._pathutils = mock.MagicMock()
self._vmops._hostutils = 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.virt.hyperv.vmops.importutils.import_object')
@ -151,7 +152,24 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
def test_get_info_exception(self):
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):
mock_instance = fake_instance.fake_instance_obj(self.context)
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')
def _test_create_root_vhd_exception(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=False, vhd_format=vhd_format,
vhd_size=(self.FAKE_SIZE + 1))
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')
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,
vhd_size=(self.FAKE_SIZE - 1))
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')
def _test_create_root_vhd(self, mock_get_cached_image, vhd_format,
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,
vhd_size=(self.FAKE_SIZE - 1))
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(
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.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(
mock_instance.name, mock.sentinel.FAKE_FORMAT)
self._vmops._vhdutils.create_dynamic_vhd.assert_called_with(
mock.sentinel.FAKE_PATH, mock_instance.ephemeral_gb * units.Gi)
self.assertEqual(mock.sentinel.FAKE_PATH, response)
self._vmops._create_ephemerals(mock_instance, fake_ephemerals)
self._vmops._pathutils.get_ephemeral_vhd_path.assert_has_calls(
[mock.call(mock_instance.name, mock.sentinel.format, 'eph0'),
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.power_on')
@ -315,62 +348,59 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch('nova.virt.configdrive.required_by')
@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.create_ephemeral_vhd')
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_root_vhd')
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps.'
'ebs_root_in_block_devices')
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_ephemerals')
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_root_device')
@mock.patch('nova.virt.hyperv.vmops.VMOps._delete_disk_files')
def _test_spawn(self, mock_delete_disk_files,
mock_ebs_root_in_block_devices, mock_create_root_vhd,
mock_create_ephemeral_vhd, mock_get_image_vm_gen,
def _test_spawn(self, mock_delete_disk_files, mock_create_root_device,
mock_create_ephemerals, mock_get_image_vm_gen,
mock_create_instance, mock_configdrive_required,
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):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_image_meta = mock.MagicMock()
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
root_device_info = mock.sentinel.ROOT_DEV_INFO
fake_vm_gen = mock_get_image_vm_gen.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
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_create_instance.side_effect = fail
if exists:
self.assertRaises(exception.InstanceExists, self._vmops.spawn,
self.context, mock_instance, mock_image_meta,
[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:
self.assertRaises(os_win_exc.HyperVException, self._vmops.spawn,
self.context, mock_instance, mock_image_meta,
[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)
else:
self._vmops.spawn(self.context, mock_instance, mock_image_meta,
[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(
mock_instance.name)
mock_delete_disk_files.assert_called_once_with(
mock_instance.name)
mock_ebs_root_in_block_devices.assert_called_once_with(
mock.sentinel.DEV_INFO)
if not boot_from_volume:
mock_create_root_vhd.assert_called_once_with(self.context,
mock_instance)
mock_create_ephemeral_vhd.assert_called_once_with(mock_instance)
mock_get_image_vm_gen.assert_called_once_with(
mock_instance.uuid, fake_root_path, mock_image_meta)
mock_validate_and_update_bdi = (
self._vmops._block_dev_man.validate_and_update_bdi)
mock_validate_and_update_bdi.assert_called_once_with(
mock_instance, mock_image_meta, fake_vm_gen, block_device_info)
mock_create_root_device.assert_called_once_with(self.context,
mock_instance,
root_device_info,
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_instance, mock.sentinel.INFO, mock.sentinel.DEV_INFO,
fake_root_path, fake_ephemeral_path, fake_vm_gen)
mock_instance, mock.sentinel.INFO, root_device_info,
block_device_info, fake_vm_gen)
mock_configdrive_required.assert_called_once_with(mock_instance)
if configdrive_required:
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)
def test_spawn(self):
self._test_spawn(exists=False, boot_from_volume=False,
configdrive_required=True, fail=None)
self._test_spawn(exists=False, configdrive_required=True, fail=None)
def test_spawn_instance_exists(self):
self._test_spawn(exists=True, boot_from_volume=False,
configdrive_required=True, fail=None)
self._test_spawn(exists=True, configdrive_required=True, fail=None)
def test_spawn_create_instance_exception(self):
self._test_spawn(exists=False, boot_from_volume=False,
configdrive_required=True,
self._test_spawn(exists=False, configdrive_required=True,
fail=os_win_exc.HyperVException)
def test_spawn_not_required(self):
self._test_spawn(exists=False, boot_from_volume=False,
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)
self._test_spawn(exists=False, configdrive_required=False, fail=None)
def test_spawn_no_admin_permissions(self):
self._vmops._vmutils.check_admin_permissions.side_effect = (
@ -413,19 +435,23 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch('nova.virt.hyperv.volumeops.VolumeOps'
'.attach_volumes')
@mock.patch.object(vmops.VMOps, '_attach_drive')
@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')
def _test_create_instance(self, mock_configure_remotefx,
mock_create_pipes, mock_attach_drive,
mock_attach_volumes, fake_root_path,
fake_ephemeral_path,
mock_attach_root_device,
mock_attach_ephemerals,
mock_create_pipes,
mock_attach_volumes,
enable_instance_metrics,
vm_gen=constants.VM_GEN_1):
mock_vif_driver = mock.MagicMock()
self._vmops._vif_driver = mock_vif_driver
self.flags(enable_instance_metrics_collection=enable_instance_metrics,
group='hyperv')
root_device_info = mock.sentinel.ROOT_DEV_INFO
block_device_info = {'ephemerals': [], 'block_device_mapping': []}
fake_network_info = {'id': mock.sentinel.ID,
'address': mock.sentinel.ADDRESS}
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,
network_info=[fake_network_info],
block_device_info=mock.sentinel.DEV_INFO,
root_vhd_path=fake_root_path,
eph_vhd_path=fake_ephemeral_path,
root_device=root_device_info,
block_device_info=block_device_info,
vm_gen=vm_gen)
self._vmops._vmutils.create_vm.assert_called_once_with(
mock_instance.name, mock_instance.memory_mb,
@ -447,32 +472,15 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
[mock_instance.uuid])
mock_configure_remotefx.assert_called_once_with(mock_instance, vm_gen)
expected = []
ctrl_type = vmops.VM_GENERATIONS_CONTROLLER_TYPES[vm_gen]
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)
mock_create_scsi_ctrl = self._vmops._vmutils.create_scsi_controller
mock_create_scsi_ctrl.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_volumes.assert_called_once_with(mock.sentinel.DEV_INFO,
mock_instance.name,
ebs_root)
expected_port_settings = {
constants.DEFAULT_SERIAL_CONSOLE_PORT:
constants.SERIAL_PORT_TYPE_RW}
mock_create_pipes.assert_called_once_with(
mock_instance, expected_port_settings)
mock_attach_root_device.assert_called_once_with(mock_instance.name,
root_device_info)
mock_attach_ephemerals.assert_called_once_with(mock_instance.name,
block_device_info['ephemerals'])
mock_attach_volumes.assert_called_once_with(
block_device_info['block_device_mapping'], mock_instance.name)
self._vmops._vmutils.create_nic.assert_called_once_with(
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)
def test_create_instance(self):
fake_ephemeral_path = mock.sentinel.FAKE_EPHEMERAL_PATH
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)
self._test_create_instance(enable_instance_metrics=True)
def test_create_instance_enable_instance_metrics_false(self):
fake_ephemeral_path = mock.sentinel.FAKE_EPHEMERAL_PATH
self._test_create_instance(fake_root_path=mock.sentinel.FAKE_ROOT_PATH,
fake_ephemeral_path=fake_ephemeral_path,
enable_instance_metrics=False)
self._test_create_instance(enable_instance_metrics=False)
def test_create_instance_gen2(self):
self._test_create_instance(fake_root_path=None,
fake_ephemeral_path=None,
enable_instance_metrics=False,
self._test_create_instance(enable_instance_metrics=False,
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):
self._vmops._attach_drive(
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]
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)
@ -555,28 +596,20 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
{"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 = (
constants.DISK_FORMAT_VHDX)
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)
def test_get_image_vm_generation_not_vhdx(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]
def test_check_vm_image_type_exception(self):
self._vmops._vhdutils.get_vhd_format.return_value = (
constants.DISK_FORMAT_VHD)
self.assertRaises(exception.InstanceUnacceptable,
self._vmops.get_image_vm_generation,
mock.sentinel.instance_id,
mock.sentinel.FAKE_PATH,
image_meta)
self._vmops.check_vm_image_type,
mock.sentinel.instance_id, constants.VM_GEN_2,
mock.sentinel.FAKE_PATH)
@mock.patch('nova.api.metadata.base.InstanceMetadata')
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder')
@ -1244,8 +1277,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock.sentinel.rescue_password)
mock_get_image_vm_gen.assert_called_once_with(
mock_instance.uuid, mock.sentinel.rescue_vhd_path,
mock_image_meta)
mock_instance.uuid, mock_image_meta)
self._vmops._vmutils.detach_vm_disk.assert_called_once_with(
mock_instance.name, mock.sentinel.root_vhd_path,
is_physical=False)

View File

@ -24,6 +24,7 @@ from nova import exception
from nova import test
from nova.tests.unit import fake_block_device
from nova.tests.unit.virt.hyperv import test_base
from nova.virt.hyperv import constants
from nova.virt.hyperv import volumeops
CONF = cfg.CONF
@ -76,13 +77,13 @@ class VolumeOpsTestCase(test_base.HyperVBaseTestCase):
def test_attach_volumes(self, mock_attach_volume):
block_device_info = get_fake_block_dev_info()
self._volumeops.attach_volumes(block_device_info,
mock.sentinel.instance_name,
ebs_root=True)
self._volumeops.attach_volumes(
block_device_info['block_device_mapping'],
mock.sentinel.instance_name)
mock_attach_volume.assert_called_once_with(
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):
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(
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):
mock_instance = mock.DEFAULT
initiator = self._volumeops._volutils.get_iscsi_initiator.return_value
@ -324,7 +315,7 @@ class ISCSIVolumeDriverTestCase(test_base.HyperVBaseTestCase):
'_get_mounted_disk_from_lun')
@mock.patch.object(volumeops.ISCSIVolumeDriver, '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()
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(
connection_info=connection_info,
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_get_mounted_disk_from_lun.assert_called_once_with(
mock.sentinel.fake_iqn,
mock.sentinel.fake_lun,
wait_for_device=True)
if ebs_root:
if disk_bus == constants.CTRL_TYPE_IDE:
get_ide_path.assert_called_once_with(
mock.sentinel.instance_name, 0)
attach_vol.assert_called_once_with(mock.sentinel.instance_name,
@ -362,11 +353,11 @@ class ISCSIVolumeDriverTestCase(test_base.HyperVBaseTestCase):
fake_mounted_disk_path,
serial=mock.sentinel.serial)
def test_attach_volume_ebs(self):
self._check_attach_volume(ebs_root=True)
def test_attach_volume_ide(self):
self._check_attach_volume(disk_bus=constants.CTRL_TYPE_IDE)
def test_attach_volume(self):
self._check_attach_volume(ebs_root=False)
def test_attach_volume_scsi(self):
self._check_attach_volume(disk_bus=constants.CTRL_TYPE_SCSI)
@mock.patch.object(volumeops.ISCSIVolumeDriver,
'_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, '_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
self._volume_driver.attach_volume(
self._FAKE_CONNECTION_INFO,
mock.sentinel.instance_name,
ebs_root)
disk_bus)
if ebs_root:
if disk_bus == constants.CTRL_TYPE_IDE:
get_vm_ide_controller = (
self._volume_driver._vmutils.get_vm_ide_controller)
get_vm_ide_controller.assert_called_once_with(
@ -527,10 +518,10 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
ctrller_path, slot)
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):
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, '_get_disk_path')

View File

@ -58,8 +58,6 @@ class BlockDeviceInfoManager(object):
# reserve one slot for the config drive on the second
# controller in case of generation 1 virtual machines
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
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_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,
block_device_info, slot_map):
# either booting from volume, or booting from image/iso

View File

@ -56,6 +56,8 @@ DISK_FORMAT_MAP = {
DVD_FORMAT.lower(): DVD
}
BDI_DEVICE_TYPE_TO_DRIVE_TYPE = {'disk': DISK}
DISK_FORMAT_VHD = "VHD"
DISK_FORMAT_VHDX = "VHDX"

View File

@ -128,6 +128,10 @@ class HyperVDriver(driver.ComputeDriver):
'Windows has been removed in Mitaka.'))
raise exception.HypervisorTooOld(version='6.2')
@property
def need_legacy_block_device_info(self):
return False
def init_host(self, host):
self._serialconsoleops.start_console_handlers()
event_handler = eventhandler.InstanceEventHandler(

View File

@ -23,6 +23,7 @@ from oslo_utils import excutils
import nova.conf
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 pathutils
from nova.virt.hyperv import serialconsoleops
@ -42,6 +43,7 @@ class LiveMigrationOps(object):
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
self._imagecache = imagecache.ImageCache()
self._vmutils = utilsfactory.get_vmutils()
self._block_dev_man = block_device_manager.BlockDeviceInfoManager()
def live_migration(self, context, instance_ref, dest, post_method,
recover_method, block_migration=False,
@ -75,7 +77,7 @@ class LiveMigrationOps(object):
self._livemigrutils.check_live_migration_config()
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)
if not boot_from_volume and instance.image_ref:
self._imagecache.get_cached_image(context, instance)

View File

@ -24,9 +24,11 @@ from oslo_utils import excutils
from oslo_utils import units
from nova import exception
from nova.i18n import _, _LE
from nova.i18n import _, _LW, _LE
from nova import objects
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 pathutils
from nova.virt.hyperv import vmops
@ -44,6 +46,7 @@ class MigrationOps(object):
self._volumeops = volumeops.VolumeOps()
self._vmops = vmops.VMOps()
self._imagecache = imagecache.ImageCache()
self._block_dev_man = block_device_manager.BlockDeviceInfoManager()
def _migrate_disk_files(self, instance_name, disk_files, dest):
# TODO(mikal): it would be nice if this method took a full instance,
@ -167,18 +170,25 @@ class MigrationOps(object):
instance_name = 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)
vm_gen = self._vmops.get_image_vm_generation(
instance.uuid, root_vhd_path, image_meta)
self._vmops.create_instance(instance, network_info, block_device_info,
root_vhd_path, eph_vhd_path, vm_gen)
vm_gen = self._vmops.get_image_vm_generation(instance.uuid, image_meta)
self._block_dev_man.validate_and_update_bdi(instance, image_meta,
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)
@ -221,8 +231,8 @@ class MigrationOps(object):
reason=_("Cannot resize the root disk to a smaller size. "
"Current size: %(curr_root_gb)s GB. Requested "
"size: %(new_root_gb)s GB.") % {
'curr_root_gb': curr_size,
'new_root_gb': new_size})
'curr_root_gb': curr_size / units.Gi,
'new_root_gb': new_size / units.Gi})
elif new_size > curr_size:
self._resize_vhd(vhd_path, new_size)
@ -260,13 +270,18 @@ class MigrationOps(object):
LOG.debug("finish_migration called", instance=instance)
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):
root_vhd_path = None
else:
self._block_dev_man.validate_and_update_bdi(instance, image_meta,
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:
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)
src_base_disk_path = root_vhd_info.get("ParentPath")
@ -278,22 +293,57 @@ class MigrationOps(object):
new_size = instance.root_gb * units.Gi
self._check_resize_vhd(root_vhd_path, root_vhd_info, new_size)
eph_vhd_path = self._pathutils.lookup_ephemeral_vhd_path(instance_name)
if 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)
ephemerals = block_device_info['ephemerals']
self._check_ephemeral_disks(instance, ephemerals, resize_instance)
vm_gen = self._vmops.get_image_vm_generation(
instance.uuid, root_vhd_path, image_meta)
self._vmops.create_instance(instance, network_info, block_device_info,
root_vhd_path, eph_vhd_path, vm_gen)
self._vmops.create_instance(instance, network_info, root_device,
block_device_info, vm_gen)
self._check_and_attach_config_drive(instance, vm_gen)
if power_on:
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

View File

@ -106,9 +106,10 @@ class PathUtils(pathutils.PathUtils):
break
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,
self.get_ephemeral_vhd_path)
self.get_ephemeral_vhd_path,
eph_name)
def get_root_vhd_path(self, instance_name, format_ext, rescue=False):
instance_path = self.get_instance_dir(instance_name)
@ -127,9 +128,9 @@ class PathUtils(pathutils.PathUtils):
return os.path.join(instance_path,
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)
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):
return self._get_instances_sub_dir('_base')

View File

@ -42,6 +42,7 @@ from nova.i18n import _, _LI, _LE, _LW
from nova import utils
from nova.virt import configdrive
from nova.virt import hardware
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 pathutils
@ -107,6 +108,8 @@ class VMOps(object):
self._volumeops = volumeops.VolumeOps()
self._imagecache = imagecache.ImageCache()
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
self._block_dev_man = (
block_device_manager.BlockDeviceInfoManager())
self._vif_driver = None
self._load_vif_driver_class()
@ -157,6 +160,13 @@ class VMOps(object):
num_cpu=info['NumberOfProcessors'],
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):
is_rescue_vhd = rescue_image_id is not None
@ -223,15 +233,17 @@ class VMOps(object):
return True
return False
def create_ephemeral_vhd(self, instance):
eph_vhd_size = instance.get('ephemeral_gb', 0) * units.Gi
if eph_vhd_size:
vhd_format = self._vhdutils.get_best_supported_vhd_format()
def _create_ephemerals(self, instance, ephemerals):
for index, eph in enumerate(ephemerals):
eph['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(
instance.name, vhd_format)
self._vhdutils.create_dynamic_vhd(eph_vhd_path, eph_vhd_size)
return eph_vhd_path
def create_ephemeral_disk(self, instance_name, eph_info):
self._vhdutils.create_dynamic_vhd(eph_info['path'],
eph_info['size'] * units.Gi)
@check_admin_permissions
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.
self._delete_disk_files(instance_name)
if self._volumeops.ebs_root_in_block_devices(block_device_info):
root_vhd_path = None
else:
root_vhd_path = self._create_root_vhd(context, instance)
vm_gen = self.get_image_vm_generation(instance.uuid, image_meta)
eph_vhd_path = self.create_ephemeral_vhd(instance)
vm_gen = self.get_image_vm_generation(
instance.uuid, root_vhd_path, image_meta)
self._block_dev_man.validate_and_update_bdi(
instance, image_meta, vm_gen, block_device_info)
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:
self.create_instance(instance, network_info, block_device_info,
root_vhd_path, eph_vhd_path,
vm_gen)
self.create_instance(instance, network_info, root_device,
block_device_info, vm_gen)
if configdrive.required_by(instance):
configdrive_path = self._create_config_drive(instance,
@ -273,8 +283,8 @@ class VMOps(object):
with excutils.save_and_reraise_exception():
self.destroy(instance)
def create_instance(self, instance, network_info, block_device_info,
root_vhd_path, eph_vhd_path, vm_gen):
def create_instance(self, instance, network_info, root_device,
block_device_info, vm_gen):
instance_name = 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._vmutils.create_scsi_controller(instance_name)
controller_type = VM_GENERATIONS_CONTROLLER_TYPES[vm_gen]
ctrl_disk_addr = 0
if root_vhd_path:
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)
self._attach_root_device(instance_name, root_device)
self._attach_ephemerals(instance_name, block_device_info['ephemerals'])
self._volumeops.attach_volumes(
block_device_info['block_device_mapping'], instance_name)
# 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
@ -368,6 +364,29 @@ class VMOps(object):
remotefx_max_resolution,
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,
controller_type, drive_type=constants.DISK):
if controller_type == constants.CTRL_TYPE_SCSI:
@ -376,18 +395,19 @@ class VMOps(object):
self._vmutils.attach_ide_drive(instance_name, path, drive_addr,
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()
image_prop_vm = image_meta.properties.get(
'hw_machine_type', default_vm_gen)
image_prop_vm = image_meta.properties.get('hw_machine_type',
default_vm_gen)
if image_prop_vm not in self._hostutils.get_supported_vm_types():
reason = _LE('Requested VM Generation %s is not supported on '
'this OS.') % image_prop_vm
raise exception.InstanceUnacceptable(instance_id=instance_id,
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
self._vhdutils.get_vhd_format(
root_vhd_path) == constants.DISK_FORMAT_VHD):
@ -396,8 +416,6 @@ class VMOps(object):
raise exception.InstanceUnacceptable(instance_id=instance_id,
reason=reason)
return vm_gen
def _create_config_drive(self, instance, injected_files, admin_password,
network_info, rescue=False):
if CONF.config_drive_format != 'iso9660':
@ -748,7 +766,6 @@ class VMOps(object):
context, instance, rescue_image_id=rescue_image_id)
rescue_vm_gen = self.get_image_vm_generation(instance.uuid,
rescue_vhd_path,
image_meta)
vm_gen = self._vmutils.get_vm_generation(instance.name)
if rescue_vm_gen != vm_gen:

View File

@ -28,12 +28,12 @@ from oslo_log import log as logging
from oslo_utils import excutils
from six.moves import range
from nova import block_device
import nova.conf
from nova import exception
from nova.i18n import _, _LE, _LW
from nova import utils
from nova.virt import driver
from nova.virt.hyperv import constants
LOG = logging.getLogger(__name__)
@ -59,14 +59,8 @@ class VolumeOps(object):
raise exception.VolumeDriverNotFound(driver_type=driver_type)
return self.volume_drivers[driver_type]
def attach_volumes(self, block_device_info, instance_name, ebs_root):
mapping = driver.block_device_info_get_mapping(block_device_info)
if ebs_root:
self.attach_volume(mapping[0]['connection_info'],
instance_name, True)
mapping = mapping[1:]
for vol in mapping:
def attach_volumes(self, volumes, instance_name):
for vol in volumes:
self.attach_volume(vol['connection_info'], instance_name)
def disconnect_volumes(self, block_device_info):
@ -77,24 +71,18 @@ class VolumeOps(object):
volume_driver = self._get_volume_driver(driver_type)
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(
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):
volume_driver = self._get_volume_driver(
connection_info=connection_info)
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):
# Mapping containing the current disk paths for each volume.
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,
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
ebs_root is True
"""
@ -246,7 +235,7 @@ class ISCSIVolumeDriver(object):
mounted_disk_path = self.get_mounted_disk_path_from_volume(
connection_info)
if ebs_root:
if disk_bus == constants.CTRL_TYPE_IDE:
# Find the IDE controller for the vm.
ctrller_path = self._vmutils.get_vm_ide_controller(
instance_name, 0)
@ -359,13 +348,14 @@ class SMBFSVolumeDriver(object):
return self._get_disk_path(connection_info)
@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)
disk_path = self._get_disk_path(connection_info)
try:
if ebs_root:
if disk_bus == constants.CTRL_TYPE_IDE:
ctrller_path = self._vmutils.get_vm_ide_controller(
instance_name, 0)
slot = 0