Skip to content

Commit

Permalink
Add support for offload plugging
Browse files Browse the repository at this point in the history
This enables the Contrail / Tungsten Fabric os-vif plugin to consume the
metadata made available by Nova since the Stein release:

https://specs.openstack.org/openstack/nova-specs/specs/stein/approved/vrouter-hw-offloads.html

With os-vif < 1.14.0, this change has no functional effect.

With os-vif >= 1.14.0, this change enables two new plugging modes:

  * 'direct' VNIC type: this corresponds to an offloaded vrouter
    datapath, plugged into the instance via IOMMU passthrough.
  * 'virtio-forwarder' VNIC type: this corresponds to an offloaded
    vrouter datapath, plugged into the instance via a virtio-forwarder.

Change-Id: Ib37eed922b30c20d11327bf792ad869de70f0bb0
Signed-off-by: Jan Gutter <jan.gutter@netronome.com>
Partial-Bug: #1767107
  • Loading branch information
Jan Gutter committed Jan 28, 2019
1 parent 51bcb0a commit ae45f3b
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 18 deletions.
151 changes: 151 additions & 0 deletions vif_plug_vrouter/tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,51 @@ def __init__(self, *args, **kwargs):
privsep.vif_plug.set_client_mode(False)
self.test_env = dict(os.environ)
self.test_env['PATH'] = self.test_env['PATH'] + ':/opt/plugin/bin'
if hasattr(objects.vif, 'DatapathOffloadRepresentor'):
# os-vif supports offloads
self.offload_subnet_bridge_4 = objects.subnet.Subnet(
cidr='101.168.1.0/24',
dns=['8.8.8.8'],
gateway='101.168.1.1',
dhcp_server='191.168.1.1'
)
self.offload_subnet_bridge_6 = objects.subnet.Subnet(
cidr='101:1db9::/64',
gateway='101:1db9::1'
)
self.offload_subnets = objects.subnet.SubnetList(
objects=[self.offload_subnet_bridge_4,
self.offload_subnet_bridge_6]
)
self.offload_network = objects.network.Network(
id='f0ff5378-7367-4451-9202-829b068143f3',
bridge='br0',
subnets=self.offload_subnets,
vlan=99)
self.vif_vrouter_direct = objects.vif.VIFHostDevice(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
address="22:52:25:62:e2:aa",
dev_type=objects.fields.VIFHostDeviceDevType.ETHERNET,
dev_address="0000:08:08.5",
port_profile=objects.vif.VIFPortProfileBase(
datapath_offload=objects.vif.DatapathOffloadRepresentor(
representor_name="nicdc065497-3c",
representor_address="0000:08:08.5")
),
vif_name="nicdc065497-3c",
network=self.offload_network)
self.vif_vrouter_forwarder = objects.vif.VIFVHostUser(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
address="22:52:25:62:e2:aa",
vif_name="nicdc065497-3c",
path='/fake/socket',
mode='client',
port_profile=objects.vif.VIFPortProfileBase(
datapath_offload=objects.vif.DatapathOffloadRepresentor(
representor_address="0000:08:08.5",
representor_name="nicdc065497-3c")
),
network=self.offload_network)

