Skip to content

Commit 64c93a1

Browse files
[RFC4292][Namespace]: Fix implementation of RouteUpdater for multi-asic platform (sonic-net#176)
* [RFC4292][Namespace]: Fix implementation of RouteUpdater for multi-asic platform. For multi-asic platforms, the default route was getting retrieved from only the last namespace as the result dictionary was being written with the same key. To avoid this, modify implementation to retrieve default routes from all front end asic namespaces.Update implementation to ensure that only external routes are displayed in the result. Add test case to test multiple namespaces.
1 parent b8f19ee commit 64c93a1

File tree

8 files changed

+429
-20
lines changed

8 files changed

+429
-20
lines changed

src/sonic_ax_impl/mibs/ietf/rfc4292.py

+36-20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from ax_interface import MIBMeta, ValueType, MIBUpdater, SubtreeMIBEntry
66
from ax_interface.util import ip2tuple_v4
77
from bisect import bisect_right
8+
from sonic_py_common import multi_asic
89

910
class RouteUpdater(MIBUpdater):
1011
def __init__(self):
@@ -49,26 +50,41 @@ def update_data(self):
4950
self.route_dest_list.append(sub_id)
5051
self.route_dest_map[sub_id] = self.loips[loip].packed
5152

52-
route_entries = Namespace.dbs_keys(self.db_conn, mibs.APPL_DB, "ROUTE_TABLE:*")
53-
if not route_entries:
54-
return
55-
56-
for route_entry in route_entries:
57-
routestr = route_entry
58-
ipnstr = routestr[len("ROUTE_TABLE:"):]
59-
if ipnstr == "0.0.0.0/0":
60-
ipn = ipaddress.ip_network(ipnstr)
61-
ent = Namespace.dbs_get_all(self.db_conn, mibs.APPL_DB, routestr, blocking=True)
62-
nexthops = ent["nexthop"]
63-
ifnames = ent["ifname"]
64-
for nh, ifn in zip(nexthops.split(','), ifnames.split(',')):
65-
## Ignore non front panel interfaces
66-
## TODO: non front panel interfaces should not be in APPL_DB at very beginning
67-
## This is to workaround the bug in current sonic-swss implementation
68-
if ifn == "eth0" or ifn == "lo" or ifn == "docker0": continue
69-
sub_id = ip2tuple_v4(ipn.network_address) + ip2tuple_v4(ipn.netmask) + (self.tos,) + ip2tuple_v4(nh)
70-
self.route_dest_list.append(sub_id)
71-
self.route_dest_map[sub_id] = ipn.network_address.packed
53+
# Get list of front end asic namespaces for multi-asic platform.
54+
# This list will be empty for single asic platform.
55+
front_ns = multi_asic.get_all_namespaces()['front_ns']
56+
ipnstr = "0.0.0.0/0"
57+
ipn = ipaddress.ip_network(ipnstr)
58+
route_str = "ROUTE_TABLE:0.0.0.0/0"
59+
60+
for db_conn in Namespace.get_non_host_dbs(self.db_conn):
61+
# For multi-asic platform, proceed to get routes only for
62+
# front end namespaces.
63+
# For single-asic platform, front_ns will be empty list.
64+
if front_ns and db_conn.namespace not in front_ns:
65+
continue
66+
port_table = multi_asic.get_port_table(db_conn.namespace)
67+
ent = db_conn.get_all(mibs.APPL_DB, route_str, blocking=False)
68+
if ent is None:
69+
continue
70+
nexthops = ent["nexthop"]
71+
ifnames = ent["ifname"]
72+
for nh, ifn in zip(nexthops.split(','), ifnames.split(',')):
73+
## Ignore non front panel interfaces
74+
## TODO: non front panel interfaces should not be in APPL_DB at very beginning
75+
## This is to workaround the bug in current sonic-swss implementation
76+
if ifn == "eth0" or ifn == "lo" or ifn == "docker0":
77+
continue
78+
# Ignore internal asic routes
79+
if multi_asic.is_port_channel_internal(ifn, db_conn.namespace):
80+
continue
81+
if (ifn in port_table and
82+
multi_asic.ROLE in port_table[ifn] and
83+
port_table[ifn][multi_asic.ROLE] == multi_asic.INTERNAL_PORT):
84+
continue
85+
sub_id = ip2tuple_v4(ipn.network_address) + ip2tuple_v4(ipn.netmask) + (self.tos,) + ip2tuple_v4(nh)
86+
self.route_dest_list.append(sub_id)
87+
self.route_dest_map[sub_id] = ipn.network_address.packed
7288

7389
self.route_dest_list.sort()
7490

+39
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,41 @@
11
{
2+
"DEVICE_METADATA|localhost": {
3+
"chassis_serial_number": "SAMPLETESTSN",
4+
"sub_role": "FrontEnd"
5+
},
6+
"PORT_TABLE:Ethernet0": {
7+
"description": "snowflake",
8+
"alias": "etp1",
9+
"role": "Ext",
10+
"speed": 100000
11+
},
12+
"PORT_TABLE:Ethernet4": {
13+
"description": "snowflake",
14+
"alias": "etp2",
15+
"role": "Ext",
16+
"speed": 100000
17+
},
18+
"PORT_TABLE:Ethernet-BP0": {
19+
"description": "snowflake",
20+
"alias": "etp3",
21+
"role": "Int",
22+
"speed": 100000
23+
},
24+
"PORT_TABLE:Ethernet-BP4": {
25+
"description": "snowflake",
26+
"alias": "etp4",
27+
"role": "Int",
28+
"speed": 100000
29+
},
30+
"LAG_MEMBER_TABLE:PortChannel01:Ethernet-BP0": {
31+
"status": "enabled"
32+
},
33+
"LAG_MEMBER_TABLE:PortChannel01:Ethernet-BP4": {
34+
"status": "enabled"
35+
},
36+
"LAG_TABLE:PortChannel01": {
37+
"admin_status": "up",
38+
"oper_status": "up",
39+
"mtu": "9216"
40+
}
241
}
+36
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,38 @@
11
{
2+
"DEVICE_METADATA|localhost": {
3+
"chassis_serial_number": "SAMPLETESTSN",
4+
"sub_role": "FrontEnd"
5+
},
6+
"PORT_TABLE:Ethernet8": {
7+
"description": "snowflake",
8+
"alias": "etp5",
9+
"role": "Ext",
10+
"speed": 100000
11+
},
12+
"PORT_TABLE:Ethernet12": {
13+
"speed": 1000,
14+
"role": "Ext",
15+
"alias": "etp6"
16+
},
17+
"PORT_TABLE:Ethernet-BP8": {
18+
"role": "Int",
19+
"alias": "etp7"
20+
},
21+
"PORT_TABLE:Ethernet-BP12": {
22+
"description": "snowflake",
23+
"role": "Int",
24+
"alias": "etp8",
25+
"speed": 1000
26+
},
27+
"LAG_MEMBER_TABLE:PortChannel02:Ethernet-BP08": {
28+
"status": "enabled"
29+
},
30+
"LAG_MEMBER_TABLE:PortChannel02:Ethernet-BP12": {
31+
"status": "enabled"
32+
},
33+
"LAG_TABLE:PortChannel02": {
34+
"admin_status": "up",
35+
"oper_status": "up",
36+
"mtu": "9216"
37+
}
238
}

tests/mock_tables/asic2/appl_db.json

+4
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
"nexthop": "",
3737
"ifname": "lo"
3838
},
39+
"ROUTE_TABLE:0.0.0.0/0": {
40+
"ifname": "PortChannel03,PortChannel04",
41+
"nexthop": "10.10.0.0,10.10.0.5"
42+
},
3943
"LAG_MEMBER_TABLE:PortChannel03:Ethernet-BP16": {
4044
"status": "enabled"
4145
},
+51
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,53 @@
11
{
2+
"DEVICE_METADATA|localhost": {
3+
"chassis_serial_number": "SAMPLETESTSN",
4+
"sub_role": "BackEnd"
5+
},
6+
"PORT_TABLE:Ethernet-BP16": {
7+
"description": "backplane",
8+
"alias": "etp9",
9+
"speed": 100000
10+
},
11+
"PORT_TABLE:Ethernet-BP20": {
12+
"description": "backplane",
13+
"alias": "etp10",
14+
"speed": 100000
15+
},
16+
"PORT_TABLE:Ethernet-BP24": {
17+
"description": "backplane",
18+
"alias": "etp11",
19+
"speed": 100000
20+
},
21+
"PORT_TABLE:Ethernet-BP28": {
22+
"description": "backplane",
23+
"alias": "etp12",
24+
"speed": 100000
25+
},
26+
"LAG_MEMBER_TABLE:PortChannel03:Ethernet-BP16": {
27+
"status": "enabled"
28+
},
29+
"LAG_MEMBER_TABLE:PortChannel03:Ethernet-BP20": {
30+
"status": "enabled"
31+
},
32+
"LAG_MEMBER_TABLE:PortChannel04:Ethernet-BP24": {
33+
"status": "enabled"
34+
},
35+
"LAG_MEMBER_TABLE:PortChannel04:Ethernet-BP28": {
36+
"status": "enabled"
37+
},
38+
"LAG_TABLE:PortChannel03": {
39+
"admin_status": "up",
40+
"oper_status": "up",
41+
"mtu": "9216"
42+
},
43+
"LAG_TABLE:PortChannel04": {
44+
"admin_status": "up",
45+
"oper_status": "up",
46+
"mtu": "9216"
47+
},
48+
"LAG_TABLE:PortChannel_Temp": {
49+
"admin_status": "up",
50+
"oper_status": "up",
51+
"mtu": "9216"
52+
}
253
}

