Netronome SmartNIC Enablement

These patches enable Netronome Agilio SmartNIC acceleration in Nova.

* This patch set adds VIF_TYPE_AGILIO_OVS to hook in the external ML2
  plugin.

* This patch set adds VNIC_TYPE_VIRTIO_FORWARDER to enable the
  virtio-forwarder plugging mode.

* nova/network/neutronv2/api.py has been modified to pass the bridge
  in the os-vif objects, similar to other OVS VIF types.

* os_vif_util.py has been extended to handle the agilio_ovs os-vif
  plugin.

* VIFPortProfileOVSRepresentor is used to pass the PCI address and vif
  name to the os-vif plugin.

* Note: this enables the external ML2 and OS-VIF plugin, hosted at
  https://github.com/Netronome/agilio-ovs-openstack-plugin
  to support the out-of-tree version of Agilio OVS. Consult the Agilio
  documentation for configuration recommendations.

Change-Id: I3da059fb62f085ff04532f8ec05857acfb628b06
Depends-On: Id95a9f311e71b3cefb170704728641a4a1a91144
Signed-off-by: Jan Gutter <jan.gutter@netronome.com>
This commit is contained in:
Jan Gutter 2017-07-13 18:04:04 +02:00
parent 15500b89be
commit 2bd008ef5c
6 changed files with 354 additions and 2 deletions

View File

@ -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

View File

@ -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']

View File

@ -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):

View File

@ -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",

View File

@ -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):
<target dev="nicdc065497-3c"/>
</interface>""", 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("""
<interface type="bridge">
<mac address="22:52:25:62:e2:aa"/>
<model type="virtio"/>
<source bridge="br0"/>
<target dev="nicdc065497-3c"/>
<virtualport type="openvswitch">
<parameters
interfaceid="07bd6cea-fb37-4594-b769-90fc51854ee9"/>
</virtualport>
</interface>""", 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("""
<interface type="vhostuser">
<mac address="22:52:25:62:e2:aa"/>
<model type="virtio"/>
<source mode="client"
path="/var/run/openvswitch/vhudc065497-3c" type="unix"/>
</interface>""", 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("""
<interface type="hostdev" managed="yes">
<mac address="22:52:25:62:e2:aa"/>
<source>
<address type="pci" domain="0x0000"
bus="0x0a" slot="0x00" function="0x1"/>
</source>
</interface>""", 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):

View File

@ -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.