Skip to content

Commit 0e427ec

Browse files
committed
Fix duplicated routes exceptions
Since the train release neutron adds routes with protocol static. Keepalived also adds the same routes with different protocols depending on the keepalived version. This can result in duplicated routes inside network namespaces. On l3 agent restarts those duplicate routes then prevent the l3 agent from updating its router state because it runs into 'RTNETLINK answers: File exists expections' when it tries to execute 'ip route' commands. This patch adds the same protocol static to each virtual route of keepalived's configuration so network namespaces do not run into duplicated routes anymore. Closes-Bug: #1956846 Change-Id: Ic35b5d4b9110b832c10345c45ec62c0923237cfd (cherry picked from commit c813b65)
1 parent 1183240 commit 0e427ec

File tree

3 files changed

+32
-25
lines changed

3 files changed

+32
-25
lines changed

neutron/agent/linux/keepalived.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ def build_config(self):
152152
LOG.warning("keepalived_use_no_track cfg option is True but "
153153
"keepalived on host seems to not support this "
154154
"option")
155+
# NOTE(mstinsky): neutron and keepalived are adding the same routes on
156+
# primary routers. With this we ensure that both are adding the routes
157+
# with the same procotol and prevent duplicated routes which result in
158+
# neutron exception for ip route commands.
159+
output += ' protocol static'
155160
return output
156161

157162

neutron/tests/functional/agent/l3/framework.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@
7878
%(int_port_ipv6)s dev %(internal_device_name)s scope link no_track
7979
}
8080
virtual_routes {
81-
0.0.0.0/0 via %(default_gateway_ip)s dev %(ex_device_name)s no_track
82-
8.8.8.0/24 via 19.4.4.4 no_track
83-
%(extra_subnet_cidr)s dev %(ex_device_name)s scope link no_track
81+
0.0.0.0/0 via %(default_gateway_ip)s dev %(ex_device_name)s no_track protocol static
82+
8.8.8.0/24 via 19.4.4.4 no_track protocol static
83+
%(extra_subnet_cidr)s dev %(ex_device_name)s scope link no_track protocol static
8484
}
85-
}"""
85+
}""" # noqa: E501 # pylint: disable=line-too-long
8686

8787

8888
def get_ovs_bridge(br_name):

neutron/tests/unit/agent/linux/test_keepalived.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class KeepalivedConfTestCase(KeepalivedBaseTestCase,
164164
192.168.55.0/24 dev eth10 no_track
165165
}
166166
virtual_routes {
167-
0.0.0.0/0 via 192.168.1.1 dev eth1 no_track
167+
0.0.0.0/0 via 192.168.1.1 dev eth1 no_track protocol static
168168
}
169169
}
170170
vrrp_instance VR_2 {
@@ -247,7 +247,7 @@ class KeepalivedConfWithoutNoTrackTestCase(KeepalivedConfTestCase):
247247
192.168.55.0/24 dev eth10
248248
}
249249
virtual_routes {
250-
0.0.0.0/0 via 192.168.1.1 dev eth1
250+
0.0.0.0/0 via 192.168.1.1 dev eth1 protocol static
251251
}
252252
}
253253
vrrp_instance VR_2 {
@@ -323,11 +323,11 @@ def test_remove_routes_on_interface(self):
323323

324324
def test_build_config(self):
325325
expected = """ virtual_routes {
326-
0.0.0.0/0 via 1.0.0.254 dev eth0 no_track
327-
::/0 via fe80::3e97:eff:fe26:3bfa/64 dev eth1 no_track
328-
10.0.0.0/8 via 1.0.0.1 no_track
329-
20.0.0.0/8 via 2.0.0.2 no_track
330-
30.0.0.0/8 dev eth0 scope link no_track
326+
0.0.0.0/0 via 1.0.0.254 dev eth0 no_track protocol static
327+
::/0 via fe80::3e97:eff:fe26:3bfa/64 dev eth1 no_track protocol static
328+
10.0.0.0/8 via 1.0.0.1 no_track protocol static
329+
20.0.0.0/8 via 2.0.0.2 no_track protocol static
330+
30.0.0.0/8 dev eth0 scope link no_track protocol static
331331
}"""
332332
with mock.patch.object(
333333
keepalived, '_is_keepalived_use_no_track_supported',
@@ -337,11 +337,11 @@ def test_build_config(self):
337337

338338
def _get_no_track_less_expected_config(self):
339339
expected = """ virtual_routes {
340-
0.0.0.0/0 via 1.0.0.254 dev eth0
341-
::/0 via fe80::3e97:eff:fe26:3bfa/64 dev eth1
342-
10.0.0.0/8 via 1.0.0.1
343-
20.0.0.0/8 via 2.0.0.2
344-
30.0.0.0/8 dev eth0 scope link
340+
0.0.0.0/0 via 1.0.0.254 dev eth0 protocol static
341+
::/0 via fe80::3e97:eff:fe26:3bfa/64 dev eth1 protocol static
342+
10.0.0.0/8 via 1.0.0.1 protocol static
343+
20.0.0.0/8 via 2.0.0.2 protocol static
344+
30.0.0.0/8 dev eth0 scope link protocol static
345345
}"""
346346
return expected
347347

@@ -395,7 +395,7 @@ def _test_remove_addresses_by_interface(self, no_track_value):
395395
192.168.1.0/24 dev eth1%(no_track)s
396396
}
397397
virtual_routes {
398-
0.0.0.0/0 via 192.168.1.1 dev eth1%(no_track)s
398+
0.0.0.0/0 via 192.168.1.1 dev eth1%(no_track)s protocol static
399399
}
400400
}
401401
vrrp_instance VR_2 {
@@ -416,7 +416,7 @@ def _test_remove_addresses_by_interface(self, no_track_value):
416416
192.168.3.0/24 dev eth6%(no_track)s
417417
192.168.55.0/24 dev eth10%(no_track)s
418418
}
419-
}""" % {'no_track': no_track_value})
419+
}""" % {'no_track': no_track_value}) # noqa: E501 # pylint: disable=line-too-long
420420

