Add support for setting boot order in Hyper-V
This patch adds support for setting boot order for Hyper-V instances. Unit tests were updated accordingly. Partially implements: blueprint hyper-v-set-boot-order Change-Id: I8516ba5f27146ea773eb59a82e0f2d246c742446
This commit is contained in:
parent
65f8580a3a
commit
d68c04299a
@ -360,3 +360,72 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
|
||||
self.assertRaises(exception.InvalidDiskInfo,
|
||||
self._bdman._check_and_update_bdm,
|
||||
mock.sentinel.FAKE_SLOT_MAP, constants.VM_GEN_1, bdm)
|
||||
|
||||
def test_sort_by_boot_order(self):
|
||||
original = [{'boot_index': 2}, {'boot_index': None}, {'boot_index': 1}]
|
||||
expected = [original[2], original[0], original[1]]
|
||||
|
||||
self._bdman._sort_by_boot_order(original)
|
||||
self.assertEqual(expected, original)
|
||||
|
||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||
'_get_boot_order_gen1')
|
||||
def test_get_boot_order_gen1_vm(self, mock_get_boot_order):
|
||||
self._bdman.get_boot_order(constants.VM_GEN_1,
|
||||
mock.sentinel.BLOCK_DEV_INFO)
|
||||
mock_get_boot_order.assert_called_once_with(
|
||||
mock.sentinel.BLOCK_DEV_INFO)
|
||||
|
||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||
'_get_boot_order_gen2')
|
||||
def test_get_boot_order_gen2_vm(self, mock_get_boot_order):
|
||||
self._bdman.get_boot_order(constants.VM_GEN_2,
|
||||
mock.sentinel.BLOCK_DEV_INFO)
|
||||
mock_get_boot_order.assert_called_once_with(
|
||||
mock.sentinel.BLOCK_DEV_INFO)
|
||||
|
||||
def test_get_boot_order_gen1_iso(self):
|
||||
fake_bdi = {'root_disk': {'type': 'iso'}}
|
||||
expected = [os_win_const.BOOT_DEVICE_CDROM,
|
||||
os_win_const.BOOT_DEVICE_HARDDISK,
|
||||
os_win_const.BOOT_DEVICE_NETWORK,
|
||||
os_win_const.BOOT_DEVICE_FLOPPY]
|
||||
|
||||
res = self._bdman._get_boot_order_gen1(fake_bdi)
|
||||
self.assertEqual(expected, res)
|
||||
|
||||
def test_get_boot_order_gen1_vhd(self):
|
||||
fake_bdi = {'root_disk': {'type': 'vhd'}}
|
||||
expected = [os_win_const.BOOT_DEVICE_HARDDISK,
|
||||
os_win_const.BOOT_DEVICE_CDROM,
|
||||
os_win_const.BOOT_DEVICE_NETWORK,
|
||||
os_win_const.BOOT_DEVICE_FLOPPY]
|
||||
|
||||
res = self._bdman._get_boot_order_gen1(fake_bdi)
|
||||
self.assertEqual(expected, res)
|
||||
|
||||
def test_get_boot_order_gen2(self):
|
||||
fake_root_disk = {'boot_index': 0,
|
||||
'path': mock.sentinel.FAKE_ROOT_PATH}
|
||||
fake_eph1 = {'boot_index': 2,
|
||||
'path': mock.sentinel.FAKE_EPH_PATH1}
|
||||
fake_eph2 = {'boot_index': 3,
|
||||
'path': mock.sentinel.FAKE_EPH_PATH2}
|
||||
fake_bdm = {'boot_index': 1,
|
||||
'connection_info': mock.sentinel.FAKE_CONN_INFO}
|
||||
fake_bdi = {'root_disk': fake_root_disk,
|
||||
'ephemerals': [fake_eph1,
|
||||
fake_eph2],
|
||||
'block_device_mapping': [fake_bdm]}
|
||||
|
||||
self._bdman._volops.get_mounted_disk_path_from_volume = (
|
||||
mock.MagicMock(return_value=fake_bdm['connection_info']))
|
||||
|
||||
expected_res = [mock.sentinel.FAKE_ROOT_PATH,
|
||||
mock.sentinel.FAKE_CONN_INFO,
|
||||
mock.sentinel.FAKE_EPH_PATH1,
|
||||
mock.sentinel.FAKE_EPH_PATH2]
|
||||
|
||||
res = self._bdman._get_boot_order_gen2(fake_bdi)
|
||||
|
||||
self.assertEqual(expected_res, res)
|
||||
|
@ -276,6 +276,9 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
mock_image.return_value)
|
||||
mock_check_attach_config_drive.assert_called_once_with(
|
||||
mock_instance, get_image_vm_gen.return_value)
|
||||
self._migrationops._vmops.set_boot_order.assert_called_once_with(
|
||||
mock_instance.name, get_image_vm_gen.return_value,
|
||||
block_device_info)
|
||||
self._migrationops._vmops.power_on.assert_called_once_with(
|
||||
mock_instance)
|
||||
|
||||
@ -438,6 +441,9 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
mock.sentinel.image_meta)
|
||||
mock_check_attach_config_drive.assert_called_once_with(
|
||||
mock_instance, get_image_vm_gen.return_value)
|
||||
self._migrationops._vmops.set_boot_order.assert_called_once_with(
|
||||
mock_instance.name, get_image_vm_gen.return_value,
|
||||
block_device_info)
|
||||
self._migrationops._vmops.power_on.assert_called_once_with(
|
||||
mock_instance)
|
||||
|
||||
|
@ -379,8 +379,20 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
self.assertEqual(mock_InstanceDeviceMetadata.return_value,
|
||||
mock_instance.device_metadata)
|
||||
|
||||
def test_set_boot_order(self):
|
||||
self._vmops.set_boot_order(mock.sentinel.instance_name,
|
||||
mock.sentinel.vm_gen,
|
||||
mock.sentinel.bdi)
|
||||
|
||||
mock_get_boot_order = self._vmops._block_dev_man.get_boot_order
|
||||
mock_get_boot_order.assert_called_once_with(
|
||||
mock.sentinel.vm_gen, mock.sentinel.bdi)
|
||||
self._vmops._vmutils.set_boot_order.assert_called_once_with(
|
||||
mock.sentinel.instance_name, mock_get_boot_order.return_value)
|
||||
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.destroy')
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.power_on')
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.set_boot_order')
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.attach_config_drive')
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_config_drive')
|
||||
@mock.patch('nova.virt.configdrive.required_by')
|
||||
@ -395,6 +407,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
mock_create_instance, mock_save_device_metadata,
|
||||
mock_configdrive_required,
|
||||
mock_create_config_drive, mock_attach_config_drive,
|
||||
mock_set_boot_order,
|
||||
mock_power_on, mock_destroy, exists,
|
||||
configdrive_required, fail,
|
||||
fake_vm_gen=constants.VM_GEN_2):
|
||||
@ -452,6 +465,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
||||
mock.sentinel.INFO)
|
||||
mock_attach_config_drive.assert_called_once_with(
|
||||
mock_instance, fake_config_drive_path, fake_vm_gen)
|
||||
mock_set_boot_order.assert_called_once_with(
|
||||
mock_instance.name, fake_vm_gen, block_device_info)
|
||||
mock_power_on.assert_called_once_with(mock_instance)
|
||||
|
||||
def test_spawn(self):
|
||||
|
@ -223,3 +223,45 @@ class BlockDeviceInfoManager(object):
|
||||
|
||||
# make sure that boot_index is set.
|
||||
bdm['boot_index'] = bdm.get('boot_index')
|
||||
|
||||
def _sort_by_boot_order(self, bd_list):
|
||||
# we sort the block devices by boot_index leaving the ones that don't
|
||||
# have a specified boot_index at the end
|
||||
bd_list.sort(key=lambda x: (x['boot_index'] is None, x['boot_index']))
|
||||
|
||||
def get_boot_order(self, vm_gen, block_device_info):
|
||||
if vm_gen == constants.VM_GEN_1:
|
||||
return self._get_boot_order_gen1(block_device_info)
|
||||
else:
|
||||
return self._get_boot_order_gen2(block_device_info)
|
||||
|
||||
def _get_boot_order_gen1(self, block_device_info):
|
||||
if block_device_info['root_disk']['type'] == 'iso':
|
||||
return [os_win_const.BOOT_DEVICE_CDROM,
|
||||
os_win_const.BOOT_DEVICE_HARDDISK,
|
||||
os_win_const.BOOT_DEVICE_NETWORK,
|
||||
os_win_const.BOOT_DEVICE_FLOPPY]
|
||||
else:
|
||||
return [os_win_const.BOOT_DEVICE_HARDDISK,
|
||||
os_win_const.BOOT_DEVICE_CDROM,
|
||||
os_win_const.BOOT_DEVICE_NETWORK,
|
||||
os_win_const.BOOT_DEVICE_FLOPPY]
|
||||
|
||||
def _get_boot_order_gen2(self, block_device_info):
|
||||
devices = [block_device_info['root_disk']]
|
||||
devices += driver.block_device_info_get_ephemerals(
|
||||
block_device_info)
|
||||
devices += driver.block_device_info_get_mapping(block_device_info)
|
||||
|
||||
self._sort_by_boot_order(devices)
|
||||
|
||||
boot_order = []
|
||||
for dev in devices:
|
||||
if dev.get('connection_info'):
|
||||
dev_path = self._volops.get_mounted_disk_path_from_volume(
|
||||
dev['connection_info'])
|
||||
boot_order.append(dev_path)
|
||||
else:
|
||||
boot_order.append(dev['path'])
|
||||
|
||||
return boot_order
|
||||
|
@ -187,7 +187,7 @@ class MigrationOps(object):
|
||||
block_device_info, vm_gen, image_meta)
|
||||
|
||||
self._check_and_attach_config_drive(instance, vm_gen)
|
||||
|
||||
self._vmops.set_boot_order(instance_name, vm_gen, block_device_info)
|
||||
if power_on:
|
||||
self._vmops.power_on(instance)
|
||||
|
||||
@ -296,7 +296,7 @@ class MigrationOps(object):
|
||||
block_device_info, vm_gen, image_meta)
|
||||
|
||||
self._check_and_attach_config_drive(instance, vm_gen)
|
||||
|
||||
self._vmops.set_boot_order(instance_name, vm_gen, block_device_info)
|
||||
if power_on:
|
||||
self._vmops.power_on(instance)
|
||||
|
||||
|
@ -269,6 +269,15 @@ class VMOps(object):
|
||||
instance.device_metadata = objects.InstanceDeviceMetadata(
|
||||
devices=metadata)
|
||||
|
||||
def set_boot_order(self, instance_name, vm_gen, block_device_info):
|
||||
boot_order = self._block_dev_man.get_boot_order(
|
||||
vm_gen, block_device_info)
|
||||
LOG.debug("Setting boot order for instance: %(instance_name)s: "
|
||||
"%(boot_order)s", {'instance_name': instance_name,
|
||||
'boot_order': boot_order})
|
||||
|
||||
self._vmutils.set_boot_order(instance_name, boot_order)
|
||||
|
||||
@check_admin_permissions
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, network_info, block_device_info=None):
|
||||
@ -303,7 +312,7 @@ class VMOps(object):
|
||||
network_info)
|
||||
|
||||
self.attach_config_drive(instance, configdrive_path, vm_gen)
|
||||
|
||||
self.set_boot_order(instance.name, vm_gen, block_device_info)
|
||||
self.power_on(instance)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added boot order support in the Hyper-V driver.
|
||||
The HyperVDriver can now set the requested boot order for instances that are
|
||||
Generation 2 VMs (the given image has the property "hw_machine_type=hyperv-gen2").
|
||||
For Generation 1 VMs, the spawned VM's boot order is changed only if the given
|
||||
image is an ISO, booting from ISO first.
|
Loading…
Reference in New Issue
Block a user