Browse Source

Avoid agents adding ports as trunk by default.

Agent OVS interface code adds ports without a vlan tag,
if neutron-openvswitch-agent fails to set the tag, or takes
too long, the port will be a trunk port, receiving
traffic from the external network or any other port
sending traffic on br-int.

Also, those kinds of ports are triggering a code path
on the ovs-vswitchd revalidator thread which can eventually
hog the CPU of the host (that's a bug under investigation [1])

[1] https://bugzilla.redhat.com/show_bug.cgi?id=1558336

Co-Authored-By: Slawek Kaplonski <skaplons@redhat.com>
Change-Id: I024bbbdf7059835b2f23c264b48478c71633a43c
Closes-Bug: 1767422
tags/13.0.0.0b2
Miguel Angel Ajo 1 year ago
parent
commit
88f5e11d8b

+ 13
- 0
neutron/agent/common/ovs_lib.py View File

@@ -296,6 +296,19 @@ class OVSBridge(BaseOVS):
296 296
         with self.ovsdb.transaction() as txn:
297 297
             txn.add(self.ovsdb.add_port(self.br_name, port_name,
298 298
                                         may_exist=False))
299
+            # NOTE(mangelajo): Port is added to dead vlan (4095) by default
300
+            # until it's handled by the neutron-openvswitch-agent. Otherwise it
301
+            # becomes a trunk port on br-int (receiving traffic for all vlans),
302
+            # and also triggers issues on ovs-vswitchd related to the
303
+            # datapath flow revalidator thread, see lp#1767422
304
+            txn.add(self.ovsdb.db_set(
305
+                    'Port', port_name, ('tag', constants.DEAD_VLAN_TAG)))
306
+
307
+            # TODO(mangelajo): We could accept attr tuples for the Port too
308
+            # but, that could potentially break usage of this function in
309
+            # stable branches (where we need to backport).
310
+            # https://review.openstack.org/#/c/564825/4/neutron/agent/common/
311
+            # ovs_lib.py@289
299 312
             if interface_attr_tuples:
300 313
                 txn.add(self.ovsdb.db_set('Interface', port_name,
301 314
                                           *interface_attr_tuples))

+ 9
- 0
neutron/agent/l3/router_info.py View File

@@ -640,6 +640,15 @@ class RouterInfo(object):
640 640
                          namespace=ns_name,
641 641
                          prefix=EXTERNAL_DEV_PREFIX,
642 642
                          mtu=ex_gw_port.get('mtu'))
643
+        if self.agent_conf.external_network_bridge:
644
+            # NOTE(slaweq): for OVS implementations remove the DEAD VLAN tag
645
+            # on ports. DEAD VLAN tag is added to each newly created port
646
+            # and should be removed by L2 agent but if
647
+            # external_network_bridge is set than external gateway port is
648
+            # created in this bridge and will not be touched by L2 agent.
649
+            # This is related to lp#1767422
650
+            self.driver.remove_vlan_tag(
651
+                self.agent_conf.external_network_bridge, interface_name)
643 652
 
644 653
     def _get_external_gw_ips(self, ex_gw_port):
645 654
         gateway_ips = []

+ 15
- 0
neutron/agent/linux/interface.py View File

@@ -223,6 +223,17 @@ class LinuxInterfaceDriver(object):
223 223
     def get_device_name(self, port):
224 224
         return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
225 225
 
226
+    def remove_vlan_tag(self, bridge, interface_name):
227
+        """Remove vlan tag from given interface.
228
+
229
+        This method is necessary only for the case when deprecated
230
+        option 'external_network_bridge' is used in L3 agent as
231
+        external gateway port is then created in this external bridge
232
+        directly and it will have DEAD_VLAN_TAG added by default.
233
+        """
234
+        # TODO(slaweq): remove it when external_network_bridge option will be
235
+        # removed
236
+
226 237
     @staticmethod
227 238
     def configure_ipv6_ra(namespace, dev_name, value):
