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:
parent
ad13b337be
commit
8c58816f46
@ -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
13
vif_plug_ovs/constants.py
Normal 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'
|
@ -25,6 +25,7 @@ from oslo_concurrency import processutils
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
|
||||||
|
from vif_plug_ovs import constants
|
||||||
from vif_plug_ovs import exception
|
from vif_plug_ovs import exception
|
||||||
from vif_plug_ovs.i18n import _LE
|
from vif_plug_ovs.i18n import _LE
|
||||||
from vif_plug_ovs import privsep
|
from vif_plug_ovs import privsep
|
||||||
@ -45,18 +46,35 @@ def _ovs_vsctl(args, timeout=None):
|
|||||||
raise exception.AgentError(method=full_args)
|
raise exception.AgentError(method=full_args)
|
||||||
|
|
||||||
|
|
||||||
@privsep.vif_plug.entrypoint
|
def _create_ovs_vif_cmd(bridge, dev, iface_id, mac,
|
||||||
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id, mtu,
|
instance_id, interface_type=None):
|
||||||
timeout=None):
|
cmd = ['--', '--if-exists', 'del-port', dev, '--',
|
||||||
_ovs_vsctl(['--', '--if-exists', 'del-port', dev, '--',
|
|
||||||
'add-port', bridge, dev,
|
'add-port', bridge, dev,
|
||||||
'--', 'set', 'Interface', dev,
|
'--', 'set', 'Interface', dev,
|
||||||
'external-ids:iface-id=%s' % iface_id,
|
'external-ids:iface-id=%s' % iface_id,
|
||||||
'external-ids:iface-status=active',
|
'external-ids:iface-status=active',
|
||||||
'external-ids:attached-mac=%s' % mac,
|
'external-ids:attached-mac=%s' % mac,
|
||||||
'external-ids:vm-uuid=%s' % instance_id],
|
'external-ids:vm-uuid=%s' % instance_id]
|
||||||
timeout=timeout)
|
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=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)
|
_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
|
@privsep.vif_plug.entrypoint
|
||||||
|
@ -21,6 +21,7 @@ from os_vif import objects
|
|||||||
from os_vif import plugin
|
from os_vif import plugin
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from vif_plug_ovs import constants
|
||||||
from vif_plug_ovs import exception
|
from vif_plug_ovs import exception
|
||||||
from vif_plug_ovs import linux_net
|
from vif_plug_ovs import linux_net
|
||||||
|
|
||||||
@ -52,11 +53,14 @@ class OvsPlugin(plugin.PluginBase):
|
|||||||
deprecated_group="DEFAULT"),
|
deprecated_group="DEFAULT"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def gen_port_name(prefix, id):
|
||||||
|
return ("%s%s" % (prefix, id))[:OvsPlugin.NIC_NAME_LEN]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_veth_pair_names(vif):
|
def get_veth_pair_names(vif):
|
||||||
iface_id = vif.id
|
return (OvsPlugin.gen_port_name("qvb", vif.id),
|
||||||
return (("qvb%s" % iface_id)[:OvsPlugin.NIC_NAME_LEN],
|
OvsPlugin.gen_port_name("qvo", vif.id))
|
||||||
("qvo%s" % iface_id)[:OvsPlugin.NIC_NAME_LEN])
|
|
||||||
|
|
||||||
def describe(self):
|
def describe(self):
|
||||||
return objects.host_info.HostPluginInfo(
|
return objects.host_info.HostPluginInfo(
|
||||||
@ -69,9 +73,23 @@ class OvsPlugin(plugin.PluginBase):
|
|||||||
objects.host_info.HostVIFInfo(
|
objects.host_info.HostVIFInfo(
|
||||||
vif_object_name=objects.vif.VIFOpenVSwitch.__name__,
|
vif_object_name=objects.vif.VIFOpenVSwitch.__name__,
|
||||||
min_version="1.0",
|
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")
|
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):
|
def _plug_bridge(self, vif, instance_info):
|
||||||
"""Plug using hybrid strategy
|
"""Plug using hybrid strategy
|
||||||
|
|
||||||
@ -107,6 +125,13 @@ class OvsPlugin(plugin.PluginBase):
|
|||||||
|
|
||||||
if isinstance(vif, objects.vif.VIFBridge):
|
if isinstance(vif, objects.vif.VIFBridge):
|
||||||
self._plug_bridge(vif, instance_info)
|
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):
|
def _unplug_bridge(self, vif, instance_info):
|
||||||
"""UnPlug using hybrid strategy
|
"""UnPlug using hybrid strategy
|
||||||
@ -132,3 +157,5 @@ class OvsPlugin(plugin.PluginBase):
|
|||||||
|
|
||||||
if isinstance(vif, objects.vif.VIFBridge):
|
if isinstance(vif, objects.vif.VIFBridge):
|
||||||
self._unplug_bridge(vif, instance_info)
|
self._unplug_bridge(vif, instance_info)
|
||||||
|
elif isinstance(vif, objects.vif.VIFVHostUser):
|
||||||
|
self._unplug_vhostuser(vif, instance_info)
|
||||||
|
@ -18,10 +18,10 @@ import testtools
|
|||||||
|
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
|
|
||||||
|
from vif_plug_ovs import constants
|
||||||
from vif_plug_ovs import linux_net
|
from vif_plug_ovs import linux_net
|
||||||
from vif_plug_ovs import privsep
|
from vif_plug_ovs import privsep
|
||||||
|
|
||||||
|
|
||||||
if six.PY2:
|
if six.PY2:
|
||||||
nested = contextlib.nested
|
nested = contextlib.nested
|
||||||
else:
|
else:
|
||||||
@ -118,3 +118,41 @@ class LinuxNetTest(testtools.TestCase):
|
|||||||
mock.call('ip', 'link', 'set', 'br0', 'up'),
|
mock.call('ip', 'link', 'set', 'br0', 'up'),
|
||||||
mock.call('brctl', 'addif', 'br0', 'vnet1'),
|
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)
|
||||||
|
@ -59,6 +59,7 @@ class PluginTest(testtools.TestCase):
|
|||||||
|
|
||||||
self.profile_ovs = objects.vif.VIFPortProfileOpenVSwitch(
|
self.profile_ovs = objects.vif.VIFPortProfileOpenVSwitch(
|
||||||
interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa')
|
interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa')
|
||||||
|
|
||||||
self.vif_ovs = objects.vif.VIFBridge(
|
self.vif_ovs = objects.vif.VIFBridge(
|
||||||
id='b679325f-ca89-4ee0-a8be-6db1409b69ea',
|
id='b679325f-ca89-4ee0-a8be-6db1409b69ea',
|
||||||
address='ca:fe:de:ad:be:ef',
|
address='ca:fe:de:ad:be:ef',
|
||||||
@ -67,6 +68,14 @@ class PluginTest(testtools.TestCase):
|
|||||||
bridge_name="qbrvif-xxx-yyy",
|
bridge_name="qbrvif-xxx-yyy",
|
||||||
port_profile=self.profile_ovs)
|
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(
|
self.instance = objects.instance_info.InstanceInfo(
|
||||||
name='demo',
|
name='demo',
|
||||||
uuid='f0000000-0000-0000-0000-000000000001')
|
uuid='f0000000-0000-0000-0000-000000000001')
|
||||||
@ -120,3 +129,32 @@ class PluginTest(testtools.TestCase):
|
|||||||
plugin.unplug(self.vif_ovs, self.instance)
|
plugin.unplug(self.vif_ovs, self.instance)
|
||||||
delete_bridge.assert_has_calls(calls['delete_bridge'])
|
delete_bridge.assert_has_calls(calls['delete_bridge'])
|
||||||
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])
|
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'])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user