tests/mock_tables/multi_asic.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import os
2+
import json
3+
4+
import tests.mock_tables.dbconnector
5+
from sonic_py_common import multi_asic
6+
from swsssdk import SonicDBConfig
7+
8+
INPUT_DIR = os.path.dirname(os.path.abspath(__file__))
9+
int_port_channel = ['PortChannel01', 'PortChannel02', 'PortChannel03', 'PortChannel04']
10+
11+
def mock_get_num_asics():
12+
ns_list = SonicDBConfig.get_ns_list()
13+
if len(ns_list) > 1:
14+
return(len(ns_list) - 1)
15+
else:
16+
return 1
17+
18+
19+
def mock_is_multi_asic():
20+
if mock_get_num_asics() > 1:
21+
return True
22+
else:
23+
return False
24+
25+
def mock_get_all_namespaces():
26+
if mock_get_num_asics() == 1:
27+
return {'front_ns': [], 'back_ns': []}
28+
else:
29+
return {'front_ns': ['asic0', 'asic1'], 'back_ns': ['asic2']}
30+
31+
def mock_is_port_channel_internal(port_channel, namespace=None):
32+
if (mock_get_num_asics() == 1):
33+
return False
34+
else:
35+
return True if port_channel in int_port_channel else False
36+
37+
def mock_get_port_table(namespace=None):
38+
if namespace is not None:
39+
fname = os.path.join(INPUT_DIR, namespace, 'config_db.json')
40+
else:
41+
fname = os.path.join(INPUT_DIR, 'config_db.json')
42+
port_table = {}
43+
db = {}
44+
with open(fname) as f:
45+
db = json.load(f)
46+
for k in db:
47+
if 'PORT_TABLE' in db:
48+
new_key = k[len('PORT_TABLE:'):]
49+
port_table[new_key] = db[k]
50+
return port_table
51+
52+
multi_asic.get_num_asics = mock_get_num_asics
53+
multi_asic.is_multi_asic = mock_is_multi_asic
54+
multi_asic.get_all_namespaces = mock_get_all_namespaces
55+
multi_asic.is_port_channel_internal = mock_is_port_channel_internal
56+
multi_asic.get_port_table = mock_get_port_table

0 commit comments

Comments
 (0)