diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py index 358e68608147..b3f1e6013d9e 100644 --- a/nova/tests/unit/virt/libvirt/test_config.py +++ b/nova/tests/unit/virt/libvirt/test_config.py @@ -1700,6 +1700,41 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest): obj2.parse_str(xml) self.assertXmlEqual(xml, obj2.to_xml()) + def test_config_ethernet_with_mtu(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "ethernet" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.target_dev = "vnet0" + obj.driver_name = "vhost" + obj.vif_inbound_average = 16384 + obj.vif_inbound_peak = 32768 + obj.vif_inbound_burst = 3276 + obj.vif_outbound_average = 32768 + obj.vif_outbound_peak = 65536 + obj.vif_outbound_burst = 6553 + obj.mtu = 9000 + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + + + + + + """) + + # parse the xml from the first object into a new object and make sure + # they are the same + obj2 = config.LibvirtConfigGuestInterface() + obj2.parse_str(xml) + self.assertXmlEqual(xml, obj2.to_xml()) + def test_config_driver_options(self): obj = config.LibvirtConfigGuestInterface() obj.net_type = "ethernet" @@ -1762,6 +1797,46 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest): obj2.parse_str(xml) self.assertXmlEqual(xml, obj2.to_xml()) + def test_config_bridge_with_mtu(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "bridge" + obj.source_dev = "br0" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.target_dev = "tap12345678" + obj.filtername = "clean-traffic" + obj.filterparams.append({"key": "IP", "value": "192.168.122.1"}) + obj.vif_inbound_average = 16384 + obj.vif_inbound_peak = 32768 + obj.vif_inbound_burst = 3276 + obj.vif_outbound_average = 32768 + obj.vif_outbound_peak = 65536 + obj.vif_outbound_burst = 6553 + obj.mtu = 9000 + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + + + + + + + + + """) + + # parse the xml from the first object into a new object and make sure + # they are the same + obj2 = config.LibvirtConfigGuestInterface() + obj2.parse_str(xml) + self.assertXmlEqual(xml, obj2.to_xml()) + def test_config_bridge_ovs(self): obj = config.LibvirtConfigGuestInterface() obj.net_type = "bridge" @@ -1790,6 +1865,36 @@ class LibvirtConfigGuestInterfaceTest(LibvirtConfigBaseTest): obj2.parse_str(xml) self.assertXmlEqual(xml, obj2.to_xml()) + def test_config_bridge_ovs_with_mtu(self): + obj = config.LibvirtConfigGuestInterface() + obj.net_type = "bridge" + obj.source_dev = "br0" + obj.mac_addr = "DE:AD:BE:EF:CA:FE" + obj.model = "virtio" + obj.target_dev = "tap12345678" + obj.vporttype = "openvswitch" + obj.vportparams.append({"key": "instanceid", "value": "foobar"}) + obj.mtu = 9000 + + xml = obj.to_xml() + self.assertXmlEqual(xml, """ + + + + + + + + + + """) + + # parse the xml from the first object into a new object and make sure + # they are the same + obj2 = config.LibvirtConfigGuestInterface() + obj2.parse_str(xml) + self.assertXmlEqual(xml, obj2.to_xml()) + def test_config_bridge_xen(self): obj = config.LibvirtConfigGuestInterface() obj.net_type = "bridge" diff --git a/nova/tests/unit/virt/libvirt/test_designer.py b/nova/tests/unit/virt/libvirt/test_designer.py index 7c701eeffc88..da3814a2c8ce 100644 --- a/nova/tests/unit/virt/libvirt/test_designer.py +++ b/nova/tests/unit/virt/libvirt/test_designer.py @@ -179,3 +179,8 @@ class DesignerTestCase(test.NoDBTestCase): self.assertEqual('unix', conf.vhostuser_type) self.assertEqual('fake-mode', conf.vhostuser_mode) self.assertEqual('fake-path', conf.vhostuser_path) + + def test_set_vif_mtu_config(self): + conf = config.LibvirtConfigGuestInterface() + designer.set_vif_mtu_config(conf, 9000) + self.assertEqual(9000, conf.mtu) diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index bb6264d19ac2..c30786b0bbe7 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -385,7 +385,8 @@ class LibvirtVifTestCase(test.NoDBTestCase): id="b82c1929-051e-481d-8110-4669916c7915", label="Demo Net", subnets=osv_objects.subnet.SubnetList( - objects=[])) + objects=[]), + mtu=9000) self.os_vif_bridge = osv_objects.vif.VIFBridge( id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", @@ -1555,29 +1556,60 @@ class LibvirtVifTestCase(test.NoDBTestCase): @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): + 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) + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=True): + cfg = d.get_config(self.instance, self.vif_bridge, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) - self._assertXmlEqual(""" - - - - - - - """, cfg.to_xml()) + 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_no_mtu(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 + + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=False): + 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") @@ -1588,23 +1620,53 @@ class LibvirtVifTestCase(test.NoDBTestCase): 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) + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=True): + cfg = d.get_config(self.instance, self.vif_bridge, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) - self._assertXmlEqual(""" - - - - - - """, cfg.to_xml()) + 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_no_mtu(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 + + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=False): + 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") @@ -1613,27 +1675,60 @@ class LibvirtVifTestCase(test.NoDBTestCase): mock_convert_vif.return_value = self.os_vif_agilio_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_agilio_ovs, - image_meta, flavor, - CONF.libvirt.virt_type, - hostimpl) + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=True): + cfg = d.get_config(self.instance, self.vif_agilio_ovs, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) - self._assertXmlEqual(""" - - - - - - - - - """, cfg.to_xml()) + 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_agilio_ovs_fallthrough_no_mtu(self, + mock_convert_vif, + mock_convert_inst): + mock_convert_vif.return_value = self.os_vif_agilio_ovs + mock_convert_inst.return_value = self.os_vif_inst_info + + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=False): + cfg = d.get_config(self.instance, self.vif_agilio_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") @@ -1688,31 +1783,64 @@ class LibvirtVifTestCase(test.NoDBTestCase): @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): + 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) + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=True): + cfg = d.get_config(self.instance, self.vif_ovs, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) - self._assertXmlEqual(""" - - - - - - - - - """, cfg.to_xml()) + 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_no_mtu(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 + + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=False): + 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") @@ -1721,25 +1849,55 @@ class LibvirtVifTestCase(test.NoDBTestCase): 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) + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=True): + cfg = d.get_config(self.instance, self.vif_ovs, + image_meta, flavor, + CONF.libvirt.virt_type, + hostimpl) - self._assertXmlEqual(""" - - - - - - - """, cfg.to_xml()) + 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_no_mtu(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 + + hostimpl = host.Host("qemu:///system") + flavor = objects.Flavor(name='m1.small') + image_meta = objects.ImageMeta.from_dict({}) + d = vif.LibvirtGenericVIFDriver() + with mock.patch.object(d, "_has_min_version_for_mtu", + return_value=False): + 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") diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index 0c0e6ee0f953..6725ddcac728 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -1328,6 +1328,7 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): self.vif_outbound_average = None self.vlan = None self.device_addr = None + self.mtu = None def format_dom(self): dev = super(LibvirtConfigGuestInterface, self).format_dom() @@ -1348,6 +1349,8 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): if self.net_type == "ethernet": if self.script is not None: dev.append(etree.Element("script", path=self.script)) + if self.mtu is not None: + dev.append(etree.Element("mtu", size=str(self.mtu))) elif self.net_type == "direct": dev.append(etree.Element("source", dev=self.source_dev, mode=self.source_mode)) @@ -1370,6 +1373,8 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): dev.append(etree.Element("source", bridge=self.source_dev)) if self.script is not None: dev.append(etree.Element("script", path=self.script)) + if self.mtu is not None: + dev.append(etree.Element("mtu", size=str(self.mtu))) else: dev.append(etree.Element("source", bridge=self.source_dev)) @@ -1501,6 +1506,8 @@ class LibvirtConfigGuestInterface(LibvirtConfigGuestDevice): elif c.tag == 'address': obj = LibvirtConfigGuestDeviceAddress.parse_dom(c) self.device_addr = obj + elif c.tag == 'mtu': + self.mtu = int(c.get('size')) def add_filter_param(self, key, value): self.filterparams.append({'key': key, 'value': value}) diff --git a/nova/virt/libvirt/designer.py b/nova/virt/libvirt/designer.py index 847faa65983e..9448847937dc 100644 --- a/nova/virt/libvirt/designer.py +++ b/nova/virt/libvirt/designer.py @@ -158,6 +158,13 @@ def set_vif_host_backend_vhostuser_config(conf, mode, path): conf.vhostuser_path = path +def set_vif_mtu_config(conf, mtu): + """Populate a LibvirtConfigGuestInterface instance + with network mtu. + """ + conf.mtu = mtu + + 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 6d5f2385e97d..6b2ad06b33a0 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -48,6 +48,8 @@ CONF = nova.conf.CONF MIN_LIBVIRT_VHOSTUSER_MQ = (1, 2, 17) # vlan tag for macvtap passthrough mode on SRIOV VFs MIN_LIBVIRT_MACVTAP_PASSTHROUGH_VLAN = (1, 3, 5) +# setting interface mtu was intoduced in libvirt 3.3 +MIN_LIBVIRT_INTERFACE_MTU = (3, 3, 0) def is_vif_model_valid_for_virt(virt_type, vif_model): @@ -237,8 +239,23 @@ class LibvirtGenericVIFDriver(object): conf.filtername = name designer.set_vif_bandwidth_config(conf, inst_type) + self._set_mtu_config(vif, host, conf) + return conf + def _set_mtu_config(self, vif, host, conf): + """:param vif: nova.network.modle.vif + :param host: nova.virt.libvirt.host.Host + :param conf: nova.virt.libvirt.config.LibvirtConfigGuestInterface + """ + network = vif.get('network') + if (network and network.get_meta("mtu") and + self._has_min_version_for_mtu(host)): + designer.set_vif_mtu_config(conf, network.get_meta("mtu")) + + def _has_min_version_for_mtu(self, host): + return host.has_min_version(MIN_LIBVIRT_INTERFACE_MTU) + def get_config_ivs_hybrid(self, instance, vif, image_meta, inst_type, virt_type, host): newvif = copy.deepcopy(vif) @@ -404,6 +421,8 @@ class LibvirtGenericVIFDriver(object): dev = self.get_vif_devname(vif) designer.set_vif_host_backend_ethernet_config(conf, dev, host) + self._set_mtu_config(vif, host, conf) + return conf def _get_vhostuser_settings(self, vif): @@ -530,6 +549,9 @@ class LibvirtGenericVIFDriver(object): func(instance, vif, conf, host) designer.set_vif_bandwidth_config(conf, inst_type) + if ('network' in vif and 'mtu' in vif.network and + self._has_min_version_for_mtu(host)): + designer.set_vif_mtu_config(conf, vif.network.mtu) return conf diff --git a/releasenotes/notes/libvirt-mtu-configuration-0a3e9129dd33b0bc.yaml b/releasenotes/notes/libvirt-mtu-configuration-0a3e9129dd33b0bc.yaml new file mode 100644 index 000000000000..14a58352078e --- /dev/null +++ b/releasenotes/notes/libvirt-mtu-configuration-0a3e9129dd33b0bc.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + For libvirt driver. Now when creating tap devices the MTU will be + configured. Requires libvirt 3.3.0 at least. +