Skip to content

Commit

Permalink
[RFC4292][Namespace]: Fix implementation of RouteUpdater for multi-as…
Browse files Browse the repository at this point in the history
…ic 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.
  • Loading branch information
SuvarnaMeenakshi authored Nov 16, 2020
1 parent b8f19ee commit 64c93a1
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 20 deletions.
56 changes: 36 additions & 20 deletions src/sonic_ax_impl/mibs/ietf/rfc4292.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ax_interface import MIBMeta, ValueType, MIBUpdater, SubtreeMIBEntry
from ax_interface.util import ip2tuple_v4
from bisect import bisect_right
from sonic_py_common import multi_asic

class RouteUpdater(MIBUpdater):
def __init__(self):
Expand Down Expand Up @@ -49,26 +50,41 @@ def update_data(self):
self.route_dest_list.append(sub_id)
self.route_dest_map[sub_id] = self.loips[loip].packed

route_entries = Namespace.dbs_keys(self.db_conn, mibs.APPL_DB, "ROUTE_TABLE:*")
if not route_entries:
return

for route_entry in route_entries:
routestr = route_entry
ipnstr = routestr[len("ROUTE_TABLE:"):]
if ipnstr == "0.0.0.0/0":
ipn = ipaddress.ip_network(ipnstr)
ent = Namespace.dbs_get_all(self.db_conn, mibs.APPL_DB, routestr, blocking=True)
nexthops = ent["nexthop"]
ifnames = ent["ifname"]
for nh, ifn in zip(nexthops.split(','), ifnames.split(',')):
## Ignore non front panel interfaces
## TODO: non front panel interfaces should not be in APPL_DB at very beginning
## This is to workaround the bug in current sonic-swss implementation
if ifn == "eth0" or ifn == "lo" or ifn == "docker0": continue
sub_id = ip2tuple_v4(ipn.network_address) + ip2tuple_v4(ipn.netmask) + (self.tos,) + ip2tuple_v4(nh)
self.route_dest_list.append(sub_id)
self.route_dest_map[sub_id] = ipn.network_address.packed
# Get list of front end asic namespaces for multi-asic platform.
# This list will be empty for single asic platform.
front_ns = multi_asic.get_all_namespaces()['front_ns']
ipnstr = "0.0.0.0/0"
ipn = ipaddress.ip_network(ipnstr)
route_str = "ROUTE_TABLE:0.0.0.0/0"

for db_conn in Namespace.get_non_host_dbs(self.db_conn):
# For multi-asic platform, proceed to get routes only for
# front end namespaces.
# For single-asic platform, front_ns will be empty list.
if front_ns and db_conn.namespace not in front_ns:
continue
port_table = multi_asic.get_port_table(db_conn.namespace)
ent = db_conn.get_all(mibs.APPL_DB, route_str, blocking=False)
if ent is None:
continue
nexthops = ent["nexthop"]
ifnames = ent["ifname"]
for nh, ifn in zip(nexthops.split(','), ifnames.split(',')):
## Ignore non front panel interfaces
## TODO: non front panel interfaces should not be in APPL_DB at very beginning
## This is to workaround the bug in current sonic-swss implementation
if ifn == "eth0" or ifn == "lo" or ifn == "docker0":
continue
# Ignore internal asic routes
if multi_asic.is_port_channel_internal(ifn, db_conn.namespace):
continue
if (ifn in port_table and
multi_asic.ROLE in port_table[ifn] and
port_table[ifn][multi_asic.ROLE] == multi_asic.INTERNAL_PORT):
continue
sub_id = ip2tuple_v4(ipn.network_address) + ip2tuple_v4(ipn.netmask) + (self.tos,) + ip2tuple_v4(nh)
self.route_dest_list.append(sub_id)
self.route_dest_map[sub_id] = ipn.network_address.packed

self.route_dest_list.sort()

