-
Notifications
You must be signed in to change notification settings - Fork 814
[debug dump util] Route Module added #1913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e68cef4
62528c2
2b7dd3d
3d36d61
8219002
7335c30
8b9228b
2fb24be
ade0c03
77f4056
22bff80
7f14bb6
ac92cdd
2a2b6c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| import json | ||
| import re | ||
| from dump.match_infra import MatchRequest, MatchRequestOptimizer | ||
| from dump.helper import create_template_dict | ||
| from .executor import Executor | ||
|
|
||
| NH = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" | ||
| NH_GRP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" | ||
| RIF = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" | ||
| CPU_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_PORT" | ||
|
|
||
| OID_HEADERS = { | ||
| NH: "0x40", | ||
| NH_GRP: "0x50", | ||
| RIF: "0x60", | ||
| CPU_PORT: "0x10" | ||
| } | ||
|
|
||
|
|
||
| def get_route_pattern(dest): | ||
| return "*\"dest\":\"" + dest + "\"*" | ||
|
|
||
|
|
||
| def get_vr_oid(asic_route_entry): | ||
| """ | ||
| Route Entry Format: ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY: | ||
| {'dest':'::0','switch_id':'oid:0x21000000000000','vr':'oid:0x3000000000002'} | ||
| """ | ||
| matches = re.findall(r"\{.*\}", asic_route_entry) | ||
| key_dict = {} | ||
| if matches: | ||
| try: | ||
| key_dict = json.loads(matches[0]) | ||
| except Exception as e: | ||
| pass | ||
| return key_dict.get("vr", "") | ||
|
|
||
|
|
||
| class Route(Executor): | ||
| """ | ||
| Debug Dump Plugin for Route Module | ||
| """ | ||
| ARG_NAME = "destination_network" | ||
|
|
||
| def __init__(self, match_engine=None): | ||
| super().__init__(match_engine) | ||
| self.nhgrp_match_engine = MatchRequestOptimizer(self.match_engine) | ||
| """ | ||
| MatchRequestOptimizer will be used for the keys related to these tables | ||
| 1) SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER | ||
| 2) SAI_OBJECT_TYPE_NEXT_HOP | ||
| 3) SAI_OBJECT_TYPE_ROUTER_INTERFACE | ||
| 4) CLASS_BASED_NEXT_HOP_GROUP_TABLE | ||
| 5) NEXTHOP_GROUP_TABLE | ||
| """ | ||
| self.ret_temp = {} | ||
| self.ns = '' | ||
| self.dest_net = '' | ||
| self.nh_id = '' | ||
| self.nh_type = '' | ||
|
|
||
| def get_all_args(self, ns=""): | ||
| req = MatchRequest(db="APPL_DB", table="ROUTE_TABLE", key_pattern="*", ns=self.ns) | ||
| ret = self.match_engine.fetch(req) | ||
| all_routes = ret.get("keys", []) | ||
| return [key[len("ROUTE_TABLE:"):] for key in all_routes] | ||
|
|
||
| def execute(self, params): | ||
| self.ret_temp = create_template_dict(dbs=["CONFIG_DB", "APPL_DB", "ASIC_DB"]) | ||
| self.dest_net = params[Route.ARG_NAME] | ||
| self.ns = params["namespace"] | ||
| # CONFIG DB | ||
| if not self.init_route_config_info(): | ||
| del self.ret_temp["CONFIG_DB"] | ||
| # APPL DB | ||
| nhgrp_field = self.init_route_appl_info() | ||
| self.init_nhgrp_cbf_appl_info(nhgrp_field) | ||
| # ASIC DB - ROUTE ENTRY | ||
| self.nh_id, vr = self.init_asic_route_entry_info() | ||
| # ASIC DB - VIRTUAL ROUTER | ||
| self.init_asic_vr_info(vr) | ||
| # ASIC DB - KEYS dependent on NEXT HOP ID | ||
| self.init_asic_nh() | ||
| return self.ret_temp | ||
|
|
||
| def add_to_ret_template(self, table, db, keys, err, add_to_tables_not_found=True): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be made a method on the Executor class, to avoid needing to redefine each time?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's another such refactoring request here: #1853 (comment), I'll also do this after once the Route and VxLan modules are merged. It'll be cleaner that way |
||
| if not err and keys: | ||
| self.ret_temp[db]["keys"].extend(keys) | ||
| return keys | ||
| elif add_to_tables_not_found: | ||
| self.ret_temp[db]["tables_not_found"].extend([table]) | ||
| return [] | ||
|
|
||
| def init_route_config_info(self): | ||
| req = MatchRequest(db="CONFIG_DB", table="STATIC_ROUTE", key_pattern=self.dest_net, ns=self.ns) | ||
| ret = self.match_engine.fetch(req) | ||
| return self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) | ||
|
|
||
| def init_route_appl_info(self): | ||
| req = MatchRequest(db="APPL_DB", table="ROUTE_TABLE", key_pattern=self.dest_net, | ||
| ns=self.ns, return_fields=["nexthop_group"]) | ||
| ret = self.match_engine.fetch(req) | ||
| self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) | ||
| if ret["keys"]: | ||
| return ret["return_values"].get(ret["keys"][0], {}).get("nexthop_group", "") | ||
| return "" | ||
|
|
||
| def init_nhgrp_cbf_appl_info(self, nhgrp_field): | ||
| if not nhgrp_field: | ||
| return | ||
|
|
||
| # Verify if the nhgrp field in the route table refers to class based next_hop_group | ||
| req = MatchRequest(db="APPL_DB", table="CLASS_BASED_NEXT_HOP_GROUP_TABLE", key_pattern=nhgrp_field, | ||
| ns=self.ns, return_fields=["members"]) | ||
| ret = self.nhgrp_match_engine.fetch(req) | ||
| self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"], False) | ||
|
|
||
| nggrp_table_key = "" | ||
| if not ret["keys"]: | ||
| nggrp_table_key = nhgrp_field | ||
| else: | ||
| nggrp_table_key = ret["return_values"].get(ret["keys"][0], {}).get("members", "") | ||
|
|
||
| if nggrp_table_key: | ||
| # Retrieve the next_hop_group key | ||
| req = MatchRequest(db="APPL_DB", table="NEXTHOP_GROUP_TABLE", key_pattern=nggrp_table_key, ns=self.ns) | ||
| ret = self.nhgrp_match_engine.fetch(req) | ||
| self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"], False) | ||
|
|
||
| def init_asic_route_entry_info(self): | ||
| nh_id_field = "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID" | ||
| req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", key_pattern=get_route_pattern(self.dest_net), | ||
| ns=self.ns, return_fields=[nh_id_field]) | ||
| ret = self.match_engine.fetch(req) | ||
| keys = self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) | ||
| asic_route_entry = keys[0] if keys else "" | ||
| vr = get_vr_oid(asic_route_entry) | ||
| nh_id = ret["return_values"].get(asic_route_entry, {}).get(nh_id_field, "") | ||
| return nh_id, vr | ||
|
|
||
| def init_asic_vr_info(self, vr): | ||
| ret = {} | ||
| if vr: | ||
| req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", key_pattern=vr, ns=self.ns) | ||
| ret = self.nhgrp_match_engine.fetch(req) | ||
| self.add_to_ret_template("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", "ASIC_DB", ret.get("keys", []), ret.get("error", "")) | ||
|
|
||
| def init_asic_nh(self): | ||
| self.nh_type = self.get_nh_type() | ||
| nh_ex = NHExtractor.initialize(self) | ||
| nh_ex.collect() | ||
|
|
||
| def get_nh_type(self): | ||
| """ | ||
| Figure out the nh_type using OID Header | ||
| """ | ||
| if not self.nh_id: | ||
| return "DROP" | ||
| oid = self.nh_id.split(":")[-1] | ||
| for nh_type in [NH_GRP, NH, RIF, CPU_PORT]: | ||
| if oid.startswith(OID_HEADERS.get(nh_type, "")): | ||
| return nh_type | ||
| return "DROP" | ||
|
|
||
|
|
||
| class NHExtractor(object): | ||
| """ | ||
| Base Class for NH_ID Type | ||
| """ | ||
| @staticmethod | ||
| def initialize(route_obj): | ||
| if route_obj.nh_type == NH: | ||
| return SingleNextHop(route_obj) | ||
| elif route_obj.nh_type == NH_GRP: | ||
| return MultipleNextHop(route_obj) | ||
| elif route_obj.nh_type == RIF: | ||
| return DirecAttachedRt(route_obj) | ||
| elif route_obj.nh_type == CPU_PORT: | ||
| return CPUPort(route_obj) | ||
| return NHExtractor(route_obj) | ||
|
|
||
| def __init__(self, route_obj): | ||
| self.rt = route_obj | ||
|
|
||
| def collect(self): | ||
| pass | ||
|
|
||
| def init_asic_rif_info(self, oid, add_to_tables_not_found=True): | ||
| ret = {} | ||
| if oid: | ||
| req = MatchRequest(db="ASIC_DB", table=RIF, key_pattern=oid, ns=self.rt.ns) | ||
| ret = self.rt.nhgrp_match_engine.fetch(req) | ||
| return self.rt.add_to_ret_template(RIF, "ASIC_DB", ret.get("keys", []), ret.get("error", ""), add_to_tables_not_found) | ||
|
|
||
| def init_asic_next_hop_info(self, oid, add_to_tables_not_found=True): | ||
| ret = {} | ||
| if oid: | ||
| req = MatchRequest(db="ASIC_DB", table=NH, key_pattern=oid, ns=self.rt.ns, | ||
| return_fields=["SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID"]) | ||
| ret = self.rt.nhgrp_match_engine.fetch(req) | ||
| keys = self.rt.add_to_ret_template(NH, "ASIC_DB", ret.get("keys", []), ret.get("error", ""), add_to_tables_not_found) | ||
| nh_key = keys[0] if keys else "" | ||
| return ret.get("return_values", {}).get(nh_key, {}).get("SAI_NEXT_HOP_ATTR_ROUTER_INTERFACE_ID", "") | ||
|
|
||
|
|
||
| class CPUPort(NHExtractor): | ||
| def collect(self): | ||
| req = MatchRequest(db="ASIC_DB", table=CPU_PORT, key_pattern=self.rt.nh_id, ns=self.rt.ns) | ||
| ret = self.rt.nhgrp_match_engine.fetch(req) | ||
| self.rt.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) | ||
|
|
||
|
|
||
| class DirecAttachedRt(NHExtractor): | ||
| def collect(self): | ||
| self.init_asic_rif_info(self.rt.nh_id) | ||
|
|
||
|
|
||
| class SingleNextHop(NHExtractor): | ||
| def collect(self): | ||
| rif_oid = self.init_asic_next_hop_info(self.rt.nh_id) | ||
| self.init_asic_rif_info(rif_oid) | ||
|
|
||
|
|
||
| class MultipleNextHop(NHExtractor): | ||
| def collect(self): | ||
| # Save nh_grp related keys | ||
| self.init_asic_nh_group_info(self.rt.nh_id) | ||
| # Save nh_grp_members info and fetch nh_oids | ||
| nh_oids = self.init_asic_nh_group_members_info(self.rt.nh_id) | ||
| # Save the actual next_hop using the nh_oids retrieved, fetch rif oid's if any | ||
| rif_oids = self.init_asic_next_hops_info(nh_oids) | ||
| # Save the rif_oid related ASIC keys | ||
| self.init_asic_rifs_info(rif_oids) | ||
|
|
||
| def init_asic_nh_group_info(self, oid): | ||
| ret = {} | ||
| if oid: | ||
| req = MatchRequest(db="ASIC_DB", table=NH_GRP, key_pattern=oid, ns=self.rt.ns) | ||
| ret = self.rt.nhgrp_match_engine.fetch(req) | ||
| self.rt.add_to_ret_template(NH_GRP, "ASIC_DB", ret.get("keys", []), ret.get("error", "")) | ||
|
|
||
| def init_asic_nh_group_members_info(self, oid): | ||
| ret = {} | ||
| if oid: | ||
| req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER", | ||
| field="SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID", value=oid, ns=self.rt.ns, | ||
| return_fields=["SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID"]) | ||
| ret = self.rt.nhgrp_match_engine.fetch(req) | ||
| keys = self.rt.add_to_ret_template("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER", "ASIC_DB", | ||
| ret.get("keys", []), ret.get("error", ""), False) | ||
| if not keys: | ||
| self.rt.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") | ||
| return [ret.get("return_values", {}).get(key, {}).get("SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID", "") for key in keys] | ||
|
|
||
| def init_asic_next_hops_info(self, nh_oids): | ||
| rif_oids = [] | ||
| for oid in nh_oids: | ||
| rif_oid = self.init_asic_next_hop_info(oid, False) | ||
| if rif_oid: | ||
| rif_oids.append(rif_oid) | ||
| if not rif_oids: | ||
| self.rt.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP") | ||
| return rif_oids | ||
|
|
||
| def init_asic_rifs_info(self, rif_oids): | ||
| nothing_found = True | ||
| for oid in rif_oids: | ||
| if self.init_asic_rif_info(oid, False): | ||
| nothing_found = False | ||
| if nothing_found: | ||
| self.rt.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment to document expectations of route entry format