Add support for vhost-user

- This change extends the ovs plugin to add support for vhost-user.
- Following this change the ovs plugin will accpet VIFVHostUser
  os-vif vif objects as part of plug and unplug function calls.
- This change will be consumed by the reference openvswitch
  ml2 driver, networking-odl and networking-ovn to enable
  vhost-user with openvswitch.

Change-Id: Ifa53e5e821093459ab7a390ff8af26028b2a9056
This commit is contained in:
Sean Mooney 2016-05-10 19:38:19 +01:00
parent ad13b337be
commit 8c58816f46
6 changed files with 155 additions and 15 deletions

View File

@ -0,0 +1,6 @@
---
features:
- The ovs plugin has been extended to support
vhost-user interfaces. vhost-user is a userspace
protocol for high speed virtual networking introduced
in qemu 2.1 and first supported in ovs 2.4 with dpdk 2.0

13
vif_plug_ovs/constants.py Normal file
View File

@ -0,0 +1,13 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
OVS_VHOSTUSER_INTERFACE_TYPE = 'dpdkvhostuser'

View File

@ -25,6 +25,7 @@ from oslo_concurrency import processutils
from oslo_log import log as logging
from oslo_utils import excutils
from vif_plug_ovs import constants
from vif_plug_ovs import exception
from vif_plug_ovs.i18n import _LE
from vif_plug_ovs import privsep
@ -45,18 +46,35 @@ def _ovs_vsctl(args, timeout=None):
raise exception.AgentError(method=full_args)
def _create_ovs_vif_cmd(bridge, dev, iface_id, mac,
instance_id, interface_type=None):
cmd = ['--', '--if-exists', 'del-port', dev, '--',
'add-port', bridge, dev,
'--', 'set', 'Interface', dev,
'external-ids:iface-id=%s' % iface_id,
'external-ids:iface-status=active',
'external-ids:attached-mac=%s' % mac,
'external-ids:vm-uuid=%s' % instance_id]
if interface_type:
cmd += ['type=%s' % interface_type]
return cmd
@privsep.vif_plug.entrypoint
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id, mtu,
timeout=None):
_ovs_vsctl(['--', '--if-exists', 'del-port', dev, '--',
'add-port', bridge, dev,
'--', 'set', 'Interface', dev,
'external-ids:iface-id=%s' % iface_id,
'external-ids:iface-status=active',
'external-ids:attached-mac=%s' % mac,
'external-ids:vm-uuid=%s' % instance_id],
timeout=timeout)
_set_device_mtu(dev, mtu)
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id,
mtu=None, interface_type=None):
_ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id,
mac, instance_id,
interface_type))
# Note at present there is no support for setting the
# mtu for vhost-user type ports.
if interface_type != constants.OVS_VHOSTUSER_INTERFACE_TYPE:
_set_device_mtu(dev, mtu)
else:
LOG.debug("MTU not set on %(interface_name)s interface "
"of type %(interface_type)s.",
{'interface_name': dev,
'interface_type': interface_type})
@privsep.vif_plug.entrypoint

View File