subnet_bridge_4 = objects.subnet.Subnet(
cidr='101.168.1.0/24',
Expand Down Expand Up @@ -193,6 +238,112 @@ def test_vrouter_vhostuser_port_delete(self):

execute_cmd.assert_has_calls(calls['execute'])

def test_unplug_vrouter_direct(self):
if not hasattr(objects.vif, 'DatapathOffloadRepresentor'):
# This version of os-vif does not support offloads
return
with mock.patch.object(processutils, 'execute') as execute:
plugin = vrouter.VrouterPlugin.load("vrouter")
plugin.unplug(self.vif_vrouter_direct, self.instance)
execute.assert_has_calls([
mock.call(
'vrouter-port-control',
'--oper=delete',
'--uuid=dc065497-3c8d-4f44-8fb4-e1d33c16a536',
'--vnic_type=direct',
'--pci_dev=0000:08:08.5',
env_variables=self.test_env
),
])

def test_plug_vrouter_direct(self):
if not hasattr(objects.vif, 'DatapathOffloadRepresentor'):
# This version of os-vif does not support offloads
return
instance = mock.Mock()
instance.name = 'instance-name'
instance.uuid = '46a4308b-e75a-4f90-a34a-650c86ca18b2'
instance.project_id = 'b168ea26fa0c49c1a84e1566d9565fa5'
with mock.patch.object(processutils, 'execute') as execute:
plugin = vrouter.VrouterPlugin.load("vrouter")
plugin.plug(self.vif_vrouter_direct, instance)
execute.assert_has_calls([
mock.call(
'vrouter-port-control',
'--oper=add',
'--uuid=dc065497-3c8d-4f44-8fb4-e1d33c16a536',
'--instance_uuid=46a4308b-e75a-4f90-a34a-650c86ca18b2',
'--vn_uuid=f0ff5378-7367-4451-9202-829b068143f3',
'--vm_project_uuid=b168ea26fa0c49c1a84e1566d9565fa5',
'--ip_address=0.0.0.0',
'--ipv6_address=None',
'--vm_name=instance-name',
'--mac=22:52:25:62:e2:aa',
'--tap_name=nicdc065497-3c',
'--port_type=NovaVMPort',
'--vnic_type=direct',
'--pci_dev=0000:08:08.5',
'--tx_vlan_id=-1',
'--rx_vlan_id=-1',
env_variables=self.test_env)
],
)

def test_unplug_vrouter_forwarder(self):
if not hasattr(objects.vif, 'DatapathOffloadRepresentor'):
# This version of os-vif does not support offloads
return
with mock.patch.object(processutils, 'execute') as execute:
plugin = vrouter.VrouterPlugin.load("vrouter")
plugin.unplug(self.vif_vrouter_forwarder, self.instance)
execute.assert_called_once_with(
'vrouter-port-control',
'--oper=delete',
'--uuid=dc065497-3c8d-4f44-8fb4-e1d33c16a536',
'--tap_name=nicdc065497-3c',
'--vnic_type=virtio-forwarder',
'--pci_dev=0000:08:08.5',
'--vhostuser_socket=/fake/socket',
'--vhostuser_mode=0',
env_variables=self.test_env
)

def test_plug_vrouter_forwarder(self):
if not hasattr(objects.vif, 'DatapathOffloadRepresentor'):
# This version of os-vif does not support offloads
return
instance = mock.Mock()
instance.name = 'instance-name'
instance.uuid = '46a4308b-e75a-4f90-a34a-650c86ca18b2'
instance.project_id = 'b168ea26fa0c49c1a84e1566d9565fa5'
with mock.patch.object(processutils, 'execute') as execute:
plugin = vrouter.VrouterPlugin.load("vrouter")
plugin.plug(self.vif_vrouter_forwarder, instance)
execute.assert_has_calls([
mock.call(
'vrouter-port-control',
'--oper=add',
'--uuid=dc065497-3c8d-4f44-8fb4-e1d33c16a536',
'--instance_uuid=46a4308b-e75a-4f90-a34a-650c86ca18b2',
'--vn_uuid=f0ff5378-7367-4451-9202-829b068143f3',
'--vm_project_uuid=b168ea26fa0c49c1a84e1566d9565fa5',
'--ip_address=0.0.0.0',
'--ipv6_address=None',
'--vm_name=instance-name',
'--mac=22:52:25:62:e2:aa',
'--tap_name=nicdc065497-3c',
'--port_type=NovaVMPort',
'--vif_type=VhostUser',
'--vnic_type=virtio-forwarder',
'--pci_dev=0000:08:08.5',
'--vhostuser_socket=/fake/socket',
'--vhostuser_mode=0',
'--tx_vlan_id=-1',
'--rx_vlan_id=-1',
env_variables=self.test_env)
]
)

def test_unplug_vrouter(self):
with mock.patch.object(processutils, 'execute') as execute:
plugin = vrouter.VrouterPlugin.load("vrouter")
Expand Down
110 changes: 92 additions & 18 deletions vif_plug_vrouter/vrouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,12 @@ def plug_contrail_vif(vif_id, vm_id, net_id, project_id, ip_addr, ip6_addr,


@privsep.vif_plug.entrypoint
def unplug_contrail_vif(port_id, pci_dev=None, vnic_type=None,
vhostuser_mode=None, vhostuser_socket=None):
def unplug_contrail_vif(port_id, dev_name=None, vnic_type=None, pci_dev=None,
vhostuser_socket=None, vhostuser_mode=None):
"""Call the vrouter port control script to unplug a vif
:param port_id: VIF ID to unplug
:param dev_name: Name of the TAP/device to unplug
:param vnic_type: Selector for offload mode (e.g. direct)
:param pci_dev: Virtual Function to assign for offloading
:param vhostuser_socket: vhost-user socket path
Expand All @@ -110,13 +111,15 @@ def unplug_contrail_vif(port_id, pci_dev=None, vnic_type=None,
'--oper=delete',
'--uuid=%s' % port_id,
)
if dev_name:
cmd += ('--tap_name=%s' % dev_name,)
if vnic_type:
cmd += ('--vnic_type=%s' % vnic_type,)
if pci_dev:
cmd += ('--pci_dev=%s' % pci_dev,)
if vhostuser_socket:
cmd += ('--vhostuser_socket=%s' % vhostuser_socket,)
if vhostuser_mode:
if vhostuser_mode is not None:
cmd += ('--vhostuser_mode=%s' % vhostuser_mode,)
try:
env = dict(os.environ)
Expand All @@ -137,25 +140,55 @@ class VrouterPlugin(plugin.PluginBase):
* DPDK vhost-user plugging (VIFVHostUser)
* Classic kernel plugging (vrouter.ko) via TAP device (VIFGeneric)
* direct offloaded kernel datapath (VIFHostDevice)
* indirect offloaded kernel datapath (VIFVHostUser)
This plugin gets called by Nova to plug the VIFs into and unplug them from
the datapath. There is corresponding code in Nova to configure the
hypervisor configure the VM to connect to the required connection method.
"""

def describe(self):
return objects.host_info.HostPluginInfo(
plugin_name="vrouter",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFGeneric.__name__,
min_version="1.0",
max_version="1.0"),
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFVHostUser.__name__,
min_version="1.0",
max_version="1.0")
])
if 'supported_port_profiles' in objects.host_info.HostVIFInfo.fields:
pp = objects.host_info.HostPortProfileInfo(
profile_object_name=
objects.vif.VIFPortProfileBase.__name__,
min_version="1.0",
max_version="1.1"
)
return objects.host_info.HostPluginInfo(
plugin_name="vrouter",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFGeneric.__name__,
min_version="1.0",
max_version="1.0",
supported_port_profiles=[pp]),
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFVHostUser.__name__,
min_version="1.0",
max_version="1.1",
supported_port_profiles=[pp]),
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFHostDevice.__name__,
min_version="1.0",
max_version="1.0",
supported_port_profiles=[pp])
])
else:
# Older versions of os-vif did not feature supported_port_profiles
return objects.host_info.HostPluginInfo(
plugin_name="vrouter",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFGeneric.__name__,
min_version="1.0",
max_version="1.0"),
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFVHostUser.__name__,
min_version="1.0",
max_version="1.0")
])