421421
self.assertEqual(expected, config.get_config_str())
422422

@@ -501,45 +501,47 @@ def test_virtual_route_with_dev(self):
501501
return_value=True):
502502
route = keepalived.KeepalivedVirtualRoute(
503503
n_consts.IPv4_ANY, '1.2.3.4', 'eth0')
504-
self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0 no_track',
505-
route.build_config())
504+
self.assertEqual(
505+
'0.0.0.0/0 via 1.2.3.4 dev eth0 no_track protocol static',
506+
route.build_config())
506507

507508
def test_virtual_route_with_dev_no_track_not_supported(self):
508509
with mock.patch.object(
509510
keepalived, '_is_keepalived_use_no_track_supported',
510511
return_value=False):
511512
route = keepalived.KeepalivedVirtualRoute(
512513
n_consts.IPv4_ANY, '1.2.3.4', 'eth0')
513-
self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0',
514+
self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0 protocol static',
514515
route.build_config())
515516

516517
def test_virtual_route_with_dev_without_no_track(self):
517518
cfg.CONF.set_override('keepalived_use_no_track', False)
518519
route = keepalived.KeepalivedVirtualRoute(n_consts.IPv4_ANY, '1.2.3.4',
519520
'eth0')
520-
self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0',
521+
self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0 protocol static',
521522
route.build_config())
522523

523524
def test_virtual_route_without_dev(self):
524525
with mock.patch.object(
525526
keepalived, '_is_keepalived_use_no_track_supported',
526527
return_value=True):
527528
route = keepalived.KeepalivedVirtualRoute('50.0.0.0/8', '1.2.3.4')
528-
self.assertEqual('50.0.0.0/8 via 1.2.3.4 no_track',
529+
self.assertEqual('50.0.0.0/8 via 1.2.3.4 no_track protocol static',
529530
route.build_config())
530531

531532
def test_virtual_route_without_dev_no_track_not_supported(self):
532533
with mock.patch.object(
533534
keepalived, '_is_keepalived_use_no_track_supported',
534535
return_value=False):
535536
route = keepalived.KeepalivedVirtualRoute('50.0.0.0/8', '1.2.3.4')
536-
self.assertEqual('50.0.0.0/8 via 1.2.3.4',
537+
self.assertEqual('50.0.0.0/8 via 1.2.3.4 protocol static',
537538
route.build_config())
538539

539540
def test_virtual_route_without_dev_without_no_track(self):
540541
cfg.CONF.set_override('keepalived_use_no_track', False)
541542
route = keepalived.KeepalivedVirtualRoute('50.0.0.0/8', '1.2.3.4')
542-
self.assertEqual('50.0.0.0/8 via 1.2.3.4', route.build_config())
543+
self.assertEqual('50.0.0.0/8 via 1.2.3.4 protocol static',
544+
route.build_config())
543545

544546

545547
class KeepalivedTrackScriptTestCase(KeepalivedBaseTestCase):

0 commit comments

Comments
 (0)