From 9e2605bee8468481ce25476b8929764a86049c8c Mon Sep 17 00:00:00 2001 From: Przemyslaw Czesnowicz Date: Mon, 12 Jan 2015 16:21:43 +0000 Subject: [PATCH] Libvirt: Support for generic vhostuser vif. This commit implements support for generic vhostuser vif. Change-Id: I827aa45698ff53418725a112acdc42dfacbc1c61 Blueprint: libvirt-vif-vhost-user Co-Authored-By: Luke Gorrie --- nova/exception.py | 5 +++ nova/network/model.py | 8 +++++ nova/tests/unit/virt/libvirt/test_config.py | 16 +++++++++ nova/tests/unit/virt/libvirt/test_vif.py | 38 +++++++++++++++++++++ nova/virt/libvirt/config.py | 7 ++++ nova/virt/libvirt/designer.py | 10 ++++++ nova/virt/libvirt/vif.py | 25 ++++++++++++++ 7 files changed, 109 insertions(+) diff --git a/nova/exception.py b/nova/exception.py index 3258e39740ea..1d9ac333d0cf 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -732,6 +732,11 @@ class NetworkMissingPhysicalNetwork(NovaException): msg_fmt = _("Physical network is missing for network %(network_uuid)s") +class VifDetailsMissingVhostuserSockPath(Invalid): + msg_fmt = _("vhostuser_sock_path not present in vif_details" + " for vif %(vif_id)s") + + class DatastoreNotFound(NotFound): msg_fmt = _("Could not find the datastore reference(s) which the VM uses.") diff --git a/nova/network/model.py b/nova/network/model.py index e612438437e5..1673884c57bc 100644 --- a/nova/network/model.py +++ b/nova/network/model.py @@ -39,6 +39,7 @@ VIF_TYPE_802_QBH = '802.1qbh' VIF_TYPE_HW_VEB = 'hw_veb' VIF_TYPE_MLNX_DIRECT = 'mlnx_direct' VIF_TYPE_MIDONET = 'midonet' +VIF_TYPE_VHOSTUSER = 'vhostuser' VIF_TYPE_OTHER = 'other' # Constants for dictionary keys in the 'vif_details' field in the VIF @@ -53,6 +54,13 @@ VIF_DETAILS_PHYSICAL_NETWORK = 'physical_network' VIF_DETAILS_PROFILEID = 'profileid' VIF_DETAILS_VLAN = 'vlan' +# Constants for vhost-user related fields in 'vif_details'. +# Sets mode on vhost-user socket, valid values are 'client' +# and 'server' +VIF_DETAILS_VHOSTUSER_MODE = 'vhostuser_mode' +# Location of the directory to store vhost-user sockets +VIF_DETAILS_VHOSTUSER_DIR = 'vhostuser_sock_dir' + # Define supported virtual NIC types. VNIC_TYPE_DIRECT and VNIC_TYPE_MACVTAP # are used for SR-IOV ports VNIC_TYPE_NORMAL = 'normal' diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py index 1dce34db4c0b..f0e87a202aa9 100644 --- a/nova/tests/unit/virt/libvirt/test_config.py +++ b/nova/tests/unit/virt/libvirt/test_config.py @@ -1297,6 +1297,22 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest): """) + def test_config_vhostuser(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "vhostuser" + obj.vhostuser_type = "unix" + obj.vhostuser_mode = "server" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.vhostuser_path = "/vhost-user/test.sock" + obj.model = "virtio" + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + """) + class LibvirtConfigGuestFeatureTest(LibvirtConfigBaseTest): diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index a33a4d149dff..f68b8363ccc8 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -261,6 +261,20 @@ class LibvirtVifTestCase(test.NoDBTestCase): type=network_model.VIF_TYPE_IOVISOR, devname='tap-xxx-yyy-zzz', ovs_interfaceid=None) + vif_vhostuser = network_model.VIF(id='vif-xxx-yyy-zzz', + address='ca:fe:de:ad:be:ef', + network=network_bridge, + type=network_model.VIF_TYPE_VHOSTUSER, + details = {network_model.VIF_DETAILS_VHOSTUSER_MODE: 'client', + network_model.VIF_DETAILS_VHOSTUSER_DIR: '/tmp'} + ) + + vif_vhostuser_no_path = network_model.VIF(id='vif-xxx-yyy-zzz', + address='ca:fe:de:ad:be:ef', + network=network_bridge, + type=network_model.VIF_TYPE_VHOSTUSER, + details = {network_model.VIF_DETAILS_VHOSTUSER_MODE: 'client'} + ) instance = { 'name': 'instance-name', @@ -1022,3 +1036,27 @@ class LibvirtVifTestCase(test.NoDBTestCase): self.assertTrue(type_id_found) self.assertTrue(typeversion_id_found) self.assertTrue(instance_id_found) + + def test_vhostuser_driver(self): + d = vif.LibvirtGenericVIFDriver() + xml = self._get_instance_xml(d, self.vif_vhostuser) + node = self._get_node(xml) + self.assertEqual(node.get("type"), + network_model.VIF_TYPE_VHOSTUSER) + + self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER, + "source", "mode", "client") + self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER, + "source", "path", "/tmp/vif-xxx-yyy-zzz") + self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER, + "source", "type", "unix") + self._assertMacEquals(node, self.vif_vhostuser) + self._assertModel(xml, network_model.VIF_MODEL_VIRTIO) + + def test_vhostuser_driver_no_path(self): + d = vif.LibvirtGenericVIFDriver() + + self.assertRaises(exception.VifDetailsMissingVhostuserSockPath, + self._get_instance_xml, + d, + self.vif_vhostuser_no_path) diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index e52ec50bfffd..c3b865e54eb3 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -1127,6 +1127,9 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): self.filtername = None self.filterparams = [] self.driver_name = None + self.vhostuser_mode = None + self.vhostuser_path = None + self.vhostuser_type = None self.vif_inbound_peak = None self.vif_inbound_burst = None self.vif_inbound_average = None @@ -1165,6 +1168,10 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): addr_elem.set("function", "0x%s" % (func)) source_elem.append(addr_elem) dev.append(source_elem) + elif self.net_type == "vhostuser": + dev.append(etree.Element("source", type=self.vhostuser_type, + mode=self.vhostuser_mode, + path=self.vhostuser_path)) else: dev.append(etree.Element("source", bridge=self.source_dev)) diff --git a/nova/virt/libvirt/designer.py b/nova/virt/libvirt/designer.py index a6842ee90609..716067c232a0 100644 --- a/nova/virt/libvirt/designer.py +++ b/nova/virt/libvirt/designer.py @@ -142,6 +142,16 @@ def set_vif_host_backend_direct_config(conf, devname): conf.model = "virtio" +def set_vif_host_backend_vhostuser_config(conf, mode, path): + """Populate a LibvirtConfigGuestInterface instance + with host backend details for vhostuser socket. + """ + conf.net_type = "vhostuser" + conf.vhostuser_type = "unix" + conf.vhostuser_mode = mode + conf.vhostuser_path = path + + def set_vif_bandwidth_config(conf, inst_type): """Config vif inbound/outbound bandwidth limit. parameters are set in instance_type_extra_specs table, key is in the format diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 509df75c5181..8ccb1047f949 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -19,6 +19,7 @@ import copy +import os from oslo_concurrency import processutils from oslo_config import cfg @@ -329,6 +330,24 @@ class LibvirtGenericVIFDriver(object): return conf + def get_config_vhostuser(self, instance, vif, image_meta, + inst_type, virt_type): + conf = self.get_base_config(instance, vif, image_meta, + inst_type, virt_type) + vif_details = vif['details'] + mode = vif_details.get(network_model.VIF_DETAILS_VHOSTUSER_MODE, + 'server') + path = vif_details.get(network_model.VIF_DETAILS_VHOSTUSER_DIR) + if path is None: + raise exception.VifDetailsMissingVhostuserSockPath( + vif_id=vif['id']) + + designer.set_vif_host_backend_vhostuser_config( + conf, + mode, + os.path.join(path, vif['id'])) + return conf + def get_config(self, instance, vif, image_meta, inst_type, virt_type): vif_type = vif['type'] @@ -510,6 +529,9 @@ class LibvirtGenericVIFDriver(object): except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) + def plug_vhostuser(self, instance, vif): + pass + def plug(self, instance, vif): vif_type = vif['type'] @@ -663,6 +685,9 @@ class LibvirtGenericVIFDriver(object): LOG.exception(_LE("Failed while unplugging vif"), instance=instance) + def unplug_vhostuser(self, instance, vif): + pass + def unplug(self, instance, vif): vif_type = vif['type']