Expand Down
39 changes: 39 additions & 0 deletions tests/mock_tables/asic0/config_db.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,41 @@
{
"DEVICE_METADATA|localhost": {
"chassis_serial_number": "SAMPLETESTSN",
"sub_role": "FrontEnd"
},
"PORT_TABLE:Ethernet0": {
"description": "snowflake",
"alias": "etp1",
"role": "Ext",
"speed": 100000
},
"PORT_TABLE:Ethernet4": {
"description": "snowflake",
"alias": "etp2",
"role": "Ext",
"speed": 100000
},
"PORT_TABLE:Ethernet-BP0": {
"description": "snowflake",
"alias": "etp3",
"role": "Int",
"speed": 100000
},
"PORT_TABLE:Ethernet-BP4": {
"description": "snowflake",
"alias": "etp4",
"role": "Int",
"speed": 100000
},
"LAG_MEMBER_TABLE:PortChannel01:Ethernet-BP0": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel01:Ethernet-BP4": {
"status": "enabled"
},
"LAG_TABLE:PortChannel01": {
"admin_status": "up",
"oper_status": "up",
"mtu": "9216"
}
}
36 changes: 36 additions & 0 deletions tests/mock_tables/asic1/config_db.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
{
"DEVICE_METADATA|localhost": {
"chassis_serial_number": "SAMPLETESTSN",
"sub_role": "FrontEnd"
},
"PORT_TABLE:Ethernet8": {
"description": "snowflake",
"alias": "etp5",
"role": "Ext",
"speed": 100000
},
"PORT_TABLE:Ethernet12": {
"speed": 1000,
"role": "Ext",
"alias": "etp6"
},
"PORT_TABLE:Ethernet-BP8": {
"role": "Int",
"alias": "etp7"
},
"PORT_TABLE:Ethernet-BP12": {
"description": "snowflake",
"role": "Int",
"alias": "etp8",
"speed": 1000
},
"LAG_MEMBER_TABLE:PortChannel02:Ethernet-BP08": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel02:Ethernet-BP12": {
"status": "enabled"
},
"LAG_TABLE:PortChannel02": {
"admin_status": "up",
"oper_status": "up",
"mtu": "9216"
}
}
4 changes: 4 additions & 0 deletions tests/mock_tables/asic2/appl_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
"nexthop": "",
"ifname": "lo"
},
"ROUTE_TABLE:0.0.0.0/0": {
"ifname": "PortChannel03,PortChannel04",
"nexthop": "10.10.0.0,10.10.0.5"
},
"LAG_MEMBER_TABLE:PortChannel03:Ethernet-BP16": {
"status": "enabled"
},
Expand Down
51 changes: 51 additions & 0 deletions tests/mock_tables/asic2/config_db.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
{
"DEVICE_METADATA|localhost": {
"chassis_serial_number": "SAMPLETESTSN",
"sub_role": "BackEnd"
},
"PORT_TABLE:Ethernet-BP16": {
"description": "backplane",
"alias": "etp9",
"speed": 100000
},
"PORT_TABLE:Ethernet-BP20": {
"description": "backplane",
"alias": "etp10",
"speed": 100000
},
"PORT_TABLE:Ethernet-BP24": {
"description": "backplane",
"alias": "etp11",
"speed": 100000
},
"PORT_TABLE:Ethernet-BP28": {
"description": "backplane",
"alias": "etp12",
"speed": 100000
},
"LAG_MEMBER_TABLE:PortChannel03:Ethernet-BP16": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel03:Ethernet-BP20": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel04:Ethernet-BP24": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel04:Ethernet-BP28": {
"status": "enabled"
},
"LAG_TABLE:PortChannel03": {
"admin_status": "up",
"oper_status": "up",
"mtu": "9216"
},
"LAG_TABLE:PortChannel04": {
"admin_status": "up",
"oper_status": "up",
"mtu": "9216"
},
"LAG_TABLE:PortChannel_Temp": {
"admin_status": "up",
"oper_status": "up",
"mtu": "9216"
}
}
56 changes: 56 additions & 0 deletions tests/mock_tables/multi_asic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os
import json

import tests.mock_tables.dbconnector
from sonic_py_common import multi_asic
from swsssdk import SonicDBConfig

INPUT_DIR = os.path.dirname(os.path.abspath(__file__))
int_port_channel = ['PortChannel01', 'PortChannel02', 'PortChannel03', 'PortChannel04']

def mock_get_num_asics():
ns_list = SonicDBConfig.get_ns_list()
if len(ns_list) > 1:
return(len(ns_list) - 1)
else:
return 1


def mock_is_multi_asic():
if mock_get_num_asics() > 1:
return True
else:
return False

def mock_get_all_namespaces():
if mock_get_num_asics() == 1:
return {'front_ns': [], 'back_ns': []}
else:
return {'front_ns': ['asic0', 'asic1'], 'back_ns': ['asic2']}

def mock_is_port_channel_internal(port_channel, namespace=None):
if (mock_get_num_asics() == 1):
return False
else:
return True if port_channel in int_port_channel else False

def mock_get_port_table(namespace=None):
if namespace is not None:
fname = os.path.join(INPUT_DIR, namespace, 'config_db.json')
else:
fname = os.path.join(INPUT_DIR, 'config_db.json')
port_table = {}
db = {}
with open(fname) as f:
db = json.load(f)
for k in db:
if 'PORT_TABLE' in db:
new_key = k[len('PORT_TABLE:'):]
port_table[new_key] = db[k]
return port_table

multi_asic.get_num_asics = mock_get_num_asics
multi_asic.is_multi_asic = mock_is_multi_asic
multi_asic.get_all_namespaces = mock_get_all_namespaces
multi_asic.is_port_channel_internal = mock_is_port_channel_internal
multi_asic.get_port_table = mock_get_port_table
Loading

0 comments on commit 64c93a1

Please sign in to comment.