diff --git a/etc/neutron/rootwrap.d/dhcp.filters b/etc/neutron/rootwrap.d/dhcp.filters index 0502eb63fdf..758a91d0b1c 100644 --- a/etc/neutron/rootwrap.d/dhcp.filters +++ b/etc/neutron/rootwrap.d/dhcp.filters @@ -10,6 +10,7 @@ # dhcp-agent dnsmasq: CommandFilter, dnsmasq, root +ethtool: CommandFilter, ethtool, root # dhcp-agent uses kill as well, that's handled by the generic KillFilter # it looks like these are the only signals needed, per # neutron/agent/linux/dhcp.py diff --git a/etc/neutron/rootwrap.d/l3.filters b/etc/neutron/rootwrap.d/l3.filters index d66fa2cff9a..100896de2af 100644 --- a/etc/neutron/rootwrap.d/l3.filters +++ b/etc/neutron/rootwrap.d/l3.filters @@ -12,6 +12,7 @@ arping: CommandFilter, arping, root # l3_agent +ethtool: CommandFilter, ethtool, root sysctl: CommandFilter, sysctl, root route: CommandFilter, route, root radvd: CommandFilter, radvd, root diff --git a/neutron/agent/linux/ethtool.py b/neutron/agent/linux/ethtool.py new file mode 100644 index 00000000000..b28c41a8c25 --- /dev/null +++ b/neutron/agent/linux/ethtool.py @@ -0,0 +1,34 @@ +# Copyright 2020 OpenStack Foundation +# All Rights Reserved. +# +# 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 +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron.agent.linux import ip_lib + + +class Ethtool(object): + + COMMAND = 'ethtool' + + @staticmethod + def _cmd(cmd, namespace, **kwargs): + ip_wrapper = ip_lib.IPWrapper(namespace) + return ip_wrapper.netns.execute(cmd, run_as_root=True, **kwargs) + + @classmethod + def offload(cls, device, rx, tx, namespace=None): + rx = 'on' if rx else 'off' + tx = 'on' if tx else 'off' + cmd = ['--offload', device, 'rx', rx, 'tx', tx] + cmd = [cls.COMMAND] + cmd + cls._cmd(cmd, namespace) diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index e8b6653c708..247d8ea8f56 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -25,8 +25,12 @@ from pyroute2.netlink import exceptions as pyroute2_exc import six from neutron.agent.common import ovs_lib +from neutron.agent.linux import ethtool from neutron.agent.linux import ip_lib from neutron.common import utils +from neutron.conf.plugins.ml2.drivers import ovs_conf +from neutron.plugins.ml2.drivers.openvswitch.agent.common \ + import constants as ovs_const LOG = logging.getLogger(__name__) @@ -346,6 +350,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): def __init__(self, conf, **kwargs): super(OVSInterfaceDriver, self).__init__(conf, **kwargs) + ovs_conf.register_ovs_agent_opts(self.conf) if self.conf.ovs_use_veth: self.DEV_NAME_PREFIX = 'ns-' @@ -433,6 +438,11 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): if link_up: ns_dev.link.set_up() if self.conf.ovs_use_veth: + # ovs-dpdk does not do checksum calculations for veth interface + # (bug 1832021) + if self.conf.OVS.datapath_type == ovs_const.OVS_DATAPATH_NETDEV: + ethtool.Ethtool.offload(ns_dev.name, rx=False, tx=False, + namespace=namespace) root_dev.link.set_up() def unplug(self, device_name, bridge=None, namespace=None, prefix=None): diff --git a/neutron/tests/unit/agent/linux/test_interface.py b/neutron/tests/unit/agent/linux/test_interface.py index 8d8c97c0590..81855d70a16 100644 --- a/neutron/tests/unit/agent/linux/test_interface.py +++ b/neutron/tests/unit/agent/linux/test_interface.py @@ -19,11 +19,14 @@ from oslo_utils import excutils from pyroute2.netlink import exceptions as pyroute2_exc from neutron.agent.common import ovs_lib +from neutron.agent.linux import ethtool from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.conf.agent import common as config from neutron.conf.plugins.ml2.drivers import ovs_conf +from neutron.plugins.ml2.drivers.openvswitch.agent.common \ + import constants as ovs_const from neutron.tests import base @@ -74,6 +77,8 @@ class TestBase(base.BaseTestCase): self.conf = config.setup_conf() ovs_conf.register_ovs_opts(self.conf) config.register_interface_opts(self.conf) + self.eth_tool_p = mock.patch.object(ethtool, 'Ethtool') + self.eth_tool = self.eth_tool_p.start() self.ip_dev_p = mock.patch.object(ip_lib, 'IPDevice') self.ip_dev = self.ip_dev_p.start() self.ip_p = mock.patch.object(ip_lib, 'IPWrapper') @@ -518,7 +523,12 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver): def setUp(self): super(TestOVSInterfaceDriverWithVeth, self).setUp() + ovs_conf.register_ovs_agent_opts(self.conf) self.conf.set_override('ovs_use_veth', True) + self.conf.set_override( + 'datapath_type', + ovs_const.OVS_DATAPATH_NETDEV, + group='OVS') def test_get_device_name(self): br = interface.OVSInterfaceDriver(self.conf) @@ -549,6 +559,7 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver): mock.patch.object( interface, '_get_veth', return_value=(root_dev, ns_dev)).start() + ns_dev.name = devname expected = [mock.call(), mock.call().add_veth('tap0', devname, @@ -579,6 +590,9 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver): self.ip.assert_has_calls(expected) root_dev.assert_has_calls([mock.call.link.set_up()]) ns_dev.assert_has_calls([mock.call.link.set_up()]) + self.eth_tool.assert_has_calls([mock.call.offload( + devname, rx=False, + tx=False, namespace=namespace)]) def test_plug_new(self): # The purpose of test_plug_new in parent class(TestOVSInterfaceDriver)