Skip to content

Commit 827358f

Browse files
authored
[debug dump] dump interface module added (sonic-net#2070)
Added the Interface Module to the Debug Dump Utility Added the Corresponding UT's Refactored Common methods required into match_helper.py
1 parent 8389c81 commit 827358f

File tree

11 files changed

+800
-77
lines changed

11 files changed

+800
-77
lines changed

dump/helper.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import os, sys, json
22

3+
34
def create_template_dict(dbs):
45
""" Generate a Template which will be returned by Executor Classes """
56
return {db: {'keys': [], 'tables_not_found': []} for db in dbs}
67

8+
79
def verbose_print(str):
810
if "VERBOSE" in os.environ and os.environ["VERBOSE"] == "1":
911
print(str)
1012

13+
1114
def handle_error(err_str, excep=False):
1215
"""
1316
Handles general error conditions, if any experienced by the module,
@@ -17,7 +20,7 @@ def handle_error(err_str, excep=False):
1720
raise Exception("ERROR : {}".format(err_str))
1821
else:
1922
print("ERROR : {}".format(err_str), file = sys.stderr)
20-
23+
2124

2225
def handle_multiple_keys_matched_error(err_str, key_to_go_with="", excep=False):
2326
if excep:

dump/match_helper.py

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from dump.match_infra import MatchRequest
2+
from dump.helper import handle_multiple_keys_matched_error
3+
4+
# Port Helper Methods
5+
6+
def fetch_port_oid(match_engine, port_name, ns):
7+
"""
8+
Fetches thr relevant SAI_OBJECT_TYPE_PORT given port name
9+
"""
10+
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF", key_pattern="*", field="SAI_HOSTIF_ATTR_NAME",
11+
value=port_name, return_fields=["SAI_HOSTIF_ATTR_OBJ_ID"], ns=ns)
12+
ret = match_engine.fetch(req)
13+
asic_port_obj_id = ""
14+
if not ret["error"] and len(ret["keys"]) != 0:
15+
sai_hostif_obj_key = ret["keys"][-1]
16+
if sai_hostif_obj_key in ret["return_values"] and "SAI_HOSTIF_ATTR_OBJ_ID" in ret["return_values"][sai_hostif_obj_key]:
17+
asic_port_obj_id = ret["return_values"][sai_hostif_obj_key]["SAI_HOSTIF_ATTR_OBJ_ID"]
18+
return req, asic_port_obj_id, ret
19+
20+
# Vlan Helper Methods
21+
22+
def fetch_vlan_oid(match_engine, vlan_name, ns):
23+
# Convert 'Vlanxxx' to 'xxx'
24+
if vlan_name[0:4] != "Vlan" or not vlan_name[4:].isnumeric():
25+
vlan_num = -1
26+
else:
27+
vlan_num = int(vlan_name[4:])
28+
29+
# Find the table named "ASIC_STATE:SAI_OBJECT_TYPE_VLAN:*" in which SAI_VLAN_ATTR_VLAN_ID = vlan_num
30+
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_VLAN", key_pattern="*", field="SAI_VLAN_ATTR_VLAN_ID",
31+
value=str(vlan_num), ns=ns)
32+
ret = match_engine.fetch(req)
33+
vlan_oid = ""
34+
if ret["keys"]:
35+
vlan_oid = ret["keys"][0].split(":", 2)[-1]
36+
return req, vlan_oid, ret
37+
38+
# LAG Helper Methods
39+
40+
def get_lag_members_from_cfg(match_engine, lag_name, ns):
41+
"""
42+
Get the members associated with a LAG from Config DB
43+
"""
44+
lag_members = []
45+
req = MatchRequest(db="CONFIG_DB", table="PORTCHANNEL_MEMBER", key_pattern=lag_name + "|*", ns=ns)
46+
ret = match_engine.fetch(req)
47+
for key in ret["keys"]:
48+
lag_members.append(key.split("|")[-1])
49+
return req, lag_members, ret
50+
51+
def get_lag_and_member_obj(match_engine, port_asic_obj, ns):
52+
"""
53+
Given the member port oid, fetch lag_member & lag oid's
54+
"""
55+
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER", key_pattern="*", field="SAI_LAG_MEMBER_ATTR_PORT_ID",
56+
value=port_asic_obj, return_fields=["SAI_LAG_MEMBER_ATTR_LAG_ID"], ns=ns)
57+
ret = match_engine.fetch(req)
58+
lag_member_key = ""
59+
lag_oid = ""
60+
if not ret["error"] and ret["keys"]:
61+
lag_member_key = ret["keys"][-1]
62+
if lag_member_key in ret["return_values"] and "SAI_LAG_MEMBER_ATTR_LAG_ID" in ret["return_values"][lag_member_key]:
63+
lag_oid = ret["return_values"][lag_member_key]["SAI_LAG_MEMBER_ATTR_LAG_ID"]
64+
return lag_member_key, lag_oid
65+
66+
def fetch_lag_oid(match_engine, lag_name, ns):
67+
"""
68+
Finding the relevant SAI_OBJECT_TYPE_LAG key directly from the ASIC is not possible given a LAG name
69+
Thus, using the members to find SAI_LAG_MEMBER_ATTR_LAG_ID
70+
"""
71+
_, lag_members, _ = get_lag_members_from_cfg(match_engine, lag_name, ns)
72+
lag_type_oids = set()
73+
for port_name in lag_members:
74+
_, port_asic_obj, _ = fetch_port_oid(match_engine, port_name, ns)
75+
if port_asic_obj:
76+
lag_member_key, lag_oid = get_lag_and_member_obj(match_engine, port_asic_obj, ns)
77+
lag_type_oids.add(lag_oid)
78+
lag_type_oid, lag_type_oids = "", list(lag_type_oids)
79+
if lag_type_oids:
80+
if len(lag_type_oids) > 1:
81+
# Ideally, only one associated lag_oid should be present for a portchannel
82+
handle_multiple_keys_matched_error("Multipe lag_oids matched for portchannel: {}, \
83+
lag_oids matched {}".format(lag_name, lag_type_oids), lag_type_oids[-1])
84+
lag_type_oid = lag_type_oids[-1]
85+
return lag_type_oid

dump/plugins/interface.py

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
from sonic_py_common.interface import get_interface_table_name, get_intf_longname, VLAN_SUB_INTERFACE_SEPARATOR
2+
from sonic_py_common.multi_asic import DEFAULT_NAMESPACE
3+
from dump.match_infra import MatchRequest
4+
from dump.helper import create_template_dict, handle_error
5+
from dump.match_helper import fetch_port_oid, fetch_vlan_oid, fetch_lag_oid
6+
from swsscommon.swsscommon import SonicDBConfig
7+
from .executor import Executor
8+
9+
10+
class Interface(Executor):
11+
"""
12+
Debug Dump Plugin for Interface Module.
13+
Interface can be of Ethernet, PortChannel, Loopback, Vlan or SubInterface type
14+
Human readable intf string names are supported
15+
"""
16+
ARG_NAME = "intf_name"
17+
18+
def __init__(self, match_engine=None):
19+
super().__init__(match_engine)
20+
self.ns = DEFAULT_NAMESPACE
21+
self.intf_type = ""
22+
self.ret_temp = dict()
23+
self.valid_cfg_tables = set(["INTERFACE",
24+
"PORTCHANNEL_INTERFACE",
25+
"VLAN_INTERFACE",
26+
"LOOPBACK_INTERFACE",
27+
"VLAN_SUB_INTERFACE"])
28+
29+
def get_all_args(self, ns=DEFAULT_NAMESPACE):
30+
"""
31+
Fetch all the interfaces from the valid cfg tables
32+
"""
33+
req = MatchRequest(db="CONFIG_DB", table="*INTERFACE", key_pattern="*", ns=ns)
34+
ret = self.match_engine.fetch(req)
35+
all_intfs = ret["keys"]
36+
filtered_keys = []
37+
for key in all_intfs:
38+
num_sep = key.count("|")
39+
if num_sep == 1:
40+
filtered_keys.append(key.split("|")[-1])
41+
return filtered_keys
42+
43+
def execute(self, params):
44+
self.ret_temp = create_template_dict(dbs=["CONFIG_DB", "APPL_DB", "STATE_DB", "ASIC_DB"])
45+
self.intf_name = params[Interface.ARG_NAME]
46+
self.ns = params["namespace"]
47+
# CONFIG_DB
48+
self.intf_type = self.init_intf_config_info()
49+
# APPL_DB
50+
self.init_intf_appl_info()
51+
# STATE_DB
52+
self.init_intf_state_info()
53+
# ASIC_DB
54+
self.init_intf_asic_info()
55+
return self.ret_temp
56+
57+
def get_sep(self, db):
58+
return SonicDBConfig.getSeparator(db)
59+
60+
def add_intf_keys(self, db_name, table_name):
61+
# Fetch Interface Keys
62+
req = MatchRequest(db=db_name, table=table_name, key_pattern=self.intf_name, ns=self.ns)
63+
ret = self.match_engine.fetch(req)
64+
self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"])
65+
# Fetch IP & Interface Related keys
66+
req = MatchRequest(db=db_name, table=table_name, key_pattern=self.intf_name+self.get_sep(db_name)+"*", ns=self.ns)
67+
ret = self.match_engine.fetch(req)
68+
self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"], False)
69+
70+
def init_intf_config_info(self):
71+
intf_table_name = get_interface_table_name(self.intf_name)
72+
if not intf_table_name:
73+
self.ret_temp["CONFIG_DB"]["tables_not_found"].extend(list(self.valid_cfg_tables))
74+
else:
75+
self.add_intf_keys("CONFIG_DB", intf_table_name)
76+
return intf_table_name
77+
78+
def init_intf_appl_info(self):
79+
self.add_intf_keys("APPL_DB", "INTF_TABLE")
80+
81+
def init_intf_state_info(self):
82+
self.add_intf_keys("STATE_DB", "INTERFACE_TABLE")
83+
84+
def init_intf_asic_info(self):
85+
"""
86+
Fetch SAI_OBJECT_TYPE_ROUTER_INTERFACE ASIC Object for the corresponding interface
87+
To find the relevant ASIC RIF object, this method would need the following:
88+
1) INTERFACE - SAI_OBJECT_TYPE_PORT oid
89+
2) PORTCHANNEL - SAI_OBJECT_TYPE_LAG oid
90+
3) VLAN - SAI_OBJECT_TYPE_VLAN
91+
4) SUB_INTERFACE - SAI_OBJECT_TYPE_PORT/SAI_OBJECT_TYPE_LAG & SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID
92+
"""
93+
rif_obj = RIF.initialize(self)
94+
rif_obj.collect()
95+
return
96+
97+
class RIF(object):
98+
"""
99+
Base Class for RIF type
100+
"""
101+
@staticmethod
102+
def initialize(intf_obj):
103+
if intf_obj.intf_type == "INTERFACE":
104+
return PortRIF(intf_obj)
105+
elif intf_obj.intf_type == "PORTCHANNEL_INTERFACE":
106+
return LagRIF(intf_obj)
107+
elif intf_obj.intf_type == "VLAN_INTERFACE":
108+
return VlanRIF(intf_obj)
109+
elif intf_obj.intf_type == "LOOPBACK_INTERFACE":
110+
return LpbRIF(intf_obj)
111+
elif intf_obj.intf_type == "VLAN_SUB_INTERFACE":
112+
return SubIntfRif(intf_obj)
113+
return RIF(intf_obj)
114+
115+
def __init__(self, intf_obj):
116+
self.intf = intf_obj
117+
118+
def fetch_rif_keys_using_port_oid(self, port_oid, rfs=["SAI_ROUTER_INTERFACE_ATTR_TYPE"]):
119+
if not port_oid:
120+
port_oid = "INVALID"
121+
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE", key_pattern="*", field="SAI_ROUTER_INTERFACE_ATTR_PORT_ID",
122+
value=port_oid, return_fields=rfs, ns=self.intf.ns)
123+
ret = self.intf.match_engine.fetch(req)
124+
return req, ret
125+
126+
def verify_valid_rif_type(self, ret, exp_rif_type=""):
127+
if not ret or not exp_rif_type:
128+
return True, ""
129+
130+
rif_type = ""
131+
if not ret["error"] and ret["keys"]:
132+
rif_key = ret["keys"][-1]
133+
rif_type = ret.get("return_values", {}).get(rif_key, {}).get("SAI_ROUTER_INTERFACE_ATTR_TYPE", "")
134+
135+
if rif_type == exp_rif_type:
136+
return True, rif_type
137+
else:
138+
return False, rif_type
139+
140+
def sanity_check_rif_type(self, ret, rif_oid, exp_type, str_name):
141+
# Sanity check to see if the TYPE is SAI_ROUTER_INTERFACE_TYPE_PORT
142+
_, recv_type = self.verify_valid_rif_type(ret, exp_type)
143+
if exp_type != recv_type:
144+
err_str = "TYPE Mismatch on SAI_OBJECT_TYPE_ROUTER_INTERFACE, {} oid:{}, expected type:{}, recieved type:{}"
145+
handle_error(err_str.format(str_name, rif_oid, exp_type, recv_type), False)
146+
return
147+
148+
def collect(self):
149+
self.intf.ret_temp["ASIC_DB"]["tables_not_found"].extend(["ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE"])
150+
return
151+
152+
153+
class LpbRIF(RIF):
154+
"""
155+
Handler for Loopback Interface
156+
"""
157+
def collect(self):
158+
# When an ip is added to Loopback interface,
159+
# no ROUTER_INTERFACE asic obj is created, so skipping it
160+
# and not adding to tables not found
161+
return
162+
163+
164+
class PortRIF(RIF):
165+
"""
166+
Handler for Port type Obj
167+
"""
168+
def collect(self):
169+
# Get port oid from port name
170+
_, port_oid, _ = fetch_port_oid(self.intf.match_engine, self.intf.intf_name, self.intf.ns)
171+
# Use Port oid to get the RIF
172+
req, ret = self.fetch_rif_keys_using_port_oid(port_oid)
173+
rif_oids = self.intf.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"])
174+
if rif_oids:
175+
# Sanity check to see if the TYPE is SAI_ROUTER_INTERFACE_TYPE_PORT
176+
exp_type = "SAI_ROUTER_INTERFACE_TYPE_PORT"
177+
self.sanity_check_rif_type(ret, rif_oids[-1], exp_type, "PORT")
178+
179+
180+
class VlanRIF(RIF):
181+
"""
182+
Handler for Vlan type Obj
183+
"""
184+
def fetch_rif_keys_using_vlan_oid(self, vlan_oid):
185+
if not vlan_oid:
186+
vlan_oid = "INVALID"
187+
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE", key_pattern="*", field="SAI_ROUTER_INTERFACE_ATTR_VLAN_ID",
188+
value=vlan_oid, return_fields=["SAI_ROUTER_INTERFACE_ATTR_TYPE"], ns=self.intf.ns)
189+
ret = self.intf.match_engine.fetch(req)
190+
return req, ret
191+
192+
def collect(self):
193+
# Get vlan oid from vlan name
194+
_, vlan_oid, _ = fetch_vlan_oid(self.intf.match_engine, self.intf.intf_name, self.intf.ns)
195+
# Use vlan oid to get the RIF
196+
req, ret = self.fetch_rif_keys_using_vlan_oid(vlan_oid)
197+
rif_oids = self.intf.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"])
198+
if rif_oids:
199+
# Sanity check to see if the TYPE is SAI_ROUTER_INTERFACE_TYPE_VLAN
200+
exp_type = "SAI_ROUTER_INTERFACE_TYPE_VLAN"
201+
self.sanity_check_rif_type(ret, rif_oids[-1], exp_type, "VLAN")
202+
203+
204+
class LagRIF(RIF):
205+
"""
206+
Handler for PortChannel/LAG type Obj
207+
"""
208+
def collect(self):
209+
# Get lag oid from lag name
210+
lag_oid = fetch_lag_oid(self.intf.match_engine, self.intf.intf_name, self.intf.ns)
211+
# Use vlan oid to get the RIF
212+
req, ret = self.fetch_rif_keys_using_port_oid(lag_oid)
213+
rif_oids = self.intf.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"])
214+
if rif_oids:
215+
# Sanity check to see if the TYPE is SAI_ROUTER_INTERFACE_TYPE_PORT
216+
exp_type = "SAI_ROUTER_INTERFACE_TYPE_PORT"
217+
self.sanity_check_rif_type(ret, rif_oids[-1], exp_type, "LAG")
218+
219+
220+
class SubIntfRif(RIF):
221+
"""
222+
Handler for PortChannel/LAG type Obj
223+
"""
224+
def fetch_vlan_id_subintf(self, sub_intf):
225+
req = MatchRequest(db="CONFIG_DB", table="VLAN_SUB_INTERFACE", key_pattern=sub_intf, return_fields=["vlan"], ns=self.intf.ns)
226+
ret = self.intf.match_engine.fetch(req)
227+
vlan_id = ""
228+
if not ret["error"] and ret["keys"]:
229+
key = ret["keys"][-1]
230+
vlan_id = ret["return_values"].get(key, {}).get("vlan", "")
231+
return vlan_id
232+
233+
def collect(self):
234+
"""
235+
To match the RIF object, two checks have to be performed,
236+
1) SAI_ROUTER_INTERFACE_ATTR_PORT_ID
237+
- This can either be SAI_OBJECT_TYPE_PORT or SAI_OBJECT_TYPE_LAG
238+
2) SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID
239+
- This will be Vlan Number (uint16)
240+
"""
241+
intf_oid = ""
242+
parent_port, _ = self.intf.intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR)
243+
parent_port = get_intf_longname(parent_port)
244+
vlan_id = self.fetch_vlan_id_subintf(self.intf.intf_name)
245+
if parent_port.startswith("Eth"):
246+
_, intf_oid, _ = fetch_port_oid(self.intf.match_engine, parent_port, self.intf.ns)
247+
else:
248+
intf_oid = fetch_lag_oid(self.intf.match_engine, parent_port, self.intf.ns)
249+
250+
# Use vlan oid to get the RIF
251+
return_fields = ["SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID", "SAI_ROUTER_INTERFACE_ATTR_TYPE"]
252+
req, ret = self.fetch_rif_keys_using_port_oid(intf_oid, rfs=return_fields)
253+
254+
# Search for keys who has SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID field
255+
filtered_keys = []
256+
if not ret["error"] and len(ret['keys']) > 0:
257+
for key in ret["keys"]:
258+
rcv_vlan_id = ret.get("return_values", {}).get(key, {}).get("SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID", "")
259+
if rcv_vlan_id == vlan_id:
260+
filtered_keys.append(key)
261+
break
262+
263+
rif_oids = self.intf.add_to_ret_template(req.table, req.db, filtered_keys, ret["error"])
264+
if rif_oids:
265+
exp_type = "SAI_ROUTER_INTERFACE_TYPE_SUB_PORT"
266+
self.sanity_check_rif_type(ret, rif_oids[-1], exp_type, "SUB_INTERFACE")

0 commit comments

Comments
 (0)