228 239
         """Configure handling of IPv6 Router Advertisements on an
@@ -322,6 +333,10 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
322 333
         ovs = ovs_lib.OVSBridge(bridge)
323 334
         ovs.replace_port(device_name, *attrs)
324 335
 
336
+    def remove_vlan_tag(self, bridge, interface):
337
+        ovs = ovs_lib.OVSBridge(bridge)
338
+        ovs.clear_db_attribute("Port", interface, "tag")
339
+
325 340
     def plug_new(self, network_id, port_id, device_name, mac_address,
326 341
                  bridge=None, namespace=None, prefix=None, mtu=None):
327 342
         """Plug in the interface."""

+ 4
- 0
neutron/tests/common/net_helpers.py View File

@@ -782,6 +782,10 @@ class OVSPortFixture(PortFixture):
782 782
             self.mac,
783 783
             bridge=self.bridge.br_name,
784 784
             namespace=self.namespace)
785
+        # NOTE(mangelajo): for OVS implementations remove the DEAD VLAN tag
786
+        # on ports that we intend to use as fake vm interfaces, they
787
+        # need to be flat. This is related to lp#1767422
788
+        self.bridge.clear_db_attribute("Port", port_name, "tag")
785 789
         self.addCleanup(self.bridge.delete_port, port_name)
786 790
         self.port = ip_lib.IPDevice(port_name, self.namespace)
787 791
 

+ 7
- 1
neutron/tests/functional/agent/l2/base.py View File

@@ -395,7 +395,13 @@ class OVSAgentTestFramework(base.BaseOVSLinuxTestCase):
395 395
         self._plug_ports(network, ports, self.agent, bridge=phys_br,
396 396
                          namespace=namespace)
397 397
 
398
-        if phys_segmentation_id and network_type == 'vlan':
398
+        if network_type == 'flat':
399
+            # NOTE(slaweq): for OVS implementations remove the DEAD VLAN tag
400
+            # on ports that belongs to flat network. DEAD VLAN tag is added
401
+            # to each newly created port. This is related to lp#1767422
402
+            for port in ports:
403
+                phys_br.clear_db_attribute("Port", port['vif_name'], "tag")
404
+        elif phys_segmentation_id and network_type == 'vlan':
399 405
             for port in ports:
400 406
                 phys_br.set_db_attribute(
401 407
                     "Port", port['vif_name'], "tag", phys_segmentation_id)

+ 26
- 2
neutron/tests/functional/agent/l3/framework.py View File

@@ -31,6 +31,7 @@ from neutron.agent.l3 import namespaces
31 31
 from neutron.agent.l3 import router_info as l3_router_info
32 32
 from neutron.agent import l3_agent as l3_agent_main
33 33
 from neutron.agent.linux import external_process
34
+from neutron.agent.linux import interface
34 35
 from neutron.agent.linux import ip_lib
35 36
 from neutron.agent.linux import keepalived
36 37
 from neutron.common import constants as n_const
@@ -44,13 +45,15 @@ from neutron.tests.functional import base
44 45
 
45 46
 _uuid = uuidutils.generate_uuid
46 47
 
48
+OVS_INTERFACE_DRIVER = 'neutron.agent.linux.interface.OVSInterfaceDriver'
49
+
47 50
 
48 51
 def get_ovs_bridge(br_name):
49 52
     return ovs_lib.OVSBridge(br_name)
50 53
 
51 54
 
52 55
 class L3AgentTestFramework(base.BaseSudoTestCase):
53
-    INTERFACE_DRIVER = 'neutron.agent.linux.interface.OVSInterfaceDriver'
56
+    INTERFACE_DRIVER = OVS_INTERFACE_DRIVER
54 57
     NESTED_NAMESPACE_SEPARATOR = '@'
55 58
 
56 59
     def setUp(self):
@@ -318,7 +321,28 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
318 321
 
319 322
     def manage_router(self, agent, router):
320 323
         self.addCleanup(agent._safe_router_removed, router['id'])
321
-        agent._process_added_router(router)
324
+
325
+        # NOTE(mangelajo): Neutron functional for l3 don't rely on openvswitch
326
+        #                  agent tagging ports, and all ports remain untagged
327
+        #                  during test execution.
328
+        #                  Workaround related to lp#1767422 plugs new ports as
329
+        #                  dead vlan (4095) to avoid issues, we need to remove
330
+        #                  such tag during functional l3 testing.
331
+        original_plug_new = interface.OVSInterfaceDriver.plug_new
332
+
333
+        def new_ovs_plug(self, *args, **kwargs):
334
+            original_plug_new(self, *args, **kwargs)
335
+            bridge = (kwargs.get('bridge') or args[4] or
336
+                      self.conf.ovs_integration_bridge)
337
+            device_name = kwargs.get('device_name') or args[2]
338
+            ovsbr = ovs_lib.OVSBridge(bridge)
339
+            ovsbr.clear_db_attribute('Port', device_name, 'tag')
340
+
341
+        with mock.patch(OVS_INTERFACE_DRIVER + '.plug_new', autospec=True) as (
342
+                ovs_plug):
343
+            ovs_plug.side_effect = new_ovs_plug
344
+            agent._process_added_router(router)
345
+
322 346
         return agent.router_info[router['id']]
323 347
 
324 348
     def _delete_router(self, agent, router_id):

+ 2
- 0
neutron/tests/functional/agent/test_ovs_lib.py View File

@@ -90,6 +90,8 @@ class OVSBridgeTestCase(OVSBridgeTestBase):
90 90
         self.assertTrue(self.br.port_exists(port_name))
91 91
         self.assertEqual('test', self.br.db_get_val('Interface', port_name,
92 92
                                                     'external_ids')['test'])
93
+        self.assertEqual(agent_const.DEAD_VLAN_TAG,
94
+                         self.br.db_get_val('Port', port_name, 'tag'))
93 95
 
94 96
     def test_attribute_lifecycle(self):
95 97
         (port_name, ofport) = self.create_ovs_port()

Loading…
Cancel
Save