summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2013-02-08 16:59:32 +0000
committerDaniel P. Berrange <berrange@redhat.com>2013-02-13 11:14:57 +0000
commit99ddc0d2ad7f2f9c27deaac08559eb794845afc3 (patch)
treeaca6302f4c82f126b42242d625b861b96b40efbb
parentd980805880c681881504e269e03130e4452630ab (diff)
downloadnova-99ddc0d2ad7f2f9c27deaac08559eb794845afc3.tar.gz
nova-99ddc0d2ad7f2f9c27deaac08559eb794845afc3.tar.xz
nova-99ddc0d2ad7f2f9c27deaac08559eb794845afc3.zip
Allow VIF model to be chosen per image
This allows for an image in glance to be annotated with a property describing the required VIF model eg # glance image-update \ --property vif_model=e1000 \ f16-x86_64-openstack-sda Valid model values vary per the libvirt_type setting: qemu/kvm: 'virtio', 'ne2k_pci', 'pcnet', 'rtl8139', 'e1000' xen: 'netfront', 'ne2k_pci', 'pcnet', 'rtl8139', 'e1000' Requesting an unsupported VIF model will cause the guest instance to fail to launch. DocImpact Blueprint: libvirt-custom-hardware Change-Id: Idbee0c61cffdb43db5668fb88869a5d30278593f Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
-rw-r--r--nova/exception.py10
-rw-r--r--nova/tests/fake_network.py2
-rw-r--r--nova/tests/test_libvirt_vif.py44
-rwxr-xr-xnova/virt/libvirt/driver.py3
-rw-r--r--nova/virt/libvirt/vif.py133
5 files changed, 151 insertions, 41 deletions
diff --git a/nova/exception.py b/nova/exception.py
index 9063d1068..9e9e5182b 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -1120,3 +1120,13 @@ class ServiceGroupUnavailable(NovaException):
class DBNotAllowed(NovaException):
message = _('%(binary)s attempted direct database access which is '
'not allowed by policy')
+
+
+class UnsupportedVirtType(Invalid):
+ message = _("Virtualization type '%(virt)s' is not supported by "
+ "this compute driver")
+
+
+class UnsupportedHardware(Invalid):
+ message = _("Requested hardware '%(model)s' is not supported by "
+ "the '%(virt)s' virt driver")
diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py
index 883466cd6..d7f43829a 100644
--- a/nova/tests/fake_network.py
+++ b/nova/tests/fake_network.py
@@ -53,7 +53,7 @@ class FakeVIFDriver(object):
def setattr(self, key, val):
self.__setattr__(key, val)
- def get_config(self, instance, network, mapping):
+ def get_config(self, instance, network, mapping, image_meta):
conf = libvirt_config.LibvirtConfigGuestInterface()
for attr, val in conf.__dict__.iteritems():
diff --git a/nova/tests/test_libvirt_vif.py b/nova/tests/test_libvirt_vif.py
index 916b961da..749fda33a 100644
--- a/nova/tests/test_libvirt_vif.py
+++ b/nova/tests/test_libvirt_vif.py
@@ -171,7 +171,7 @@ class LibvirtVifTestCase(test.TestCase):
self.stubs.Set(utils, 'execute', fake_execute)
- def _get_instance_xml(self, driver, net, mapping):
+ def _get_instance_xml(self, driver, net, mapping, image_meta=None):
conf = vconfig.LibvirtConfigGuest()
conf.virt_type = "qemu"
conf.name = "fake-name"
@@ -179,7 +179,7 @@ class LibvirtVifTestCase(test.TestCase):
conf.memory = 100 * 1024
conf.vcpus = 4
- nic = driver.get_config(self.instance, net, mapping)
+ nic = driver.get_config(self.instance, net, mapping, image_meta)
conf.add_device(nic)
return conf.to_xml()
@@ -269,6 +269,46 @@ class LibvirtVifTestCase(test.TestCase):
ret = node.findall("driver")
self.assertEqual(len(ret), 0)
+ def test_model_kvm_custom(self):
+ self.flags(libvirt_use_virtio_for_bridges=True,
+ libvirt_type='kvm')
+
+ def get_connection():
+ return fakelibvirt.Connection("qemu:///session",
+ False)
+ d = vif.LibvirtGenericVIFDriver(get_connection)
+ image_meta = {'properties': {'vif_model': 'e1000'}}
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge,
+ image_meta)
+
+ doc = etree.fromstring(xml)
+ ret = doc.findall('./devices/interface')
+ self.assertEqual(len(ret), 1)
+ node = ret[0]
+
+ model = node.find("model").get("type")
+ self.assertEqual(model, "e1000")
+ ret = node.findall("driver")
+ self.assertEqual(len(ret), 0)
+
+ def test_model_kvm_bogus(self):
+ self.flags(libvirt_use_virtio_for_bridges=True,
+ libvirt_type='kvm')
+
+ def get_connection():
+ return fakelibvirt.Connection("qemu:///session",
+ False)
+ d = vif.LibvirtGenericVIFDriver(get_connection)
+ image_meta = {'properties': {'vif_model': 'acme'}}
+ self.assertRaises(exception.UnsupportedHardware,
+ self._get_instance_xml,
+ d,
+ self.net_bridge,
+ self.mapping_bridge,
+ image_meta)
+
def test_model_qemu(self):
self.flags(libvirt_use_virtio_for_bridges=True,
libvirt_type='qemu')
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index b65789b10..bdb875ed3 100755
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -1894,7 +1894,8 @@ class LibvirtDriver(driver.ComputeDriver):
for (network, mapping) in network_info:
cfg = self.vif_driver.get_config(instance,
- network, mapping)
+ network, mapping,
+ image_meta)
guest.add_device(cfg)
if CONF.libvirt_type == "qemu" or CONF.libvirt_type == "kvm":
diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py
index aec6c2836..ee4f7e194 100644
--- a/nova/virt/libvirt/vif.py
+++ b/nova/virt/libvirt/vif.py
@@ -54,6 +54,24 @@ CONF.import_opt('use_ipv6', 'nova.netconf')
LIBVIRT_OVS_VPORT_VERSION = 9011
+def is_vif_model_valid_for_virt(virt_type, vif_model):
+ valid_models = {
+ 'qemu': ['virtio', 'ne2k_pci', 'pcnet', 'rtl8139', 'e1000'],
+ 'kvm': ['virtio', 'ne2k_pci', 'pcnet', 'rtl8139', 'e1000'],
+ 'xen': ['netfront', 'ne2k_pci', 'pcnet', 'rtl8139', 'e1000'],
+ 'lxc': [],
+ 'uml': [],
+ }
+
+ if vif_model is None:
+ return True
+
+ if virt_type not in valid_models:
+ raise exception.UnsupportedVirtType(virt=virt_type)
+
+ return vif_model in valid_models[virt_type]
+
+
class LibvirtBaseVIFDriver(object):
def __init__(self, get_connection):
@@ -74,17 +92,35 @@ class LibvirtBaseVIFDriver(object):
return mapping['vif_devname']
return ("nic" + mapping['vif_uuid'])[:network_model.NIC_NAME_LEN]
- def get_config(self, instance, network, mapping):
+ def get_config(self, instance, network, mapping, image_meta):
conf = vconfig.LibvirtConfigGuestInterface()
+ # Default to letting libvirt / the hypervisor choose the model
model = None
driver = None
- if (CONF.libvirt_type in ('kvm', 'qemu') and
+
+ # If the user has specified a 'vif_model' against the
+ # image then honour that model
+ if image_meta:
+ vif_model = image_meta.get('properties', {}).get('vif_model')
+ if vif_model is not None:
+ model = vif_model
+
+ # Else if the virt type is KVM/QEMU, use virtio according
+ # to the global config parameter
+ if (model is None and
+ CONF.libvirt_type in ('kvm', 'qemu') and
CONF.libvirt_use_virtio_for_bridges):
model = "virtio"
- # Workaround libvirt bug, where it mistakenly
- # enables vhost mode, even for non-KVM guests
- if CONF.libvirt_type == "qemu":
- driver = "qemu"
+
+ # Workaround libvirt bug, where it mistakenly
+ # enables vhost mode, even for non-KVM guests
+ if model == "virtio" and CONF.libvirt_type == "qemu":
+ driver = "qemu"
+
+ if not is_vif_model_valid_for_virt(CONF.libvirt_type,
+ model):
+ raise exception.UnsupportedHardware(model=model,
+ virt=CONF.libvirt_type)
designer.set_vif_guest_frontend_config(
conf, mapping['mac'], model, driver)
@@ -122,12 +158,13 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
return True
return False
- def get_config_bridge(self, instance, network, mapping):
+ def get_config_bridge(self, instance, network, mapping, image_meta):
"""Get VIF configurations for bridge type."""
conf = super(LibvirtGenericVIFDriver,
self).get_config(instance,
network,
- mapping)
+ mapping,
+ image_meta)
designer.set_vif_host_backend_bridge_config(
conf, self.get_bridge_name(network),
@@ -154,22 +191,24 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
return conf
- def get_config_ovs_ethernet(self, instance, network, mapping):
+ def get_config_ovs_ethernet(self, instance, network, mapping, image_meta):
conf = super(LibvirtGenericVIFDriver,
self).get_config(instance,
network,
- mapping)
+ mapping,
+ image_meta)
dev = self.get_vif_devname(mapping)
designer.set_vif_host_backend_ethernet_config(conf, dev)
return conf
- def get_config_ovs_bridge(self, instance, network, mapping):
+ def get_config_ovs_bridge(self, instance, network, mapping, image_meta):
conf = super(LibvirtGenericVIFDriver,
self).get_config(instance,
network,
- mapping)
+ mapping,
+ image_meta)
designer.set_vif_host_backend_ovs_config(
conf, self.get_bridge_name(network),
@@ -178,29 +217,34 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
return conf
- def get_config_ovs_hybrid(self, instance, network, mapping):
+ def get_config_ovs_hybrid(self, instance, network, mapping, image_meta):
newnet = copy.deepcopy(network)
newnet['bridge'] = self.get_br_name(mapping['vif_uuid'])
return self.get_config_bridge(instance,
newnet,
- mapping)
+ mapping,
+ image_meta)
- def get_config_ovs(self, instance, network, mapping):
+ def get_config_ovs(self, instance, network, mapping, image_meta):
if self.get_firewall_required():
return self.get_config_ovs_hybrid(instance, network,
- mapping)
+ mapping,
+ image_meta)
elif self.has_libvirt_version(LIBVIRT_OVS_VPORT_VERSION):
return self.get_config_ovs_bridge(instance, network,
- mapping)
+ mapping,
+ image_meta)
else:
return self.get_config_ovs_ethernet(instance, network,
- mapping)
+ mapping,
+ image_meta)
- def get_config_802qbg(self, instance, network, mapping):
+ def get_config_802qbg(self, instance, network, mapping, image_meta):
conf = super(LibvirtGenericVIFDriver,
self).get_config(instance,
network,
- mapping)
+ mapping,
+ image_meta)
params = mapping["qbg_params"]
designer.set_vif_host_backend_802qbg_config(
@@ -212,11 +256,12 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
return conf
- def get_config_802qbh(self, instance, network, mapping):
+ def get_config_802qbh(self, instance, network, mapping, image_meta):
conf = super(LibvirtGenericVIFDriver,
self).get_config(instance,
network,
- mapping)
+ mapping,
+ image_meta)
params = mapping["qbh_params"]
designer.set_vif_host_backend_802qbh_config(
@@ -225,7 +270,7 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
return conf
- def get_config(self, instance, network, mapping):
+ def get_config(self, instance, network, mapping, image_meta):
vif_type = mapping.get('vif_type')
LOG.debug(_("vif_type=%(vif_type)s instance=%(instance)s "
@@ -238,13 +283,21 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
"for this vif_driver implementation"))
if vif_type == network_model.VIF_TYPE_BRIDGE:
- return self.get_config_bridge(instance, network, mapping)
+ return self.get_config_bridge(instance,
+ network, mapping,
+ image_meta)
elif vif_type == network_model.VIF_TYPE_OVS:
- return self.get_config_ovs(instance, network, mapping)
+ return self.get_config_ovs(instance,
+ network, mapping,
+ image_meta)
elif vif_type == network_model.VIF_TYPE_802_QBG:
- return self.get_config_802qbg(instance, network, mapping)
+ return self.get_config_802qbg(instance,
+ network, mapping,
+ image_meta)
elif vif_type == network_model.VIF_TYPE_802_QBH:
- return self.get_config_802qbh(instance, network, mapping)
+ return self.get_config_802qbh(instance,
+ network, mapping,
+ image_meta)
else:
raise exception.NovaException(
_("Unexpected vif_type=%s") % vif_type)
@@ -453,8 +506,8 @@ class LibvirtBridgeDriver(LibvirtGenericVIFDriver):
drivers which do not yet report 'vif_type' port binding.
Will be deprecated in Havana, and removed in Ixxxx."""
- def get_config(self, instance, network, mapping):
- return self.get_config_bridge(instance, network, mapping)
+ def get_config(self, instance, network, mapping, image_meta):
+ return self.get_config_bridge(instance, network, mapping, image_meta)
def plug(self, instance, vif):
self.plug_bridge(instance, vif)
@@ -474,8 +527,10 @@ class LibvirtOpenVswitchDriver(LibvirtGenericVIFDriver):
def get_ovs_interfaceid(self, mapping):
return mapping.get('ovs_interfaceid') or mapping['vif_uuid']
- def get_config(self, instance, network, mapping):
- return self.get_config_ovs_ethernet(instance, network, mapping)
+ def get_config(self, instance, network, mapping, image_meta):
+ return self.get_config_ovs_ethernet(instance,
+ network, mapping,
+ image_meta)
def plug(self, instance, vif):
self.plug_ovs_ethernet(instance, vif)
@@ -495,8 +550,10 @@ class LibvirtHybridOVSBridgeDriver(LibvirtGenericVIFDriver):
def get_ovs_interfaceid(self, mapping):
return mapping.get('ovs_interfaceid') or mapping['vif_uuid']
- def get_config(self, instance, network, mapping):
- return self.get_config_ovs_hybrid(instance, network, mapping)
+ def get_config(self, instance, network, mapping, image_meta):
+ return self.get_config_ovs_hybrid(instance,
+ network, mapping,
+ image_meta)
def plug(self, instance, vif):
return self.plug_ovs_hybrid(instance, vif)
@@ -516,8 +573,10 @@ class LibvirtOpenVswitchVirtualPortDriver(LibvirtGenericVIFDriver):
def get_ovs_interfaceid(self, mapping):
return mapping.get('ovs_interfaceid') or mapping['vif_uuid']
- def get_config(self, instance, network, mapping):
- return self.get_config_ovs_bridge(instance, network, mapping)
+ def get_config(self, instance, network, mapping, image_meta):
+ return self.get_config_ovs_bridge(instance,
+ network, mapping,
+ image_meta)
def plug(self, instance, vif):
return self.plug_ovs_bridge(instance, vif)
@@ -535,12 +594,12 @@ class QuantumLinuxBridgeVIFDriver(LibvirtGenericVIFDriver):
def_bridge = ("brq" + network['id'])[:network_model.NIC_NAME_LEN]
return network.get('bridge') or def_bridge
- def get_config(self, instance, network, mapping):
+ def get_config(self, instance, network, mapping, image_meta):
# In order for libvirt to make use of the bridge name then it has
# to ensure that the bridge exists
if 'should_create_bridge' not in mapping:
mapping['should_create_bridge'] = True
- return self.get_config_bridge(instance, network, mapping)
+ return self.get_config_bridge(instance, network, mapping, image_meta)
def plug(self, instance, vif):
self.plug_bridge(instance, vif)