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:

committed by
Claudiu Belu

parent
65f8580a3a
commit
d68c04299a
@@ -360,3 +360,72 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self.assertRaises(exception.InvalidDiskInfo,
|
self.assertRaises(exception.InvalidDiskInfo,
|
||||||
self._bdman._check_and_update_bdm,
|
self._bdman._check_and_update_bdm,
|
||||||
mock.sentinel.FAKE_SLOT_MAP, constants.VM_GEN_1, 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_image.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.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(
|
self._migrationops._vmops.power_on.assert_called_once_with(
|
||||||
mock_instance)
|
mock_instance)
|
||||||
|
|
||||||
@@ -438,6 +441,9 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.sentinel.image_meta)
|
mock.sentinel.image_meta)
|
||||||
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.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(
|
self._migrationops._vmops.power_on.assert_called_once_with(
|
||||||
mock_instance)
|
mock_instance)
|
||||||
|
|
||||||
|
@@ -379,8 +379,20 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
self.assertEqual(mock_InstanceDeviceMetadata.return_value,
|
self.assertEqual(mock_InstanceDeviceMetadata.return_value,
|
||||||
mock_instance.device_metadata)
|
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.destroy')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.power_on')
|
@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.attach_config_drive')
|
||||||
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_config_drive')
|
@mock.patch('nova.virt.hyperv.vmops.VMOps._create_config_drive')
|
||||||
@mock.patch('nova.virt.configdrive.required_by')
|
@mock.patch('nova.virt.configdrive.required_by')
|
||||||
@@ -395,6 +407,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock_create_instance, mock_save_device_metadata,
|
mock_create_instance, mock_save_device_metadata,
|
||||||
mock_configdrive_required,
|
mock_configdrive_required,
|
||||||
mock_create_config_drive, mock_attach_config_drive,
|
mock_create_config_drive, mock_attach_config_drive,
|
||||||
|
mock_set_boot_order,
|
||||||
mock_power_on, mock_destroy, exists,
|
mock_power_on, mock_destroy, exists,
|
||||||
configdrive_required, fail,
|
configdrive_required, fail,
|
||||||
fake_vm_gen=constants.VM_GEN_2):
|
fake_vm_gen=constants.VM_GEN_2):
|
||||||
@@ -452,6 +465,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.sentinel.INFO)
|
mock.sentinel.INFO)
|
||||||
mock_attach_config_drive.assert_called_once_with(
|
mock_attach_config_drive.assert_called_once_with(
|
||||||
mock_instance, fake_config_drive_path, fake_vm_gen)
|
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)
|
mock_power_on.assert_called_once_with(mock_instance)
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
|
@@ -223,3 +223,45 @@ class BlockDeviceInfoManager(object):
|
|||||||
|
|
||||||
# make sure that boot_index is set.
|
# make sure that boot_index is set.
|
||||||
bdm['boot_index'] = bdm.get('boot_index')
|
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)
|
block_device_info, vm_gen, image_meta)
|
||||||
|
|
||||||
self._check_and_attach_config_drive(instance, vm_gen)
|
self._check_and_attach_config_drive(instance, vm_gen)
|
||||||
|
self._vmops.set_boot_order(instance_name, vm_gen, block_device_info)
|
||||||
if power_on:
|
if power_on:
|
||||||
self._vmops.power_on(instance)
|
self._vmops.power_on(instance)
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ class MigrationOps(object):
|
|||||||
block_device_info, vm_gen, image_meta)
|
block_device_info, vm_gen, image_meta)
|
||||||
|
|
||||||
self._check_and_attach_config_drive(instance, vm_gen)
|
self._check_and_attach_config_drive(instance, vm_gen)
|
||||||
|
self._vmops.set_boot_order(instance_name, vm_gen, block_device_info)
|
||||||
if power_on:
|
if power_on:
|
||||||
self._vmops.power_on(instance)
|
self._vmops.power_on(instance)
|
||||||
|
|
||||||
|
@@ -269,6 +269,15 @@ class VMOps(object):
|
|||||||
instance.device_metadata = objects.InstanceDeviceMetadata(
|
instance.device_metadata = objects.InstanceDeviceMetadata(
|
||||||
devices=metadata)
|
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
|
@check_admin_permissions
|
||||||
def spawn(self, context, instance, image_meta, injected_files,
|
def spawn(self, context, instance, image_meta, injected_files,
|
||||||
admin_password, network_info, block_device_info=None):
|
admin_password, network_info, block_device_info=None):
|
||||||
@@ -303,7 +312,7 @@ class VMOps(object):
|
|||||||
network_info)
|
network_info)
|
||||||
|
|
||||||
self.attach_config_drive(instance, configdrive_path, vm_gen)
|
self.attach_config_drive(instance, configdrive_path, vm_gen)
|
||||||
|
self.set_boot_order(instance.name, vm_gen, block_device_info)
|
||||||
self.power_on(instance)
|
self.power_on(instance)
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_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.
|
Reference in New Issue
Block a user