diff --git a/doc/source/user/vif-types.rst b/doc/source/user/vif-types.rst index a2f4db4d..d8e9ffab 100644 --- a/doc/source/user/vif-types.rst +++ b/doc/source/user/vif-types.rst @@ -128,6 +128,12 @@ This profile provides the metadata required to associate a VIF with a specified, it indicates a desire to rename the representor to the given name on plugging. +.. note:: This port profile is provided for backwards compatibility only. + +This interface has been superceded by the one provided by the +`DatapathOffloadRepresentor` class, which is now a field element of the +`VIFPortProfileBase` class. + VIFPortProfileFPBridge ---------------------- @@ -146,6 +152,23 @@ VIFPortProfileK8sDPDK This profile provides the metadata required to associate nested DPDK VIF with a Kubernetes pod. +Datapath Offload type object +============================ + +Port profiles can be associated with a `datapath_offload` object. This +provides a set of metadata attributes that serve to identify the datapath +offload parameters of a VIF. Each different type of datapath offload is +associated with a versioned object, subclassing `DatapathOffloadBase`. + +DatapathOffloadRepresentor +-------------------------- + +This object provides the metadata required to associate a VIF with a +:term:`VF` representor conforming to the +`switchdev `_ kernel model. +If `representor_name` is specified, it indicates a desire to rename the +representor to the given name on plugging. + VIF network objects =================== diff --git a/os_vif/objects/vif.py b/os_vif/objects/vif.py index 05bed93d..26f435e8 100644 --- a/os_vif/objects/vif.py +++ b/os_vif/objects/vif.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +from debtcollector import removals + from oslo_utils import versionutils from oslo_versionedobjects import base from oslo_versionedobjects import fields @@ -177,11 +179,45 @@ class VIFNestedDPDK(VIFBase): } +@base.VersionedObjectRegistry.register +class DatapathOffloadBase(osv_base.VersionedObject, + base.ComparableVersionedObject): + # Base class for all types of datapath offload + VERSION = '1.0' + + +@base.VersionedObjectRegistry.register +class DatapathOffloadRepresentor(DatapathOffloadBase): + # Offload type for VF Representors conforming to the switchdev model + VERSION = '1.0' + + fields = { + # Name to set on the representor (if set) + 'representor_name': fields.StringField(nullable=True), + + # The PCI address of the Virtual Function + 'representor_address': fields.StringField(nullable=True), + } + + @base.VersionedObjectRegistry.register class VIFPortProfileBase(osv_base.VersionedObject, base.ComparableVersionedObject): # Base class for all types of port profile - VERSION = '1.0' + # Version 1.0: Initial release + # Version 1.1: Added 'datapath_offload' + VERSION = '1.1' + + fields = { + # Datapath offload type of the port + 'datapath_offload': fields.ObjectField('DatapathOffloadBase', + nullable=True, + subclasses=True), + } + + obj_relationships = { + 'datapath_offload': (('1.1', '1.0'),), + } @base.VersionedObjectRegistry.register @@ -189,7 +225,8 @@ class VIFPortProfileOpenVSwitch(VIFPortProfileBase): # Port profile info for OpenVSwitch networks # Version 1.0: Initial release # Version 1.1: Added 'datapath_type' - VERSION = '1.1' + # Version 1.2: VIFPortProfileBase updated to 1.1 + VERSION = '1.2' fields = { 'interface_id': fields.UUIDField(), @@ -205,6 +242,9 @@ class VIFPortProfileOpenVSwitch(VIFPortProfileBase): target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 1) and 'datapath_type' in primitive: del primitive['datapath_type'] + if target_version < (1, 2): + super(VIFPortProfileOpenVSwitch, self).obj_make_compatible( + primitive, "1.0") @base.VersionedObjectRegistry.register @@ -212,7 +252,8 @@ class VIFPortProfileFPOpenVSwitch(VIFPortProfileOpenVSwitch): # Port profile info for OpenVSwitch networks using fastpath # Version 1.0: Initial release # Version 1.1: VIFPortProfileOpenVSwitch updated to 1.1 - VERSION = '1.1' + # Version 1.2: VIFPortProfileOpenVSwitch updated to 1.2 + VERSION = '1.2' fields = { # Name of the bridge (managed by fast path) to connect to @@ -227,14 +268,23 @@ class VIFPortProfileFPOpenVSwitch(VIFPortProfileOpenVSwitch): if target_version < (1, 1): super(VIFPortProfileFPOpenVSwitch, self).obj_make_compatible( primitive, "1.0") + if target_version < (1, 2): + super(VIFPortProfileFPOpenVSwitch, self).obj_make_compatible( + primitive, "1.1") +@removals.removed_class("VIFPortProfileOVSRepresentor", + category=PendingDeprecationWarning) @base.VersionedObjectRegistry.register class VIFPortProfileOVSRepresentor(VIFPortProfileOpenVSwitch): # Port profile info for OpenVSwitch networks using a representor + # This class is now frozen and retained for backwards compatibility. The + # 'datapath_offload' field in port profiles should be used instead. + # # Version 1.0: Initial release # Version 1.1: VIFPortProfileOpenVSwitch updated to 1.1 - VERSION = '1.1' + # Version 1.2: VIFPortProfileOpenVSwitch updated to 1.2 + VERSION = '1.2' fields = { # Name to set on the representor (if set) @@ -249,37 +299,58 @@ class VIFPortProfileOVSRepresentor(VIFPortProfileOpenVSwitch): if target_version < (1, 1): super(VIFPortProfileOVSRepresentor, self).obj_make_compatible( primitive, "1.0") + if target_version < (1, 2): + super(VIFPortProfileOVSRepresentor, self).obj_make_compatible( + primitive, "1.1") @base.VersionedObjectRegistry.register class VIFPortProfileFPBridge(VIFPortProfileBase): # Port profile info for LinuxBridge networks using fastpath - - VERSION = '1.0' + # + # Version 1.0: Initial release + # Version 1.1: VIFPortProfileBase updated to 1.1 + VERSION = '1.1' fields = { # Name of the bridge (managed by fast path) to connect to 'bridge_name': fields.StringField(), } + def obj_make_compatible(self, primitive, target_version): + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + super(VIFPortProfileFPBridge, self).obj_make_compatible( + primitive, "1.0") + @base.VersionedObjectRegistry.register class VIFPortProfileFPTap(VIFPortProfileBase): # Port profile info for Calico networks using fastpath - - VERSION = '1.0' + # + # Version 1.0: Initial release + # Version 1.1: VIFPortProfileBase updated to 1.1 + VERSION = '1.1' fields = { # The mac address of the host vhostuser port 'mac_address': fields.MACAddressField(nullable=True), } + def obj_make_compatible(self, primitive, target_version): + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + super(VIFPortProfileFPTap, self).obj_make_compatible( + primitive, "1.0") + @base.VersionedObjectRegistry.register class VIFPortProfile8021Qbg(VIFPortProfileBase): # Port profile info for VEPA 802.1qbg networks - - VERSION = '1.0' + # + # Version 1.0: Initial release + # Version 1.1: VIFPortProfileBase updated to 1.1 + VERSION = '1.1' fields = { 'manager_id': fields.IntegerField(), @@ -288,23 +359,39 @@ class VIFPortProfile8021Qbg(VIFPortProfileBase): 'instance_id': fields.UUIDField(), } + def obj_make_compatible(self, primitive, target_version): + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + super(VIFPortProfile8021Qbg, self).obj_make_compatible( + primitive, "1.0") + @base.VersionedObjectRegistry.register class VIFPortProfile8021Qbh(VIFPortProfileBase): # Port profile info for VEPA 802.1qbh networks - - VERSION = '1.0' + # + # Version 1.0: Initial release + # Version 1.1: VIFPortProfileBase updated to 1.1 + VERSION = '1.1' fields = { 'profile_id': fields.StringField() } + def obj_make_compatible(self, primitive, target_version): + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + super(VIFPortProfile8021Qbh, self).obj_make_compatible( + primitive, "1.0") + @base.VersionedObjectRegistry.register class VIFPortProfileK8sDPDK(VIFPortProfileBase): # Port profile info for Kuryr-Kubernetes DPDK ports - - VERSION = '1.0' + # + # Version 1.0: Initial release + # Version 1.1: VIFPortProfileBase updated to 1.1 + VERSION = '1.1' fields = { # Specify whether this vif requires L3 setup. @@ -317,3 +404,9 @@ class VIFPortProfileK8sDPDK(VIFPortProfileBase): # the server's internal version of this object. 'resourceversion': fields.StringField() } + + def obj_make_compatible(self, primitive, target_version): + target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 1): + super(VIFPortProfileK8sDPDK, self).obj_make_compatible( + primitive, "1.0") diff --git a/os_vif/tests/unit/test_objects.py b/os_vif/tests/unit/test_objects.py index 12c3e7ca..b9248ebc 100644 --- a/os_vif/tests/unit/test_objects.py +++ b/os_vif/tests/unit/test_objects.py @@ -37,17 +37,19 @@ object_data = { 'VIFGeneric': '1.0-c72e637ed620f0135ea50a9409a3f389', 'VIFHostDevice': '1.0-bb090f1869c3b4df36efda216ab97a61', 'VIFOpenVSwitch': '1.0-e78d355f3505361fafbf0797ffad484a', - 'VIFPortProfile8021Qbg': '1.0-167f305f6e982b9368cc38763815d429', - 'VIFPortProfile8021Qbh': '1.0-4b945f07d2666ab00a48d1dc225669b1', - 'VIFPortProfileBase': '1.0-77509ea1ea0dd750d5864b9bd87d3f9d', - 'VIFPortProfileOpenVSwitch': '1.1-70d36e09c8d800345ce71177265212df', - 'VIFPortProfileFPOpenVSwitch': '1.1-74e77f46aa5806930df6f37a0b76ff8b', - 'VIFPortProfileFPBridge': '1.0-d50872b3cddd245ffebef6053dfbe27a', - 'VIFPortProfileFPTap': '1.0-11670d8dbabd772ff0da26961adadc5a', + 'VIFPortProfile8021Qbg': '1.1-b3011621809dca9216b50579ce9d6b19', + 'VIFPortProfile8021Qbh': '1.1-226b61b2e76ba452f7b31530cff80ac9', + 'VIFPortProfileBase': '1.1-4982d1621df12ebd1f3b07948f3d0e5f', + 'VIFPortProfileOpenVSwitch': '1.2-25aec86b7ec9fcb3434f896f694818de', + 'VIFPortProfileFPOpenVSwitch': '1.2-4a4f230d89a5ea0e43011f678b626dd9', + 'VIFPortProfileFPBridge': '1.1-49f1952bf50bab7a95112c908534751f', + 'VIFPortProfileFPTap': '1.1-fd178229477604dfb65de5ce929488e5', 'VIFVHostUser': '1.1-1f95b43be1f884f090ca1f4d79adfd35', - 'VIFPortProfileOVSRepresentor': '1.1-30e555981003a109b133da5b43ded5df', + 'VIFPortProfileOVSRepresentor': '1.2-d0609e93ea884ef7b4949177e9fcdc39', 'VIFNestedDPDK': '1.0-fdbaf6b20afd116529929b21aa7158dc', - 'VIFPortProfileK8sDPDK': '1.0-f1e0daa66b041ded4e6dbc053b4a66d5', + 'VIFPortProfileK8sDPDK': '1.1-e2a2abd112b14e0239e76b99d9b252ae', + 'DatapathOffloadBase': '1.0-77509ea1ea0dd750d5864b9bd87d3f9d', + 'DatapathOffloadRepresentor': '1.0-802a5dff22f73046df3742c815c51421', } diff --git a/os_vif/tests/unit/test_vif.py b/os_vif/tests/unit/test_vif.py index 46dba29f..88cf23cb 100644 --- a/os_vif/tests/unit/test_vif.py +++ b/os_vif/tests/unit/test_vif.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +import warnings + import os_vif from os_vif import objects from os_vif.tests.unit import base @@ -52,6 +54,17 @@ class TestVIFS(base.TestCase): vif_name="vif123", bridge_name="br0") + def test_port_profile_base_backport_1_0(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileBase( + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.0') + self.assertEqual('1.0', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertNotIn('datapath_type', data) + def test_vif_bridge_ovs(self): prof = objects.vif.VIFPortProfileOpenVSwitch( interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", @@ -62,7 +75,7 @@ class TestVIFS(base.TestCase): bridge_name="br0", port_profile=prof) - def test_vif_bridge_ovs_backport_1_0(self): + def test_port_profile_ovs_backport_1_0(self): obj = objects.vif.VIFPortProfileOpenVSwitch( interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", profile_id="fishfood", @@ -75,6 +88,24 @@ class TestVIFS(base.TestCase): self.assertEqual('fishfood', data['profile_id']) self.assertNotIn('datapath_type', data) + def test_port_profile_ovs_backport_1_1(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileOpenVSwitch( + interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", + profile_id="fishfood", + datapath_type='netdev', + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.1') + self.assertEqual('1.1', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual('07bd6cea-fb37-4594-b769-90fc51854ee9', + data['interface_id']) + self.assertEqual('fishfood', data['profile_id']) + self.assertEqual('netdev', data['datapath_type']) + self.assertNotIn('datapath_offload', data) + def test_vif_direct_plain(self): self._test_vif(objects.vif.VIFDirect, vif_name="vif123", @@ -118,7 +149,7 @@ class TestVIFS(base.TestCase): vif_name="tap123", port_profile=prof) - def test_vif_vhost_user_fp_ovs_backport_1_0(self): + def test_port_profile_fp_ovs_backport_1_0(self): obj = objects.vif.VIFPortProfileFPOpenVSwitch( interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", profile_id="fishfood", @@ -135,6 +166,28 @@ class TestVIFS(base.TestCase): self.assertEqual(False, data['hybrid_plug']) self.assertNotIn('datapath_type', data) + def test_port_profile_fp_ovs_backport_1_1(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileFPOpenVSwitch( + interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", + profile_id="fishfood", + datapath_type='netdev', + bridge_name="br-int", + hybrid_plug=False, + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.1') + self.assertEqual('1.1', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual('07bd6cea-fb37-4594-b769-90fc51854ee9', + data['interface_id']) + self.assertEqual('fishfood', data['profile_id']) + self.assertEqual('br-int', data['bridge_name']) + self.assertEqual(False, data['hybrid_plug']) + self.assertEqual('netdev', data['datapath_type']) + self.assertNotIn('datapath_offload', data) + def test_vif_vhost_user_ovs_representor(self): prof = objects.vif.VIFPortProfileOVSRepresentor( interface_id="07bd6cea-fb37-4594-b769-90fc51854ee8", @@ -148,7 +201,7 @@ class TestVIFS(base.TestCase): vif_name="tap123", port_profile=prof) - def test_vif_vhost_user_ovs_representor_backport_1_0(self): + def test_port_profile_ovs_representor_backport_1_0(self): obj = objects.vif.VIFPortProfileOVSRepresentor( interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", profile_id="fishfood", @@ -165,6 +218,41 @@ class TestVIFS(base.TestCase): self.assertEqual("0002:24:12.3", data['representor_address']) self.assertNotIn('datapath_type', data) + def test_port_profile_ovs_representor_backport_1_1(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileOVSRepresentor( + interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", + profile_id="fishfood", + datapath_type='netdev', + representor_name="tap123", + representor_address="0002:24:12.3", + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.1') + self.assertEqual('1.1', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual('07bd6cea-fb37-4594-b769-90fc51854ee9', + data['interface_id']) + self.assertEqual('fishfood', data['profile_id']) + self.assertEqual('tap123', data['representor_name']) + self.assertEqual("0002:24:12.3", data['representor_address']) + self.assertEqual('netdev', data['datapath_type']) + self.assertNotIn('datapath_offload', data) + + def test_vif_vhost_user_generic_representor(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + prof = objects.vif.VIFPortProfileBase( + datapath_offload=datapath_offload, + ) + self._test_vif(objects.vif.VIFVHostUser, + path="/some/socket.path", + mode=objects.fields.VIFVHostUserMode.SERVER, + vif_name="felix", + port_profile=prof) + def test_vif_vhost_user_fp_lb(self): prof = objects.vif.VIFPortProfileFPBridge(bridge_name="brq456") self._test_vif(objects.vif.VIFVHostUser, @@ -204,3 +292,104 @@ class TestVIFS(base.TestCase): pci_adress="0002:24:12.3", dev_driver="virtio_pci", port_profile=prof) + + def test_port_profile_fp_bridge_backport_1_0(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileFPBridge( + bridge_name='joe', + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.0') + self.assertEqual('1.0', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual('joe', data['bridge_name']) + self.assertNotIn('datapath_type', data) + + def test_port_profile_fp_tap_backport_1_0(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileFPTap( + mac_address='00:de:ad:be:ef:01', + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.0') + self.assertEqual('1.0', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual('00:de:ad:be:ef:01', data['mac_address']) + self.assertNotIn('datapath_type', data) + + def test_port_profile_8021qbg_backport_1_0(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfile8021Qbg( + manager_id=42, + type_id=43, + type_id_version=44, + instance_id='07bd6cea-fb37-4594-b769-90fc51854ee9', + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.0') + self.assertEqual('1.0', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual(42, data['manager_id']) + self.assertEqual(43, data['type_id']) + self.assertEqual(44, data['type_id_version']) + self.assertEqual('07bd6cea-fb37-4594-b769-90fc51854ee9', + data['instance_id']) + self.assertNotIn('datapath_type', data) + + def test_port_profile_8021qbh_backport_1_0(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfile8021Qbh( + profile_id='catfood', + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.0') + self.assertEqual('1.0', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual('catfood', data['profile_id']) + self.assertNotIn('datapath_type', data) + + def test_port_profile_dpdk_k8s_backport_1_0(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + obj = objects.vif.VIFPortProfileK8sDPDK( + l3_setup=False, + selflink="/some/url", + resourceversion="1", + datapath_offload=datapath_offload) + primitive = obj.obj_to_primitive(target_version='1.0') + self.assertEqual('1.0', primitive['versioned_object.version']) + data = primitive['versioned_object.data'] + self.assertEqual(False, data['l3_setup']) + self.assertEqual("/some/url", data['selflink']) + self.assertEqual("1", data['resourceversion']) + self.assertNotIn('datapath_type', data) + + def test_vif_host_dev_ovs_offload(self): + datapath_offload = objects.vif.DatapathOffloadRepresentor( + representor_name="felix", + representor_address="0002:24:12.3") + prof = objects.vif.VIFPortProfileOpenVSwitch( + interface_id="07bd6cea-fb37-4594-b769-90fc51854ee8", + profile_id="fishfood", + datapath_type='netdev', + datapath_offload=datapath_offload) + self._test_vif( + objects.vif.VIFHostDevice, + dev_type=objects.fields.VIFHostDeviceDevType.ETHERNET, + dev_address="0002:24:12.3", + port_profile=prof) + + def test_pending_warnings_emitted_class_direct(self): + with warnings.catch_warnings(record=True) as capture: + warnings.simplefilter("always") + pp = objects.vif.VIFPortProfileOVSRepresentor() + self.assertEqual(1, len(capture)) + w = capture[0] + self.assertEqual(PendingDeprecationWarning, w.category) + self.assertEqual(pp.VERSION, + objects.vif.VIFPortProfileOVSRepresentor.VERSION) diff --git a/releasenotes/notes/generic-datapath-offloads-41cabb6842b41533.yaml b/releasenotes/notes/generic-datapath-offloads-41cabb6842b41533.yaml new file mode 100644 index 00000000..9bc2d55b --- /dev/null +++ b/releasenotes/notes/generic-datapath-offloads-41cabb6842b41533.yaml @@ -0,0 +1,11 @@ +--- +features: + - A new set of attributes to port profiles has been introduced, namely + ``Datapath Offload Types``, with ``DatapathOffloadRepresentor`` allowing + os-vif to pass the required metadata for representors conforming to the + kernel switchdev representor model. +deprecations: + - The API for ``VIFPortProfileOVSRepresentor`` has been frozen pending + deprecation of the class. Users should transition to setting the + ``datapath_offload`` of ``VIFPortProfileOpenVSwitch`` to a + ``DatapathOffloadRepresentor`` object to pass representor information. diff --git a/requirements.txt b/requirements.txt index 0111051c..a0ff0ffb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ ovsdbapp>=0.12.1 # Apache-2.0 pyroute2>=0.5.2;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) six>=1.10.0 # MIT stevedore>=1.20.0 # Apache-2.0 +debtcollector>=1.19.0 # Apache-2.0