@ -21,6 +21,7 @@ from os_vif import objects
from os_vif import plugin
from oslo_config import cfg
from vif_plug_ovs import constants
from vif_plug_ovs import exception
from vif_plug_ovs import linux_net
@ -52,11 +53,14 @@ class OvsPlugin(plugin.PluginBase):
deprecated_group="DEFAULT"),
)
@staticmethod
def gen_port_name(prefix, id):
return ("%s%s" % (prefix, id))[:OvsPlugin.NIC_NAME_LEN]
@staticmethod
def get_veth_pair_names(vif):
iface_id = vif.id
return (("qvb%s" % iface_id)[:OvsPlugin.NIC_NAME_LEN],
("qvo%s" % iface_id)[:OvsPlugin.NIC_NAME_LEN])
return (OvsPlugin.gen_port_name("qvb", vif.id),
OvsPlugin.gen_port_name("qvo", vif.id))
def describe(self):
return objects.host_info.HostPluginInfo(
@ -69,9 +73,23 @@ class OvsPlugin(plugin.PluginBase):
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFOpenVSwitch.__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")
])
def _plug_vhostuser(self, vif, instance_info):
linux_net.create_ovs_vif_port(
vif.network.bridge,
OvsPlugin.gen_port_name("vhu", vif.id),
vif.port_profile.interface_id,
vif.address, instance_info.uuid,
self.config.network_device_mtu,
timeout=self.config.ovs_vsctl_timeout,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
def _plug_bridge(self, vif, instance_info):
"""Plug using hybrid strategy
@ -107,6 +125,13 @@ class OvsPlugin(plugin.PluginBase):
if isinstance(vif, objects.vif.VIFBridge):
self._plug_bridge(vif, instance_info)
elif isinstance(vif, objects.vif.VIFVHostUser):
self._plug_vhostuser(vif, instance_info)
def _unplug_vhostuser(self, vif, instance_info):
linux_net.delete_ovs_vif_port(vif.network.bridge,
OvsPlugin.gen_port_name("vhu", vif.id),
timeout=self.config.ovs_vsctl_timeout)
def _unplug_bridge(self, vif, instance_info):
"""UnPlug using hybrid strategy
@ -132,3 +157,5 @@ class OvsPlugin(plugin.PluginBase):
if isinstance(vif, objects.vif.VIFBridge):
self._unplug_bridge(vif, instance_info)
elif isinstance(vif, objects.vif.VIFVHostUser):
self._unplug_vhostuser(vif, instance_info)

View File

@ -18,10 +18,10 @@ import testtools
from oslo_concurrency import processutils
from vif_plug_ovs import constants
from vif_plug_ovs import linux_net
from vif_plug_ovs import privsep
if six.PY2:
nested = contextlib.nested
else:
@ -118,3 +118,41 @@ class LinuxNetTest(testtools.TestCase):
mock.call('ip', 'link', 'set', 'br0', 'up'),
mock.call('brctl', 'addif', 'br0', 'vnet1'),
])
def test_ovs_vif_port_cmd(self):
expected = ['--', '--if-exists',
'del-port', 'fake-dev', '--', 'add-port',
'fake-bridge', 'fake-dev',
'--', 'set', 'Interface', 'fake-dev',
'external-ids:iface-id=fake-iface-id',
'external-ids:iface-status=active',
'external-ids:attached-mac=fake-mac',
'external-ids:vm-uuid=fake-instance-uuid']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid')
self.assertEqual(expected, cmd)
expected += ['type=fake-type']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid',
'fake-type')
self.assertEqual(expected, cmd)
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_type_vhostuser(self, mock_set_device_mtu,
mock_create_cmd, mock_vsctl):
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", mtu=1500,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
mock_create_cmd.assert_called_once_with('fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", constants.OVS_VHOSTUSER_INTERFACE_TYPE)
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)

View File

@ -59,6 +59,7 @@ class PluginTest(testtools.TestCase):
self.profile_ovs = objects.vif.VIFPortProfileOpenVSwitch(
interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa')
self.vif_ovs = objects.vif.VIFBridge(
id='b679325f-ca89-4ee0-a8be-6db1409b69ea',
address='ca:fe:de:ad:be:ef',
@ -67,6 +68,14 @@ class PluginTest(testtools.TestCase):
bridge_name="qbrvif-xxx-yyy",
port_profile=self.profile_ovs)
self.vif_vhostuser = objects.vif.VIFVHostUser(
id='b679325f-ca89-4ee0-a8be-6db1409b69ea',
address='ca:fe:de:ad:be:ef',
network=self.network_ovs,
path='/var/run/openvswitch/vhub679325f-ca',
mode='client',
port_profile=self.profile_ovs)
self.instance = objects.instance_info.InstanceInfo(
name='demo',
uuid='f0000000-0000-0000-0000-000000000001')
@ -120,3 +129,32 @@ class PluginTest(testtools.TestCase):
plugin.unplug(self.vif_ovs, self.instance)
delete_bridge.assert_has_calls(calls['delete_bridge'])
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])
def test_plug_ovs_vhostuser(self):
calls = {
'create_ovs_vif_port': [mock.call(
'br0', 'vhub679325f-ca',
'e65867e0-9340-4a7f-a256-09af6eb7a3aa',
'ca:fe:de:ad:be:ef',
'f0000000-0000-0000-0000-000000000001',
1500,
interface_type='dpdkvhostuser',
timeout=120)]
}
with mock.patch.object(linux_net, 'create_ovs_vif_port') \
as (create_ovs_vif_port):
plugin = ovs.OvsPlugin.load("ovs")
plugin.plug(self.vif_vhostuser, self.instance)
create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port'])
def test_unplug_ovs_vhostuser(self):
calls = {
'delete_ovs_vif_port': [mock.call('br0', 'vhub679325f-ca',
timeout=120)]
}
with mock.patch.object(linux_net, 'delete_ovs_vif_port') \
as delete_ovs_vif_port:
plugin = ovs.OvsPlugin.load("ovs")
plugin.unplug(self.vif_vhostuser, self.instance)
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])