diff --git a/nova/network/model.py b/nova/network/model.py
index f82fa5154128..763e56d68dc4 100644
--- a/nova/network/model.py
+++ b/nova/network/model.py
@@ -42,6 +42,7 @@ VIF_TYPE_VROUTER = 'vrouter'
VIF_TYPE_OTHER = 'other'
VIF_TYPE_TAP = 'tap'
VIF_TYPE_MACVTAP = 'macvtap'
+VIF_TYPE_AGILIO_OVS = 'agilio_ovs'
VIF_TYPE_BINDING_FAILED = 'binding_failed'
VIF_TYPE_VIF = 'vif'
@@ -97,12 +98,15 @@ VNIC_TYPE_DIRECT = 'direct'
VNIC_TYPE_MACVTAP = 'macvtap'
VNIC_TYPE_DIRECT_PHYSICAL = 'direct-physical'
VNIC_TYPE_BAREMETAL = 'baremetal'
+VNIC_TYPE_VIRTIO_FORWARDER = 'virtio-forwarder'
# Define list of ports which needs pci request.
# Note: The macvtap port needs a PCI request as it is a tap interface
# with VF as the lower physical interface.
+# Note: Currently, VNIC_TYPE_VIRTIO_FORWARDER assumes a 1:1
+# relationship with a VF. This is expected to change in the future.
VNIC_TYPES_SRIOV = (VNIC_TYPE_DIRECT, VNIC_TYPE_MACVTAP,
- VNIC_TYPE_DIRECT_PHYSICAL)
+ VNIC_TYPE_DIRECT_PHYSICAL, VNIC_TYPE_VIRTIO_FORWARDER)
# Define list of ports which are passthrough to the guest
# and need a special treatment on snapshot and suspend/resume
diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py
index 5d98e910fadb..65cc89baef70 100644
--- a/nova/network/neutronv2/api.py
+++ b/nova/network/neutronv2/api.py
@@ -2159,7 +2159,8 @@ class API(base_api.NetworkAPI):
should_create_bridge = None
vif_type = port.get('binding:vif_type')
port_details = port.get('binding:vif_details', {})
- if vif_type == network_model.VIF_TYPE_OVS:
+ if vif_type in [network_model.VIF_TYPE_OVS,
+ network_model.VIF_TYPE_AGILIO_OVS]:
bridge = port_details.get(network_model.VIF_DETAILS_BRIDGE_NAME,
CONF.neutron.ovs_bridge)
ovs_interfaceid = port['id']
diff --git a/nova/network/os_vif_util.py b/nova/network/os_vif_util.py
index cf142c131264..32b2465ee8ea 100644
--- a/nova/network/os_vif_util.py
+++ b/nova/network/os_vif_util.py
@@ -299,6 +299,49 @@ def _nova_to_osvif_vif_ovs(vif):
return obj
+# VIF_TYPE_AGILIO_OVS = 'agilio_ovs'
+def _nova_to_osvif_vif_agilio_ovs(vif):
+ vnic_type = vif.get('vnic_type', model.VNIC_TYPE_NORMAL)
+ # In practice, vif_name gets its value from vif["devname"], passed by
+ # the mechanism driver.
+ vif_name = _get_vif_name(vif)
+ agilio_vnic_types = [model.VNIC_TYPE_DIRECT,
+ model.VNIC_TYPE_VIRTIO_FORWARDER]
+ if vnic_type in agilio_vnic_types:
+ # Note: passing representor_name asks os-vif to rename the
+ # representor, setting this to vif_name is helpful for tracing.
+ # VIF.port_profile.representor_address is used by the os-vif plugin's
+ # plug/unplug, this should be the same as VIF.dev_address in the
+ # VNIC_TYPE_DIRECT case.
+ profile = objects.vif.VIFPortProfileOVSRepresentor(
+ interface_id=vif.get('ovs_interfaceid') or vif['id'],
+ representor_name=vif_name,
+ representor_address=vif["profile"]["pci_slot"])
+ if vnic_type == model.VNIC_TYPE_DIRECT:
+ # VIF.dev_address is used by the hypervisor to plug the instance into
+ # the PCI device.
+ obj = _get_vif_instance(
+ vif,
+ objects.vif.VIFHostDevice,
+ port_profile=profile,
+ plugin="agilio_ovs",
+ dev_address=vif["profile"]["pci_slot"],
+ dev_type=objects.fields.VIFHostDeviceDevType.ETHERNET)
+ if vif["network"]["bridge"] is not None:
+ obj.network.bridge = vif["network"]["bridge"]
+ elif vnic_type == model.VNIC_TYPE_VIRTIO_FORWARDER:
+ obj = _get_vif_instance(vif, objects.vif.VIFVHostUser,
+ port_profile=profile, plugin="agilio_ovs",
+ vif_name=vif_name)
+ _set_vhostuser_settings(vif, obj)
+ if vif["network"]["bridge"] is not None:
+ obj.network.bridge = vif["network"]["bridge"]
+ else:
+ LOG.debug("agilio_ovs falling through to ovs %s", vif)
+ obj = _nova_to_osvif_vif_ovs(vif)
+ return obj
+
+
# VIF_TYPE_VHOST_USER = 'vhostuser'
def _nova_to_osvif_vif_vhostuser(vif):
if vif['details'].get(model.VIF_DETAILS_VHOSTUSER_FP_PLUG, False):
diff --git a/nova/tests/unit/network/test_os_vif_util.py b/nova/tests/unit/network/test_os_vif_util.py
index acc0bc3c46d1..930c59b1ba11 100644
--- a/nova/tests/unit/network/test_os_vif_util.py
+++ b/nova/tests/unit/network/test_os_vif_util.py
@@ -475,6 +475,126 @@ class OSVIFUtilTestCase(test.NoDBTestCase):
self.assertObjEqual(expect, actual)
+ def test_nova_to_osvif_vif_agilio_ovs_fallthrough(self):
+ vif = model.VIF(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ type=model.VIF_TYPE_AGILIO_OVS,
+ address="22:52:25:62:e2:aa",
+ network=model.Network(
+ id="b82c1929-051e-481d-8110-4669916c7915",
+ label="Demo Net",
+ subnets=[]),
+ details={
+ model.VIF_DETAILS_PORT_FILTER: True,
+ }
+ )
+
+ actual = os_vif_util.nova_to_osvif_vif(vif)
+
+ expect = osv_objects.vif.VIFOpenVSwitch(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ active=False,
+ address="22:52:25:62:e2:aa",
+ has_traffic_filtering=True,
+ plugin="ovs",
+ port_profile=osv_objects.vif.VIFPortProfileOpenVSwitch(
+ interface_id="dc065497-3c8d-4f44-8fb4-e1d33c16a536"),
+ preserve_on_delete=False,
+ vif_name="nicdc065497-3c",
+ network=osv_objects.network.Network(
+ id="b82c1929-051e-481d-8110-4669916c7915",
+ bridge_interface=None,
+ label="Demo Net",
+ subnets=osv_objects.subnet.SubnetList(
+ objects=[])))
+
+ self.assertObjEqual(expect, actual)
+
+ def test_nova_to_osvif_vif_agilio_ovs_direct(self):
+ vif = model.VIF(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ type=model.VIF_TYPE_AGILIO_OVS,
+ address="22:52:25:62:e2:aa",
+ profile={
+ "pci_slot": "0000:08:08.5",
+ },
+ network=model.Network(
+ id="b82c1929-051e-481d-8110-4669916c7915",
+ label="Demo Net",
+ subnets=[]),
+ vnic_type=model.VNIC_TYPE_DIRECT,
+ )
+
+ actual = os_vif_util.nova_to_osvif_vif(vif)
+
+ expect = osv_objects.vif.VIFHostDevice(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ active=False,
+ has_traffic_filtering=False,
+ address="22:52:25:62:e2:aa",
+ dev_type=osv_objects.fields.VIFHostDeviceDevType.ETHERNET,
+ dev_address="0000:08:08.5",
+ plugin="agilio_ovs",
+ port_profile=osv_objects.vif.VIFPortProfileOVSRepresentor(
+ interface_id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ representor_name="nicdc065497-3c",
+ representor_address="0000:08:08.5"),
+ preserve_on_delete=False,
+ vif_name="nicdc065497-3c",
+ network=osv_objects.network.Network(
+ id="b82c1929-051e-481d-8110-4669916c7915",
+ bridge_interface=None,
+ label="Demo Net",
+ subnets=osv_objects.subnet.SubnetList(
+ objects=[])))
+
+ self.assertObjEqual(expect, actual)
+
+ def test_nova_to_osvif_vif_agilio_ovs_forwarder(self):
+ vif = model.VIF(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ type=model.VIF_TYPE_AGILIO_OVS,
+ address="22:52:25:62:e2:aa",
+ profile={
+ "pci_slot": "0000:08:08.5",
+ },
+ network=model.Network(
+ id="b82c1929-051e-481d-8110-4669916c7915",
+ label="Demo Net",
+ subnets=[]),
+ vnic_type=model.VNIC_TYPE_VIRTIO_FORWARDER,
+ details={
+ model.VIF_DETAILS_VHOSTUSER_MODE: 'client',
+ model.VIF_DETAILS_VHOSTUSER_OVS_PLUG: True,
+ model.VIF_DETAILS_VHOSTUSER_SOCKET: '/fake/socket',
+ }
+ )
+
+ actual = os_vif_util.nova_to_osvif_vif(vif)
+
+ expect = osv_objects.vif.VIFVHostUser(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ active=False,
+ address="22:52:25:62:e2:aa",
+ has_traffic_filtering=False,
+ plugin="agilio_ovs",
+ port_profile=osv_objects.vif.VIFPortProfileOVSRepresentor(
+ interface_id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ representor_address="0000:08:08.5",
+ representor_name="nicdc065497-3c",),
+ preserve_on_delete=False,
+ vif_name="nicdc065497-3c",
+ path='/fake/socket',
+ mode='client',
+ network=osv_objects.network.Network(
+ id="b82c1929-051e-481d-8110-4669916c7915",
+ bridge_interface=None,
+ label="Demo Net",
+ subnets=osv_objects.subnet.SubnetList(
+ objects=[])))
+
+ self.assertObjEqual(expect, actual)
+
def test_nova_to_osvif_vif_ovs_plain(self):
vif = model.VIF(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py
index 8711cc023f7f..48af5e564323 100644
--- a/nova/tests/unit/virt/libvirt/test_vif.py
+++ b/nova/tests/unit/virt/libvirt/test_vif.py
@@ -105,6 +105,35 @@ class LibvirtVifTestCase(test.NoDBTestCase):
bridge_interface=None,
vlan=99)
+ vif_agilio_ovs = network_model.VIF(id='vif-xxx-yyy-zzz',
+ address='ca:fe:de:ad:be:ef',
+ network=network_ovs,
+ type=network_model.VIF_TYPE_AGILIO_OVS,
+ details={'port_filter': False},
+ devname='tap-xxx-yyy-zzz',
+ ovs_interfaceid='aaa-bbb-ccc')
+
+ vif_agilio_ovs_direct = network_model.VIF(id='vif-xxx-yyy-zzz',
+ address='ca:fe:de:ad:be:ef',
+ network=network_ovs,
+ type=network_model.VIF_TYPE_AGILIO_OVS,
+ vnic_type=network_model.VNIC_TYPE_DIRECT,
+ ovs_interfaceid='aaa-bbb-ccc',
+ devname='tap-xxx-yyy-zzz',
+ profile={'pci_slot': '0000:0a:00.1'})
+
+ vif_agilio_ovs_forwarder = network_model.VIF(id='vif-xxx-yyy-zzz',
+ address='ca:fe:de:ad:be:ef',
+ network=network_ovs,
+ type=network_model.VIF_TYPE_AGILIO_OVS,
+ vnic_type=network_model.VNIC_TYPE_VIRTIO_FORWARDER,
+ profile={'pci_slot': '0000:0a:00.1'},
+ details={
+ network_model.VIF_DETAILS_VHOSTUSER_MODE: 'client',
+ network_model.VIF_DETAILS_VHOSTUSER_SOCKET: '/tmp/usv-xxx-yyy-zzz',
+ network_model.VIF_DETAILS_VHOSTUSER_OVS_PLUG: True},
+ ovs_interfaceid='aaa-bbb-ccc', mtu=1500)
+
vif_ovs = network_model.VIF(id='vif-xxx-yyy-zzz',
address='ca:fe:de:ad:be:ef',
network=network_ovs,
@@ -370,6 +399,41 @@ class LibvirtVifTestCase(test.NoDBTestCase):
interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9",
profile_id="fishfood")
+ self.os_vif_repr_prof = osv_objects.vif.VIFPortProfileOVSRepresentor(
+ interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9",
+ profile_id="fishfood",
+ representor_name='nicdc065497-3c',
+ representor_address='0000:0a:00.1')
+
+ self.os_vif_agilio_ovs = osv_objects.vif.VIFOpenVSwitch(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ address="22:52:25:62:e2:aa",
+ plugin="agilio_ovs",
+ vif_name="nicdc065497-3c",
+ bridge_name="br0",
+ port_profile=self.os_vif_ovs_prof,
+ network=self.os_vif_network)
+
+ self.os_vif_agilio_forwarder = osv_objects.vif.VIFVHostUser(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ address="22:52:25:62:e2:aa",
+ plugin="agilio_ovs",
+ vif_name="nicdc065497-3c",
+ path='/var/run/openvswitch/vhudc065497-3c',
+ mode='client',
+ port_profile=self.os_vif_repr_prof,
+ network=self.os_vif_network)
+
+ self.os_vif_agilio_direct = osv_objects.vif.VIFHostDevice(
+ id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
+ address="22:52:25:62:e2:aa",
+ plugin="agilio_ovs",
+ vif_name="nicdc065497-3c",
+ dev_type=osv_fields.VIFHostDeviceDevType.ETHERNET,
+ dev_address='0000:0a:00.1',
+ port_profile=self.os_vif_repr_prof,
+ network=self.os_vif_network)
+
self.os_vif_ovs = osv_objects.vif.VIFOpenVSwitch(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
address="22:52:25:62:e2:aa",
@@ -1416,6 +1480,32 @@ class LibvirtVifTestCase(test.NoDBTestCase):
self._assertMacEquals(node, self.vif_vhostuser_ovs)
self._assertModel(xml, network_model.VIF_MODEL_VIRTIO)
+ def test_agilio_ovs_direct(self):
+ d = vif.LibvirtGenericVIFDriver()
+ xml = self._get_instance_xml(d, self.vif_agilio_ovs_direct)
+ node = self._get_node(xml)
+ self._assertTypeAndPciEquals(node,
+ "hostdev",
+ self.vif_agilio_ovs_direct)
+ self._assertMacEquals(node, self.vif_agilio_ovs_direct)
+
+ def test_agilio_ovs_forwarder(self):
+ d = vif.LibvirtGenericVIFDriver()
+ xml = self._get_instance_xml(d,
+ self.vif_agilio_ovs_forwarder)
+ node = self._get_node(xml)
+ self.assertEqual(node.get("type"),
+ network_model.VIF_TYPE_VHOSTUSER)
+
+ self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER,
+ "source", "mode", "client")
+ self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER,
+ "source", "path", "/tmp/usv-xxx-yyy-zzz")
+ self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER,
+ "source", "type", "unix")
+ self._assertMacEquals(node, self.vif_agilio_ovs_forwarder)
+ self._assertModel(xml, network_model.VIF_MODEL_VIRTIO)
+
@mock.patch("nova.network.os_vif_util.nova_to_osvif_instance")
@mock.patch("nova.network.os_vif_util.nova_to_osvif_vif")
@mock.patch.object(os_vif, "plug")
@@ -1521,6 +1611,86 @@ class LibvirtVifTestCase(test.NoDBTestCase):
""", cfg.to_xml())
+ @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance")
+ @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif")
+ def test_config_os_vif_agilio_ovs_fallthrough(self, mock_convert_vif,
+ mock_convert_inst):
+ mock_convert_vif.return_value = self.os_vif_agilio_ovs
+ mock_convert_inst.return_value = self.os_vif_inst_info
+
+ d = vif.LibvirtGenericVIFDriver()
+ hostimpl = host.Host("qemu:///system")
+ flavor = objects.Flavor(name='m1.small')
+ image_meta = objects.ImageMeta.from_dict({})
+ d = vif.LibvirtGenericVIFDriver()
+ cfg = d.get_config(self.instance, self.vif_agilio_ovs,
+ image_meta, flavor,
+ CONF.libvirt.virt_type,
+ hostimpl)
+
+ self._assertXmlEqual("""
+
+
+
+
+
+
+
+
+ """, cfg.to_xml())
+
+ @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance")
+ @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif")
+ def test_config_os_vif_agilio_ovs_forwarder(self, mock_convert_vif,
+ mock_convert_inst):
+ mock_convert_vif.return_value = self.os_vif_agilio_forwarder
+ mock_convert_inst.return_value = self.os_vif_inst_info
+
+ d = vif.LibvirtGenericVIFDriver()
+ hostimpl = host.Host("qemu:///system")
+ flavor = objects.Flavor(name='m1.small')
+ image_meta = objects.ImageMeta.from_dict({})
+ d = vif.LibvirtGenericVIFDriver()
+ cfg = d.get_config(self.instance, self.vif_agilio_ovs_forwarder,
+ image_meta, flavor,
+ CONF.libvirt.virt_type,
+ hostimpl)
+
+ self._assertXmlEqual("""
+
+
+
+
+ """, cfg.to_xml())
+
+ @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance")
+ @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif")
+ def test_config_os_vif_agilio_ovs_direct(self, mock_convert_vif,
+ mock_convert_inst):
+ mock_convert_vif.return_value = self.os_vif_agilio_direct
+ mock_convert_inst.return_value = self.os_vif_inst_info
+
+ d = vif.LibvirtGenericVIFDriver()
+ hostimpl = host.Host("qemu:///system")
+ flavor = objects.Flavor(name='m1.small')
+ image_meta = objects.ImageMeta.from_dict({})
+ d = vif.LibvirtGenericVIFDriver()
+ cfg = d.get_config(self.instance, self.vif_agilio_ovs_direct,
+ image_meta, flavor,
+ CONF.libvirt.virt_type,
+ hostimpl)
+
+ self._assertXmlEqual("""
+
+
+
+ """, cfg.to_xml())
+
@mock.patch("nova.network.os_vif_util.nova_to_osvif_instance")
@mock.patch("nova.network.os_vif_util.nova_to_osvif_vif")
def test_config_os_vif_ovs(self, mock_convert_vif, mock_convert_inst):
diff --git a/releasenotes/notes/netronome-smartnic-enablement-d3897fb294429282.yaml b/releasenotes/notes/netronome-smartnic-enablement-d3897fb294429282.yaml
new file mode 100644
index 000000000000..63b534e8470a
--- /dev/null
+++ b/releasenotes/notes/netronome-smartnic-enablement-d3897fb294429282.yaml
@@ -0,0 +1,14 @@
+---
+features:
+ - This release adds support for Netronome's Agilio OVS VIF type. In order
+ to use the accelerated plugging modes, external Neutron and OS-VIF plugins
+ are required. Consult
+ https://github.com/Netronome/agilio-ovs-openstack-plugin for installation
+ and operation instructions.
+ Consult the Agilio documentation available at
+ https://support.netronome.com/ for more information about the plugin
+ compatibility and support matrix.
+ - The ``virtio-forwarder`` VNIC type has been added to the list of VNICs.
+ This VNIC type is intended to request a low-latency virtio port inside the
+ instance, likely backed by hardware acceleration. Currently the Agilio OVS
+ external Neutron and OS-VIF plugins provide support for this VNIC mode.