From e2fa516b915ba2de8be0d2948edc4826d4298f80 Mon Sep 17 00:00:00 2001 From: Sean Mooney Date: Fri, 19 Aug 2016 00:06:21 +0000 Subject: [PATCH] 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 --- ...vhost-user-reconnect-fa4cbb731b787f71.yaml | 14 ++++++++ vif_plug_ovs/constants.py | 2 ++ vif_plug_ovs/linux_net.py | 16 +++++++--- vif_plug_ovs/ovs.py | 19 ++++++++--- vif_plug_ovs/tests/test_linux_net.py | 32 +++++++++++++++++-- vif_plug_ovs/tests/test_plugin.py | 31 ++++++++++++++++++ 6 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/vhost-user-reconnect-fa4cbb731b787f71.yaml diff --git a/releasenotes/notes/vhost-user-reconnect-fa4cbb731b787f71.yaml b/releasenotes/notes/vhost-user-reconnect-fa4cbb731b787f71.yaml new file mode 100644 index 00000000..0aea5205 --- /dev/null +++ b/releasenotes/notes/vhost-user-reconnect-fa4cbb731b787f71.yaml @@ -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. + diff --git a/vif_plug_ovs/constants.py b/vif_plug_ovs/constants.py index b1da7431..0908980f 100644 --- a/vif_plug_ovs/constants.py +++ b/vif_plug_ovs/constants.py @@ -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' diff --git a/vif_plug_ovs/linux_net.py b/vif_plug_ovs/linux_net.py index b8aa762c..59e96995 100644 --- a/vif_plug_ovs/linux_net.py +++ b/vif_plug_ovs/linux_net.py @@ -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 " diff --git a/vif_plug_ovs/ovs.py b/vif_plug_ovs/ovs.py index 95f37a36..44b4cabe 100644 --- a/vif_plug_ovs/ovs.py +++ b/vif_plug_ovs/ovs.py @@ -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): diff --git a/vif_plug_ovs/tests/test_linux_net.py b/vif_plug_ovs/tests/test_linux_net.py index 3ab62d70..25a10b69 100644 --- a/vif_plug_ovs/tests/test_linux_net.py +++ b/vif_plug_ovs/tests/test_linux_net.py @@ -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) diff --git a/vif_plug_ovs/tests/test_plugin.py b/vif_plug_ovs/tests/test_plugin.py index 884c4b5a..d442591d 100644 --- a/vif_plug_ovs/tests/test_plugin.py +++ b/vif_plug_ovs/tests/test_plugin.py @@ -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 = {