Browse Source

Enable ipv6_forwarding in HA router's namespace

When HA router is created in "stanby" mode, ipv6 forwarding is
disabled by default in its namespace.
But when router is transitioned to be "master" on node, ipv6
forwarding should be enabled. This was fine for routers with
configured gateway but we somehow missed the case when router don't
have gateway configured.
Because of that missing ipv6 forwarding setting in such case, IPv6
W-E traffic between 2 subnets was not working fine in L3 HA case.

This patch fixes it by adding configuring ipv6_forwarding on
"all" interface in router's namespace always, even if it don't have
gateway configured.

Change-Id: I8b1b2b426f7a26a4b2407a83f9bf29dd6e9ba7b0
CLoses-Bug: #1818224
Slawek Kaplonski 1 month ago
parent
commit
66eb1e29f3

+ 19
- 13
neutron/agent/l3/ha.py View File

@@ -125,7 +125,7 @@ class AgentMixin(object):
125 125
         # configuration to keepalived-state-change in order to remove the
126 126
         # dependency that currently exists on l3-agent running for the IPv6
127 127
         # failover.
128
-        self._configure_ipv6_params_on_ext_gw_port_if_necessary(ri, state)
128
+        self._configure_ipv6_params(ri, state)
129 129
         if self.conf.enable_metadata_proxy:
130 130
             self._update_metadata_proxy(ri, router_id, state)
131 131
         self._update_radvd_daemon(ri, state)
@@ -133,25 +133,31 @@ class AgentMixin(object):
133 133
         self.state_change_notifier.queue_event((router_id, state))
134 134
         self.l3_ext_manager.ha_state_change(self.context, state_change_data)
135 135
 
136
-    def _configure_ipv6_params_on_ext_gw_port_if_necessary(self, ri, state):
137
-        # If ipv6 is enabled on the platform, ipv6_gateway config flag is
138
-        # not set and external_network associated to the router does not
139
-        # include any IPv6 subnet, enable the gateway interface to accept
140
-        # Router Advts from upstream router for default route on master
141
-        # instances as well as ipv6 forwarding. Otherwise, disable them.
142
-        ex_gw_port_id = ri.ex_gw_port and ri.ex_gw_port['id']
143
-        if not ex_gw_port_id:
136
+    def _configure_ipv6_params(self, ri, state):
137
+        if not self.use_ipv6:
144 138
             return
145 139
 
146
-        interface_name = ri.get_external_device_name(ex_gw_port_id)
140
+        ipv6_forwarding_enable = state == 'master'
147 141
         if ri.router.get('distributed', False):
148 142
             namespace = ri.ha_namespace
149 143
         else:
150 144
             namespace = ri.ns_name
151 145
 
152
-        enable = state == 'master'
153
-        ri._configure_ipv6_params_on_gw(ri.ex_gw_port, namespace,
154
-                                        interface_name, enable)
146
+        if ipv6_forwarding_enable:
147
+            ri.driver.configure_ipv6_forwarding(
148
+                namespace, 'all', ipv6_forwarding_enable)
149
+
150
+        # If ipv6 is enabled on the platform, ipv6_gateway config flag is
151
+        # not set and external_network associated to the router does not
152
+        # include any IPv6 subnet, enable the gateway interface to accept
153
+        # Router Advts from upstream router for default route on master
154
+        # instances as well as ipv6 forwarding. Otherwise, disable them.
155
+        ex_gw_port_id = ri.ex_gw_port and ri.ex_gw_port['id']
156
+        if ex_gw_port_id:
157
+            interface_name = ri.get_external_device_name(ex_gw_port_id)
158
+            ri._configure_ipv6_params_on_gw(
159
+                ri.ex_gw_port, namespace, interface_name,
160
+                ipv6_forwarding_enable)
155 161
 
156 162
     def _update_metadata_proxy(self, ri, router_id, state):
157 163
         # NOTE(slaweq): Since the metadata proxy is spawned in the qrouter

+ 3
- 1
neutron/tests/functional/agent/l3/framework.py View File

@@ -112,7 +112,8 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
112 112
                              extra_routes=True,
113 113
                              enable_fip=True, enable_snat=True,
114 114
                              num_internal_ports=1,
115
-                             dual_stack=False, v6_ext_gw_with_sub=True,
115
+                             dual_stack=False, enable_gw=True,
116
+                             v6_ext_gw_with_sub=True,
116 117
                              enable_pf_floating_ip=False,
117 118
                              qos_policy_id=None):
118 119
         if ip_version == constants.IP_VERSION_6 and not dual_stack:
@@ -129,6 +130,7 @@ class L3AgentTestFramework(base.BaseSudoTestCase):
129 130
                                                   enable_ha=enable_ha,
130 131
                                                   extra_routes=extra_routes,
131 132
                                                   dual_stack=dual_stack,
133
+                                                  enable_gw=enable_gw,
132 134
                                                   v6_ext_gw_with_sub=(
133 135
                                                       v6_ext_gw_with_sub),
134 136
                                                   enable_pf_floating_ip=(

+ 23
- 2
neutron/tests/functional/agent/l3/test_ha_router.py View File

@@ -119,8 +119,11 @@ class L3HATestCase(framework.L3AgentTestFramework):
119 119
         router_info['gw_port'] = ex_port
120 120
         router.process()
121 121
         self._assert_ipv6_accept_ra(router, expected_ra)
122
+        # As router is going first to master and than to backup mode,
123
+        # ipv6_forwarding should be enabled on "all" interface always after
124
+        # that transition
122 125
         self._assert_ipv6_forwarding(router, expected_forwarding,
123
-                                     expected_forwarding)
126
+                                     True)
124 127
 
