diff options
| author | Daniel P. Berrange <berrange@redhat.com> | 2013-02-08 16:59:32 +0000 |
|---|---|---|
| committer | Daniel P. Berrange <berrange@redhat.com> | 2013-02-13 11:14:57 +0000 |
| commit | 99ddc0d2ad7f2f9c27deaac08559eb794845afc3 (patch) | |
| tree | aca6302f4c82f126b42242d625b861b96b40efbb | |
| parent | d980805880c681881504e269e03130e4452630ab (diff) | |
| download | nova-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.py | 10 | ||||
| -rw-r--r-- | nova/tests/fake_network.py | 2 | ||||
| -rw-r--r-- | nova/tests/test_libvirt_vif.py | 44 | ||||
| -rwxr-xr-x | nova/virt/libvirt/driver.py | 3 | ||||
| -rw-r--r-- | nova/virt/libvirt/vif.py | 133 |
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) |
