diff --git a/setup.py b/setup.py index 6dda17f32..a17c6bc6e 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ 'mockredispy>=2.9.3', 'pytest', 'pytest-cov', + 'bitstring>=3.1.6', ] high_performance_deps = [ diff --git a/src/sonic_ax_impl/main.py b/src/sonic_ax_impl/main.py index 672a70171..47eeb15f3 100644 --- a/src/sonic_ax_impl/main.py +++ b/src/sonic_ax_impl/main.py @@ -11,7 +11,7 @@ import ax_interface from sonic_ax_impl.mibs import ieee802_1ab from . import logger -from .mibs.ietf import rfc1213, rfc2737, rfc2863, rfc3433, rfc4292, rfc4363 +from .mibs.ietf import rfc1213, rfc2737, rfc2863, rfc3433, rfc4292, rfc4363, rfc4188 from .mibs.vendor import dell, cisco # Background task update frequency ( in seconds ) @@ -29,7 +29,12 @@ class SonicMIB( rfc3433.PhysicalSensorTableMIB, rfc2863.InterfaceMIBObjects, rfc4363.QBridgeMIBObjects, + rfc4363.Dot1qFdbMIBObjects, + rfc4363.Dot1qVlanCurrentMIBObjects, + rfc4363.Dot1qVlanStaticMIBObjects, + rfc4363.Dot1qPortVlanMIBObjects, rfc4292.IpCidrRouteTable, + rfc4188.Dot1dBaseMIB, ieee802_1ab.LLDPLocalSystemData, ieee802_1ab.LLDPLocalSystemData.LLDPLocPortTable, ieee802_1ab.LLDPLocalSystemData.LLDPLocManAddrTable, diff --git a/src/sonic_ax_impl/mibs/ietf/rfc4188.py b/src/sonic_ax_impl/mibs/ietf/rfc4188.py new file mode 100644 index 000000000..9fed8f35d --- /dev/null +++ b/src/sonic_ax_impl/mibs/ietf/rfc4188.py @@ -0,0 +1,126 @@ +#import json +#import ipaddress +#from enum import unique, Enum + +#from swsssdk import port_util +from sonic_ax_impl import mibs +from sonic_ax_impl.mibs import Namespace +from ax_interface import MIBMeta, ValueType, MIBUpdater, SubtreeMIBEntry, MIBEntry +#from ax_interface.encodings import OctetString +#from ax_interface.util import mac_decimals, ip2tuple_v4 +from bisect import bisect_right +#import re + + +class Dot1dBaseTypeConst: + unknown = 1 + transparent = 2 + source_route = 3 + srt = 4 + +class Dot1dBaseUpdater(MIBUpdater): + def __init__(self): + super().__init__() + self.db_conn = Namespace.init_namespace_dbs() + self.dot1dbase_port_map = {} + self.dot1dbase_port_list = [] + self.dot1dbase_bridge_addr = None + self.dot1d_aging_time = 600 + + def reinit_data(self): + """ + Subclass update interface information + """ + Namespace.connect_all_dbs(self.db_conn, mibs.CONFIG_DB) + self.dot1dbase_bridge_addr = self.db_conn[0].get(mibs.CONFIG_DB, "DEVICE_METADATA|localhost", 'mac') + + def update_data(self): + """ + Update redis (caches config) + Pulls the table references for each vlan member. + """ + self.dot1dbase_port_map = {} + self.dot1dbase_port_list = [] + + fdb_aging_time = self.db_conn[0].get(mibs.CONFIG_DB, "SWITCH|switch", 'fdb_aging_time') + if fdb_aging_time: + self.dot1d_aging_time = int(fdb_aging_time) + else: + self.dot1d_aging_time = 600 + + vlanmem_entries = Namespace.dbs_keys(self.db_conn, mibs.CONFIG_DB, "VLAN_MEMBER|*") + if not vlanmem_entries: + return + + for vmem_entry in vlanmem_entries: + ifname = vmem_entry.split('|')[2] + if_index = mibs.get_index_from_str(ifname) + if if_index is None: + continue + self.dot1dbase_port_map[if_index-1] = if_index + + self.dot1dbase_port_list = sorted(self.dot1dbase_port_map.keys()) + self.dot1dbase_port_list = [(i,) for i in self.dot1dbase_port_list] + mibs.logger.debug('Port map entries : {}' .format(self.dot1dbase_port_map)) + mibs.logger.debug('Port list : {}' .format(self.dot1dbase_port_list)) + + def get_dot1dbase_bridge_addr(self): + return self.dot1dbase_bridge_addr + + def get_dot1d_base_num_ports(self): + return len(self.dot1dbase_port_map) + + def get_dot1d_base_type(self): + return Dot1dBaseTypeConst.transparent + + def get_dot1d_aging_time(self): + return self.dot1d_aging_time + + def get_dot1dbase_port(self, sub_id): + if sub_id: + if sub_id in self.dot1dbase_port_list: + return sub_id[0] + return + + def get_dot1dbase_port_ifindex(self, sub_id): + if sub_id: + return self.dot1dbase_port_map.get(sub_id[0], None) + + def get_dot1dbase_port_delay_discard(self, sub_id): + if sub_id: + return 0 + + def get_dot1dbase_port_mtu_discard(self, sub_id): + if sub_id: + return 0 + + def get_next(self, sub_id): + right = bisect_right(self.dot1dbase_port_list, sub_id) + if right == len(self.dot1dbase_port_list): + return None + + return self.dot1dbase_port_list[right] + +class Dot1dBaseMIB(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.17'): + """ + ' dot1dBase MIB' https://tools.ietf.org/html/rfc4188 + """ + + dot1dbase_updater = Dot1dBaseUpdater() + + # (subtree, value_type, callable_, *args, handler=None) + dot1dBaseBridgeAddress = MIBEntry('1.1.0', ValueType.OCTET_STRING, dot1dbase_updater.get_dot1dbase_bridge_addr) + dot1dBaseNumPorts = MIBEntry('1.2.0', ValueType.INTEGER, dot1dbase_updater.get_dot1d_base_num_ports) + dot1dBaseType = MIBEntry('1.3.0', ValueType.INTEGER, dot1dbase_updater.get_dot1d_base_type) + + dot1dBasePort = \ + SubtreeMIBEntry('1.4.1.1', dot1dbase_updater, ValueType.INTEGER, dot1dbase_updater.get_dot1dbase_port) + dot1dBasePortIfIndex = \ + SubtreeMIBEntry('1.4.1.2', dot1dbase_updater, ValueType.INTEGER, dot1dbase_updater.get_dot1dbase_port_ifindex) + dot1dBasePortDelayExceededDiscards = \ + SubtreeMIBEntry('1.4.1.4', dot1dbase_updater, ValueType.COUNTER_32, dot1dbase_updater.get_dot1dbase_port_delay_discard) + dot1dBasePortMtuExceededDiscards = \ + SubtreeMIBEntry('1.4.1.5', dot1dbase_updater, ValueType.COUNTER_32, dot1dbase_updater.get_dot1dbase_port_mtu_discard) + + dot1dTpAgingTime = MIBEntry('4.2.0', ValueType.INTEGER, dot1dbase_updater.get_dot1d_aging_time) + diff --git a/src/sonic_ax_impl/mibs/ietf/rfc4363.py b/src/sonic_ax_impl/mibs/ietf/rfc4363.py index 294e16f1d..22a8345a5 100644 --- a/src/sonic_ax_impl/mibs/ietf/rfc4363.py +++ b/src/sonic_ax_impl/mibs/ietf/rfc4363.py @@ -2,9 +2,19 @@ from sonic_ax_impl import mibs from sonic_ax_impl.mibs import Namespace -from ax_interface import MIBMeta, ValueType, MIBUpdater, SubtreeMIBEntry +from ax_interface import MIBMeta, ValueType, MIBUpdater, SubtreeMIBEntry, MIBEntry from ax_interface.util import mac_decimals from bisect import bisect_right +from bitstring import BitArray + +ZERO_STRING = '0' + +class TpFdbStatusConst: + other = 1 + invalid = 2 + learned = 3 + self = 4 + mgmt = 5 class FdbUpdater(MIBUpdater): def __init__(self): @@ -18,8 +28,12 @@ def __init__(self): self.sai_lag_map = {} self.vlanmac_ifindex_map = {} self.vlanmac_ifindex_list = [] + self.vlan_keys = [] self.if_bpid_map = {} self.bvid_vlan_map = {} + self.tp_fdb_status_map = {} + self.vlan_dynamic_count_map = {} + self.vlan_id_list = [] def fdb_vlanmac(self, fdb): if 'vlan' in fdb: @@ -38,6 +52,17 @@ def fdb_vlanmac(self, fdb): return None return (int(vlan_id),) + mac_decimals(fdb["mac"]) + def get_tp_fdb_status(self, ent, fdb): + #TODO:add code for self type + if 'SAI_FDB_ENTRY_ATTR_TYPE' not in ent: + return TpFdbStatusConst.invalid + ent_type = ent['SAI_FDB_ENTRY_ATTR_TYPE'] + if ent_type == 'SAI_FDB_ENTRY_TYPE_DYNAMIC': + status = TpFdbStatusConst.learned + else: + status = TpFdbStatusConst.mgmt + return status + def reinit_data(self): """ Subclass update interface information @@ -66,6 +91,12 @@ def update_data(self): """ self.vlanmac_ifindex_map = {} self.vlanmac_ifindex_list = [] + self.vlan_dynamic_count_map = {} + self.vlan_id_list = [] + + # connect to config and get VLAN keys + self.vlan_keys = [] + self.vlan_keys = Namespace.dbs_keys(self.db_conn, mibs.CONFIG_DB, "VLAN|*") fdb_strings = Namespace.dbs_keys(self.db_conn, mibs.ASIC_DB, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:*") if not fdb_strings: @@ -79,7 +110,10 @@ def update_data(self): mibs.logger.error("SyncD 'ASIC_DB' includes invalid FDB_ENTRY '{}': {}.".format(fdb_str, e)) continue - ent = Namespace.dbs_get_all(self.db_conn, mibs.ASIC_DB, s, blocking=True) + try: + ent = Namespace.dbs_get_all(self.db_conn, mibs.ASIC_DB, s, blocking=False) + except Exception as e: + continue # Example output: oid:0x3a000000000608 bridge_port_id = ent["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"][6:] if bridge_port_id not in self.if_bpid_map: @@ -98,13 +132,48 @@ def update_data(self): if not vlanmac: mibs.logger.error("SyncD 'ASIC_DB' includes invalid FDB_ENTRY '{}': failed in fdb_vlanmac().".format(fdb_str)) continue + vlanid = vlanmac[0] + self.tp_fdb_status_map[vlanmac] = self.get_tp_fdb_status(ent, fdb) self.vlanmac_ifindex_map[vlanmac] = port_index self.vlanmac_ifindex_list.append(vlanmac) + + try: + ent_type = ent["SAI_FDB_ENTRY_ATTR_TYPE"] + if ent_type == 'SAI_FDB_ENTRY_TYPE_DYNAMIC': + if vlanid in self.vlan_dynamic_count_map: + self.vlan_dynamic_count_map[vlanid] = self.vlan_dynamic_count_map[vlanid] + 1 + else: + self.vlan_dynamic_count_map[vlanid] = 1 + self.vlan_id_list.append(vlanid) + except Exception as e: + continue + self.vlan_id_list.sort() + self.vlan_id_list = [(i,) for i in self.vlan_id_list] + mibs.logger.debug('vlan_dynamic_count_map={}'.format(self.vlan_dynamic_count_map)) + self.vlanmac_ifindex_list.sort() def fdb_ifindex(self, sub_id): return self.vlanmac_ifindex_map.get(sub_id, None) + def fdb_status(self, sub_id): + return self.tp_fdb_status_map.get(sub_id, None) + + def get_vlan_version(self): + return 2 + + def get_MaxVlanId(self): + return 4094 + + def get_MaxSupportedVlans(self): + return 4094 + + def get_dot1qNumVlans(self): + if self.vlan_keys: + return len(self.vlan_keys) + else: + return 0 + def get_next(self, sub_id): right = bisect_right(self.vlanmac_ifindex_list, sub_id) if right >= len(self.vlanmac_ifindex_list): @@ -119,5 +188,351 @@ class QBridgeMIBObjects(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.17.7.1'): fdb_updater = FdbUpdater() + dot1qVlanVersionNumber = \ + MIBEntry('1.1.0', ValueType.INTEGER, fdb_updater.get_vlan_version) + + dot1qMaxVlanId = \ + MIBEntry('1.2.0', ValueType.INTEGER, fdb_updater.get_MaxVlanId) + + dot1qMaxSupportedVlans = \ + MIBEntry('1.3.0', ValueType.GAUGE_32, fdb_updater.get_MaxSupportedVlans) + + dot1qNumVlans = \ + MIBEntry('1.4.0', ValueType.GAUGE_32, fdb_updater.get_dot1qNumVlans) + dot1qTpFdbPort = \ SubtreeMIBEntry('2.2.1.2', fdb_updater, ValueType.INTEGER, fdb_updater.fdb_ifindex) + + dot1qTpFdbStatus = \ + SubtreeMIBEntry('2.2.1.3', fdb_updater, ValueType.INTEGER, fdb_updater.fdb_status) + +class FdbTableUpdater(MIBUpdater): + def __init__(self): + super().__init__() + + self.vlan_dynamic_count_map = {} + self.vlan_id_list = [] + + def update_data(self): + """ + Update redis (caches config) + Pulls the table references for each interface. + """ + fdb_data = QBridgeMIBObjects.fdb_updater + self.vlan_dynamic_count_map = fdb_data.vlan_dynamic_count_map + self.vlan_id_list = fdb_data.vlan_id_list + + def fdb_dynamic_count(self, sub_id): + if sub_id: + if sub_id[0] in self.vlan_dynamic_count_map: + return self.vlan_dynamic_count_map.get(sub_id[0], None) + else: + return 0 + + def get_next(self, sub_id): + right = bisect_right(self.vlan_id_list, sub_id) + if right >= len(self.vlan_id_list): + return None + + return self.vlan_id_list[right] + +class Dot1qFdbMIBObjects(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.17.7.1'): + """ + 'Forwarding Database' https://tools.ietf.org/html/rfc4363 + """ + + fdb_table_updater = FdbTableUpdater() + + dot1qFdbDynamicCount = \ + SubtreeMIBEntry('2.1.1.2', fdb_table_updater, ValueType.COUNTER_32, fdb_table_updater.fdb_dynamic_count) + +class Dot1qVlanStatusConst: + other = 1 + permanent = 2 + dynamicGvrp = 3 + +class Dot1qVlanCurrentUpdater(MIBUpdater): + def __init__(self): + super().__init__() + self.db_conn = Namespace.init_namespace_dbs() + self.vlan_egress_ports_map = {} + self.vlan_untag_ports_map = {} + self.vlan_static_egress_ports_map = {} + self.vlan_static_untag_ports_map = {} + self.vlan_time_index_list = [] + self.vlan_name_map = {} + self.vlan_max_port = {} + self.vlan_index_list = [] + self.dot1q_pvid = {} + self.dot1q_port_vlan_list = [] + + def update_data(self): + """ + Update redis (caches config) + Pulls the table references for each interface. + """ + self.vlan_egress_ports_map = {} + self.vlan_untag_ports_map = {} + self.vlan_static_egress_ports_map = {} + self.vlan_static_untag_ports_map = {} + self.vlan_time_index_list = [] + self.vlan_name_map = {} + self.vlan_max_port = {} + self.vlan_index_list = [] + self.dot1q_pvid = {} + self.dot1q_port_vlan_list = [] + + Namespace.connect_all_dbs(self.db_conn, mibs.CONFIG_DB) + vlan_entries = Namespace.dbs_keys(self.db_conn, mibs.CONFIG_DB, "VLAN|*") + if not vlan_entries: + return + for vlan_entry in vlan_entries: + vlan = vlan_entry.split('|')[1] + vlan_id = int(vlan.strip('Vlan')) + vlan_index = (0, vlan_id) + self.vlan_time_index_list.append(vlan_index) + self.vlan_index_list.append(vlan_id) + self.vlan_name_map[vlan_id] = vlan + self.vlan_index_list.sort() + self.vlan_index_list = [(i,) for i in self.vlan_index_list] + + vlanmem_entries = Namespace.dbs_keys(self.db_conn, mibs.CONFIG_DB, "VLAN_MEMBER|*") + if not vlanmem_entries: + return + for vmem_entry in vlanmem_entries: + vlan = vmem_entry.split('|')[1] + vlan_id = int(vlan.strip('Vlan')) + vlan_index = (0, vlan_id) + port_name = vmem_entry.split('|')[2] + if 'Ethernet' in port_name: + port = port_name[len('Ethernet'):] + elif 'PortChannel' in port_name: + port = port_name[len('PortChannel'):] + else: + continue + + port = int(port) + tag_type = self.db_conn[0].get(mibs.CONFIG_DB, vmem_entry, 'tagging_mode') + if tag_type: + if vlan_index in self.vlan_egress_ports_map: + portBitArray = self.vlan_egress_ports_map[vlan_index] + portBitArray[port] = True + if port > self.vlan_max_port[vlan_id]: + self.vlan_max_port[vlan_id] = port + self.vlan_egress_ports_map[vlan_index] = portBitArray + self.vlan_static_egress_ports_map[vlan_id] = portBitArray + else: + portBitArray = BitArray(4096) + portBitArray[port] = True + self.vlan_max_port[vlan_id] = port + self.vlan_egress_ports_map[vlan_index] = portBitArray + self.vlan_static_egress_ports_map[vlan_id] = portBitArray + if tag_type == 'untagged': + if vlan_index in self.vlan_untag_ports_map: + portBitArray = self.vlan_egress_ports_map[vlan_index] + portBitArray[port] = True + if port > self.vlan_max_port[vlan_id]: + self.vlan_max_port[vlan_id] = port + self.vlan_untag_ports_map[vlan_index] = portBitArray + self.vlan_static_untag_ports_map[vlan_id] = portBitArray + else: + portBitArray = BitArray(4096) + portBitArray[port] = True + self.vlan_max_port[vlan_id] = port + self.vlan_untag_ports_map[vlan_index] = portBitArray + self.vlan_static_untag_ports_map[vlan_id] = portBitArray + + if_index = mibs.get_index_from_str(port_name) + if tag_type == 'untagged': + self.dot1q_pvid[if_index-1] = vlan_id + else: + self.dot1q_pvid[if_index-1] = 0 + self.dot1q_port_vlan_list = sorted(self.dot1q_pvid.keys()) + self.dot1q_port_vlan_list = [(i,) for i in self.dot1q_port_vlan_list] + self.vlan_time_index_list.sort() + mibs.logger.debug('vlan_egress_ports_map={}'.format(self.vlan_egress_ports_map)) + mibs.logger.debug('vlan_untag_ports_map={}'.format(self.vlan_untag_ports_map)) + mibs.logger.debug('vlan_time_index_list={}'.format(self.vlan_time_index_list)) + mibs.logger.debug('vlan_index_list={}'.format(self.vlan_index_list)) + mibs.logger.debug('vlan_name_map={}'.format(self.vlan_name_map)) + + def dot1q_vlan_current_egress_ports(self, sub_id): + if sub_id: + if sub_id in self.vlan_egress_ports_map: + data = self.vlan_egress_ports_map.get(sub_id, None) + if data: + byts = data.tobytes() + bs = byts[0:self.vlan_max_port[sub_id[1]]//8 +2] + bs_hex = " ".join([format(i, '02x') for i in bs]) + return bs_hex + else: + return ZERO_STRING + + def dot1q_vlan_current_untag_ports(self, sub_id): + if sub_id: + if sub_id in self.vlan_untag_ports_map: + data = self.vlan_untag_ports_map.get(sub_id, None) + if data: + byts = data.tobytes() + bs = byts[0:self.vlan_max_port[sub_id[1]]//8 +2] + bs_hex = " ".join([format(i, '02x') for i in bs]) + return bs_hex + else: + return ZERO_STRING + + def dot1q_vlan_status(self, sub_id): + if sub_id: + return Dot1qVlanStatusConst.permanent + + def get_vlan_deletes(self): + return 0 + + def get_next(self, sub_id): + right = bisect_right(self.vlan_time_index_list, sub_id) + if right == len(self.vlan_time_index_list): + return None + + return self.vlan_time_index_list[right] + +class Dot1qVlanCurrentMIBObjects(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.17.7.1'): + """ + 'Forwarding Database' https://tools.ietf.org/html/rfc4363 + """ + + fdb_vlan_updater = Dot1qVlanCurrentUpdater() + + dot1qVlanNumDeletes = \ + MIBEntry('4.1.0', ValueType.COUNTER_32, fdb_vlan_updater.get_vlan_deletes) + + dot1qVlanCurrentEgressPorts = \ + SubtreeMIBEntry('4.2.1.4', fdb_vlan_updater, ValueType.OCTET_STRING, fdb_vlan_updater.dot1q_vlan_current_egress_ports) + dot1qVlanCurrentUntaggedPorts = \ + SubtreeMIBEntry('4.2.1.5', fdb_vlan_updater, ValueType.OCTET_STRING, fdb_vlan_updater.dot1q_vlan_current_untag_ports) + dot1qVlanStatus = \ + SubtreeMIBEntry('4.2.1.6', fdb_vlan_updater, ValueType.INTEGER, fdb_vlan_updater.dot1q_vlan_status) + +class Dot1qVlanRowStatusConst: + active = 1 + notInService = 2 + +class Dot1qVlanStaticUpdater(Dot1qVlanCurrentUpdater): + def __init__(self): + super().__init__() + self.vlan_static_egress_ports_map = {} + self.vlan_static_untag_ports_map = {} + self.vlan_name_map = {} + self.vlan_max_port = {} + self.vlan_index_list = [] + + def update_data(self): + """ + Update redis (caches config) + Pulls the table references for each interface. + """ + fdb_vlan = Dot1qVlanCurrentMIBObjects.fdb_vlan_updater + self.vlan_static_egress_ports_map = fdb_vlan.vlan_static_egress_ports_map + self.vlan_static_untag_ports_map = fdb_vlan.vlan_static_untag_ports_map + self.vlan_name_map = fdb_vlan.vlan_name_map + self.vlan_max_port = fdb_vlan.vlan_max_port + self.vlan_index_list = fdb_vlan.vlan_index_list + + def dot1q_vlan_static_egress_ports(self, sub_id): + if sub_id: + if sub_id[0] in self.vlan_static_egress_ports_map: + data = self.vlan_static_egress_ports_map.get(sub_id[0], None) + if data: + byts = data.tobytes() + bs = byts[0:self.vlan_max_port[sub_id[0]]//8 +2] + bs_hex = " ".join([format(i, '02x') for i in bs]) + return bs_hex + else: + return '0' + + def dot1q_vlan_static_untag_ports(self, sub_id): + if sub_id: + if sub_id[0] in self.vlan_static_untag_ports_map: + data = self.vlan_static_untag_ports_map.get(sub_id[0], None) + if data: + byts = data.tobytes() + bs = byts[0:self.vlan_max_port[sub_id[0]]//8 +2] + bs_hex = " ".join([format(i, '02x') for i in bs]) + return bs_hex + else: + return '0' + + def dot1q_vlan_static_name(self, sub_id): + if sub_id: + if sub_id[0] in self.vlan_name_map: + return self.vlan_name_map.get(sub_id[0], None) + + def dot1q_vlan_static_row_status(self, sub_id): + if sub_id: + return Dot1qVlanRowStatusConst.active + + def get_next(self, sub_id): + right = bisect_right(self.vlan_index_list, sub_id) + if right == len(self.vlan_index_list): + return None + + return self.vlan_index_list[right] + +class Dot1qVlanStaticMIBObjects(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.17.7.1'): + """ + 'Forwarding Database' https://tools.ietf.org/html/rfc4363 + """ + + fdb_vlan_updater = Dot1qVlanStaticUpdater() + + dot1qVlanStaticName = \ + SubtreeMIBEntry('4.3.1.1', fdb_vlan_updater, ValueType.OCTET_STRING, fdb_vlan_updater.dot1q_vlan_static_name) + dot1qVlanStaticEgressPorts = \ + SubtreeMIBEntry('4.3.1.2', fdb_vlan_updater, ValueType.OCTET_STRING, fdb_vlan_updater.dot1q_vlan_static_egress_ports) + dot1qVlanStaticUntaggedPorts = \ + SubtreeMIBEntry('4.3.1.4', fdb_vlan_updater, ValueType.OCTET_STRING, fdb_vlan_updater.dot1q_vlan_static_untag_ports) + dot1qVlanStaticRowStatus = \ + SubtreeMIBEntry('4.3.1.5', fdb_vlan_updater, ValueType.INTEGER, fdb_vlan_updater.dot1q_vlan_static_row_status) + + +class Dot1qPortVlanUpdater(MIBUpdater): + def __init__(self): + super().__init__() + self.dot1q_pvid = {} + self.dot1q_port_vlan_list = [] + + def update_data(self): + """ + Update redis (caches config) + Pulls the table references for each interface. + """ + port_updater = Dot1qVlanCurrentMIBObjects.fdb_vlan_updater + self.dot1q_pvid = port_updater.dot1q_pvid + self.dot1q_port_vlan_list = port_updater.dot1q_port_vlan_list + + def get_dot1dbase_port(self, sub_id): + if sub_id: + if sub_id in self.dot1q_port_vlan_list: + return sub_id[0] + return + + def get_dot1q_pvid(self, sub_id): + if sub_id: + return self.dot1q_pvid.get(sub_id[0], None) + + def get_next(self, sub_id): + right = bisect_right(self.dot1q_port_vlan_list, sub_id) + if right == len(self.dot1q_port_vlan_list): + return None + + return self.dot1q_port_vlan_list[right] + +class Dot1qPortVlanMIBObjects(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.17.7.1'): + """ + 'Forwarding Database' https://tools.ietf.org/html/rfc4363 + """ + + fdb_vlan_updater = Dot1qPortVlanUpdater() + + dot1qPvid = \ + SubtreeMIBEntry('4.5.1.1', fdb_vlan_updater, ValueType.INTEGER, fdb_vlan_updater.get_dot1q_pvid) + + diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 2d9dd54e0..5652ae46d 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1,4 +1,22 @@ { + "SWITCH|switch": { + "fdb_aging_time": "300" + }, + "VLAN|Vlan10": { + "vlanid": "10" + }, + "VLAN|Vlan20": { + "vlanid": "20" + }, + "VLAN_MEMBER|Vlan10|Ethernet8": { + "tagging_mode": "untagged" + }, + "VLAN_MEMBER|Vlan20|Ethernet20": { + "tagging_mode": "untagged" + }, + "VLAN_MEMBER|Vlan20|PortChannel10": { + "tagging_mode": "tagged" + }, "MGMT_PORT|eth0": { "description": "snowflake", "speed": 1000 @@ -16,6 +34,7 @@ "speed": 1000 }, "DEVICE_METADATA|localhost": { + "mac": "1a:2b:3c:4d:5e:6f", "chassis_serial_number": "SAMPLETESTSN", "hostname" : "test_hostname" } diff --git a/tests/test_fdb.py b/tests/test_fdb.py index dca9b10f0..d6efd9700 100644 --- a/tests/test_fdb.py +++ b/tests/test_fdb.py @@ -114,7 +114,7 @@ def test_getnextpdu_empty(self): get_pdu = GetNextPDU( header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), oids=( - ObjectIdentifier(20, 0, 0, 0, (1, 3, 6, 1, 2, 1, 17, 7, 1, 2, 2, 1, 3)), + ObjectIdentifier(20, 0, 0, 0, (1, 3, 6, 1, 2, 1, 17, 7, 1, 2, 2, 1, 4)), ) ) diff --git a/tests/test_rfc4188.py b/tests/test_rfc4188.py new file mode 100644 index 000000000..53b0bee49 --- /dev/null +++ b/tests/test_rfc4188.py @@ -0,0 +1,228 @@ +import os +import sys + +modules_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, os.path.join(modules_path, 'src')) + +from unittest import TestCase + +# noinspection PyUnresolvedReferences +import tests.mock_tables.dbconnector + +from ax_interface.mib import MIBTable +from ax_interface.pdu import PDUHeader +from ax_interface.pdu_implementations import GetPDU, GetNextPDU +from ax_interface import ValueType +from ax_interface.encodings import ObjectIdentifier +from ax_interface.constants import PduTypes +from sonic_ax_impl.mibs.ietf import rfc4188 +from sonic_ax_impl.main import SonicMIB + +class TestSonicMIB(TestCase): + @classmethod + def setUpClass(cls): + #cls.lut = MIBTable(SonicMIB) + cls.lut = MIBTable(rfc4188.Dot1dBaseMIB) + for updater in cls.lut.updater_instances: + updater.update_data() + updater.reinit_data() + updater.update_data() + + + def test_getbridge_addr(self): + oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 1,0)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.OCTET_STRING) + self.assertEqual(str(value0.name), str(oid)) + self.assertEqual(str(value0.data), '1a:2b:3c:4d:5e:6f') + + def test_getnextbridge_addr(self): + oid = ObjectIdentifier(9, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 1)) + expect_oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 1,0)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.OCTET_STRING) + self.assertEqual(str(value0.name), str(expect_oid)) + self.assertEqual(str(value0.data), '1a:2b:3c:4d:5e:6f') + + def test_getNumPorts(self): + oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 2,0)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(oid)) + self.assertEqual(value0.data, 3) + + def test_getnextNumPorts(self): + oid = ObjectIdentifier(9, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 2)) + expect_oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 2,0)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(expect_oid)) + self.assertEqual(value0.data, 3) + + def test_getBaseType(self): + oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 3,0)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(oid)) + self.assertEqual(value0.data, 2) + + def test_getnextBaseType(self): + oid = ObjectIdentifier(9, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 3)) + expect_oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 3,0)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(expect_oid)) + self.assertEqual(value0.data, 2) + + def test_getAgingTime(self): + oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 4, 2,0)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(oid)) + self.assertEqual(value0.data, 300) + + def test_getnextAgingTime(self): + oid = ObjectIdentifier(9, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 4, 2)) + expect_oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 4, 2,0)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(expect_oid)) + self.assertEqual(value0.data, 300) + + def test_getBasePort(self): + oid = ObjectIdentifier(12, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 4, 1, 1, 8)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(oid)) + self.assertEqual(value0.data, 8) + + def test_getnextBasePort(self): + oid = ObjectIdentifier(12, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 4, 1, 1)) + expect_oid = ObjectIdentifier(12, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 4, 1, 1, 8)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(expect_oid)) + self.assertEqual(value0.data, 8) + + def test_getBasePortIfIndex(self): + oid = ObjectIdentifier(12, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 4, 1, 2, 8)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(oid)) + self.assertEqual(value0.data, 9) + + def test_getnextBasePortIfIndex(self): + oid = ObjectIdentifier(12, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 4, 1, 2)) + expect_oid = ObjectIdentifier(12, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 4, 1, 2, 8)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET_NEXT, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(expect_oid)) + self.assertEqual(value0.data, 9) diff --git a/tests/test_rfc4363.py b/tests/test_rfc4363.py new file mode 100644 index 000000000..c9d095f54 --- /dev/null +++ b/tests/test_rfc4363.py @@ -0,0 +1,174 @@ +import os +import sys + +modules_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, os.path.join(modules_path, 'src')) + +from unittest import TestCase + +# noinspection PyUnresolvedReferences +import tests.mock_tables.dbconnector + +from ax_interface.mib import MIBTable +from ax_interface.pdu import PDUHeader +from ax_interface.pdu_implementations import GetPDU, GetNextPDU +from ax_interface import ValueType +from ax_interface.encodings import ObjectIdentifier +from ax_interface.constants import PduTypes +from sonic_ax_impl.mibs.ietf import rfc4363 +from sonic_ax_impl.main import SonicMIB + +class Dot1qMIB( + rfc4363.QBridgeMIBObjects, + rfc4363.Dot1qFdbMIBObjects, + rfc4363.Dot1qVlanCurrentMIBObjects, + rfc4363.Dot1qVlanStaticMIBObjects, + rfc4363.Dot1qPortVlanMIBObjects, +): + """ + Class for dot1q mib objects + """ + +class TestSonicMIB(TestCase): + @classmethod + def setUpClass(cls): + cls.lut = MIBTable(Dot1qMIB) + for updater in cls.lut.updater_instances: + updater.update_data() + updater.reinit_data() + updater.update_data() + + + def getpdu(self, objid, value_type, expected_value): + #oid = ObjectIdentifier(10, 0, 1, 0, (1, 3, 6, 1, 2, 1, 17, 1, 1,0)) + oid = ObjectIdentifier(len(objid), 0, 0, 0, objid) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + #self.assertEqual(value0.value_type_, ValueType.OCTET_STRING) + self.assertEqual(value0.type_, value_type) + self.assertEqual(str(value0.name), str(oid)) + if expected_value is None: + return + if value0.type_ == ValueType.OCTET_STRING: + self.assertEqual(str(value0.data), expected_value) + else: + self.assertEqual(value0.data, expected_value) + + def getnextpdu(self, objid, value_type, expected_value, expected_objid): + oid = ObjectIdentifier(len(objid), 0, 0, 0, objid) + expect_oid = ObjectIdentifier(10, 0, 0, 0, expected_objid) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, value_type) + self.assertEqual(str(value0.name), str(expect_oid)) + if value0.type_ == ValueType.OCTET_STRING: + self.assertEqual(str(value0.data), expected_value) + else: + self.assertEqual(value0.data, expected_value) + + def test_get_vlan_version(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 1, 0),ValueType.INTEGER, 2) + + def test_getnext_vlan_version(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 1),ValueType.INTEGER, 2, (1, 3, 6, 1, 2, 1, 17, 7, 1, 1,1,0)) + + def test_get_max_vlanid(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 2, 0),ValueType.INTEGER, 4094) + + def test_getnext_max_vlanid(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 2),ValueType.INTEGER, 4094, (1, 3, 6, 1, 2, 1, 17, 7, 1, 1,2,0)) + + def test_get_max_vlans(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 3, 0),ValueType.GAUGE_32, 4094) + + def test_getnext_max_vlans(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 3),ValueType.GAUGE_32, 4094, (1, 3, 6, 1, 2, 1, 17, 7, 1, 1,3,0)) + + def test_get_num_vlans(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 4, 0),ValueType.GAUGE_32, 2) + + def test_getnext_num_vlans(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 1, 4),ValueType.GAUGE_32, 2, (1, 3, 6, 1, 2, 1, 17, 7, 1, 1,4,0)) + + def test_get_fdb_status(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 2, 2, 1, 3, 1000, 124, 254, 144, 128, 159, 4),ValueType.INTEGER, 3) + + def test_getnext_fdb_status(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 2, 2, 1,3),ValueType.INTEGER, 3, (1, 3, 6, 1, 2, 1, 17, 7, 1, 2,2,1,3,102, 124, 254, 144, 128, 159, 6)) + + def test_get_fdb_dynamic_count(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 2, 1, 1, 2, 1000), ValueType.COUNTER_32, 2) + + def test_getnext_fdb_dynamic_count(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 2, 1, 1,2),ValueType.COUNTER_32, 1, (1, 3, 6, 1, 2, 1, 17, 7, 1, 2,1,1,2,102 )) + + def test_get_vlan_deletes(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 1, 0),ValueType.COUNTER_32, 0) + + def test_getnext_vlan_deletes(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 1),ValueType.COUNTER_32, 0, (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,1,0)) + + def test_get_current_egress_ports(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 2, 1, 4, 0, 20),ValueType.OCTET_STRING, '00 20 08 00') + + def test_getnext_current_egress_ports(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 2, 1,4),ValueType.OCTET_STRING, '00 80 00', (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,2,1,4,0,10)) + + def test_get_current_untagged_ports(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 2, 1, 5, 0, 20),ValueType.OCTET_STRING, '00 00 08 00') + + def test_getnext_current_untagged_ports(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 2, 1,5),ValueType.OCTET_STRING, '00 80 00' , (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,2,1,5,0,10)) + + def test_get_valn_status(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 2, 1, 6, 0, 20),ValueType.INTEGER, 2) + + def test_getnext_valn_status(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 2, 1,6),ValueType.INTEGER, 2, (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,2,1,6,0,10)) + + def test_get_valn_name(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1, 1, 20),ValueType.OCTET_STRING, 'Vlan20') + + def test_getnext_valn_name(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1,1),ValueType.OCTET_STRING, 'Vlan10', (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,3,1,1,10)) + + def test_get_static_egress_ports(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1, 2, 20),ValueType.OCTET_STRING, '00 20 08 00') + + def test_getnext_static_egress_ports(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1,2),ValueType.OCTET_STRING, '00 80 00', (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,3,1,2,10)) + + def test_get_static_untagged_ports(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1, 4, 20),ValueType.OCTET_STRING, '00 00 08 00') + + def test_getnext_static_untagged_ports(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1,4),ValueType.OCTET_STRING, '00 80 00', (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,3,1,4,10)) + + def test_get_static_row_status(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1, 5, 20),ValueType.INTEGER, 1) + + def test_getnext_static_row_status(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 3, 1,5),ValueType.INTEGER, 1, (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,3,1,5,10)) + + def test_get_dot1qPvid(self): + self.getpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 5, 1, 1, 20),ValueType.INTEGER, 20) + + def test_getnext_dot1qPvid(self): + self.getnextpdu((1, 3, 6, 1, 2, 1, 17, 7, 1, 4, 5, 1,1),ValueType.INTEGER, 10, (1, 3, 6, 1, 2, 1, 17, 7, 1, 4,5,1,1,8)) +