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:
Claudiu Belu 2016-06-21 01:50:03 +03:00
parent 20dc4942bb
commit d47b5f993e
5 changed files with 191 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

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