Skip to content

Commit 266bd15

Browse files
Restored snmp vlan support per RFC1213 and added the missing support for RFC2863 (#218)
- What I did Restored snmp vlan support per RFC1213, which had been reverted due to inconsistency with RFC2863, and added the missing support for RFC2863. Added unit tests for RFC2863. - How I did it Reverted #191, which was itself a revert of #169. Then added code to support vlan in rfc2863.py and unit tests. Since a long time has elapsed since the original commit and other code has been pushed in the meantime, great care is needed in merging. Note in particular that mibs.init_sync_d_lag_tables now returns five parameters, the last two of which were added: lag_sai_map and sai_lap_map. This PR needs one of those maps, and a concurrent commit supporting RFC4363 needs the other, so all the callers were updated and use the one they need. - How to verify it Unit tests are run via make target/python-wheels/asyncsnmp-2.1.0-py3-none-any.whl. See also Azure#169. - Description for the changelog Restored support for aggregated router interface counters and L3 VLAN counters to RFC1213 MIB, and extended to RFC2863.
1 parent 28b9dfd commit 266bd15

19 files changed

+2037
-69
lines changed

src/sonic_ax_impl/mibs/__init__.py

+69-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@
2727

2828
HOST_NAMESPACE_DB_IDX = 0
2929

30+
RIF_COUNTERS_AGGR_MAP = {
31+
"SAI_PORT_STAT_IF_IN_OCTETS": "SAI_ROUTER_INTERFACE_STAT_IN_OCTETS",
32+
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS",
33+
"SAI_PORT_STAT_IF_IN_ERRORS": "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS",
34+
"SAI_PORT_STAT_IF_OUT_OCTETS": "SAI_ROUTER_INTERFACE_STAT_OUT_OCTETS",
35+
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "SAI_ROUTER_INTERFACE_STAT_OUT_PACKETS",
36+
"SAI_PORT_STAT_IF_OUT_ERRORS": "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS"
37+
}
38+
39+
RIF_DROPS_AGGR_MAP = {
40+
"SAI_PORT_STAT_IF_IN_ERRORS": "SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS",
41+
"SAI_PORT_STAT_IF_OUT_ERRORS": "SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS"
42+
}
43+
3044
redis_kwargs = {'unix_socket_path': '/var/run/redis/redis.sock'}
3145

3246

@@ -136,6 +150,14 @@ def if_entry_table(if_name):
136150
return 'PORT_TABLE:' + if_name
137151

138152

153+
def vlan_entry_table(if_name):
154+
"""
155+
:param if_name: given interface to cast.
156+
:return: VLAN_TABLE key.
157+
"""
158+
return 'VLAN_TABLE:' + if_name
159+
160+
139161
def lag_entry_table(lag_name):
140162
"""
141163
:param lag_name: given lag to cast.
@@ -292,11 +314,52 @@ def init_sync_d_interface_tables(db_conn):
292314

293315
return if_name_map, if_alias_map, if_id_map, oid_name_map
294316

317+
318+
def init_sync_d_rif_tables(db_conn):
319+
"""
320+
Initializes map of RIF SAI oids to port SAI oid.
321+
:return: dict
322+
"""
323+
rif_port_map = {get_sai_id_key(db_conn.namespace, rif): get_sai_id_key(db_conn.namespace, port)
324+
for rif, port in port_util.get_rif_port_map(db_conn).items()}
325+
port_rif_map = {port: rif for rif, port in rif_port_map.items()}
326+
logger.debug("Rif port map:\n" + pprint.pformat(rif_port_map, indent=2))
327+
328+
return rif_port_map, port_rif_map
329+
330+
331+
def init_sync_d_vlan_tables(db_conn):
332+
"""
333+
Initializes vlan interface maps for SyncD-connected MIB(s).
334+
:return: tuple(vlan_name_map, oid_sai_map, oid_name_map)
335+
"""
336+
337+
vlan_name_map = port_util.get_vlan_interface_oid_map(db_conn)
338+
339+
logger.debug("Vlan oid map:\n" + pprint.pformat(vlan_name_map, indent=2))
340+
341+
oid_sai_map = {}
342+
oid_name_map = {}
343+
for sai_id, if_name in vlan_name_map.items():
344+
port_index = get_index_from_str(if_name)
345+
if not port_index:
346+
continue
347+
# { OID -> sai_id }
348+
oid_sai_map[port_index] = sai_id
349+
# { OID -> if_name (SONiC) }
350+
oid_name_map[port_index] = if_name
351+
352+
logger.debug("OID sai map:\n" + pprint.pformat(oid_sai_map, indent=2))
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+
295358
def init_sync_d_lag_tables(db_conn):
296359
"""
297360
Helper method. Connects to and initializes LAG interface maps for SyncD-connected MIB(s).
298361
:param db_conn: database connector
299-
:return: tuple(lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map)
362+
:return: tuple(lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map, sai_lag_map)
300363
"""
301364
# { lag_name (SONiC) -> [ lag_members (if_name) ] }
302365
# ex: { "PortChannel0" : [ "Ethernet0", "Ethernet4" ] }
@@ -316,7 +379,7 @@ def init_sync_d_lag_tables(db_conn):
316379
lag_entries = db_conn.keys(APPL_DB, "LAG_TABLE:*")
317380

318381
if not lag_entries:
319-
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map
382+
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map, sai_lag_map
320383

321384
db_conn.connect(COUNTERS_DB)
322385
lag_sai_map = db_conn.get_all(COUNTERS_DB, "COUNTERS_LAG_NAME_MAP")
@@ -345,7 +408,7 @@ def member_name_str(val, lag_name):
345408
if idx:
346409
oid_lag_name_map[idx] = if_name
347410

348-
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, sai_lag_map
411+
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map, sai_lag_map
349412

350413
def init_sync_d_queue_tables(db_conn):
351414
"""
@@ -360,7 +423,7 @@ def init_sync_d_queue_tables(db_conn):
360423

361424
# Parse the queue_name_map and create the following maps:
362425
# port_queues_map -> {"port_index : queue_index" : sai_oid}
363-
# queue_stat_map -> {"port_index : queue stat table name" : {counter name : value}}
426+
# queue_stat_map -> {"port_index : queue stat table name" : {counter name : value}}
364427
# port_queue_list_map -> {port_index: [sorted queue list]}
365428
port_queues_map = {}
366429
queue_stat_map = {}
@@ -424,7 +487,7 @@ class RedisOidTreeUpdater(MIBUpdater):
424487
def __init__(self, prefix_str):
425488
super().__init__()
426489

427-
self.db_conn = Namespace.init_namespace_dbs()
490+
self.db_conn = Namespace.init_namespace_dbs()
428491
if prefix_str.startswith('.'):
429492
prefix_str = prefix_str[1:]
430493
self.prefix_str = prefix_str
@@ -537,7 +600,7 @@ def dbs_get_all(dbs, db_name, _hash, *args, **kwargs):
537600
db get_all function executed on global and all namespace DBs.
538601
"""
539602
result = {}
540-
# If there are multiple namespaces, _hash might not be
603+
# If there are multiple namespaces, _hash might not be
541604
# present in all namespace, ignore if not present in a
542605
# specfic namespace.
543606
if len(dbs) > 1:

src/sonic_ax_impl/mibs/ietf/rfc1213.py

+114-10
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class DbTables(int, Enum):
5050
class IfTypes(int, Enum):
5151
""" IANA ifTypes """
5252
ethernetCsmacd = 6
53-
ieee8023adLag = 161
53+
l3ipvlan = 136
54+
ieee8023adLag = 161
5455

5556
class ArpUpdater(MIBUpdater):
5657
def __init__(self):
@@ -192,8 +193,13 @@ def __init__(self):
192193
self.lag_name_if_name_map = {}
193194
self.if_name_lag_name_map = {}
194195
self.oid_lag_name_map = {}
196+
self.lag_sai_map = {}
195197
self.mgmt_oid_name_map = {}
196198
self.mgmt_alias_map = {}
199+
self.vlan_oid_name_map = {}
200+
self.vlan_name_map = {}
201+
self.rif_port_map = {}
202+
self.port_rif_map = {}
197203

198204
# cache of interface counters
199205
self.if_counters = {}
@@ -202,6 +208,8 @@ def __init__(self):
202208
self.if_alias_map = {}
203209
self.if_id_map = {}
204210
self.oid_name_map = {}
211+
self.rif_counters = {}
212+
205213
self.namespace_db_map = Namespace.get_namespace_db_map(self.db_conn)
206214

207215
def reinit_data(self):
@@ -220,26 +228,56 @@ def reinit_data(self):
220228
self.mgmt_oid_name_map, \
221229
self.mgmt_alias_map = mibs.init_mgmt_interface_tables(self.db_conn[0])
222230

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

234249
self.lag_name_if_name_map, \
235250
self.if_name_lag_name_map, \
236-
self.oid_lag_name_map, _ = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
251+
self.oid_lag_name_map, \
252+
self.lag_sai_map, self.sai_lag_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
237253

238254
self.if_range = sorted(list(self.oid_name_map.keys()) +
239255
list(self.oid_lag_name_map.keys()) +
240-
list(self.mgmt_oid_name_map.keys()))
256+
list(self.mgmt_oid_name_map.keys()) +
257+
list(self.vlan_oid_name_map.keys()))
241258
self.if_range = [(i,) for i in self.if_range]
242259

260+
def update_if_counters(self):
261+
for sai_id_key in self.if_id_map:
262+
namespace, sai_id = mibs.split_sai_id_key(sai_id_key)
263+
if_idx = mibs.get_index_from_str(self.if_id_map[sai_id_key])
264+
counters_db_data = self.namespace_db_map[namespace].get_all(mibs.COUNTERS_DB,
265+
mibs.counter_table(sai_id),
266+
blocking=True)
267+
self.if_counters[if_idx] = {
268+
counter: int(value) for counter, value in counters_db_data.items()
269+
}
270+
271+
def update_rif_counters(self):
272+
rif_sai_ids = list(self.rif_port_map) + list(self.vlan_name_map)
273+
for sai_id in rif_sai_ids:
274+
counters_db_data = Namespace.dbs_get_all(self.db_conn, mibs.COUNTERS_DB,
275+
mibs.counter_table(mibs.split_sai_id_key(sai_id)[1]),
276+
blocking=False)
277+
self.rif_counters[sai_id] = {
278+
counter: int(value) for counter, value in counters_db_data.items()
279+
}
280+
243281
def get_next(self, sub_id):
244282
"""
245283
:param sub_id: The 1-based sub-identifier query.
@@ -281,6 +319,8 @@ def interface_description(self, sub_id):
281319
return self.oid_lag_name_map[oid]
282320
elif oid in self.mgmt_oid_name_map:
283321
return self.mgmt_alias_map[self.mgmt_oid_name_map[oid]]
322+
elif oid in self.vlan_oid_name_map:
323+
return self.vlan_oid_name_map[oid]
284324

285325
return self.if_alias_map[self.oid_name_map[oid]]
286326

@@ -291,18 +331,46 @@ def _get_counter(self, oid, table_name):
291331
:return: the counter for the respective sub_id/table.
292332
"""
293333
# Enum.name or table_name = 'name_of_the_table'
334+
# Example:
335+
# table_name = <DbTables.SAI_PORT_STAT_IF_OUT_ERRORS: 20>
336+
# _table_name = 'SAI_PORT_STAT_IF_OUT_ERRORS'
294337
_table_name = getattr(table_name, 'name', table_name)
295338

296339
try:
297340
counter_value = self.if_counters[oid][_table_name]
298341
# truncate to 32-bit counter (database implements 64-bit counters)
299-
counter_value = int(counter_value) & 0x00000000ffffffff
342+
counter_value = counter_value & 0x00000000ffffffff
300343
# done!
301344
return counter_value
302345
except KeyError as e:
303346
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}'.".format(e))
304347
return None
305348

349+
def aggregate_counters(self):
350+
"""
351+
For ports with l3 router interfaces l3 drops may be counted separately (RIF counters)
352+
add l3 drops to l2 drop counters cache according to mapping
353+
354+
For l3vlan map l3 counters to l2 counters
355+
"""
356+
for rif_sai_id, port_sai_id in self.rif_port_map.items():
357+
if port_sai_id in self.if_id_map:
358+
port_idx = mibs.get_index_from_str(self.if_id_map[port_sai_id])
359+
for port_counter_name, rif_counter_name in mibs.RIF_DROPS_AGGR_MAP.items():
360+
self.if_counters[port_idx][port_counter_name] = \
361+
self.if_counters[port_idx][port_counter_name] + \
362+
self.rif_counters[rif_sai_id][rif_counter_name]
363+
364+
for vlan_sai_id, vlan_name in self.vlan_name_map.items():
365+
for port_counter_name, rif_counter_name in mibs.RIF_COUNTERS_AGGR_MAP.items():
366+
vlan_idx = mibs.get_index_from_str(vlan_name)
367+
vlan_rif_counters = self.rif_counters[vlan_sai_id]
368+
if rif_counter_name in vlan_rif_counters:
369+
self.if_counters.setdefault(vlan_idx, {})
370+
self.if_counters[vlan_idx][port_counter_name] = \
371+
vlan_rif_counters[rif_counter_name]
372+
373+
306374
def get_counter(self, sub_id, table_name):
307375
"""
308376
:param sub_id: The 1-based sub-identifier query.
@@ -320,9 +388,40 @@ def get_counter(self, sub_id, table_name):
320388
return 0
321389
elif oid in self.oid_lag_name_map:
322390
counter_value = 0
391+
# Sum the values of this counter for all ports in the LAG.
392+
# Example:
393+
# table_name = <DbTables.SAI_PORT_STAT_IF_OUT_ERRORS: 20>
394+
# oid = 1001
395+
# self.oid_lag_name_map = {1001: 'PortChannel01', 1002: 'PortChannel02', 1003: 'PortChannel03'}
396+
# self.oid_lag_name_map[oid] = 'PortChannel01'
397+
# self.lag_name_if_name_map = {'PortChannel01': ['Ethernet112'], 'PortChannel02': ['Ethernet116'], 'PortChannel03': ['Ethernet120']}
398+
# self.lag_name_if_name_map['PortChannel01'] = ['Ethernet112']
399+
# mibs.get_index_from_str('Ethernet112') = 113 (because Ethernet N = N + 1)
400+
# self._get_counter retrieves the counter per oid and table.
323401
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[oid]]:
324402
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), table_name)
325-
403+
# Check if we need to add a router interface count.
404+
# Example:
405+
# self.lag_sai_map = {'PortChannel01': '2000000000006', 'PortChannel02': '2000000000005', 'PortChannel03': '2000000000004'}
406+
# self.port_rif_map = {'2000000000006': '6000000000006', '2000000000005': '6000000000005', '2000000000004': '6000000000004'}
407+
# self.rif_port_map = {'6000000000006': '2000000000006', '6000000000005': '2000000000005', '6000000000004': '2000000000004'}
408+
# self.lag_sai_map['PortChannel01'] = '2000000000006'
409+
# self.port_rif_map['2000000000006'] = '6000000000006'
410+
sai_lag_id = self.lag_sai_map[self.oid_lag_name_map[oid]]
411+
sai_lag_rif_id = self.port_rif_map[sai_lag_id]
412+
if sai_lag_rif_id in self.rif_port_map:
413+
# Extract the 'name' part of 'table_name'.
414+
# Example:
415+
# table_name = <DbTables.SAI_PORT_STAT_IF_OUT_ERRORS: 20>
416+
# _table_name = 'SAI_PORT_STAT_IF_OUT_ERRORS'
417+
table_name = getattr(table_name, 'name', table_name)
418+
# Find rif counter table if applicable and add the count for this table.
419+
# Example:
420+
# mibs.RIF_DROPS_AGGR_MAP = {'SAI_PORT_STAT_IF_IN_ERRORS': 'SAI_ROUTER_INTERFACE_STAT_IN_ERROR_PACKETS', 'SAI_PORT_STAT_IF_OUT_ERRORS': 'SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS'}
421+
# self.rif_counters['6000000000006'] = {'SAI_ROUTER_INTERFACE_STAT_IN_PACKETS': 6, ... 'SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS': 6, ...}
422+
if table_name in mibs.RIF_DROPS_AGGR_MAP:
423+
rif_table_name = mibs.RIF_DROPS_AGGR_MAP[table_name]
424+
counter_value += self.rif_counters[sai_lag_rif_id].get(rif_table_name, 0)
326425
# truncate to 32-bit counter
327426
return counter_value & 0x00000000ffffffff
328427
else:
@@ -352,6 +451,8 @@ def _get_if_entry(self, sub_id):
352451
elif oid in self.mgmt_oid_name_map:
353452
if_table = mibs.mgmt_if_entry_table(self.mgmt_oid_name_map[oid])
354453
db = mibs.CONFIG_DB
454+
elif oid in self.vlan_oid_name_map:
455+
if_table = mibs.vlan_entry_table(self.vlan_oid_name_map[oid])
355456
elif oid in self.oid_name_map:
356457
if_table = mibs.if_entry_table(self.oid_name_map[oid])
357458
else:
@@ -456,6 +557,7 @@ def get_if_type(self, sub_id):
456557
457558
ethernetCsmacd(6), -- for all ethernet-like interfaces,
458559
-- regardless of speed, as per RFC3635
560+
l3ipvlan(136) -- Layer 3 Virtual LAN using IP
459561
ieee8023adLag(161) -- IEEE 802.3ad Link Aggregate
460562
"""
461563
oid = self.get_oid(sub_id)
@@ -464,6 +566,8 @@ def get_if_type(self, sub_id):
464566

465567
if oid in self.oid_lag_name_map:
466568
return IfTypes.ieee8023adLag
569+
elif oid in self.vlan_oid_name_map:
570+
return IfTypes.l3ipvlan
467571
else:
468572
return IfTypes.ethernetCsmacd
469573

0 commit comments

Comments
 (0)