Skip to content

Commit e54036c

Browse files
[rfc1213] Interface MIB add l3 vlan interfaces & aggregate rif counters (sonic-net#169)
**- What I did** Rework based on sonic-net#133 and feedback sonic-net#148. **- How I did it** - merge sonic-net#133 with upstream master. - Fix UnavailableDataError when RIF counters are not enabled. - Make RIF keys in maps unique in multi asic env. **- How to verify it** - Disable rif counters - restart swss - snmpwalk - Verify no UnavailableDataError in logs - Verify port counters are returned - Enable rif counters - snmpwalk - Verify no error in logs - Verify error in/out counters are aggregated for rif ports and portchannels - Verify VLAN RIF counters are present in the MIB. **- Description for the changelog** Added support for aggregated router interface counters and L3 VLAN counters to RFC1213 MIB.
1 parent fd1eae7 commit e54036c

16 files changed

+1533
-44
lines changed

src/sonic_ax_impl/mibs/__init__.py

+76-5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@
2525
TABLE_NAME_SEPARATOR_COLON = ':'
2626
TABLE_NAME_SEPARATOR_VBAR = '|'
2727

28+
RIF_COUNTERS_AGGR_MAP = {
29+
"SAI_PORT_STAT_IF_IN_OCTETS": "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS",
30+
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS",
31+
"SAI_PORT_STAT_IF_IN_ERRORS": "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS",
32+
"SAI_PORT_STAT_IF_OUT_OCTETS": "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS",
33+
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS",
34+
"SAI_PORT_STAT_IF_OUT_ERRORS": "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS"
35+
}
36+
37+
RIF_DROPS_AGGR_MAP = {
38+
"SAI_PORT_STAT_IF_IN_ERRORS": "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS",
39+
"SAI_PORT_STAT_IF_OUT_ERRORS": "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS"
40+
}
41+
42+
# IfIndex to OID multiplier for transceiver
43+
IFINDEX_SUB_ID_MULTIPLIER = 1000
44+
2845
redis_kwargs = {'unix_socket_path': '/var/run/redis/redis.sock'}
2946

3047

@@ -134,6 +151,14 @@ def if_entry_table(if_name):
134151
return 'PORT_TABLE:' + if_name
135152

136153

154+
def vlan_entry_table(if_name):
155+
"""
156+
:param if_name: given interface to cast.
157+
:return: VLAN_TABLE key.
158+
"""
159+
return b'VLAN_TABLE:' + if_name
160+
161+
137162
def lag_entry_table(lag_name):
138163
"""
139164
:param lag_name: given lag to cast.
@@ -290,6 +315,46 @@ def init_sync_d_interface_tables(db_conn):
290315

291316
return if_name_map, if_alias_map, if_id_map, oid_name_map
292317

318+
319+
def init_sync_d_rif_tables(db_conn):
320+
"""
321+
Initializes map of RIF SAI oids to port SAI oid.
322+
:return: dict
323+
"""
324+
rif_port_map = {get_sai_id_key(db_conn.namespace, rif): get_sai_id_key(db_conn.namespace, port)
325+
for rif, port in port_util.get_rif_port_map(db_conn).items()}
326+
port_rif_map = {port: rif for rif, port in rif_port_map.items()}
327+
logger.debug("Rif port map:\n" + pprint.pformat(rif_port_map, indent=2))
328+
329+
return rif_port_map, port_rif_map
330+
331+
332+
def init_sync_d_vlan_tables(db_conn):
333+
"""
334+
Initializes vlan interface maps for SyncD-connected MIB(s).
335+
:return: tuple(vlan_name_map, oid_sai_map, oid_name_map)
336+
"""
337+
338+
vlan_name_map = port_util.get_vlan_interface_oid_map(db_conn)
339+
340+
logger.debug("Vlan oid map:\n" + pprint.pformat(vlan_name_map, indent=2))
341+
342+
# { OID -> sai_id }
343+
oid_sai_map = {get_index_from_str(if_name): sai_id for sai_id, if_name in vlan_name_map.items()
344+
# only map the interface if it's a style understood to be a SONiC interface.
345+
if get_index_from_str(if_name) is not None}
346+
logger.debug("OID sai map:\n" + pprint.pformat(oid_sai_map, indent=2))
347+
348+
# { OID -> if_name (SONiC) }
349+
oid_name_map = {get_index_from_str(if_name): if_name for sai_id, if_name in vlan_name_map.items()
350+
# only map the interface if it's a style understood to be a SONiC interface.
351+
if get_index_from_str(if_name) is not None}
352+
353+
logger.debug("OID name map:\n" + pprint.pformat(oid_name_map, indent=2))
354+
355+
return vlan_name_map, oid_sai_map, oid_name_map
356+
357+
293358
def init_sync_d_lag_tables(db_conn):
294359
"""
295360
Helper method. Connects to and initializes LAG interface maps for SyncD-connected MIB(s).
@@ -304,13 +369,19 @@ def init_sync_d_lag_tables(db_conn):
304369
if_name_lag_name_map = {}
305370
# { OID -> lag_name (SONiC) }
306371
oid_lag_name_map = {}
372+
# { lag_name (SONiC) -> lag_oid (SAI) }
373+
lag_sai_map = {}
307374

308375
db_conn.connect(APPL_DB)
309376

310377
lag_entries = db_conn.keys(APPL_DB, "LAG_TABLE:*")
311378

312379
if not lag_entries:
313-
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map
380+
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map
381+
382+
db_conn.connect(COUNTERS_DB)
383+
lag_sai_map = db_conn.get_all(COUNTERS_DB, "COUNTERS_LAG_NAME_MAP")
384+
lag_sai_map = {name: get_sai_id_key(db_conn.namespace, sai_id.lstrip("oid:0x")) for name, sai_id in lag_sai_map.items()}
314385

315386
for lag_entry in lag_entries:
316387
lag_name = lag_entry[len("LAG_TABLE:"):]
@@ -332,7 +403,7 @@ def member_name_str(val, lag_name):
332403
if idx:
333404
oid_lag_name_map[idx] = if_name
334405

335-
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map
406+
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map
336407

337408
def init_sync_d_queue_tables(db_conn):
338409
"""
@@ -347,7 +418,7 @@ def init_sync_d_queue_tables(db_conn):
347418

348419
# Parse the queue_name_map and create the following maps:
349420
# port_queues_map -> {"port_index : queue_index" : sai_oid}
350-
# queue_stat_map -> {"port_index : queue stat table name" : {counter name : value}}
421+
# queue_stat_map -> {"port_index : queue stat table name" : {counter name : value}}
351422
# port_queue_list_map -> {port_index: [sorted queue list]}
352423
port_queues_map = {}
353424
queue_stat_map = {}
@@ -411,7 +482,7 @@ class RedisOidTreeUpdater(MIBUpdater):
411482
def __init__(self, prefix_str):
412483
super().__init__()
413484

414-
self.db_conn = Namespace.init_namespace_dbs()
485+
self.db_conn = Namespace.init_namespace_dbs()
415486
if prefix_str.startswith('.'):
416487
prefix_str = prefix_str[1:]
417488
self.prefix_str = prefix_str
@@ -524,7 +595,7 @@ def dbs_get_all(dbs, db_name, _hash, *args, **kwargs):
524595
db get_all function executed on global and all namespace DBs.
525596
"""
526597
result = {}
527-
# If there are multiple namespaces, _hash might not be
598+
# If there are multiple namespaces, _hash might not be
528599
# present in all namespace, ignore if not present in a
529600
# specfic namespace.
530601
if len(dbs) > 1:

src/sonic_ax_impl/mibs/ietf/rfc1213.py

+79-9
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class DbTables(int, Enum):
4949
class IfTypes(int, Enum):
5050
""" IANA ifTypes """
5151
ethernetCsmacd = 6
52-
ieee8023adLag = 161
52+
l3ipvlan = 136
53+
ieee8023adLag = 161
5354

5455
class ArpUpdater(MIBUpdater):
5556
def __init__(self):
@@ -191,8 +192,13 @@ def __init__(self):
191192
self.lag_name_if_name_map = {}
192193
self.if_name_lag_name_map = {}
193194
self.oid_lag_name_map = {}
195+
self.lag_sai_map = {}
194196
self.mgmt_oid_name_map = {}
195197
self.mgmt_alias_map = {}
198+
self.vlan_oid_name_map = {}
199+
self.vlan_name_map = {}
200+
self.rif_port_map = {}
201+
self.port_rif_map = {}
196202

197203
# cache of interface counters
198204
self.if_counters = {}
@@ -201,6 +207,8 @@ def __init__(self):
201207
self.if_alias_map = {}
202208
self.if_id_map = {}
203209
self.oid_name_map = {}
210+
self.rif_counters = {}
211+
204212
self.namespace_db_map = Namespace.get_namespace_db_map(self.db_conn)
205213

206214
def reinit_data(self):
@@ -219,26 +227,50 @@ def reinit_data(self):
219227
self.mgmt_oid_name_map, \
220228
self.mgmt_alias_map = mibs.init_mgmt_interface_tables(self.db_conn[0])
221229

230+
self.vlan_name_map, \
231+
self.vlan_oid_sai_map, \
232+
self.vlan_oid_name_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_vlan_tables, self.db_conn)
233+
234+
self.rif_port_map, \
235+
self.port_rif_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_rif_tables, self.db_conn)
236+
222237
def update_data(self):
223238
"""
224239
Update redis (caches config)
225240
Pulls the table references for each interface.
226241
"""
227-
for sai_id_key in self.if_id_map:
228-
namespace, sai_id = mibs.split_sai_id_key(sai_id_key)
229-
if_idx = mibs.get_index_from_str(self.if_id_map[sai_id_key])
230-
self.if_counters[if_idx] = self.namespace_db_map[namespace].get_all(mibs.COUNTERS_DB, \
231-
mibs.counter_table(sai_id), blocking=True)
242+
243+
self.update_if_counters()
244+
self.update_rif_counters()
245+
246+
self.aggregate_counters()
232247