125 128
     @testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(),
126 129
                           "IPv6 is not enabled")
@@ -364,6 +367,24 @@ class L3HATestCase(framework.L3AgentTestFramework):
364 367
         self._wait_until_ipv6_forwarding_has_state(
365 368
             router.ns_name, external_device_name, 1)
366 369
 
370
+    @testtools.skipUnless(ipv6_utils.is_enabled_and_bind_by_default(),
371
+                          "IPv6 is not enabled")
372
+    def test_ha_router_without_gw_ipv6_forwarding_state(self):
373
+        router_info = self.generate_router_info(
374
+            enable_ha=True, enable_gw=False)
375
+        router_info[constants.HA_INTERFACE_KEY]['status'] = (
376
+            constants.PORT_STATUS_DOWN)
377
+        router = self.manage_router(self.agent, router_info)
378
+
379
+        common_utils.wait_until_true(lambda: router.ha_state == 'backup')
380
+        self._wait_until_ipv6_forwarding_has_state(router.ns_name, 'all', 0)
381
+
382
+        router.router[constants.HA_INTERFACE_KEY]['status'] = (
383
+            constants.PORT_STATUS_ACTIVE)
384
+        self.agent._process_updated_router(router.router)
385
+        common_utils.wait_until_true(lambda: router.ha_state == 'master')
386
+        self._wait_until_ipv6_forwarding_has_state(router.ns_name, 'all', 1)
387
+
367 388
 
368 389
 class L3HATestFailover(framework.L3AgentTestFramework):
369 390
 
@@ -428,7 +449,7 @@ class L3HATestFailover(framework.L3AgentTestFramework):
428 449
         self._assert_ipv6_forwarding(new_master, True, True)
429 450
         self._assert_ipv6_accept_ra(new_slave, False)
430 451
         # after transition from master -> slave, 'all' IPv6 forwarding should
431
-        # still be enabled
452
+        # be enabled
432 453
         self._assert_ipv6_forwarding(new_slave, False, True)
433 454
 
434 455
     def test_ha_router_lost_gw_connection(self):

+ 37
- 21
neutron/tests/unit/agent/l3/test_agent.py View File

@@ -270,29 +270,45 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
270 270
         spawn_metadata_proxy.assert_called()
271 271
         destroy_metadata_proxy.assert_not_called()
272 272
 
273
-    def _test__configure_ipv6_params_on_ext_gw_port_if_necessary_helper(
274
-            self, state, enable_expected):
273
+    def _test__configure_ipv6_params_helper(self, state, gw_port_id):
275 274
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
276 275
         router_info = l3router.RouterInfo(agent, _uuid(), {}, **self.ri_kwargs)
277
-        router_info.ex_gw_port = {'id': _uuid()}
278
-        with mock.patch.object(router_info, '_configure_ipv6_params_on_gw'
279
-                               ) as mock_configure_ipv6:
280
-            agent._configure_ipv6_params_on_ext_gw_port_if_necessary(
281
-                router_info, state)
282
-            interface_name = router_info.get_external_device_name(
283
-                    router_info.ex_gw_port['id'])
284
-
285
-            mock_configure_ipv6.assert_called_once_with(
286
-                router_info.ex_gw_port, router_info.ns_name, interface_name,
287
-                enable_expected)
288
-
289
-    def test__configure_ipv6_params_on_ext_gw_port_if_necessary_master(self):
290
-        self._test__configure_ipv6_params_on_ext_gw_port_if_necessary_helper(
291
-            'master', True)
292
-
293
-    def test__configure_ipv6_params_on_ext_gw_port_if_necessary_backup(self):
294
-        self._test__configure_ipv6_params_on_ext_gw_port_if_necessary_helper(
295
-            'backup', False)
276
+        if gw_port_id:
277
+            router_info.ex_gw_port = {'id': gw_port_id}
278
+        expected_forwarding_state = state == 'master'
279
+        with mock.patch.object(
280
+            router_info.driver, "configure_ipv6_forwarding"
281
+        ) as configure_ipv6_forwarding, mock.patch.object(
282
+            router_info, "_configure_ipv6_params_on_gw"
283
+        ) as configure_ipv6_on_gw:
284
+            agent._configure_ipv6_params(router_info, state)
285
+
286
+            if state == 'master':
287
+                configure_ipv6_forwarding.assert_called_once_with(
288
+                    router_info.ns_name, 'all', expected_forwarding_state)
289
+            else:
290
+                configure_ipv6_forwarding.assert_not_called()
291
+
292
+            if gw_port_id:
293
+                interface_name = router_info.get_external_device_name(
294
+                        router_info.ex_gw_port['id'])
295
+                configure_ipv6_on_gw.assert_called_once_with(
296
+                    router_info.ex_gw_port, router_info.ns_name,
297
+                    interface_name, expected_forwarding_state)
298
+            else:
299
+                configure_ipv6_on_gw.assert_not_called()
300
+
301
+    def test__configure_ipv6_params_master(self):
302
+        self._test__configure_ipv6_params_helper('master', gw_port_id=_uuid())
303
+
304
+    def test__configure_ipv6_params_backup(self):
305
+        self._test__configure_ipv6_params_helper('backup', gw_port_id=_uuid())
306
+
307
+    def test__configure_ipv6_params_master_no_gw_port(self):
308
+        self._test__configure_ipv6_params_helper('master', gw_port_id=None)
309
+
310
+    def test__configure_ipv6_params_backup_no_gw_port(self):
311
+        self._test__configure_ipv6_params_helper('backup', gw_port_id=None)
296 312
 
297 313
     def test_check_ha_state_for_router_master_standby(self):
298 314
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)

Loading…
Cancel
Save