add support for vhost-user reconnect

- This change introduces support for createing
  dpdkvhostuserclient type port in ovs.
- This change allows vhost-user to be configured with
  qemu as the vhost server and ovs as the client allowing
  the vhost frontend to reconnect to the backend in
  the event the backend restarts.

Change-Id: Ie22e12c9578a2f5bd89a37b9e80efef7ae055b7a
This commit is contained in:
Sean Mooney 2016-08-19 00:06:21 +00:00
parent 345ff074b5
commit e2fa516b91
6 changed files with 103 additions and 11 deletions

View File

@ -0,0 +1,14 @@
---
features:
- vhost-user reconnect is a new feature of qemu that
allows a vhost-user frontend(e.g. qemu) to reconnect
to a vhost-user backend (e.g. ovs with dpdk) in the event
that backend is restarted while the interface is in use.
vhost-user reconnect leverages qemu vhost-user server mode
with ovs-dpdk in client mode. This configuration requires
ovs 2.6 with dpdk 16.07 and qemu 2.7 or newer to function.
When qemu server mode is used with older qemu versions such
as 2.5, vhost-user will still function with ovs 2.6 and
dpdk 16.07, however, reconnect functionality will not be
available.

View File

@ -11,6 +11,8 @@
# under the License.
OVS_VHOSTUSER_INTERFACE_TYPE = 'dpdkvhostuser'
OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE = 'dpdkvhostuserclient'
OVS_VHOSTUSER_PREFIX = 'vhu'
OVS_DATAPATH_SYSTEM = 'system'
OVS_DATAPATH_NETDEV = 'netdev'

View File

@ -47,7 +47,8 @@ def _ovs_vsctl(args, timeout=None):
def _create_ovs_vif_cmd(bridge, dev, iface_id, mac,
instance_id, interface_type=None):
instance_id, interface_type=None,
vhost_server_path=None):
cmd = ['--', '--if-exists', 'del-port', dev, '--',
'add-port', bridge, dev,
'--', 'set', 'Interface', dev,
@ -57,6 +58,8 @@ def _create_ovs_vif_cmd(bridge, dev, iface_id, mac,
'external-ids:vm-uuid=%s' % instance_id]
if interface_type:
cmd += ['type=%s' % interface_type]
if vhost_server_path:
cmd += ['options:vhost-server-path=%s' % vhost_server_path]
return cmd
@ -67,13 +70,16 @@ def _create_ovs_bridge_cmd(bridge, datapath_type):
@privsep.vif_plug.entrypoint
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id,
mtu=None, interface_type=None, timeout=None):
mtu=None, interface_type=None, timeout=None,
vhost_server_path=None):
_ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id,
mac, instance_id,
interface_type), timeout=timeout)
mac, instance_id, interface_type,
vhost_server_path), timeout=timeout)
# Note at present there is no support for setting the
# mtu for vhost-user type ports.
if mtu and interface_type != constants.OVS_VHOSTUSER_INTERFACE_TYPE:
if mtu and interface_type not in [
constants.OVS_VHOSTUSER_INTERFACE_TYPE,
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE]:
_set_device_mtu(dev, mtu)
else:
LOG.debug("MTU not set on %(interface_name)s interface "

View File

@ -99,10 +99,19 @@ class OvsPlugin(plugin.PluginBase):
def _plug_vhostuser(self, vif, instance_info):
linux_net.ensure_ovs_bridge(vif.network.bridge,
constants.OVS_DATAPATH_NETDEV)
vif_name = OvsPlugin.gen_port_name("vhu", vif.id)
vif_name = OvsPlugin.gen_port_name(
constants.OVS_VHOSTUSER_PREFIX, vif.id)
args = {}
if vif.mode == "client":
args['interface_type'] = \
constants.OVS_VHOSTUSER_INTERFACE_TYPE
else:
args['interface_type'] = \
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE
args['vhost_server_path'] = vif.path
self._create_vif_port(
vif, vif_name, instance_info,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
vif, vif_name, instance_info, **args)
def _plug_bridge(self, vif, instance_info):
"""Plug using hybrid strategy
@ -160,7 +169,9 @@ class OvsPlugin(plugin.PluginBase):
def _unplug_vhostuser(self, vif, instance_info):
linux_net.delete_ovs_vif_port(vif.network.bridge,
OvsPlugin.gen_port_name("vhu", vif.id),
OvsPlugin.gen_port_name(
constants.OVS_VHOSTUSER_PREFIX,
vif.id),
timeout=self.config.ovs_vsctl_timeout)
def _unplug_bridge(self, vif, instance_info):

View File

@ -131,6 +131,14 @@ class LinuxNetTest(testtools.TestCase):
'fake-type')
self.assertEqual(expected, cmd)
expected += ['options:vhost-server-path=/fake/path']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid',
'fake-type',
vhost_server_path='/fake/path')
self.assertEqual(expected, cmd)
@mock.patch.object(linux_net, '_create_ovs_bridge_cmd')
@mock.patch.object(linux_net, '_ovs_vsctl')
def test_ensure_ovs_bridge(self, mock_vsctl, mock_create_ovs_bridge):
@ -161,7 +169,27 @@ class LinuxNetTest(testtools.TestCase):
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)
"fake-instance-uuid", constants.OVS_VHOSTUSER_INTERFACE_TYPE,
None)
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)
@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_vhostuserclient(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_CLIENT_INTERFACE_TYPE,
vhost_server_path="/fake/path")
mock_create_cmd.assert_called_once_with('fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid",
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE,
"/fake/path")
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)
@ -176,7 +204,7 @@ class LinuxNetTest(testtools.TestCase):
"fake-instance-uuid")
mock_create_cmd.assert_called_once_with('fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", None)
"fake-instance-uuid", None, None)
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)

View File

@ -80,6 +80,14 @@ class PluginTest(testtools.TestCase):
mode='client',
port_profile=self.profile_ovs)
self.vif_vhostuser_client = 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='server', # qemu server mode <=> ovs client mode
port_profile=self.profile_ovs)
self.instance = objects.instance_info.InstanceInfo(
name='demo',
uuid='f0000000-0000-0000-0000-000000000001')
@ -242,6 +250,29 @@ class PluginTest(testtools.TestCase):
_create_vif_port.assert_has_calls(calls['_create_vif_port'])
ensure_ovs_bridge.assert_has_calls(calls['ensure_ovs_bridge'])
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(linux_net, 'create_ovs_vif_port')
def test_plug_ovs_vhostuser_client(self, create_ovs_vif_port,
ensure_ovs_bridge):
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='dpdkvhostuserclient',
vhost_server_path='/var/run/openvswitch/vhub679325f-ca',
timeout=120)],
'ensure_ovs_bridge': [mock.call('br0',
constants.OVS_DATAPATH_NETDEV)]
}
plugin = ovs.OvsPlugin.load("ovs")
plugin.plug(self.vif_vhostuser_client, self.instance)
create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port'])
ensure_ovs_bridge.assert_has_calls(calls['ensure_ovs_bridge'])
@mock.patch.object(linux_net, 'delete_ovs_vif_port')
def test_unplug_ovs_vhostuser(self, delete_ovs_vif_port):
calls = {