summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-07-13 22:59:16 +0000
committerGerrit Code Review <review@openstack.org>2016-07-13 22:59:17 +0000
commit78c6c3b58643d0d5a69ba8acfb459fd0423688f1 (patch)
treeba6692ac2572d71d96cd49b2aefe729fc4e36198
parentd99aab56364939281a93685c64c50c1c4abba5b0 (diff)
parentd47b5f993ece3aa52b486a8b6add2d96e9381599 (diff)
downloadnova-78c6c3b58643d0d5a69ba8acfb459fd0423688f1.tar.gz
Merge "hyper-v: device tagging"
-rw-r--r--nova/tests/unit/virt/hyperv/test_block_device_manager.py60
-rw-r--r--nova/tests/unit/virt/hyperv/test_vmops.py54
-rw-r--r--nova/virt/hyperv/block_device_manager.py44
-rw-r--r--nova/virt/hyperv/driver.py3
-rw-r--r--nova/virt/hyperv/vmops.py32
5 files changed, 191 insertions, 2 deletions
diff --git a/nova/tests/unit/virt/hyperv/test_block_device_manager.py b/nova/tests/unit/virt/hyperv/test_block_device_manager.py
index 810a0b5891..8ef7ae2c96 100644
--- a/nova/tests/unit/virt/hyperv/test_block_device_manager.py
+++ b/nova/tests/unit/virt/hyperv/test_block_device_manager.py
@@ -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):
diff --git a/nova/tests/unit/virt/hyperv/test_vmops.py b/nova/tests/unit/virt/hyperv/test_vmops.py
index 4d016487f5..0df6de62a7 100644
--- a/nova/tests/unit/virt/hyperv/test_vmops.py
+++ b/nova/tests/unit/virt/hyperv/test_vmops.py
@@ -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(
diff --git a/nova/virt/hyperv/block_device_manager.py b/nova/virt/hyperv/block_device_manager.py
index c5f88eb4d4..c744795d72 100644
--- a/nova/virt/hyperv/block_device_manager.py
+++ b/nova/virt/hyperv/block_device_manager.py
@@ -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 = {
diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py
index 3c7bbb341a..dc5a7d3719 100644
--- a/nova/virt/hyperv/driver.py
+++ b/nova/virt/hyperv/driver.py
@@ -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):
diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py
index f68d758d26..49031b3b79 100644
--- a/nova/virt/hyperv/vmops.py
+++ b/nova/virt/hyperv/vmops.py
@@ -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,