hyper-v: device tagging
Sets instance's devices_metadata during spawn. Partially-Implements: blueprint virt-device-role-tagging Change-Id: I49bb1903c9b6461f415438750a08eb7c1732c67b
This commit is contained in:
parent
20dc4942bb
commit
d47b5f993e
|
@ -28,6 +28,66 @@ class BlockDeviceManagerTestCase(test_base.HyperVBaseTestCase):
|
|||
super(BlockDeviceManagerTestCase, self).setUp()
|
||||
self._bdman = block_device_manager.BlockDeviceInfoManager()
|
||||
|
||||
def test_get_device_bus_scsi(self):
|
||||
bdm = {'disk_bus': constants.CTRL_TYPE_SCSI,
|
||||
'drive_addr': 0, 'ctrl_disk_addr': 2}
|
||||
|
||||
bus = self._bdman._get_device_bus(bdm)
|
||||
self.assertEqual('0:0:0:2', bus.address)
|
||||
|
||||
def test_get_device_bus_ide(self):
|
||||
bdm = {'disk_bus': constants.CTRL_TYPE_IDE,
|
||||
'drive_addr': 0, 'ctrl_disk_addr': 1}
|
||||
|
||||
bus = self._bdman._get_device_bus(bdm)
|
||||
self.assertEqual('0:1', bus.address)
|
||||
|
||||
@staticmethod
|
||||
def _bdm_mock(**kwargs):
|
||||
bdm = mock.MagicMock(**kwargs)
|
||||
bdm.__contains__.side_effect = (
|
||||
lambda attr: getattr(bdm, attr, None) is not None)
|
||||
return bdm
|
||||
|
||||
@mock.patch.object(block_device_manager.objects, 'DiskMetadata')
|
||||
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
|
||||
'_get_device_bus')
|
||||
@mock.patch.object(block_device_manager.objects.BlockDeviceMappingList,
|
||||
'get_by_instance_uuid')
|
||||
def test_get_bdm_metadata(self, mock_get_by_inst_uuid, mock_get_device_bus,
|
||||
mock_DiskMetadata):
|
||||
mock_instance = mock.MagicMock()
|
||||
root_disk = {'mount_device': mock.sentinel.dev0}
|
||||
ephemeral = {'device_name': mock.sentinel.dev1}
|
||||
block_device_info = {
|
||||
'root_disk': root_disk,
|
||||
'block_device_mapping': [
|
||||
{'mount_device': mock.sentinel.dev2},
|
||||
{'mount_device': mock.sentinel.dev3},
|
||||
],
|
||||
'ephemerals': [ephemeral],
|
||||
}
|
||||
|
||||
bdm = self._bdm_mock(device_name=mock.sentinel.dev0, tag='taggy')
|
||||
eph = self._bdm_mock(device_name=mock.sentinel.dev1, tag='ephy')
|
||||
mock_get_by_inst_uuid.return_value = [
|
||||
bdm, eph, self._bdm_mock(device_name=mock.sentinel.dev2, tag=None),
|
||||
]
|
||||
|
||||
bdm_metadata = self._bdman.get_bdm_metadata(mock.sentinel.context,
|
||||
mock_instance,
|
||||
block_device_info)
|
||||
|
||||
mock_get_by_inst_uuid.assert_called_once_with(mock.sentinel.context,
|
||||
mock_instance.uuid)
|
||||
mock_get_device_bus.assert_has_calls(
|
||||
[mock.call(root_disk), mock.call(ephemeral)], any_order=True)
|
||||
mock_DiskMetadata.assert_has_calls(
|
||||
[mock.call(bus=mock_get_device_bus.return_value, tags=[bdm.tag]),
|
||||
mock.call(bus=mock_get_device_bus.return_value, tags=[eph.tag])],
|
||||
any_order=True)
|
||||
self.assertEqual([mock_DiskMetadata.return_value] * 2, bdm_metadata)
|
||||
|
||||
@mock.patch('nova.virt.configdrive.required_by')
|
||||
def test_init_controller_slot_counter_gen1_no_configdrive(
|
||||
self, mock_cfg_drive_req):
|
||||
|
|
|
@ -341,11 +341,60 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||
mock_create_dynamic_vhd.assert_called_once_with('fake_eph_path',
|
||||
10 * units.Gi)
|
||||
|
||||
@mock.patch.object(vmops.objects, 'PCIDeviceBus')
|
||||
@mock.patch.object(vmops.objects, 'NetworkInterfaceMetadata')
|
||||
@mock.patch.object(vmops.objects.VirtualInterfaceList,
|
||||
'get_by_instance_uuid')
|
||||
def test_get_vif_metadata(self, mock_get_by_inst_uuid,
|
||||
mock_NetworkInterfaceMetadata, mock_PCIDevBus):
|
||||
mock_vif = mock.MagicMock(tag='taggy')
|
||||
mock_vif.__contains__.side_effect = (
|
||||
lambda attr: getattr(mock_vif, attr, None) is not None)
|
||||
mock_get_by_inst_uuid.return_value = [mock_vif,
|
||||
mock.MagicMock(tag=None)]
|
||||
|
||||
vif_metadata = self._vmops._get_vif_metadata(self.context,
|
||||
mock.sentinel.instance_id)
|
||||
|
||||
mock_get_by_inst_uuid.assert_called_once_with(
|
||||
self.context, mock.sentinel.instance_id)
|
||||
mock_NetworkInterfaceMetadata.assert_called_once_with(
|
||||
mac=mock_vif.address,
|
||||
bus=mock_PCIDevBus.return_value,
|
||||
tags=[mock_vif.tag])
|
||||
self.assertEqual([mock_NetworkInterfaceMetadata.return_value],
|
||||
vif_metadata)
|
||||
|
||||
@mock.patch.object(vmops.objects, 'InstanceDeviceMetadata')
|
||||
@mock.patch.object(vmops.VMOps, '_get_vif_metadata')
|
||||
def test_save_device_metadata(self, mock_get_vif_metadata,
|
||||
mock_InstanceDeviceMetadata):
|
||||
mock_instance = mock.MagicMock()
|
||||
mock_get_vif_metadata.return_value = [mock.sentinel.vif_metadata]
|
||||
self._vmops._block_dev_man.get_bdm_metadata.return_value = [
|
||||
mock.sentinel.bdm_metadata]
|
||||
|
||||
self._vmops._save_device_metadata(self.context, mock_instance,
|
||||
mock.sentinel.block_device_info)
|
||||
|
||||
mock_get_vif_metadata.assert_called_once_with(self.context,
|
||||
mock_instance.uuid)
|
||||
self._vmops._block_dev_man.get_bdm_metadata.assert_called_once_with(
|
||||
self.context, mock_instance, mock.sentinel.block_device_info)
|
||||
|
||||
expected_metadata = [mock.sentinel.vif_metadata,
|
||||
mock.sentinel.bdm_metadata]
|
||||
mock_InstanceDeviceMetadata.assert_called_once_with(
|
||||
devices=expected_metadata)
|
||||
self.assertEqual(mock_InstanceDeviceMetadata.return_value,
|
||||
mock_instance.device_metadata)
|
||||
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.destroy')
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps.power_on')
|
||||
@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')
|
||||
@mock.patch('nova.virt.hyperv.vmops.VMOps._save_device_metadata')
|
||||
@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_ephemerals')
|
||||
|
@ -353,7 +402,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||
@mock.patch('nova.virt.hyperv.vmops.VMOps._delete_disk_files')
|
||||
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_instance, mock_save_device_metadata,
|
||||
mock_configdrive_required,
|
||||
mock_create_config_drive, mock_attach_config_drive,
|
||||
mock_power_on, mock_destroy, exists,
|
||||
configdrive_required, fail):
|
||||
|
@ -401,6 +451,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||
mock_create_instance.assert_called_once_with(
|
||||
mock_instance, mock.sentinel.INFO, root_device_info,
|
||||
block_device_info, fake_vm_gen)
|
||||
mock_save_device_metadata.assert_called_once_with(
|
||||
self.context, mock_instance, block_device_info)
|
||||
mock_configdrive_required.assert_called_once_with(mock_instance)
|
||||
if configdrive_required:
|
||||
mock_create_config_drive.assert_called_once_with(
|
||||
|
|
|
@ -18,11 +18,14 @@ Handling of block device information and mapping
|
|||
Module contains helper methods for dealing with block device information
|
||||
"""
|
||||
|
||||
import itertools
|
||||
|
||||
from os_win import constants as os_win_const
|
||||
|
||||
from nova import block_device
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova import objects
|
||||
from nova.virt import configdrive
|
||||
from nova.virt import driver
|
||||
from nova.virt.hyperv import constants
|
||||
|
@ -45,6 +48,47 @@ class BlockDeviceInfoManager(object):
|
|||
def __init__(self):
|
||||
self._volops = volumeops.VolumeOps()
|
||||
|
||||
@staticmethod
|
||||
def _get_device_bus(bdm):
|
||||
"""Determines the device bus and it's hypervisor assigned address.
|
||||
"""
|
||||
if bdm['disk_bus'] == constants.CTRL_TYPE_SCSI:
|
||||
address = ':'.join(['0', '0', str(bdm['drive_addr']),
|
||||
str(bdm['ctrl_disk_addr'])])
|
||||
return objects.SCSIDeviceBus(address=address)
|
||||
elif bdm['disk_bus'] == constants.CTRL_TYPE_IDE:
|
||||
address = ':'.join([str(bdm['drive_addr']),
|
||||
str(bdm['ctrl_disk_addr'])])
|
||||
return objects.IDEDeviceBus(address=address)
|
||||
|
||||
def get_bdm_metadata(self, context, instance, block_device_info):
|
||||
"""Builds a metadata object for instance devices, that maps the user
|
||||
provided tag to the hypervisor assigned device address.
|
||||
"""
|
||||
# block_device_info does not contain tags information.
|
||||
bdm_obj_list = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
||||
context, instance.uuid)
|
||||
|
||||
# create a map between BDM object and its device name.
|
||||
bdm_obj_map = {bdm.device_name: bdm for bdm in bdm_obj_list}
|
||||
|
||||
bdm_metadata = []
|
||||
for bdm in itertools.chain(block_device_info['block_device_mapping'],
|
||||
block_device_info['ephemerals'],
|
||||
[block_device_info['root_disk']]):
|
||||
# NOTE(claudiub): ephemerals have device_name instead of
|
||||
# mount_device.
|
||||
device_name = bdm.get('mount_device') or bdm.get('device_name')
|
||||
bdm_obj = bdm_obj_map.get(device_name)
|
||||
|
||||
if bdm_obj and 'tag' in bdm_obj and bdm_obj.tag:
|
||||
bus = self._get_device_bus(bdm)
|
||||
device = objects.DiskMetadata(bus=bus,
|
||||
tags=[bdm_obj.tag])
|
||||
bdm_metadata.append(device)
|
||||
|
||||
return bdm_metadata
|
||||
|
||||
def _initialize_controller_slot_counter(self, instance, vm_gen):
|
||||
# we have 2 IDE controllers, for a total of 4 slots
|
||||
free_slots_by_device_type = {
|
||||
|
|
|
@ -97,7 +97,8 @@ class HyperVDriver(driver.ComputeDriver):
|
|||
"has_imagecache": True,
|
||||
"supports_recreate": False,
|
||||
"supports_migrate_to_same_host": True,
|
||||
"supports_attach_interface": True
|
||||
"supports_attach_interface": True,
|
||||
"supports_device_tagging": True,
|
||||
}
|
||||
|
||||
def __init__(self, virtapi):
|
||||
|
|
|
@ -39,6 +39,7 @@ from nova.compute import vm_states
|
|||
import nova.conf
|
||||
from nova import exception
|
||||
from nova.i18n import _, _LI, _LE, _LW
|
||||
from nova import objects
|
||||
from nova import utils
|
||||
from nova.virt import configdrive
|
||||
from nova.virt import hardware
|
||||
|
@ -245,6 +246,36 @@ class VMOps(object):
|
|||
self._vhdutils.create_dynamic_vhd(eph_info['path'],
|
||||
eph_info['size'] * units.Gi)
|
||||
|
||||
@staticmethod
|
||||
def _get_vif_metadata(context, instance_id):
|
||||
vifs = objects.VirtualInterfaceList.get_by_instance_uuid(context,
|
||||
instance_id)
|
||||
vif_metadata = []
|
||||
for vif in vifs:
|
||||
if 'tag' in vif and vif.tag:
|
||||
device = objects.NetworkInterfaceMetadata(
|
||||
mac=vif.address,
|
||||
bus=objects.PCIDeviceBus(),
|
||||
tags=[vif.tag])
|
||||
vif_metadata.append(device)
|
||||
|
||||
return vif_metadata
|
||||
|
||||
def _save_device_metadata(self, context, instance, block_device_info):
|
||||
"""Builds a metadata object for instance devices, that maps the user
|
||||
provided tag to the hypervisor assigned device address.
|
||||
"""
|
||||
metadata = []
|
||||
|
||||
metadata.extend(self._get_vif_metadata(context, instance.uuid))
|
||||
if block_device_info:
|
||||
metadata.extend(self._block_dev_man.get_bdm_metadata(
|
||||
context, instance, block_device_info))
|
||||
|
||||
if metadata:
|
||||
instance.device_metadata = objects.InstanceDeviceMetadata(
|
||||
devices=metadata)
|
||||
|
||||
@check_admin_permissions
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, network_info, block_device_info=None):
|
||||
|
@ -269,6 +300,7 @@ class VMOps(object):
|
|||
try:
|
||||
self.create_instance(instance, network_info, root_device,
|
||||
block_device_info, vm_gen)
|
||||
self._save_device_metadata(context, instance, block_device_info)
|
||||
|
||||
if configdrive.required_by(instance):
|
||||
configdrive_path = self._create_config_drive(instance,
|
||||
|
|
Loading…
Reference in New Issue