From 745f5fbb3a1b0a42eb54e2be2ecfffca3cbbb872 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 3 Aug 2016 12:55:55 +0100 Subject: [PATCH] libvirt: convert over to use os-vif for Linux Bridge & OVS This converts the libvirt VIF handler class to call out to os-vif for plugging/unplugging the Linux Bridge and OpenVSwitch VIF types. This should provide identical functionality that which exists today, allowing seemless upgrade. The new os-vif plugins have some config parameters in a new section, however, the os-vif config params have suitable deprecated annotations to ensure they will pull values from the old names/groups in the config file. Blueprint: os-vif-library Change-Id: Ib951680917a3cda8c44d237c63d3ef2c09aa0057 --- nova/tests/unit/virt/libvirt/test_designer.py | 9 - nova/tests/unit/virt/libvirt/test_driver.py | 2 +- nova/tests/unit/virt/libvirt/test_vif.py | 306 ++++++++++++------ nova/virt/libvirt/designer.py | 13 - nova/virt/libvirt/vif.py | 239 ++++++++------ ...-uses-os-vif-plugins-31a0617de0c248b9.yaml | 35 ++ 6 files changed, 385 insertions(+), 219 deletions(-) create mode 100644 releasenotes/notes/libvirt-uses-os-vif-plugins-31a0617de0c248b9.yaml diff --git a/nova/tests/unit/virt/libvirt/test_designer.py b/nova/tests/unit/virt/libvirt/test_designer.py index 0b222fb28812..b5cb9946014d 100644 --- a/nova/tests/unit/virt/libvirt/test_designer.py +++ b/nova/tests/unit/virt/libvirt/test_designer.py @@ -60,15 +60,6 @@ class DesignerTestCase(test.NoDBTestCase): self.assertEqual('fake-tap', conf.target_dev) self.assertEqual('', conf.script) - def test_set_vif_host_backend_ovs_config(self): - conf = config.LibvirtConfigGuestInterface() - designer.set_vif_host_backend_ovs_config(conf, 'fake-bridge', - 'fake-interface', 'fake-tap') - self.assertEqual('bridge', conf.net_type) - self.assertEqual('fake-bridge', conf.source_dev) - self.assertEqual('openvswitch', conf.vporttype) - self.assertEqual('fake-tap', conf.target_dev) - def test_set_vif_host_backend_802qbg_config(self): conf = config.LibvirtConfigGuestInterface() designer.set_vif_host_backend_802qbg_config(conf, 'fake-devname', diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 0b143f45f4d0..2e8d61c072b7 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -6854,7 +6854,7 @@ class LibvirtConnTestCase(test.NoDBTestCase): filterref = './devices/interface/filterref' vif = network_info[0] - nic_id = vif['address'].replace(':', '') + nic_id = vif['address'].lower().replace(':', '') fw = firewall.NWFilterFirewall(drvr) instance_filter_name = fw._instance_filter_name(instance_ref, nic_id) diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index 7aa20fe3def5..5cd0439ec22d 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -17,6 +17,9 @@ import os import fixtures from lxml import etree import mock +import os_vif +from os_vif import exception as osv_exception +from os_vif import objects as osv_objects from oslo_concurrency import processutils from oslo_config import cfg import six @@ -27,6 +30,7 @@ from nova.network import model as network_model from nova import objects from nova.pci import utils as pci_utils from nova import test +from nova.tests.unit import matchers from nova.tests.unit.virt import fakelibosinfo from nova import utils from nova.virt.libvirt import config as vconfig @@ -384,6 +388,49 @@ class LibvirtVifTestCase(test.NoDBTestCase): 'quota:vif_outbound_burst': '30' } + os_vif_network = osv_objects.network.Network( + id="b82c1929-051e-481d-8110-4669916c7915", + label="Demo Net", + subnets=osv_objects.subnet.SubnetList( + objects=[])) + + os_vif_bridge = osv_objects.vif.VIFBridge( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + address="22:52:25:62:e2:aa", + plugin="linux_bridge", + vif_name="nicdc065497-3c", + bridge_name="br100", + has_traffic_filtering=False, + network=os_vif_network) + + os_vif_ovs_prof = osv_objects.vif.VIFPortProfileOpenVSwitch( + interface_id="07bd6cea-fb37-4594-b769-90fc51854ee9", + profile_id="fishfood") + + os_vif_ovs = osv_objects.vif.VIFOpenVSwitch( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + address="22:52:25:62:e2:aa", + unplugin="linux_bridge", + vif_name="nicdc065497-3c", + bridge_name="br0", + port_profile=os_vif_ovs_prof, + network=os_vif_network) + + os_vif_ovs_hybrid = osv_objects.vif.VIFBridge( + id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", + address="22:52:25:62:e2:aa", + unplugin="linux_bridge", + vif_name="nicdc065497-3c", + bridge_name="br0", + port_profile=os_vif_ovs_prof, + has_traffic_filtering=False, + network=os_vif_network) + + os_vif_inst_info = osv_objects.instance_info.InstanceInfo( + uuid="d5b1090c-9e00-4fa4-9504-4b1494857970", + name="instance-000004da", + project_id="2f37d7f6-e51a-4a1f-8b6e-b0917ffc8390") + def setUp(self): super(LibvirtVifTestCase, self).setUp() self.flags(allow_same_net_traffic=True) @@ -455,6 +502,9 @@ class LibvirtVifTestCase(test.NoDBTestCase): pci_slot_want = vif['profile']['pci_slot'] self.assertEqual(pci_slot, pci_slot_want) + def _assertXmlEqual(self, expectedXmlstr, actualXmlstr): + self.assertThat(actualXmlstr, matchers.XMLMatches(expectedXmlstr)) + def _get_conf(self): conf = vconfig.LibvirtConfigGuest() conf.virt_type = "qemu" @@ -616,7 +666,7 @@ class LibvirtVifTestCase(test.NoDBTestCase): d = vif.LibvirtGenericVIFDriver() image_meta = {'properties': {'os_name': 'fedora22'}} image_meta = objects.ImageMeta.from_dict(image_meta) - d.get_base_config(None, self.vif_bridge, image_meta, + d.get_base_config(None, 'ca:fe:de:ad:be:ef', image_meta, None, 'kvm') mock_set.assert_called_once_with(mock.ANY, 'ca:fe:de:ad:be:ef', 'virtio', None, None) @@ -719,85 +769,6 @@ class LibvirtVifTestCase(test.NoDBTestCase): delete.side_effect = processutils.ProcessExecutionError d.unplug(self.instance, self.vif_ivs) - def _test_plug_ovs_hybrid(self, ipv6_exists): - calls = { - 'device_exists': [mock.call('qbrvif-xxx-yyy'), - mock.call('qvovif-xxx-yyy')], - '_create_veth_pair': [mock.call('qvbvif-xxx-yyy', - 'qvovif-xxx-yyy', 1000)], - 'execute': [mock.call('brctl', 'addbr', 'qbrvif-xxx-yyy', - run_as_root=True), - mock.call('brctl', 'setfd', 'qbrvif-xxx-yyy', 0, - run_as_root=True), - mock.call('brctl', 'stp', 'qbrvif-xxx-yyy', 'off', - run_as_root=True), - mock.call('tee', ('/sys/class/net/qbrvif-xxx-yyy' - '/bridge/multicast_snooping'), - process_input='0', run_as_root=True, - check_exit_code=[0, 1])], - 'create_ovs_vif_port': [mock.call( - 'br0', 'qvovif-xxx-yyy', 'aaa-bbb-ccc', - 'ca:fe:de:ad:be:ef', - 'f0000000-0000-0000-0000-000000000001', - 1000)] - } - # The disable_ipv6 call needs to be added in the middle, if required - if ipv6_exists: - calls['execute'].extend([ - mock.call('tee', ('/proc/sys/net/ipv6/conf' - '/qbrvif-xxx-yyy/disable_ipv6'), - process_input='1', run_as_root=True, - check_exit_code=[0, 1])]) - calls['execute'].extend([ - mock.call('ip', 'link', 'set', 'qbrvif-xxx-yyy', 'up', - run_as_root=True), - mock.call('brctl', 'addif', 'qbrvif-xxx-yyy', - 'qvbvif-xxx-yyy', run_as_root=True)]) - with test.nested( - mock.patch.object(linux_net, 'device_exists', - return_value=False), - mock.patch.object(utils, 'execute'), - mock.patch.object(linux_net, '_create_veth_pair'), - mock.patch.object(linux_net, 'create_ovs_vif_port'), - mock.patch.object(os.path, 'exists', return_value=ipv6_exists) - ) as (device_exists, execute, _create_veth_pair, create_ovs_vif_port, - path_exists): - d = vif.LibvirtGenericVIFDriver() - d.plug(self.instance, self.vif_ovs) - device_exists.assert_has_calls(calls['device_exists']) - _create_veth_pair.assert_has_calls(calls['_create_veth_pair']) - execute.assert_has_calls(calls['execute']) - create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port']) - - def test_plug_ovs_hybrid_ipv6(self): - self._test_plug_ovs_hybrid(ipv6_exists=True) - - def test_plug_ovs_hybrid_no_ipv6(self): - self._test_plug_ovs_hybrid(ipv6_exists=False) - - def test_unplug_ovs_hybrid(self): - calls = { - 'device_exists': [mock.call('qbrvif-xxx-yyy')], - 'execute': [mock.call('brctl', 'delif', 'qbrvif-xxx-yyy', - 'qvbvif-xxx-yyy', run_as_root=True), - mock.call('ip', 'link', 'set', - 'qbrvif-xxx-yyy', 'down', run_as_root=True), - mock.call('brctl', 'delbr', - 'qbrvif-xxx-yyy', run_as_root=True)], - 'delete_ovs_vif_port': [mock.call('br0', 'qvovif-xxx-yyy')] - } - with test.nested( - mock.patch.object(linux_net, 'device_exists', - return_value=True), - mock.patch.object(utils, 'execute'), - mock.patch.object(linux_net, 'delete_ovs_vif_port') - ) as (device_exists, execute, delete_ovs_vif_port): - d = vif.LibvirtGenericVIFDriver() - d.unplug(self.instance, self.vif_ovs) - device_exists.assert_has_calls(calls['device_exists']) - execute.assert_has_calls(calls['execute']) - delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port']) - @mock.patch.object(utils, 'execute') @mock.patch.object(pci_utils, 'get_ifname_by_pci_address') @mock.patch.object(pci_utils, 'get_vf_num_by_pci_address', return_value=1) @@ -839,21 +810,6 @@ class LibvirtVifTestCase(test.NoDBTestCase): d = vif.LibvirtGenericVIFDriver() self._test_hw_veb_op(d.unplug, 0) - def test_unplug_ovs_hybrid_bridge_does_not_exist(self): - calls = { - 'device_exists': [mock.call('qbrvif-xxx-yyy')], - 'delete_ovs_vif_port': [mock.call('br0', 'qvovif-xxx-yyy')] - } - with test.nested( - mock.patch.object(linux_net, 'device_exists', - return_value=False), - mock.patch.object(linux_net, 'delete_ovs_vif_port') - ) as (device_exists, delete_ovs_vif_port): - d = vif.LibvirtGenericVIFDriver() - d.unplug(self.instance, self.vif_ovs) - device_exists.assert_has_calls(calls['device_exists']) - delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port']) - def test_plug_ivs_hybrid(self): calls = { 'device_exists': [mock.call('qbrvif-xxx-yyy'), @@ -1482,3 +1438,163 @@ class LibvirtVifTestCase(test.NoDBTestCase): execute.assert_has_calls(calls['execute']) delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port']) delete_fp_dev.assert_has_calls(calls['delete_fp_dev']) + + @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") + @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") + @mock.patch.object(os_vif, "plug") + def _test_osvif_plug(self, fail, mock_plug, + mock_convert_vif, mock_convert_inst): + mock_convert_vif.return_value = self.os_vif_bridge + mock_convert_inst.return_value = self.os_vif_inst_info + + d = vif.LibvirtGenericVIFDriver() + if fail: + mock_plug.side_effect = osv_exception.ExceptionBase("Wibble") + self.assertRaises(exception.NovaException, + d.plug, + self.instance, self.vif_bridge) + else: + d.plug(self.instance, self.vif_bridge) + + mock_plug.assert_called_once_with(self.os_vif_bridge, + self.os_vif_inst_info) + + def test_osvif_plug_normal(self): + self._test_osvif_plug(False) + + def test_osvif_plug_fail(self): + self._test_osvif_plug(True) + + @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") + @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") + @mock.patch.object(os_vif, "unplug") + def _test_osvif_unplug(self, fail, mock_unplug, + mock_convert_vif, mock_convert_inst): + mock_convert_vif.return_value = self.os_vif_bridge + mock_convert_inst.return_value = self.os_vif_inst_info + + d = vif.LibvirtGenericVIFDriver() + if fail: + mock_unplug.side_effect = osv_exception.ExceptionBase("Wibble") + self.assertRaises(exception.NovaException, + d.unplug, + self.instance, self.vif_bridge) + else: + d.unplug(self.instance, self.vif_bridge) + + mock_unplug.assert_called_once_with(self.os_vif_bridge, + self.os_vif_inst_info) + + def test_osvif_unplug_normal(self): + self._test_osvif_unplug(False) + + def test_osvif_unplug_fail(self): + self._test_osvif_unplug(True) + + @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") + @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") + def test_config_os_vif_bridge(self, mock_convert_vif, mock_convert_inst): + mock_convert_vif.return_value = self.os_vif_bridge + mock_convert_inst.return_value = self.os_vif_inst_info + + d = vif.LibvirtGenericVIFDriver() + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + cfg = d.get_config(self.instance, self.vif_bridge, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) + + self._assertXmlEqual(""" + + + + + + + """, cfg.to_xml()) + + @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") + @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") + def test_config_os_vif_bridge_nofw(self, mock_convert_vif, + mock_convert_inst): + self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") + + mock_convert_vif.return_value = self.os_vif_bridge + mock_convert_inst.return_value = self.os_vif_inst_info + + d = vif.LibvirtGenericVIFDriver() + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + cfg = d.get_config(self.instance, self.vif_bridge, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) + + self._assertXmlEqual(""" + + + + + + """, cfg.to_xml()) + + @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") + @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") + def test_config_os_vif_ovs(self, mock_convert_vif, mock_convert_inst): + mock_convert_vif.return_value = self.os_vif_ovs + mock_convert_inst.return_value = self.os_vif_inst_info + + d = vif.LibvirtGenericVIFDriver() + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + cfg = d.get_config(self.instance, self.vif_ovs, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) + + self._assertXmlEqual(""" + + + + + + + + + """, cfg.to_xml()) + + @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") + @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") + def test_config_os_vif_ovs_hybrid(self, mock_convert_vif, + mock_convert_inst): + mock_convert_vif.return_value = self.os_vif_ovs_hybrid + mock_convert_inst.return_value = self.os_vif_inst_info + + d = vif.LibvirtGenericVIFDriver() + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + cfg = d.get_config(self.instance, self.vif_ovs, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) + + self._assertXmlEqual(""" + + + + + + + """, cfg.to_xml()) diff --git a/nova/virt/libvirt/designer.py b/nova/virt/libvirt/designer.py index 2522efee4328..df22f02fd616 100644 --- a/nova/virt/libvirt/designer.py +++ b/nova/virt/libvirt/designer.py @@ -61,19 +61,6 @@ def set_vif_host_backend_ethernet_config(conf, tapname): conf.script = "" -def set_vif_host_backend_ovs_config(conf, brname, interfaceid, tapname=None): - """Populate a LibvirtConfigGuestInterface instance - with host backend details for an OpenVSwitch bridge. - """ - - conf.net_type = "bridge" - conf.source_dev = brname - conf.vporttype = "openvswitch" - conf.add_vport_param("interfaceid", interfaceid) - if tapname: - conf.target_dev = tapname - - def set_vif_host_backend_802qbg_config(conf, devname, managerid, typeid, typeidversion, instanceid, tapname=None): diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 2b1be374b5e5..b9e46a501102 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -2,6 +2,7 @@ # Copyright (C) 2011 Nicira, Inc # Copyright 2011 OpenStack Foundation # All Rights Reserved. +# Copyright 2016 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -20,6 +21,8 @@ import copy import os +import os_vif +from os_vif import exception as osv_exception from oslo_concurrency import processutils from oslo_log import log as logging @@ -29,6 +32,7 @@ from nova.i18n import _ from nova.i18n import _LE from nova.network import linux_net from nova.network import model as network_model +from nova.network import os_vif_util from nova import objects from nova import utils from nova.virt.libvirt import config as vconfig @@ -90,7 +94,7 @@ class LibvirtGenericVIFDriver(object): devname = self.get_vif_devname(vif) return prefix + devname[3:] - def get_base_config(self, instance, vif, image_meta, + def get_base_config(self, instance, mac, image_meta, inst_type, virt_type): conf = vconfig.LibvirtConfigGuestInterface() # Default to letting libvirt / the hypervisor choose the model @@ -127,7 +131,7 @@ class LibvirtGenericVIFDriver(object): driver = vhost_drv or driver designer.set_vif_guest_frontend_config( - conf, vif['address'], model, driver, vhost_queues) + conf, mac, model, driver, vhost_queues) return conf @@ -186,17 +190,28 @@ class LibvirtGenericVIFDriver(object): return (("qvb%s" % iface_id)[:network_model.NIC_NAME_LEN], ("qvo%s" % iface_id)[:network_model.NIC_NAME_LEN]) + @staticmethod + def is_no_op_firewall(): + return CONF.firewall_driver == "nova.virt.firewall.NoopFirewallDriver" + def get_firewall_required(self, vif): if vif.is_neutron_filtering_enabled(): return False - if CONF.firewall_driver != "nova.virt.firewall.NoopFirewallDriver": - return True - return False + if self.is_no_op_firewall(): + return False + return True + + def get_firewall_required_os_vif(self, vif): + if vif.has_traffic_filtering: + return False + if self.is_no_op_firewall(): + return False + return True def get_config_bridge(self, instance, vif, image_meta, inst_type, virt_type, host): """Get VIF configurations for bridge type.""" - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) designer.set_vif_host_backend_bridge_config( @@ -211,42 +226,6 @@ class LibvirtGenericVIFDriver(object): return conf - def get_config_ovs_bridge(self, instance, vif, image_meta, - inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, - inst_type, virt_type) - - designer.set_vif_host_backend_ovs_config( - conf, self.get_bridge_name(vif), - self.get_ovs_interfaceid(vif), - self.get_vif_devname(vif)) - - designer.set_vif_bandwidth_config(conf, inst_type) - - return conf - - def get_config_ovs_hybrid(self, instance, vif, image_meta, - inst_type, virt_type, host): - newvif = copy.deepcopy(vif) - newvif['network']['bridge'] = self.get_br_name(vif['id']) - return self.get_config_bridge(instance, newvif, image_meta, - inst_type, virt_type, host) - - def get_config_ovs(self, instance, vif, image_meta, - inst_type, virt_type, host): - if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled(): - return self.get_config_ovs_hybrid(instance, vif, - image_meta, - inst_type, - virt_type, - host) - else: - return self.get_config_ovs_bridge(instance, vif, - image_meta, - inst_type, - virt_type, - host) - def get_config_ivs_hybrid(self, instance, vif, image_meta, inst_type, virt_type, host): newvif = copy.deepcopy(vif) @@ -261,7 +240,7 @@ class LibvirtGenericVIFDriver(object): def get_config_ivs_ethernet(self, instance, vif, image_meta, inst_type, virt_type, host): conf = self.get_base_config(instance, - vif, + vif['address'], image_meta, inst_type, virt_type) @@ -288,7 +267,7 @@ class LibvirtGenericVIFDriver(object): def get_config_802qbg(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) params = vif["qbg_params"] @@ -305,7 +284,7 @@ class LibvirtGenericVIFDriver(object): def get_config_802qbh(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) profile = vif["profile"] @@ -324,7 +303,7 @@ class LibvirtGenericVIFDriver(object): def get_config_hw_veb(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) profile = vif["profile"] @@ -347,7 +326,7 @@ class LibvirtGenericVIFDriver(object): def get_config_macvtap(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) vif_details = vif['details'] @@ -378,7 +357,7 @@ class LibvirtGenericVIFDriver(object): def get_config_iovisor(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) dev = self.get_vif_devname(vif) @@ -390,7 +369,7 @@ class LibvirtGenericVIFDriver(object): def get_config_midonet(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) dev = self.get_vif_devname(vif) @@ -400,7 +379,7 @@ class LibvirtGenericVIFDriver(object): def get_config_tap(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) dev = self.get_vif_devname(vif) @@ -420,7 +399,7 @@ class LibvirtGenericVIFDriver(object): def get_config_vhostuser(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], 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) @@ -439,7 +418,7 @@ class LibvirtGenericVIFDriver(object): def get_config_vrouter(self, instance, vif, image_meta, inst_type, virt_type, host): - conf = self.get_base_config(instance, vif, image_meta, + conf = self.get_base_config(instance, vif['address'], image_meta, inst_type, virt_type) dev = self.get_vif_devname(vif) designer.set_vif_host_backend_ethernet_config(conf, dev) @@ -447,6 +426,69 @@ class LibvirtGenericVIFDriver(object): designer.set_vif_bandwidth_config(conf, inst_type) return conf + def _set_config_VIFBridge(self, instance, vif, conf): + conf.net_type = "bridge" + conf.source_dev = vif.bridge_name + conf.target_dev = vif.vif_name + + if self.get_firewall_required_os_vif(vif): + mac_id = vif.address.replace(':', '') + name = "nova-instance-" + instance.name + "-" + mac_id + conf.filtername = name + + def _set_config_VIFOpenVSwitch(self, instance, vif, conf): + conf.net_type = "bridge" + conf.source_dev = vif.bridge_name + conf.target_dev = vif.vif_name + self._set_config_VIFPortProfile(instance, vif, conf) + + def _set_config_VIFPortProfileOpenVSwitch(self, profile, conf): + conf.vporttype = "openvswitch" + conf.add_vport_param("interfaceid", + profile.interface_id) + + def _set_config_VIFPortProfile(self, instance, vif, conf): + # Set any port profile that may be required + profilefunc = "_set_config_" + vif.port_profile.obj_name() + func = getattr(self, profilefunc, None) + if not func: + raise exception.NovaException( + "Unsupported VIF port profile type %(obj)s func %(func)s" % + {'obj': vif.port_profile.obj_name(), 'func': profilefunc}) + + func(vif.port_profile, conf) + + def _get_config_os_vif(self, instance, vif, image_meta, inst_type, + virt_type, host): + """Get the domain config for a VIF + + :param instance: nova.objects.Instance + :param vif: os_vif.objects.vif.VIFBase subclass + :param image_meta: nova.objects.ImageMeta + :param inst_type: nova.objects.Flavor + :param virt_type: virtualization type + :param host: nova.virt.libvirt.host.Host + + :returns: nova.virt.libvirt.config.LibvirtConfigGuestInterface + """ + + # Do the config that's common to all vif types + conf = self.get_base_config(instance, vif.address, image_meta, + inst_type, virt_type) + + # Do the VIF type specific config + viffunc = "_set_config_" + vif.obj_name() + func = getattr(self, viffunc, None) + if not func: + raise exception.NovaException( + "Unsupported VIF type %(obj)s func %(func)s" % + {'obj': vif.obj_name(), 'func': viffunc}) + func(instance, vif, conf) + + designer.set_vif_bandwidth_config(conf, inst_type) + + return conf + def get_config(self, instance, vif, image_meta, inst_type, virt_type, host): vif_type = vif['type'] @@ -460,6 +502,14 @@ class LibvirtGenericVIFDriver(object): raise exception.NovaException( _("vif_type parameter must be present " "for this vif_driver implementation")) + + # Try os-vif codepath first + vif_obj = os_vif_util.nova_to_osvif_vif(vif) + if vif_obj is not None: + return self._get_config_os_vif(instance, vif_obj, image_meta, + inst_type, virt_type, host) + + # Legacy non-os-vif codepath vif_slug = self._normalize_vif_type(vif_type) func = getattr(self, 'get_config_%s' % vif_slug, None) if not func: @@ -468,35 +518,6 @@ class LibvirtGenericVIFDriver(object): return func(instance, vif, image_meta, inst_type, virt_type, host) - def plug_bridge(self, instance, vif): - """Ensure that the bridge exists, and add VIF to it.""" - network = vif['network'] - if (not network.get_meta('multi_host', False) and - network.get_meta('should_create_bridge', False)): - if network.get_meta('should_create_vlan', False): - iface = CONF.vlan_interface or \ - network.get_meta('bridge_interface') - LOG.debug('Ensuring vlan %(vlan)s and bridge %(bridge)s', - {'vlan': network.get_meta('vlan'), - 'bridge': self.get_bridge_name(vif)}, - instance=instance) - linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge( - network.get_meta('vlan'), - self.get_bridge_name(vif), - iface) - else: - iface = CONF.flat_interface or \ - network.get_meta('bridge_interface') - LOG.debug("Ensuring bridge %s", - self.get_bridge_name(vif), instance=instance) - linux_net.LinuxBridgeInterfaceDriver.ensure_bridge( - self.get_bridge_name(vif), - iface) - - def plug_ovs_bridge(self, instance, vif): - """No manual plugging required.""" - pass - def _plug_bridge_with_port(self, instance, vif, port): iface_id = self.get_ovs_interfaceid(vif) br_name = self.get_br_name(vif['id']) @@ -544,12 +565,6 @@ class LibvirtGenericVIFDriver(object): """ self._plug_bridge_with_port(instance, vif, port='ovs') - def plug_ovs(self, instance, vif): - if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled(): - self.plug_ovs_hybrid(instance, vif) - else: - self.plug_ovs_bridge(instance, vif) - def plug_ivs_ethernet(self, instance, vif): iface_id = self.get_ovs_interfaceid(vif) dev = self.get_vif_devname(vif) @@ -757,6 +772,16 @@ class LibvirtGenericVIFDriver(object): except processutils.ProcessExecutionError: LOG.exception(_LE("Failed while plugging vif"), instance=instance) + def _plug_os_vif(self, instance, vif): + instance_info = os_vif_util.nova_to_osvif_instance(instance) + + try: + os_vif.plug(vif, instance_info) + except osv_exception.ExceptionBase as ex: + msg = (_("Failure running os_vif plugin plug method: %(ex)s") + % {'ex': ex}) + raise exception.NovaException(msg) + def plug(self, instance, vif): vif_type = vif['type'] @@ -769,6 +794,14 @@ class LibvirtGenericVIFDriver(object): raise exception.VirtualInterfacePlugException( _("vif_type parameter must be present " "for this vif_driver implementation")) + + # Try os-vif codepath first + vif_obj = os_vif_util.nova_to_osvif_vif(vif) + if vif_obj is not None: + self._plug_os_vif(instance, vif_obj) + return + + # Legacy non-os-vif codepath vif_slug = self._normalize_vif_type(vif_type) func = getattr(self, 'plug_%s' % vif_slug, None) if not func: @@ -777,14 +810,6 @@ class LibvirtGenericVIFDriver(object): "vif_type=%s") % vif_type) func(instance, vif) - def unplug_bridge(self, instance, vif): - """No manual unplugging required.""" - pass - - def unplug_ovs_bridge(self, instance, vif): - """No manual unplugging required.""" - pass - def unplug_ovs_hybrid(self, instance, vif): """UnPlug using hybrid strategy @@ -809,12 +834,6 @@ class LibvirtGenericVIFDriver(object): LOG.exception(_LE("Failed while unplugging vif"), instance=instance) - def unplug_ovs(self, instance, vif): - if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled(): - self.unplug_ovs_hybrid(instance, vif) - else: - self.unplug_ovs_bridge(instance, vif) - def unplug_ivs_ethernet(self, instance, vif): """Unplug the VIF by deleting the port from the bridge.""" try: @@ -974,6 +993,16 @@ class LibvirtGenericVIFDriver(object): LOG.exception( _LE("Failed while unplugging vif"), instance=instance) + def _unplug_os_vif(self, instance, vif): + instance_info = os_vif_util.nova_to_osvif_instance(instance) + + try: + os_vif.unplug(vif, instance_info) + except osv_exception.ExceptionBase as ex: + msg = (_("Failure running os_vif plugin unplug method: %(ex)s") + % {'ex': ex}) + raise exception.NovaException(msg) + def unplug(self, instance, vif): vif_type = vif['type'] @@ -986,6 +1015,14 @@ class LibvirtGenericVIFDriver(object): raise exception.NovaException( _("vif_type parameter must be present " "for this vif_driver implementation")) + + # Try os-vif codepath first + vif_obj = os_vif_util.nova_to_osvif_vif(vif) + if vif_obj is not None: + self._unplug_os_vif(instance, vif_obj) + return + + # Legacy non-os-vif codepath vif_slug = self._normalize_vif_type(vif_type) func = getattr(self, 'unplug_%s' % vif_slug, None) if not func: diff --git a/releasenotes/notes/libvirt-uses-os-vif-plugins-31a0617de0c248b9.yaml b/releasenotes/notes/libvirt-uses-os-vif-plugins-31a0617de0c248b9.yaml new file mode 100644 index 000000000000..768d34f39264 --- /dev/null +++ b/releasenotes/notes/libvirt-uses-os-vif-plugins-31a0617de0c248b9.yaml @@ -0,0 +1,35 @@ +--- +features: + - The Libvirt driver now uses os-vif plugins for + handling plug/unplug actions for the Linux Bridge + and OpenVSwitch VIF types. Each os-vif plugin will + have its own group in nova.conf for configuration + parameters it needs. These plugins will be installed + by default as part of the os-vif module installation + so no special action is required. +upgrade: + - With the introduction of os-vif, some networking related + configuration options have moved, and users will need to update + their ``nova.conf``. + + For OpenVSwitch users the following options have moved from + ``[DEFAULT]`` to ``[vif_plug_ovs]`` + + - network_device_mtu + - ovs_vsctl_timeout + + For Linux Bridge users the following options have moved from + ``[DEFAULT]`` to ``[vif_plug_linux_bridge]`` + + - use_ipv6 + - iptables_top_regex + - iptables_bottom_regex + - iptables_drop_action + - forward_bridge_interface + - vlan_interface + - flat_interface + - network_device_mtu + + For backwards compatibility, and ease of upgrade, these options + will continue to work from ``[DEFAULT]`` during the Newton + release. However they will not in future releases.