@staticmethod
def _vrouter_port_add(instance_info, vif):
Expand Down Expand Up @@ -202,6 +235,19 @@ def _vrouter_port_add(instance_info, vif):
else:
vhostuser_mode = VHOSTUSER_MODE_CLIENT

if ('port_profile' in vif and
hasattr(vif, 'port_profile') and
isinstance(vif.port_profile, objects.vif.VIFPortProfileBase)):
if ('datapath_offload' in vif.port_profile and
hasattr(vif.port_profile, 'datapath_offload') and
isinstance(vif.port_profile.datapath_offload,
objects.vif.DatapathOffloadRepresentor)):
if isinstance(vif, objects.vif.VIFVHostUser):
vnic_type = 'virtio-forwarder'
elif isinstance(vif, objects.vif.VIFHostDevice):
vnic_type = 'direct'
pci_dev = vif.port_profile.datapath_offload.representor_address

plug_contrail_vif(vif.id, instance_info.uuid, vif.network.id,
instance_info.project_id, ip_addr, ip6_addr,
instance_info.name, vif.address,
Expand All @@ -210,18 +256,46 @@ def _vrouter_port_add(instance_info, vif):

def plug(self, vif, instance_info):
if not (isinstance(vif, objects.vif.VIFVHostUser) or
isinstance(vif, objects.vif.VIFGeneric)):
isinstance(vif, objects.vif.VIFGeneric) or
isinstance(vif, objects.vif.VIFHostDevice)):
raise exception.VrouterUnknownVIFError(id=vif.id)

self._vrouter_port_add(instance_info, vif)

@staticmethod
def _vrouter_port_delete(instance_info, vif):
unplug_contrail_vif(vif.id)
vhostuser_socket = None
vhostuser_mode = None
vnic_type = None
pci_dev = None
dev_name = None

if ('port_profile' in vif and
hasattr(vif, 'port_profile') and
isinstance(vif.port_profile, objects.vif.VIFPortProfileBase)):
if ('datapath_offload' in vif.port_profile and
hasattr(vif.port_profile, 'datapath_offload') and
isinstance(vif.port_profile.datapath_offload,
objects.vif.DatapathOffloadRepresentor)):
if isinstance(vif, objects.vif.VIFVHostUser):
vnic_type = 'virtio-forwarder'
vhostuser_socket = vif.path
dev_name = vif.vif_name
if vif.mode == 'server':
vhostuser_mode = VHOSTUSER_MODE_SERVER
else:
vhostuser_mode = VHOSTUSER_MODE_CLIENT
elif isinstance(vif, objects.vif.VIFHostDevice):
vnic_type = 'direct'
pci_dev = vif.port_profile.datapath_offload.representor_address

unplug_contrail_vif(vif.id, dev_name, vnic_type, pci_dev,
vhostuser_socket, vhostuser_mode)

def unplug(self, vif, instance_info):
if not (isinstance(vif, objects.vif.VIFVHostUser) or
isinstance(vif, objects.vif.VIFGeneric)):
isinstance(vif, objects.vif.VIFGeneric) or
isinstance(vif, objects.vif.VIFHostDevice)):
raise exception.VrouterUnknownVIFError(id=vif.id)

self._vrouter_port_delete(instance_info, vif)

0 comments on commit ae45f3b

Please sign in to comment.