XenAPI: device tagging
XenAPI implementation of device tagging Partially-Implements: blueprint virt-device-role-tagging-xenapi Change-Id: I565617e05acf33e6254ea091b88d975270ffde05 Depends-On: I9fe520bc9b68d0bc7f879617f2cd27dd1029e4de
This commit is contained in:
parent
3a5d592e60
commit
0c0361e8bf
|
@ -403,6 +403,8 @@ class InstanceMetadata(object):
|
|||
bus = 'scsi'
|
||||
elif isinstance(device.bus, metadata_obj.IDEDeviceBus):
|
||||
bus = 'ide'
|
||||
elif isinstance(device.bus, metadata_obj.XenDeviceBus):
|
||||
bus = 'xen'
|
||||
else:
|
||||
LOG.debug('Metadata for device with unknown bus %s '
|
||||
'has not been included in the '
|
||||
|
|
|
@ -1032,6 +1032,14 @@ class IDEAddress(AddressBase):
|
|||
return AddressBase.coerce(IDEAddress, attr, value)
|
||||
|
||||
|
||||
class XenAddress(AddressBase):
|
||||
PATTERN = '(00[0-9]{2}00)|[1-9][0-9]+'
|
||||
|
||||
@staticmethod
|
||||
def coerce(obj, attr, value):
|
||||
return AddressBase.coerce(XenAddress, attr, value)
|
||||
|
||||
|
||||
class PCIAddressField(AutoTypedField):
|
||||
AUTO_TYPE = PCIAddress()
|
||||
|
||||
|
@ -1048,6 +1056,10 @@ class IDEAddressField(AutoTypedField):
|
|||
AUTO_TYPE = IDEAddress()
|
||||
|
||||
|
||||
class XenAddressField(AutoTypedField):
|
||||
AUTO_TYPE = XenAddress()
|
||||
|
||||
|
||||
class ArchitectureField(BaseEnumField):
|
||||
AUTO_TYPE = Architecture()
|
||||
|
||||
|
|
|
@ -61,6 +61,15 @@ class IDEDeviceBus(DeviceBus):
|
|||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class XenDeviceBus(DeviceBus):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'address': fields.XenAddressField(),
|
||||
}
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class DeviceMetadata(base.NovaObject):
|
||||
VERSION = '1.0'
|
||||
|
|
|
@ -707,6 +707,20 @@ class TestIDEAddress(TestField):
|
|||
self.from_primitive_values = self.coerce_good_values
|
||||
|
||||
|
||||
class TestXenAddress(TestField):
|
||||
def setUp(self):
|
||||
super(TestXenAddress, self).setUp()
|
||||
self.field = fields.Field(fields.XenAddressField())
|
||||
self.coerce_good_values = [('000100', '000100'),
|
||||
('768', '768')]
|
||||
self.coerce_bad_values = [
|
||||
'1',
|
||||
'00100',
|
||||
]
|
||||
self.to_primitive_values = self.coerce_good_values
|
||||
self.from_primitive_values = self.coerce_good_values
|
||||
|
||||
|
||||
class TestSecureBoot(TestField):
|
||||
def setUp(self):
|
||||
super(TestSecureBoot, self).setUp()
|
||||
|
|
|
@ -1179,6 +1179,7 @@ object_data = {
|
|||
'VirtualInterface': '1.3-efd3ca8ebcc5ce65fff5a25f31754c54',
|
||||
'VirtualInterfaceList': '1.0-9750e2074437b3077e46359102779fc6',
|
||||
'VolumeUsage': '1.0-6c8190c46ce1469bb3286a1f21c2e475',
|
||||
'XenDeviceBus': '1.0-272a4f899b24e31e42b2b9a7ed7e9194',
|
||||
'XenapiLiveMigrateData': '1.1-79e69f5ac9abfbcfcbaec18e8280bec6',
|
||||
}
|
||||
|
||||
|
|
|
@ -302,6 +302,8 @@ class SpawnTestCase(VMOpsTestBase):
|
|||
self.mox.StubOutWithMock(self.vmops, '_create_vm_record')
|
||||
self.mox.StubOutWithMock(self.vmops, '_destroy')
|
||||
self.mox.StubOutWithMock(self.vmops, '_attach_disks')
|
||||
self.mox.StubOutWithMock(self.vmops, '_save_device_metadata')
|
||||
self.mox.StubOutWithMock(self.vmops, '_prepare_disk_metadata')
|
||||
self.mox.StubOutWithMock(pci_manager, 'get_instance_pci_devs')
|
||||
self.mox.StubOutWithMock(vm_utils, 'set_other_config_pci')
|
||||
self.mox.StubOutWithMock(self.vmops, '_attach_orig_disks')
|
||||
|
@ -325,13 +327,22 @@ class SpawnTestCase(VMOpsTestBase):
|
|||
self.mox.StubOutWithMock(self.vmops, '_update_last_dom_id')
|
||||
self.mox.StubOutWithMock(self.vmops._session, 'call_xenapi')
|
||||
|
||||
@staticmethod
|
||||
def _new_instance(obj):
|
||||
class _Instance(dict):
|
||||
__getattr__ = dict.__getitem__
|
||||
__setattr__ = dict.__setitem__
|
||||
return _Instance(**obj)
|
||||
|
||||
def _test_spawn(self, name_label_param=None, block_device_info_param=None,
|
||||
rescue=False, include_root_vdi=True, throw_exception=None,
|
||||
attach_pci_dev=False, neutron_exception=False,
|
||||
network_info=None):
|
||||
self._stub_out_common()
|
||||
|
||||
instance = {"name": "dummy", "uuid": "fake_uuid"}
|
||||
instance = self._new_instance({"name": "dummy", "uuid": "fake_uuid",
|
||||
"device_metadata": None})
|
||||
|
||||
name_label = name_label_param
|
||||
if name_label is None:
|
||||
name_label = "dummy"
|
||||
|
@ -383,6 +394,7 @@ class SpawnTestCase(VMOpsTestBase):
|
|||
step += 1
|
||||
self.vmops._update_instance_progress(context, instance, step, steps)
|
||||
|
||||
self.vmops._save_device_metadata(context, instance, block_device_info)
|
||||
self.vmops._attach_disks(context, instance, image_meta, vm_ref,
|
||||
name_label, vdis, di_type, network_info, rescue,
|
||||
admin_password, injected_files)
|
||||
|
@ -508,6 +520,103 @@ class SpawnTestCase(VMOpsTestBase):
|
|||
'_neutron_failed_callback')
|
||||
self._test_spawn(network_info=network_info)
|
||||
|
||||
@staticmethod
|
||||
def _dev_mock(obj):
|
||||
dev = mock.MagicMock(**obj)
|
||||
dev.__contains__.side_effect = (
|
||||
lambda attr: getattr(dev, attr, None) is not None)
|
||||
return dev
|
||||
|
||||
@mock.patch.object(objects, 'XenDeviceBus')
|
||||
@mock.patch.object(objects, 'IDEDeviceBus')
|
||||
@mock.patch.object(objects, 'DiskMetadata')
|
||||
def test_prepare_disk_metadata(self, mock_DiskMetadata,
|
||||
mock_IDEDeviceBus, mock_XenDeviceBus):
|
||||
mock_IDEDeviceBus.side_effect = \
|
||||
lambda **kw: \
|
||||
self._dev_mock({"address": kw.get("address"), "bus": "ide"})
|
||||
mock_XenDeviceBus.side_effect = \
|
||||
lambda **kw: \
|
||||
self._dev_mock({"address": kw.get("address"), "bus": "xen"})
|
||||
mock_DiskMetadata.side_effect = \
|
||||
lambda **kw: self._dev_mock(dict(**kw))
|
||||
|
||||
bdm = self._dev_mock({"device_name": "/dev/xvda", "tag": "disk_a"})
|
||||
disk_metadata = self.vmops._prepare_disk_metadata(bdm)
|
||||
|
||||
self.assertEqual(disk_metadata[0].tags, ["disk_a"])
|
||||
self.assertEqual(disk_metadata[0].bus.bus, "ide")
|
||||
self.assertEqual(disk_metadata[0].bus.address, "0:0")
|
||||
self.assertEqual(disk_metadata[1].tags, ["disk_a"])
|
||||
self.assertEqual(disk_metadata[1].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[1].bus.address, "000000")
|
||||
self.assertEqual(disk_metadata[2].tags, ["disk_a"])
|
||||
self.assertEqual(disk_metadata[2].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[2].bus.address, "51712")
|
||||
self.assertEqual(disk_metadata[3].tags, ["disk_a"])
|
||||
self.assertEqual(disk_metadata[3].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[3].bus.address, "768")
|
||||
|
||||
bdm = self._dev_mock({"device_name": "/dev/xvdc", "tag": "disk_c"})
|
||||
disk_metadata = self.vmops._prepare_disk_metadata(bdm)
|
||||
|
||||
self.assertEqual(disk_metadata[0].tags, ["disk_c"])
|
||||
self.assertEqual(disk_metadata[0].bus.bus, "ide")
|
||||
self.assertEqual(disk_metadata[0].bus.address, "1:0")
|
||||
self.assertEqual(disk_metadata[1].tags, ["disk_c"])
|
||||
self.assertEqual(disk_metadata[1].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[1].bus.address, "000200")
|
||||
self.assertEqual(disk_metadata[2].tags, ["disk_c"])
|
||||
self.assertEqual(disk_metadata[2].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[2].bus.address, "51744")
|
||||
self.assertEqual(disk_metadata[3].tags, ["disk_c"])
|
||||
self.assertEqual(disk_metadata[3].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[3].bus.address, "5632")
|
||||
|
||||
bdm = self._dev_mock({"device_name": "/dev/xvde", "tag": "disk_e"})
|
||||
disk_metadata = self.vmops._prepare_disk_metadata(bdm)
|
||||
|
||||
self.assertEqual(disk_metadata[0].tags, ["disk_e"])
|
||||
self.assertEqual(disk_metadata[0].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[0].bus.address, "000400")
|
||||
self.assertEqual(disk_metadata[1].tags, ["disk_e"])
|
||||
self.assertEqual(disk_metadata[1].bus.bus, "xen")
|
||||
self.assertEqual(disk_metadata[1].bus.address, "51776")
|
||||
|
||||
@mock.patch.object(objects.VirtualInterfaceList, 'get_by_instance_uuid')
|
||||
@mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
|
||||
@mock.patch.object(objects, 'NetworkInterfaceMetadata')
|
||||
@mock.patch.object(objects, 'InstanceDeviceMetadata')
|
||||
@mock.patch.object(objects, 'PCIDeviceBus')
|
||||
@mock.patch.object(vmops.VMOps, '_prepare_disk_metadata')
|
||||
def test_save_device_metadata(self, mock_prepare_disk_metadata,
|
||||
mock_PCIDeviceBus, mock_InstanceDeviceMetadata,
|
||||
mock_NetworkInterfaceMetadata, mock_get_bdms, mock_get_vifs):
|
||||
context = {}
|
||||
instance = {"uuid": "fake_uuid"}
|
||||
block_device_info = {'block_device_mapping': []}
|
||||
vif = self._dev_mock({"address": "fake_address", "tag": "vif_tag"})
|
||||
bdm = self._dev_mock({"device_name": "/dev/xvdx", "tag": "bdm_tag"})
|
||||
|
||||
mock_get_vifs.return_value = [vif]
|
||||
mock_get_bdms.return_value = [bdm]
|
||||
mock_InstanceDeviceMetadata.side_effect = \
|
||||
lambda **kw: {"devices": kw.get("devices")}
|
||||
mock_NetworkInterfaceMetadata.return_value = mock.sentinel.vif_metadata
|
||||
mock_prepare_disk_metadata.return_value = [mock.sentinel.bdm_metadata]
|
||||
|
||||
dev_meta = self.vmops._save_device_metadata(context, instance,
|
||||
block_device_info)
|
||||
|
||||
mock_get_vifs.assert_called_once_with(context, instance["uuid"])
|
||||
|
||||
mock_NetworkInterfaceMetadata.assert_called_once_with(mac=vif.address,
|
||||
bus=mock_PCIDeviceBus.return_value,
|
||||
tags=[vif.tag])
|
||||
mock_prepare_disk_metadata.assert_called_once_with(bdm)
|
||||
self.assertEqual(dev_meta["devices"],
|
||||
[mock.sentinel.vif_metadata, mock.sentinel.bdm_metadata])
|
||||
|
||||
def test_spawn_with_neutron_exception(self):
|
||||
self.mox.StubOutWithMock(self.vmops, '_get_neutron_events')
|
||||
self.assertRaises(exception.VirtualInterfaceCreateException,
|
||||
|
@ -523,8 +632,8 @@ class SpawnTestCase(VMOpsTestBase):
|
|||
context = "context"
|
||||
migration = {}
|
||||
name_label = "dummy"
|
||||
instance = {"name": name_label, "uuid": "fake_uuid",
|
||||
"root_device_name": "/dev/xvda"}
|
||||
instance = self._new_instance({"name": name_label, "uuid": "fake_uuid",
|
||||
"root_device_name": "/dev/xvda", "device_metadata": None})
|
||||
disk_info = "disk_info"
|
||||
network_info = "net_info"
|
||||
image_meta = objects.ImageMeta.from_dict({"id": uuids.image_id})
|
||||
|
@ -566,6 +675,7 @@ class SpawnTestCase(VMOpsTestBase):
|
|||
|
||||
if resize_instance:
|
||||
self.vmops._resize_up_vdis(instance, vdis)
|
||||
self.vmops._save_device_metadata(context, instance, block_device_info)
|
||||
self.vmops._attach_disks(context, instance, image_meta, vm_ref,
|
||||
name_label, vdis, di_type, network_info, False,
|
||||
None, None)
|
||||
|
|
|
@ -71,7 +71,7 @@ class XenAPIDriver(driver.ComputeDriver):
|
|||
"supports_recreate": False,
|
||||
"supports_migrate_to_same_host": False,
|
||||
"supports_attach_interface": True,
|
||||
"supports_device_tagging": False,
|
||||
"supports_device_tagging": True,
|
||||
}
|
||||
|
||||
def __init__(self, virtapi, read_only=False):
|
||||
|
|
|
@ -458,6 +458,8 @@ class VMOps(object):
|
|||
if resize:
|
||||
self._resize_up_vdis(instance, vdis)
|
||||
|
||||
instance.device_metadata = self._save_device_metadata(
|
||||
context, instance, block_device_info)
|
||||
self._attach_disks(context, instance, image_meta, vm_ref,
|
||||
name_label, vdis, disk_image_type,
|
||||
network_info, rescue,
|
||||
|
@ -816,6 +818,79 @@ class VMOps(object):
|
|||
admin_password=admin_password,
|
||||
files=files)
|
||||
|
||||
@staticmethod
|
||||
def _prepare_disk_metadata(bdm):
|
||||
"""Returns the disk metadata with dual disk buses - ide and xen. More
|
||||
details about Xen device number can be found in
|
||||
http://xenbits.xen.org/docs/4.2-testing/misc/vbd-interface.txt
|
||||
"""
|
||||
path = bdm.device_name
|
||||
disk_num = volume_utils.get_device_number(path)
|
||||
|
||||
xen0 = objects.XenDeviceBus(address=("00%02d00" % disk_num))
|
||||
|
||||
registry = ('HKLM\\SYSTEM\\ControlSet001\\Enum\\SCSI\\'
|
||||
'Disk&Ven_XENSRC&Prod_PVDISK\\')
|
||||
vbd_prefix = '/sys/devices/vbd-'
|
||||
|
||||
if disk_num < 4:
|
||||
ide = objects.IDEDeviceBus(
|
||||
address=("%d:%d" % (disk_num / 2, disk_num % 2)))
|
||||
|
||||
xen1 = objects.XenDeviceBus(
|
||||
address=("%d" % (202 << 8 | disk_num << 4)))
|
||||
xen2 = objects.XenDeviceBus()
|
||||
if disk_num < 2:
|
||||
xen2.address = "%d" % (3 << 8 | disk_num << 6)
|
||||
else:
|
||||
xen2.address = "%d" % (22 << 8 | (disk_num - 2) << 6)
|
||||
|
||||
return [objects.DiskMetadata(path=path, bus=ide, tags=[bdm.tag]),
|
||||
objects.DiskMetadata(path=registry + xen0.address,
|
||||
bus=xen0, tags=[bdm.tag]),
|
||||
objects.DiskMetadata(path=vbd_prefix + xen1.address,
|
||||
bus=xen1, tags=[bdm.tag]),
|
||||
objects.DiskMetadata(path=vbd_prefix + xen2.address,
|
||||
bus=xen2, tags=[bdm.tag])]
|
||||
else:
|
||||
xen1 = objects.XenDeviceBus()
|
||||
|
||||
if disk_num < 16:
|
||||
xen1.address = "%d" % (202 << 8 | disk_num << 4)
|
||||
else:
|
||||
xen1.address = "%d" % (1 << 28 | disk_num << 8)
|
||||
|
||||
return [objects.DiskMetadata(path=registry + xen0.address,
|
||||
bus=xen0, tags=[bdm.tag]),
|
||||
objects.DiskMetadata(path=vbd_prefix + xen1.address,
|
||||
bus=xen1, tags=[bdm.tag])]
|
||||
|
||||
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.
|
||||
"""
|
||||
vifs = objects.VirtualInterfaceList.get_by_instance_uuid(
|
||||
context, instance["uuid"])
|
||||
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
||||
context, instance["uuid"])
|
||||
|
||||
metadata = []
|
||||
for vif in vifs:
|
||||
if 'tag' in vif and vif.tag:
|
||||
device = objects.NetworkInterfaceMetadata(
|
||||
mac=vif.address,
|
||||
bus=objects.PCIDeviceBus(),
|
||||
tags=[vif.tag])
|
||||
metadata.append(device)
|
||||
|
||||
if block_device_info:
|
||||
for bdm in bdms:
|
||||
if 'tag' in bdm and bdm.tag:
|
||||
metadata.extend(self._prepare_disk_metadata(bdm))
|
||||
|
||||
if metadata:
|
||||
return objects.InstanceDeviceMetadata(devices=metadata)
|
||||
|
||||
def _wait_for_instance_to_start(self, instance, vm_ref):
|
||||
LOG.debug('Waiting for instance state to become running',
|
||||
instance=instance)
|
||||
|
|
Loading…
Reference in New Issue