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.