233248
self.lag_name_if_name_map, \
234249
self.if_name_lag_name_map, \
235-
self.oid_lag_name_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
250+
self.oid_lag_name_map, \
251+
self.lag_sai_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
236252

237253
self.if_range = sorted(list(self.oid_name_map.keys()) +
238254
list(self.oid_lag_name_map.keys()) +
239-
list(self.mgmt_oid_name_map.keys()))
255+
list(self.mgmt_oid_name_map.keys()) +
256+
list(self.vlan_oid_name_map.keys()))
240257
self.if_range = [(i,) for i in self.if_range]
241258

259+
def update_if_counters(self):
260+
for sai_id_key in self.if_id_map:
261+
namespace, sai_id = mibs.split_sai_id_key(sai_id_key)
262+
if_idx = mibs.get_index_from_str(self.if_id_map[sai_id_key])
263+
self.if_counters[if_idx] = self.namespace_db_map[namespace].get_all(mibs.COUNTERS_DB, \
264+
mibs.counter_table(sai_id), blocking=True)
265+
266+
def update_rif_counters(self):
267+
rif_sai_ids = list(self.rif_port_map) + list(self.vlan_name_map)
268+
self.rif_counters = \
269+
{sai_id: Namespace.dbs_get_all(self.db_conn, mibs.COUNTERS_DB,
270+
mibs.counter_table(mibs.split_sai_id_key(sai_id)[1]),
271+
blocking=False)
272+
for sai_id in rif_sai_ids}
273+
242274
def get_next(self, sub_id):
243275
"""
244276
:param sub_id: The 1-based sub-identifier query.
@@ -280,6 +312,8 @@ def interface_description(self, sub_id):
280312
return self.oid_lag_name_map[oid]
281313
elif oid in self.mgmt_oid_name_map:
282314
return self.mgmt_alias_map[self.mgmt_oid_name_map[oid]]
315+
elif oid in self.vlan_oid_name_map:
316+
return self.vlan_oid_name_map[oid]
283317

284318
return self.if_alias_map[self.oid_name_map[oid]]
285319

@@ -302,6 +336,31 @@ def _get_counter(self, oid, table_name):
302336
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}'.".format(e))
303337
return None
304338

339+
def aggregate_counters(self):
340+
"""
341+
For ports with l3 router interfaces l3 drops may be counted separately (RIF counters)
342+
add l3 drops to l2 drop counters cache according to mapping
343+
344+
For l3vlan map l3 counters to l2 counters
345+
"""
346+
for rif_sai_id, port_sai_id in self.rif_port_map.items():
347+
if port_sai_id in self.if_id_map:
348+
port_idx = mibs.get_index_from_str(self.if_id_map[port_sai_id])
349+
for port_counter_name, rif_counter_name in mibs.RIF_DROPS_AGGR_MAP.items():
350+
self.if_counters[port_idx][port_counter_name] = \
351+
int(self.if_counters[port_idx][port_counter_name]) + \
352+
int(self.rif_counters[rif_sai_id][rif_counter_name])
353+
354+
for vlan_sai_id, vlan_name in self.vlan_name_map.items():
355+
for port_counter_name, rif_counter_name in mibs.RIF_COUNTERS_AGGR_MAP.items():
356+
vlan_idx = mibs.get_index_from_str(vlan_name)
357+
vlan_rif_counters = self.rif_counters[vlan_sai_id]
358+
if rif_counter_name in vlan_rif_counters:
359+
self.if_counters.setdefault(vlan_idx, {})
360+
self.if_counters[vlan_idx][port_counter_name] = \
361+
int(vlan_rif_counters[rif_counter_name])
362+
363+
305364
def get_counter(self, sub_id, table_name):
306365
"""
307366
:param sub_id: The 1-based sub-identifier query.
@@ -321,7 +380,13 @@ def get_counter(self, sub_id, table_name):
321380
counter_value = 0
322381
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[oid]]:
323382
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), table_name)
324-
383+
sai_lag_id = self.lag_sai_map[self.oid_lag_name_map[oid]]
384+
sai_lag_rif_id = self.port_rif_map[sai_lag_id]
385+
if sai_lag_rif_id in self.rif_port_map:
386+
table_name = getattr(table_name, 'name', table_name)
387+
if table_name in mibs.RIF_DROPS_AGGR_MAP:
388+
rif_table_name = mibs.RIF_DROPS_AGGR_MAP[table_name]
389+
counter_value += int(self.rif_counters[sai_lag_rif_id].get(rif_table_name, 0))
325390
# truncate to 32-bit counter
326391
return counter_value & 0x00000000ffffffff
327392
else:
@@ -351,6 +416,8 @@ def _get_if_entry(self, sub_id):
351416
elif oid in self.mgmt_oid_name_map:
352417
if_table = mibs.mgmt_if_entry_table(self.mgmt_oid_name_map[oid])
353418
db = mibs.CONFIG_DB
419+
elif oid in self.vlan_oid_name_map:
420+
if_table = mibs.vlan_entry_table(self.vlan_oid_name_map[oid])
354421
elif oid in self.oid_name_map:
355422
if_table = mibs.if_entry_table(self.oid_name_map[oid])
356423
else:
@@ -455,6 +522,7 @@ def get_if_type(self, sub_id):
455522
456523
ethernetCsmacd(6), -- for all ethernet-like interfaces,
457524
-- regardless of speed, as per RFC3635
525+
l3ipvlan(136) -- Layer 3 Virtual LAN using IP
458526
ieee8023adLag(161) -- IEEE 802.3ad Link Aggregate
459527
"""
460528
oid = self.get_oid(sub_id)
@@ -463,6 +531,8 @@ def get_if_type(self, sub_id):
463531

464532
if oid in self.oid_lag_name_map:
465533
return IfTypes.ieee8023adLag
534+
elif oid in self.vlan_oid_name_map:
535+
return IfTypes.l3ipvlan
466536
else:
467537
return IfTypes.ethernetCsmacd
468538

src/sonic_ax_impl/mibs/ietf/rfc2863.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def reinit_data(self):
7979

8080
self.lag_name_if_name_map, \
8181
self.if_name_lag_name_map, \
82-
self.oid_lag_name_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
82+
self.oid_lag_name_map, _ = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
8383
"""
8484
db_conn - will have db_conn to all namespace DBs and
8585
global db. First db in the list is global db.

