From 1a0778d280658a1da08b4cd903b8dbb6fbbd8d91 Mon Sep 17 00:00:00 2001 From: Vladik Romanovsky Date: Thu, 1 Dec 2016 10:41:21 -0500 Subject: [PATCH] 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 --- nova/api/metadata/base.py | 23 ++++++++++--- nova/tests/unit/test_metadata.py | 32 +++++++++++++++---- ...gh-neutron-port-vlan-7d19df7ac6e2730a.yaml | 13 ++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/sriov-pf-passthrough-neutron-port-vlan-7d19df7ac6e2730a.yaml diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index 169f67f65b8c..fe0343974c96 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -73,6 +73,7 @@ HAVANA = '2013-10-17' LIBERTY = '2015-10-15' NEWTON_ONE = '2016-06-30' NEWTON_TWO = '2016-10-06' +OCATA = '2017-02-22' OPENSTACK_VERSIONS = [ FOLSOM, @@ -81,6 +82,7 @@ OPENSTACK_VERSIONS = [ LIBERTY, NEWTON_ONE, NEWTON_TWO, + OCATA, ] VERSION = "version" @@ -372,19 +374,19 @@ class InstanceMetadata(object): metadata['project_id'] = self.instance.project_id 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) 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 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 controlled. """ device_metadata_list = [] - + vif_vlans_supported = self._check_os_version(OCATA, version) if self.instance.device_metadata is not None: for device in self.instance.device_metadata.devices: device_metadata = {} @@ -413,8 +415,19 @@ class InstanceMetadata(object): address = device.bus.address 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['mac'] = device.mac + if vlan: + device_metadata['vlan'] = vlan elif isinstance(device, metadata_obj.DiskMetadata): device_metadata['type'] = 'disk' # serial and path are optional parameters @@ -430,10 +443,10 @@ class InstanceMetadata(object): device_metadata['bus'] = bus device_metadata['address'] = address - device_metadata['tags'] = device.tags + if 'tags' in device: + device_metadata['tags'] = device.tags device_metadata_list.append(device_metadata) - return device_metadata_list def _handle_content(self, path_tokens): diff --git a/nova/tests/unit/test_metadata.py b/nova/tests/unit/test_metadata.py index 21be2e0f66d1..83a1934c6486 100644 --- a/nova/tests/unit/test_metadata.py +++ b/nova/tests/unit/test_metadata.py @@ -176,6 +176,11 @@ def fake_metadata_objects(): mac='00:00:00:00:00:00', 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( bus=metadata_obj.IDEDeviceBus(address='0:0'), serial='disk-vol-2352423', @@ -203,11 +208,11 @@ def fake_metadata_objects(): mdlist = metadata_obj.InstanceDeviceMetadata( instance_uuid='b65cee2f-8c69-4aeb-be2f-f79742548fc2', 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 -def fake_metadata_dicts(): +def fake_metadata_dicts(include_vlan=False): nic_meta = { 'type': 'nic', 'bus': 'pci', @@ -215,6 +220,13 @@ def fake_metadata_dicts(): 'mac': '00:00:00:00:00:00', '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 = { 'type': 'disk', 'bus': 'ide', @@ -232,7 +244,10 @@ def fake_metadata_dicts(): usb_disk_meta['bus'] = 'usb' 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): @@ -437,6 +452,11 @@ class MetadataTestCase(test.TestCase): 'openstack/2016-10-06/vendor_data.json', 'openstack/2016-10-06/network_data.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/user_data', 'openstack/latest/vendor_data.json', @@ -524,8 +544,8 @@ class MetadataTestCase(test.TestCase): if md._check_os_version(base.LIBERTY, os_version): expected_metadata['project_id'] = instance.project_id 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 md._metadata_as_json(os_version, 'non useless path parameter') if instance.key_name: @@ -589,7 +609,7 @@ class OpenStackMetadataTestCase(test.TestCase): mdinst = fake_InstanceMetadata(self, inst) mdjson = mdinst.lookup("/openstack/latest/meta_data.json") 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): # request for /openstack// should show metadata.json diff --git a/releasenotes/notes/sriov-pf-passthrough-neutron-port-vlan-7d19df7ac6e2730a.yaml b/releasenotes/notes/sriov-pf-passthrough-neutron-port-vlan-7d19df7ac6e2730a.yaml new file mode 100644 index 000000000000..7813914696cd --- /dev/null +++ b/releasenotes/notes/sriov-pf-passthrough-neutron-port-vlan-7d19df7ac6e2730a.yaml @@ -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.