|
| 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