src/sonic_ax_impl/mibs/vendor/cisco/ciscoPfcExtMIB.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def update_data(self):
5252

5353
self.lag_name_if_name_map, \
5454
self.if_name_lag_name_map, \
55-
self.oid_lag_name_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
55+
self.oid_lag_name_map, _ = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
5656

5757
self.if_range = sorted(list(self.oid_name_map.keys()) + list(self.oid_lag_name_map.keys()))
5858
self.if_range = [(i,) for i in self.if_range]
@@ -94,7 +94,7 @@ def _get_counter(self, oid, counter_name):
9494
_counter_name = getattr(counter_name, 'name', counter_name)
9595

9696
try:
97-
counter_value = self.if_counters[oid][_counter_name]
97+
counter_value = self.if_counters[oid][_counter_name]
9898
counter_value = int(counter_value) & 0xffffffffffffffff
9999
# done!
100100
return counter_value

tests/mock_tables/asic0/asic_db.json

+7
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,12 @@
2323
"SAI_BRIDGE_PORT_ATTR_TYPE": "SAI_BRIDGE_PORT_TYPE_PORT",
2424
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x1000000000005",
2525
"SAI_BRIDGE_PORT_ATTR_ADMIN_STATE": "true"
26+
},
27+
"ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x6000000000006": {
28+
"SAI_ROUTER_INTERFACE_ATTR_TYPE": "SAI_ROUTER_INTERFACE_TYPE_PORT",
29+
"SAI_ROUTER_INTERFACE_ATTR_PORT_ID": "oid:0x1000000000007"
30+
},
31+
"ASIC_STATE:SAI_OBJECT_TYPE_LAG:oid:0x1000000000007": {
32+
"NULL": "NULL"
2633
}
2734
}

0 commit comments

Comments
 (0)