Skip to content

Commit 3b1e0a7

Browse files
raphaelt-nvidiaSuvarnaMeenakshi
authored andcommitted
Restored snmp vlan support per RFC1213 and added the missing support for RFC2863 (sonic-net#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 sonic-net#191, which was itself a revert of sonic-net#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. (cherry picked from commit 266bd15)
1 parent 7147354 commit 3b1e0a7

19 files changed

+2041
-72
lines changed

src/sonic_ax_impl/mibs/__init__.py

+69-6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626
TABLE_NAME_SEPARATOR_COLON = ':'
2727
TABLE_NAME_SEPARATOR_VBAR = '|'
2828

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

3145

@@ -135,6 +149,14 @@ def if_entry_table(if_name):
135149
return 'PORT_TABLE:' + if_name
136150

137151

152+
def vlan_entry_table(if_name):
153+
"""
154+
:param if_name: given interface to cast.
155+
:return: VLAN_TABLE key.
156+
"""
157+
return 'VLAN_TABLE:' + if_name
158+
159+
138160
def lag_entry_table(lag_name):
139161
"""
140162
:param lag_name: given lag to cast.
@@ -296,11 +318,52 @@ def init_sync_d_interface_tables(db_conn):
296318

297319
return if_name_map, if_alias_map, if_id_map, oid_name_map
298320

321+
322+
def init_sync_d_rif_tables(db_conn):
323+
"""
324+
Initializes map of RIF SAI oids to port SAI oid.
325+
:return: dict
326+
"""
327+
rif_port_map = {get_sai_id_key(db_conn.namespace, rif): get_sai_id_key(db_conn.namespace, port)
328+
for rif, port in port_util.get_rif_port_map(db_conn).items()}
329+
port_rif_map = {port: rif for rif, port in rif_port_map.items()}
330+
logger.debug("Rif port map:\n" + pprint.pformat(rif_port_map, indent=2))
331+
332+
return rif_port_map, port_rif_map
333+
334+
335+
def init_sync_d_vlan_tables(db_conn):
336+
"""
337+
Initializes vlan interface maps for SyncD-connected MIB(s).
338+
:return: tuple(vlan_name_map, oid_sai_map, oid_name_map)
339+
"""
340+
341+
vlan_name_map = port_util.get_vlan_interface_oid_map(db_conn)
342+
343+
logger.debug("Vlan oid map:\n" + pprint.pformat(vlan_name_map, indent=2))
344+
345+
oid_sai_map = {}
346+
oid_name_map = {}
347+
for sai_id, if_name in vlan_name_map.items():
348+
port_index = get_index_from_str(if_name)
349+
if not port_index:
350+
continue
351+
# { OID -> sai_id }
352+
oid_sai_map[port_index] = sai_id
353+
# { OID -> if_name (SONiC) }
354+
oid_name_map[port_index] = if_name
355+
356+
logger.debug("OID sai map:\n" + pprint.pformat(oid_sai_map, indent=2))
357+
logger.debug("OID name map:\n" + pprint.pformat(oid_name_map, indent=2))
358+
359+
return vlan_name_map, oid_sai_map, oid_name_map
360+
361+
299362
def init_sync_d_lag_tables(db_conn):
300363
"""
301364
Helper method. Connects to and initializes LAG interface maps for SyncD-connected MIB(s).
302365
:param db_conn: database connector
303-
:return: tuple(lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map)
366+
:return: tuple(lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map, sai_lag_map)
304367
"""
305368
# { lag_name (SONiC) -> [ lag_members (if_name) ] }
306369
# ex: { "PortChannel0" : [ "Ethernet0", "Ethernet4" ] }
@@ -320,7 +383,7 @@ def init_sync_d_lag_tables(db_conn):
320383
lag_entries = db_conn.keys(APPL_DB, "LAG_TABLE:*")
321384

322385
if not lag_entries:
323-
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map
386+
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map, sai_lag_map
324387

325388
db_conn.connect(COUNTERS_DB)
326389
lag_sai_map = db_conn.get_all(COUNTERS_DB, "COUNTERS_LAG_NAME_MAP")
@@ -349,7 +412,7 @@ def member_name_str(val, lag_name):
349412
if idx:
350413
oid_lag_name_map[idx] = if_name
351414

352-
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, sai_lag_map
415+
return lag_name_if_name_map, if_name_lag_name_map, oid_lag_name_map, lag_sai_map, sai_lag_map
353416

354417
def init_sync_d_queue_tables(db_conn):
355418
"""
@@ -364,7 +427,7 @@ def init_sync_d_queue_tables(db_conn):
364427

365428
# Parse the queue_name_map and create the following maps:
366429
# port_queues_map -> {"port_index : queue_index" : sai_oid}
367-
# queue_stat_map -> {"port_index : queue stat table name" : {counter name : value}}
430+
# queue_stat_map -> {"port_index : queue stat table name" : {counter name : value}}
368431
# port_queue_list_map -> {port_index: [sorted queue list]}
369432
port_queues_map = {}
370433
queue_stat_map = {}
@@ -428,7 +491,7 @@ class RedisOidTreeUpdater(MIBUpdater):
428491
def __init__(self, prefix_str):
429492
super().__init__()
430493

431-
self.db_conn = Namespace.init_namespace_dbs()
494+
self.db_conn = Namespace.init_namespace_dbs()
432495
if prefix_str.startswith('.'):
433496
prefix_str = prefix_str[1:]
434497
self.prefix_str = prefix_str
@@ -551,7 +614,7 @@ def dbs_get_all(dbs, db_name, _hash, *args, **kwargs):
551614
db get_all function executed on global and all namespace DBs.
552615
"""
553616
result = {}
554-
# If there are multiple namespaces, _hash might not be
617+
# If there are multiple namespaces, _hash might not be
555618
# present in all namespace, ignore if not present in a
556619
# specific namespace.
557620
if len(dbs) > 1:

src/sonic_ax_impl/mibs/ietf/rfc1213.py

+118-13
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):
@@ -196,8 +197,13 @@ def __init__(self):
196197
self.lag_name_if_name_map = {}
197198
self.if_name_lag_name_map = {}
198199
self.oid_lag_name_map = {}
200+
self.lag_sai_map = {}
199201
self.mgmt_oid_name_map = {}
200202
self.mgmt_alias_map = {}
203+
self.vlan_oid_name_map = {}
204+
self.vlan_name_map = {}
205+
self.rif_port_map = {}
206+
self.port_rif_map = {}
201207

202208
# cache of interface counters
203209
self.if_counters = {}
@@ -206,6 +212,8 @@ def __init__(self):
206212
self.if_alias_map = {}
207213
self.if_id_map = {}
208214
self.oid_name_map = {}
215+
self.rif_counters = {}
216+
209217
self.namespace_db_map = Namespace.get_namespace_db_map(self.db_conn)
210218

211219
def reinit_data(self):
@@ -226,27 +234,58 @@ def reinit_data(self):
226234

227235
self.lag_name_if_name_map, \
228236
self.if_name_lag_name_map, \
229-
self.oid_lag_name_map, _ = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
237+
self.oid_lag_name_map, \
238+
self.lag_sai_map, self.sai_lag_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
239+
240+
self.vlan_name_map, \
241+
self.vlan_oid_sai_map, \
242+
self.vlan_oid_name_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_vlan_tables, self.db_conn)
243+
244+
self.rif_port_map, \
245+
self.port_rif_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_rif_tables, self.db_conn)
230246

231247
def update_data(self):
232248
"""
233249
Update redis (caches config)
234250
Pulls the table references for each interface.
235251
"""
236-
for sai_id_key in self.if_id_map:
237-
namespace, sai_id = mibs.split_sai_id_key(sai_id_key)
238-
if_idx = mibs.get_index_from_str(self.if_id_map[sai_id_key])
239-
counter_table = self.namespace_db_map[namespace].get_all(mibs.COUNTERS_DB, \
240-
mibs.counter_table(sai_id))
241-
if counter_table is None:
242-
counter_table = {}
243-
self.if_counters[if_idx] = counter_table
252+
self.update_if_counters()
253+
self.update_rif_counters()
254+
255+
self.aggregate_counters()
256+
257+
self.lag_name_if_name_map, \
258+
self.if_name_lag_name_map, \
259+
self.oid_lag_name_map, \
260+
self.lag_sai_map, self.sai_lag_map = Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_lag_tables, self.db_conn)
244261

245262
self.if_range = sorted(list(self.oid_name_map.keys()) +
246263
list(self.oid_lag_name_map.keys()) +
247-
list(self.mgmt_oid_name_map.keys()))
264+
list(self.mgmt_oid_name_map.keys()) +
265+
list(self.vlan_oid_name_map.keys()))
248266
self.if_range = [(i,) for i in self.if_range]
249267

268+
def update_if_counters(self):
269+
for sai_id_key in self.if_id_map:
270+
namespace, sai_id = mibs.split_sai_id_key(sai_id_key)
271+
if_idx = mibs.get_index_from_str(self.if_id_map[sai_id_key])
272+
counters_db_data = self.namespace_db_map[namespace].get_all(mibs.COUNTERS_DB,
273+
mibs.counter_table(sai_id),
274+
blocking=True)
275+
self.if_counters[if_idx] = {
276+
counter: int(value) for counter, value in counters_db_data.items()
277+
}
278+
279+
def update_rif_counters(self):
280+
rif_sai_ids = list(self.rif_port_map) + list(self.vlan_name_map)
281+
for sai_id in rif_sai_ids:
282+
counters_db_data = Namespace.dbs_get_all(self.db_conn, mibs.COUNTERS_DB,
283+
mibs.counter_table(mibs.split_sai_id_key(sai_id)[1]),
284+
blocking=False)
285+
self.rif_counters[sai_id] = {
286+
counter: int(value) for counter, value in counters_db_data.items()
287+
}
288+
250289
def get_next(self, sub_id):
251290
"""
252291
:param sub_id: The 1-based sub-identifier query.
@@ -288,6 +327,8 @@ def interface_description(self, sub_id):
288327
return self.oid_lag_name_map[oid]
289328
elif oid in self.mgmt_oid_name_map:
290329
return self.mgmt_alias_map[self.mgmt_oid_name_map[oid]]
330+
elif oid in self.vlan_oid_name_map:
331+
return self.vlan_oid_name_map[oid]
291332

292333
return self.if_alias_map[self.oid_name_map[oid]]
293334

@@ -298,18 +339,46 @@ def _get_counter(self, oid, table_name):
298339
:return: the counter for the respective sub_id/table.
299340
"""
300341
# Enum.name or table_name = 'name_of_the_table'
342+
# Example:
343+
# table_name = <DbTables.SAI_PORT_STAT_IF_OUT_ERRORS: 20>
344+
# _table_name = 'SAI_PORT_STAT_IF_OUT_ERRORS'
301345
_table_name = getattr(table_name, 'name', table_name)
302346

303347
try:
304348
counter_value = self.if_counters[oid][_table_name]
305349
# truncate to 32-bit counter (database implements 64-bit counters)
306-
counter_value = int(counter_value) & 0x00000000ffffffff
350+
counter_value = counter_value & 0x00000000ffffffff
307351
# done!
308352
return counter_value
309353
except KeyError as e:
310354
mibs.logger.warning("SyncD 'COUNTERS_DB' missing attribute '{}'.".format(e))
311355
return None
312356

357+
def aggregate_counters(self):
358+
"""
359+
For ports with l3 router interfaces l3 drops may be counted separately (RIF counters)
360+
add l3 drops to l2 drop counters cache according to mapping
361+
362+
For l3vlan map l3 counters to l2 counters
363+
"""
364+
for rif_sai_id, port_sai_id in self.rif_port_map.items():
365+
if port_sai_id in self.if_id_map:
366+
port_idx = mibs.get_index_from_str(self.if_id_map[port_sai_id])
367+
for port_counter_name, rif_counter_name in mibs.RIF_DROPS_AGGR_MAP.items():
368+
self.if_counters[port_idx][port_counter_name] = \
369+
self.if_counters[port_idx][port_counter_name] + \
370+
self.rif_counters[rif_sai_id][rif_counter_name]
371+
372+
for vlan_sai_id, vlan_name in self.vlan_name_map.items():
373+
for port_counter_name, rif_counter_name in mibs.RIF_COUNTERS_AGGR_MAP.items():
374+
vlan_idx = mibs.get_index_from_str(vlan_name)
375+
vlan_rif_counters = self.rif_counters[vlan_sai_id]
376+
if rif_counter_name in vlan_rif_counters:
377+
self.if_counters.setdefault(vlan_idx, {})
378+
self.if_counters[vlan_idx][port_counter_name] = \
379+
vlan_rif_counters[rif_counter_name]
380+
381+
313382
def get_counter(self, sub_id, table_name):
314383
"""
315384
:param sub_id: The 1-based sub-identifier query.
@@ -327,9 +396,40 @@ def get_counter(self, sub_id, table_name):
327396
return 0
328397
elif oid in self.oid_lag_name_map:
329398
counter_value = 0
399+
# Sum the values of this counter for all ports in the LAG.
400+
# Example:
401+
# table_name = <DbTables.SAI_PORT_STAT_IF_OUT_ERRORS: 20>
402+
# oid = 1001
403+
# self.oid_lag_name_map = {1001: 'PortChannel01', 1002: 'PortChannel02', 1003: 'PortChannel03'}
404+
# self.oid_lag_name_map[oid] = 'PortChannel01'
405+
# self.lag_name_if_name_map = {'PortChannel01': ['Ethernet112'], 'PortChannel02': ['Ethernet116'], 'PortChannel03': ['Ethernet120']}
406+
# self.lag_name_if_name_map['PortChannel01'] = ['Ethernet112']
407+
# mibs.get_index_from_str('Ethernet112') = 113 (because Ethernet N = N + 1)
408+
# self._get_counter retrieves the counter per oid and table.
330409
for lag_member in self.lag_name_if_name_map[self.oid_lag_name_map[oid]]:
331410
counter_value += self._get_counter(mibs.get_index_from_str(lag_member), table_name)
332-
411+
# Check if we need to add a router interface count.
412+
# Example:
413+
# self.lag_sai_map = {'PortChannel01': '2000000000006', 'PortChannel02': '2000000000005', 'PortChannel03': '2000000000004'}
414+
# self.port_rif_map = {'2000000000006': '6000000000006', '2000000000005': '6000000000005', '2000000000004': '6000000000004'}
415+
# self.rif_port_map = {'6000000000006': '2000000000006', '6000000000005': '2000000000005', '6000000000004': '2000000000004'}
416+
# self.lag_sai_map['PortChannel01'] = '2000000000006'
417+
# self.port_rif_map['2000000000006'] = '6000000000006'
418+
sai_lag_id = self.lag_sai_map[self.oid_lag_name_map[oid]]
419+
sai_lag_rif_id = self.port_rif_map[sai_lag_id]
420+
if sai_lag_rif_id in self.rif_port_map:
421+
# Extract the 'name' part of 'table_name'.
422+
# Example:
423+
# table_name = <DbTables.SAI_PORT_STAT_IF_OUT_ERRORS: 20>
424+
# _table_name = 'SAI_PORT_STAT_IF_OUT_ERRORS'
425+
table_name = getattr(table_name, 'name', table_name)
426+
# Find rif counter table if applicable and add the count for this table.
427+
# Example:
428+
# 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'}
429+
# self.rif_counters['6000000000006'] = {'SAI_ROUTER_INTERFACE_STAT_IN_PACKETS': 6, ... 'SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_PACKETS': 6, ...}
430+
if table_name in mibs.RIF_DROPS_AGGR_MAP:
431+
rif_table_name = mibs.RIF_DROPS_AGGR_MAP[table_name]
432+
counter_value += self.rif_counters[sai_lag_rif_id].get(rif_table_name, 0)
333433
# truncate to 32-bit counter
334434
return counter_value & 0x00000000ffffffff
335435
else:
@@ -359,6 +459,8 @@ def _get_if_entry(self, sub_id):
359459
elif oid in self.mgmt_oid_name_map:
360460
if_table = mibs.mgmt_if_entry_table(self.mgmt_oid_name_map[oid])
361461
db = mibs.CONFIG_DB
462+
elif oid in self.vlan_oid_name_map:
463+
if_table = mibs.vlan_entry_table(self.vlan_oid_name_map[oid])
362464
elif oid in self.oid_name_map:
363465
if_table = mibs.if_entry_table(self.oid_name_map[oid])
364466
else:
@@ -463,6 +565,7 @@ def get_if_type(self, sub_id):
463565
464566
ethernetCsmacd(6), -- for all ethernet-like interfaces,
465567
-- regardless of speed, as per RFC3635
568+
l3ipvlan(136) -- Layer 3 Virtual LAN using IP
466569
ieee8023adLag(161) -- IEEE 802.3ad Link Aggregate
467570
"""
468571
oid = self.get_oid(sub_id)
@@ -471,6 +574,8 @@ def get_if_type(self, sub_id):
471574

472575
if oid in self.oid_lag_name_map:
473576
return IfTypes.ieee8023adLag
577+
elif oid in self.vlan_oid_name_map:
578+
return IfTypes.l3ipvlan
474579
else:
475580
return IfTypes.ethernetCsmacd
476581

0 commit comments

Comments
 (0)