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:
Adelina Tuvenie 2015-11-20 03:27:24 -08:00 committed by Claudiu Belu
parent 65f8580a3a
commit d68c04299a
7 changed files with 152 additions and 3 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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():

View File

@ -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.