diff --git a/etc/nova/rootwrap.d/network.filters b/etc/nova/rootwrap.d/network.filters index 527ab40c27..7a5e4cc32d 100644 --- a/etc/nova/rootwrap.d/network.filters +++ b/etc/nova/rootwrap.d/network.filters @@ -89,3 +89,6 @@ sysctl: CommandFilter, sysctl, root # nova/network/linux_net.py: 'conntrack' conntrack: CommandFilter, conntrack, root + +# nova/network/linux_net.py: 'fp-vdev' +fp-vdev: CommandFilter, fp-vdev, root diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 9b9d3862f3..81812d6ed9 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -1413,6 +1413,20 @@ def create_tap_dev(dev, mac_address=None): check_exit_code=[0, 2, 254]) +def create_fp_dev(dev, sockpath, sockmode): + if not device_exists(dev): + utils.execute('fp-vdev', 'add', dev, '--sockpath', sockpath, + '--sockmode', sockmode, run_as_root=True) + _set_device_mtu(dev) + utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True, + check_exit_code=[0, 2, 254]) + + +def delete_fp_dev(dev): + if device_exists(dev): + utils.execute('fp-vdev', 'del', dev, run_as_root=True) + + def delete_net_dev(dev): """Delete a network device only if it exists.""" if device_exists(dev): diff --git a/nova/network/model.py b/nova/network/model.py index d81611bccc..79e1428d51 100644 --- a/nova/network/model.py +++ b/nova/network/model.py @@ -76,6 +76,9 @@ VIF_DETAILS_VHOSTUSER_SOCKET = 'vhostuser_socket' # Specifies whether vhost-user socket should be plugged # into ovs bridge. Valid values are True and False VIF_DETAILS_VHOSTUSER_OVS_PLUG = 'vhostuser_ovs_plug' +# Specifies whether vhost-user socket should be used to +# create a fp netdevice interface. +VIF_DETAILS_VHOSTUSER_FP_PLUG = 'vhostuser_fp_plug' # Constants for dictionary keys in the 'vif_details' field that are # valid for VIF_TYPE_TAP. diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index 3be8894f45..0694eb675c 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -280,6 +280,16 @@ class LibvirtVifTestCase(test.NoDBTestCase): '/tmp/vif-xxx-yyy-zzz'} ) + vif_vhostuser_fp = network_model.VIF(id='vif-xxx-yyy-zzz', + address='ca:fe:de:ad:be:ef', + network=network_bridge, + type=network_model.VIF_TYPE_VHOSTUSER, + devname='tap-xxx-yyy-zzz', + details = {network_model.VIF_DETAILS_VHOSTUSER_SOCKET: + '/tmp/usv-xxx-yyy-zzz', + network_model.VIF_DETAILS_VHOSTUSER_FP_PLUG: True}, + ) + vif_vhostuser_ovs = network_model.VIF(id='vif-xxx-yyy-zzz', address='ca:fe:de:ad:be:ef', network=network_bridge, @@ -1217,6 +1227,19 @@ class LibvirtVifTestCase(test.NoDBTestCase): self._assertMacEquals(node, self.vif_vhostuser_ovs) self._assertModel(xml, network_model.VIF_MODEL_VIRTIO) + @mock.patch.object(linux_net, 'create_fp_dev') + def test_vhostuser_fp_plug(self, mock_create_fp_dev): + d = vif.LibvirtGenericVIFDriver() + d.plug_vhostuser(self.instance, self.vif_vhostuser_fp) + mock_create_fp_dev.assert_has_calls( + [mock.call('tap-xxx-yyy-zzz', '/tmp/usv-xxx-yyy-zzz', 'client')]) + + @mock.patch.object(linux_net, 'delete_fp_dev') + def test_vhostuser_fp_unplug(self, mock_delete_fp_dev): + d = vif.LibvirtGenericVIFDriver() + d.unplug_vhostuser(None, self.vif_vhostuser_fp) + mock_delete_fp_dev.assert_has_calls([mock.call('tap-xxx-yyy-zzz')]) + def test_vhostuser_ovs_plug(self): calls = { diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index a55cb272dc..2c516d1c85 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -386,10 +386,7 @@ class LibvirtGenericVIFDriver(object): return conf - def get_config_vhostuser(self, instance, vif, image_meta, - inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, - inst_type, virt_type) + def _get_vhostuser_settings(self, vif): vif_details = vif['details'] mode = vif_details.get(network_model.VIF_DETAILS_VHOSTUSER_MODE, 'server') @@ -397,6 +394,13 @@ class LibvirtGenericVIFDriver(object): if sock_path is None: raise exception.VifDetailsMissingVhostuserSockPath( vif_id=vif['id']) + return mode, sock_path + + def get_config_vhostuser(self, instance, vif, image_meta, + inst_type, virt_type, host): + conf = self.get_base_config(instance, vif, image_meta, + inst_type, virt_type) + mode, sock_path = self._get_vhostuser_settings(vif) designer.set_vif_host_backend_vhostuser_config(conf, mode, sock_path) # (vladikr) Not setting up driver and queues for vhostuser # as queues are not supported in Libvirt until version 1.2.17 @@ -626,18 +630,41 @@ class LibvirtGenericVIFDriver(object): linux_net.create_tap_dev(dev, mac) linux_net._set_device_mtu(dev) + def plug_vhostuser_fp(self, instance, vif): + """Create a fp netdevice interface with a vhostuser socket""" + dev = self.get_vif_devname(vif) + if linux_net.device_exists(dev): + return + + sockmode_qemu, sockpath = self._get_vhostuser_settings(vif) + sockmode_port = 'client' if sockmode_qemu == 'server' else 'server' + + try: + linux_net.create_fp_dev(dev, sockpath, sockmode_port) + except processutils.ProcessExecutionError: + LOG.exception(_LE("Failed while plugging vif"), instance=instance) + + def plug_vhostuser_ovs(self, instance, vif): + """Plug a VIF_TYPE_VHOSTUSER into an ovs bridge""" + iface_id = self.get_ovs_interfaceid(vif) + port_name = os.path.basename( + vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET]) + linux_net.create_ovs_vif_port(self.get_bridge_name(vif), + port_name, iface_id, vif['address'], + instance.uuid) + linux_net.ovs_set_vhostuser_port_type(port_name) + def plug_vhostuser(self, instance, vif): + fp_plug = vif['details'].get( + network_model.VIF_DETAILS_VHOSTUSER_FP_PLUG, + False) ovs_plug = vif['details'].get( network_model.VIF_DETAILS_VHOSTUSER_OVS_PLUG, False) - if ovs_plug: - iface_id = self.get_ovs_interfaceid(vif) - port_name = os.path.basename( - vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET]) - linux_net.create_ovs_vif_port(self.get_bridge_name(vif), - port_name, iface_id, vif['address'], - instance.uuid) - linux_net.ovs_set_vhostuser_port_type(port_name) + if fp_plug: + self.plug_vhostuser_fp(instance, vif) + elif ovs_plug: + self.plug_vhostuser_ovs(instance, vif) def plug_vrouter(self, instance, vif): """Plug into Contrail's network port @@ -844,15 +871,33 @@ class LibvirtGenericVIFDriver(object): LOG.exception(_LE("Failed while unplugging vif"), instance=instance) + def unplug_vhostuser_fp(self, instance, vif): + """Delete a fp netdevice interface with a vhostuser socket""" + dev = self.get_vif_devname(vif) + try: + linux_net.delete_fp_dev(dev) + except processutils.ProcessExecutionError: + LOG.exception(_LE("Failed while unplugging vif"), + instance=instance) + + def unplug_vhostuser_ovs(self, instance, vif): + """Unplug a VIF_TYPE_VHOSTUSER into an ovs bridge""" + port_name = os.path.basename( + vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET]) + linux_net.delete_ovs_vif_port(self.get_bridge_name(vif), + port_name) + def unplug_vhostuser(self, instance, vif): + fp_plug = vif['details'].get( + network_model.VIF_DETAILS_VHOSTUSER_FP_PLUG, + False) ovs_plug = vif['details'].get( network_model.VIF_DETAILS_VHOSTUSER_OVS_PLUG, False) - if ovs_plug: - port_name = os.path.basename( - vif['details'][network_model.VIF_DETAILS_VHOSTUSER_SOCKET]) - linux_net.delete_ovs_vif_port(self.get_bridge_name(vif), - port_name) + if fp_plug: + self.unplug_vhostuser_fp(instance, vif) + elif ovs_plug: + self.unplug_vhostuser_ovs(instance, vif) def unplug_vrouter(self, instance, vif): """Unplug Contrail's network port