Adding vlans field to Device tagging metadata

Handle the network interface new vlans field and expose it in the devices
metadata
The vlan tag will be exposed to the instance through the metadata API and
on the config drive.

Implements: blueprint sriov-pf-passthrough-neutron-port-vlan
Change-Id: Id7b9f3f1c2107ec604e7f0ef4fbfd31a9e05d0b0
This commit is contained in:
Vladik Romanovsky 2016-12-01 10:41:21 -05:00 committed by Matt Riedemann
parent 18a23b747b
commit 1a0778d280
3 changed files with 57 additions and 11 deletions

View File

@ -73,6 +73,7 @@ HAVANA = '2013-10-17'
LIBERTY = '2015-10-15' LIBERTY = '2015-10-15'
NEWTON_ONE = '2016-06-30' NEWTON_ONE = '2016-06-30'
NEWTON_TWO = '2016-10-06' NEWTON_TWO = '2016-10-06'
OCATA = '2017-02-22'
OPENSTACK_VERSIONS = [ OPENSTACK_VERSIONS = [
FOLSOM, FOLSOM,
@ -81,6 +82,7 @@ OPENSTACK_VERSIONS = [
LIBERTY, LIBERTY,
NEWTON_ONE, NEWTON_ONE,
NEWTON_TWO, NEWTON_TWO,
OCATA,
] ]
VERSION = "version" VERSION = "version"
@ -372,19 +374,19 @@ class InstanceMetadata(object):
metadata['project_id'] = self.instance.project_id metadata['project_id'] = self.instance.project_id
if self._check_os_version(NEWTON_ONE, version): if self._check_os_version(NEWTON_ONE, version):
metadata['devices'] = self._get_device_metadata() metadata['devices'] = self._get_device_metadata(version)
self.set_mimetype(MIME_TYPE_APPLICATION_JSON) self.set_mimetype(MIME_TYPE_APPLICATION_JSON)
return jsonutils.dump_as_bytes(metadata) return jsonutils.dump_as_bytes(metadata)
def _get_device_metadata(self): def _get_device_metadata(self, version):
"""Build a device metadata dict based on the metadata objects. This is """Build a device metadata dict based on the metadata objects. This is
done here in the metadata API as opposed to in the objects themselves done here in the metadata API as opposed to in the objects themselves
because the metadata dict is part of the guest API and thus must be because the metadata dict is part of the guest API and thus must be
controlled. controlled.
""" """
device_metadata_list = [] device_metadata_list = []
vif_vlans_supported = self._check_os_version(OCATA, version)
if self.instance.device_metadata is not None: if self.instance.device_metadata is not None:
for device in self.instance.device_metadata.devices: for device in self.instance.device_metadata.devices:
device_metadata = {} device_metadata = {}
@ -413,8 +415,19 @@ class InstanceMetadata(object):
address = device.bus.address address = device.bus.address
if isinstance(device, metadata_obj.NetworkInterfaceMetadata): if isinstance(device, metadata_obj.NetworkInterfaceMetadata):
vlan = None
if vif_vlans_supported and 'vlan' in device:
vlan = device.vlan
# Skip devices without tags on versions that
# don't support vlans
if not (vlan or 'tags' in device):
continue
device_metadata['type'] = 'nic' device_metadata['type'] = 'nic'
device_metadata['mac'] = device.mac device_metadata['mac'] = device.mac
if vlan:
device_metadata['vlan'] = vlan
elif isinstance(device, metadata_obj.DiskMetadata): elif isinstance(device, metadata_obj.DiskMetadata):
device_metadata['type'] = 'disk' device_metadata['type'] = 'disk'
# serial and path are optional parameters # serial and path are optional parameters
@ -430,10 +443,10 @@ class InstanceMetadata(object):
device_metadata['bus'] = bus device_metadata['bus'] = bus
device_metadata['address'] = address device_metadata['address'] = address
if 'tags' in device:
device_metadata['tags'] = device.tags device_metadata['tags'] = device.tags
device_metadata_list.append(device_metadata) device_metadata_list.append(device_metadata)
return device_metadata_list return device_metadata_list
def _handle_content(self, path_tokens): def _handle_content(self, path_tokens):

View File

@ -176,6 +176,11 @@ def fake_metadata_objects():
mac='00:00:00:00:00:00', mac='00:00:00:00:00:00',
tags=['foo'] tags=['foo']
) )
nic_vlans_obj = metadata_obj.NetworkInterfaceMetadata(
bus=metadata_obj.PCIDeviceBus(address='0000:80:01.0'),
mac='e3:a0:d0:12:c5:10',
vlan=1000,
)
ide_disk_obj = metadata_obj.DiskMetadata( ide_disk_obj = metadata_obj.DiskMetadata(
bus=metadata_obj.IDEDeviceBus(address='0:0'), bus=metadata_obj.IDEDeviceBus(address='0:0'),
serial='disk-vol-2352423', serial='disk-vol-2352423',
@ -203,11 +208,11 @@ def fake_metadata_objects():
mdlist = metadata_obj.InstanceDeviceMetadata( mdlist = metadata_obj.InstanceDeviceMetadata(
instance_uuid='b65cee2f-8c69-4aeb-be2f-f79742548fc2', instance_uuid='b65cee2f-8c69-4aeb-be2f-f79742548fc2',
devices=[nic_obj, ide_disk_obj, scsi_disk_obj, usb_disk_obj, devices=[nic_obj, ide_disk_obj, scsi_disk_obj, usb_disk_obj,
fake_device_obj, device_with_fake_bus_obj]) fake_device_obj, device_with_fake_bus_obj, nic_vlans_obj])
return mdlist return mdlist
def fake_metadata_dicts(): def fake_metadata_dicts(include_vlan=False):
nic_meta = { nic_meta = {
'type': 'nic', 'type': 'nic',
'bus': 'pci', 'bus': 'pci',
@ -215,6 +220,13 @@ def fake_metadata_dicts():
'mac': '00:00:00:00:00:00', 'mac': '00:00:00:00:00:00',
'tags': ['foo'], 'tags': ['foo'],
} }
vlan_nic_meta = {
'type': 'nic',
'bus': 'pci',
'address': '0000:80:01.0',
'mac': 'e3:a0:d0:12:c5:10',
'vlan': 1000,
}
ide_disk_meta = { ide_disk_meta = {
'type': 'disk', 'type': 'disk',
'bus': 'ide', 'bus': 'ide',
@ -232,7 +244,10 @@ def fake_metadata_dicts():
usb_disk_meta['bus'] = 'usb' usb_disk_meta['bus'] = 'usb'
usb_disk_meta['address'] = '05c8:021e' usb_disk_meta['address'] = '05c8:021e'
return [nic_meta, ide_disk_meta, scsi_disk_meta, usb_disk_meta] dicts = [nic_meta, ide_disk_meta, scsi_disk_meta, usb_disk_meta]
if include_vlan:
dicts += [vlan_nic_meta]
return dicts
class MetadataTestCase(test.TestCase): class MetadataTestCase(test.TestCase):
@ -437,6 +452,11 @@ class MetadataTestCase(test.TestCase):
'openstack/2016-10-06/vendor_data.json', 'openstack/2016-10-06/vendor_data.json',
'openstack/2016-10-06/network_data.json', 'openstack/2016-10-06/network_data.json',
'openstack/2016-10-06/vendor_data2.json', 'openstack/2016-10-06/vendor_data2.json',
'openstack/2017-02-22/meta_data.json',
'openstack/2017-02-22/user_data',
'openstack/2017-02-22/vendor_data.json',
'openstack/2017-02-22/network_data.json',
'openstack/2017-02-22/vendor_data2.json',
'openstack/latest/meta_data.json', 'openstack/latest/meta_data.json',
'openstack/latest/user_data', 'openstack/latest/user_data',
'openstack/latest/vendor_data.json', 'openstack/latest/vendor_data.json',
@ -524,8 +544,8 @@ class MetadataTestCase(test.TestCase):
if md._check_os_version(base.LIBERTY, os_version): if md._check_os_version(base.LIBERTY, os_version):
expected_metadata['project_id'] = instance.project_id expected_metadata['project_id'] = instance.project_id
if md._check_os_version(base.NEWTON_ONE, os_version): if md._check_os_version(base.NEWTON_ONE, os_version):
expected_metadata['devices'] = fake_metadata_dicts() expose_vlan = md._check_os_version(base.OCATA, os_version)
expected_metadata['devices'] = fake_metadata_dicts(expose_vlan)
mock_cells_keypair.return_value = keypair mock_cells_keypair.return_value = keypair
md._metadata_as_json(os_version, 'non useless path parameter') md._metadata_as_json(os_version, 'non useless path parameter')
if instance.key_name: if instance.key_name:
@ -589,7 +609,7 @@ class OpenStackMetadataTestCase(test.TestCase):
mdinst = fake_InstanceMetadata(self, inst) mdinst = fake_InstanceMetadata(self, inst)
mdjson = mdinst.lookup("/openstack/latest/meta_data.json") mdjson = mdinst.lookup("/openstack/latest/meta_data.json")
mddict = jsonutils.loads(mdjson) mddict = jsonutils.loads(mdjson)
self.assertEqual(fake_metadata_dicts(), mddict['devices']) self.assertEqual(fake_metadata_dicts(True), mddict['devices'])
def test_top_level_listing(self): def test_top_level_listing(self):
# request for /openstack/<version>/ should show metadata.json # request for /openstack/<version>/ should show metadata.json

View File

@ -0,0 +1,13 @@
---
features:
- |
VLAN tags associated with instance network interfaces are now exposed via
the metadata API and instance config drives and can be consumed by the
instance. This is an extension of the device tagging mechanism added in
past releases. This is useful for instances utilizing SR-IOV physical
functions (PFs). The VLAN configuration for the guest's virtual interfaces
associated with these devices cannot be configured inside the guest OS from
the host, but nonetheless must be configured with the VLAN tags of the
device to ensure packet delivery. This feature makes this possible.
.. note:: VLAN tags are currently only supported via the Libvirt driver.