def _update_entity_cache(self, interface): """ Update data for single transceiver :param: interface: Interface name associated with transceiver """ # get interface from interface name ifindex = port_util.get_index_from_str(interface) if ifindex is None: # interface name invalid, skip this entry mibs.logger.warning("Invalid interface name in {} \ in STATE_DB, skipping".format(interface)) return # get transceiver information from transceiver info entry in STATE DB transceiver_info = Namespace.dbs_get_all( self.mib_updater.statedb, mibs.STATE_DB, mibs.transceiver_info_table(interface)) if not transceiver_info: return # update xcvr info from DB # use port's name as key for transceiver info entries sub_id = get_transceiver_sub_id(ifindex) # add interface to available OID list self.mib_updater.add_sub_id(sub_id) self._add_entity_related_oid(interface, sub_id) # physical class - network port self.mib_updater.set_phy_class(sub_id, PhysicalClass.PORT) # save values into cache sfp_type, hw_version, serial_number, mfg_name, model_name, replaceable = get_db_data( transceiver_info, XcvrInfoDB) self.mib_updater.set_phy_hw_ver(sub_id, hw_version) self.mib_updater.set_phy_serial_num(sub_id, serial_number) self.mib_updater.set_phy_mfg_name(sub_id, mfg_name) self.mib_updater.set_phy_model_name(sub_id, model_name) self.mib_updater.set_phy_contained_in(sub_id, CHASSIS_SUB_ID) self.mib_updater.set_phy_fru(sub_id, replaceable) # Relative position of SFP can be changed at run time. For example, plug out a normal cable SFP3 and plug in # a 1 split 4 SFP, the relative position of SFPs after SPF3 will change. In this case, it is hard to determine # the relative position for other SFP. According to RFC 2737, 'If the agent cannot determine the parent-relative position # for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned'. # See https://tools.ietf.org/html/rfc2737. self.mib_updater.set_phy_parent_relative_pos(sub_id, -1) ifalias = self.if_alias_map.get(interface, "") # generate a description for this transceiver self.mib_updater.set_phy_descr( sub_id, get_transceiver_description(sfp_type, ifalias)) self.mib_updater.set_phy_name(sub_id, interface) # update transceiver sensor cache self._update_transceiver_sensor_cache(interface, sub_id)
def _update_transceiver_cache(self, interface): """ Update data for single transceiver :param: interface: Interface name associated with transceiver """ # get interface from interface name ifindex = port_util.get_index_from_str(interface) # update xcvr info from DB # use port's name as key for transceiver info entries sub_id = mibs.get_transceiver_sub_id(ifindex) # add interface to available OID list insort_right(self.physical_entities, sub_id) # get transceiver information from transceiver info entry in STATE DB transceiver_info = Namespace.dbs_get_all( self.statedb, mibs.STATE_DB, mibs.transceiver_info_table(interface)) if not transceiver_info: return # physical class - network port self.physical_classes_map[sub_id] = PhysicalClass.PORT # save values into cache sfp_type, \ self.physical_hw_version_map[sub_id],\ self.physical_serial_number_map[sub_id], \ self.physical_mfg_name_map[sub_id], \ self.physical_model_name_map[sub_id] = get_transceiver_data(transceiver_info) ifalias = self.if_alias_map.get(interface.encode(), b"").decode() # generate a description for this transceiver self.physical_description_map[sub_id] = get_transceiver_description( sfp_type, ifalias) # update transceiver sensor cache self._update_transceiver_sensor_cache(interface)
class XcvrCacheUpdater(PhysicalEntityCacheUpdater): KEY_PATTERN = mibs.transceiver_info_table("*") def __init__(self, mib_updater): super(XcvrCacheUpdater, self).__init__(mib_updater) self.if_alias_map = {} def get_key_pattern(self): return XcvrCacheUpdater.KEY_PATTERN def reinit_data(self): # update interface maps _, self.if_alias_map, _, _ = \ Namespace.get_sync_d_from_all_namespace(mibs.init_sync_d_interface_tables, Namespace.init_namespace_dbs()) PhysicalEntityCacheUpdater.reinit_data(self) def _update_entity_cache(self, interface): """ Update data for single transceiver :param: interface: Interface name associated with transceiver """ # get interface from interface name ifindex = port_util.get_index_from_str(interface) if ifindex is None: # interface name invalid, skip this entry mibs.logger.warning("Invalid interface name in {} \ in STATE_DB, skipping".format(interface)) return # get transceiver information from transceiver info entry in STATE DB transceiver_info = Namespace.dbs_get_all( self.mib_updater.statedb, mibs.STATE_DB, mibs.transceiver_info_table(interface)) if not transceiver_info: return # update xcvr info from DB # use port's name as key for transceiver info entries sub_id = get_transceiver_sub_id(ifindex) # add interface to available OID list self.mib_updater.add_sub_id(sub_id) self._add_entity_related_oid(interface, sub_id) # physical class - network port self.mib_updater.set_phy_class(sub_id, PhysicalClass.PORT) # save values into cache sfp_type, hw_version, serial_number, mfg_name, model_name, replaceable = get_db_data( transceiver_info, XcvrInfoDB) self.mib_updater.set_phy_hw_ver(sub_id, hw_version) self.mib_updater.set_phy_serial_num(sub_id, serial_number) self.mib_updater.set_phy_mfg_name(sub_id, mfg_name) self.mib_updater.set_phy_model_name(sub_id, model_name) self.mib_updater.set_phy_contained_in(sub_id, CHASSIS_SUB_ID) self.mib_updater.set_phy_fru(sub_id, replaceable) # Relative position of SFP can be changed at run time. For example, plug out a normal cable SFP3 and plug in # a 1 split 4 SFP, the relative position of SFPs after SPF3 will change. In this case, it is hard to determine # the relative position for other SFP. According to RFC 2737, 'If the agent cannot determine the parent-relative position # for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned'. # See https://tools.ietf.org/html/rfc2737. self.mib_updater.set_phy_parent_relative_pos(sub_id, -1) ifalias = self.if_alias_map.get(interface, "") # generate a description for this transceiver self.mib_updater.set_phy_descr( sub_id, get_transceiver_description(sfp_type, ifalias)) self.mib_updater.set_phy_name(sub_id, interface) # update transceiver sensor cache self._update_transceiver_sensor_cache(interface, sub_id) def _update_transceiver_sensor_cache(self, interface, sub_id): """ Update sensor data for single transceiver :param: interface: Interface name associated with transceiver :param: sub_id: OID of transceiver """ ifalias = self.if_alias_map.get(interface, "") ifindex = port_util.get_index_from_str(interface) # get transceiver sensors from transceiver dom entry in STATE DB transceiver_dom_entry = Namespace.dbs_get_all( self.mib_updater.statedb, mibs.STATE_DB, mibs.transceiver_dom_table(interface)) if not transceiver_dom_entry: return # go over transceiver sensors for sensor in transceiver_dom_entry: if sensor not in XCVR_SENSOR_NAME_MAP: continue sensor_sub_id = get_transceiver_sensor_sub_id(ifindex, sensor) self._add_entity_related_oid(interface, sensor_sub_id) sensor_description = get_transceiver_sensor_description( sensor, ifalias) self.mib_updater.set_phy_class(sensor_sub_id, PhysicalClass.SENSOR) self.mib_updater.set_phy_descr(sensor_sub_id, sensor_description) self.mib_updater.set_phy_name(sensor_sub_id, sensor_description) self.mib_updater.set_phy_contained_in(sensor_sub_id, sub_id) self.mib_updater.set_phy_parent_relative_pos( sensor_sub_id, XCVR_SENSOR_INDEX_MAP[sensor]) self.mib_updater.set_phy_fru(sensor_sub_id, False) # add to available OIDs list self.mib_updater.add_sub_id(sensor_sub_id)
class PhysicalTableMIBUpdater(MIBUpdater): """ Updater class for physical table MIB """ CHASSIS_ID = 1 TRANSCEIVER_KEY_PATTERN = mibs.transceiver_info_table("*") def __init__(self): super().__init__() self.statedb = Namespace.init_namespace_dbs() Namespace.connect_all_dbs(self.statedb, mibs.STATE_DB) self.if_alias_map = {} # List of available sub OIDs. self.physical_entities = [] # Map sub ID to its data. self.physical_classes_map = {} self.physical_description_map = {} self.physical_hw_version_map = {} self.physical_serial_number_map = {} self.physical_mfg_name_map = {} self.physical_model_name_map = {} self.pubsub = [None] * len(self.statedb) def reinit_data(self): """ Re-initialize all data. """ # reinit cache self.physical_classes_map = {} self.physical_description_map = {} self.physical_hw_version_map = {} self.physical_serial_number_map = {} self.physical_mfg_name_map = {} self.physical_model_name_map = {} # update interface maps _, self.if_alias_map, _, _, _ = \ Namespace.init_namespace_sync_d_interface_tables(Namespace.init_namespace_dbs()) device_metadata = mibs.get_device_metadata(self.statedb[0]) chassis_sub_id = (self.CHASSIS_ID, ) self.physical_entities = [chassis_sub_id] if not device_metadata or not device_metadata.get(b"chassis_serial_number"): chassis_serial_number = "" else: chassis_serial_number = device_metadata[b"chassis_serial_number"] self.physical_classes_map[chassis_sub_id] = PhysicalClass.CHASSIS self.physical_serial_number_map[chassis_sub_id] = chassis_serial_number # retrieve the initial list of transceivers that are present in the system transceiver_info = Namespace.dbs_keys(self.statedb, mibs.STATE_DB, self.TRANSCEIVER_KEY_PATTERN) if transceiver_info: self.transceiver_entries = [entry.decode() \ for entry in transceiver_info] else: self.transceiver_entries = [] # update cache with initial data for transceiver_entry in self.transceiver_entries: # extract interface name interface = transceiver_entry.split(mibs.TABLE_NAME_SEPARATOR_VBAR)[-1] self._update_transceiver_cache(interface) def _update_per_namespace_data(self, pubsub): """ Update cache. Here we listen to changes in STATE_DB TRANSCEIVER_INFO table and update data only when there is a change (SET, DELETE) """ # This code is not executed in unit test, since mockredis # does not support pubsub while True: msg = pubsub.get_message() if not msg: break transceiver_entry = msg["channel"].split(b":")[-1].decode() data = msg['data'] # event data # extract interface name interface = transceiver_entry.split(mibs.TABLE_NAME_SEPARATOR_VBAR)[-1] # get interface from interface name ifindex = port_util.get_index_from_str(interface) if ifindex is None: # interface name invalid, skip this entry mibs.logger.warning( "Invalid interface name in {} \ in STATE_DB, skipping".format(transceiver_entry)) continue if b"set" in data: self._update_transceiver_cache(interface) elif b"del" in data: # remove deleted transceiver remove_sub_ids = [mibs.get_transceiver_sub_id(ifindex)] # remove all sensor OIDs associated with removed transceiver for sensor in SENSOR_NAME_MAP: remove_sub_ids.append(mibs.get_transceiver_sensor_sub_id(ifindex, sensor)) for sub_id in remove_sub_ids: if sub_id and sub_id in self.physical_entities: self.physical_entities.remove(sub_id) def update_data(self): # This code is not executed in unit test, since mockredis # does not support pubsub for i in range(len(self.statedb)): if not self.pubsub[i]: pattern = self.TRANSCEIVER_KEY_PATTERN self.pubsub[i] = mibs.get_redis_pubsub(self.statedb[i], self.statedb[i].STATE_DB, pattern) self._update_per_namespace_data(self.pubsub[i]) def _update_transceiver_cache(self, interface): """ Update data for single transceiver :param: interface: Interface name associated with transceiver """ # get interface from interface name ifindex = port_util.get_index_from_str(interface) # update xcvr info from DB # use port's name as key for transceiver info entries sub_id = mibs.get_transceiver_sub_id(ifindex) # add interface to available OID list insort_right(self.physical_entities, sub_id) # get transceiver information from transceiver info entry in STATE DB transceiver_info = Namespace.dbs_get_all(self.statedb, mibs.STATE_DB, mibs.transceiver_info_table(interface)) if not transceiver_info: return # physical class - network port self.physical_classes_map[sub_id] = PhysicalClass.PORT # save values into cache sfp_type, \ self.physical_hw_version_map[sub_id],\ self.physical_serial_number_map[sub_id], \ self.physical_mfg_name_map[sub_id], \ self.physical_model_name_map[sub_id] = get_transceiver_data(transceiver_info) ifalias = self.if_alias_map.get(interface.encode(), b"").decode() # generate a description for this transceiver self.physical_description_map[sub_id] = get_transceiver_description(sfp_type, ifalias) # update transceiver sensor cache self._update_transceiver_sensor_cache(interface) def _update_transceiver_sensor_cache(self, interface): """ Update sensor data for single transceiver :param: interface: Interface name associated with transceiver """ ifalias = self.if_alias_map.get(interface.encode(), b"").decode() ifindex = port_util.get_index_from_str(interface) # get transceiver sensors from transceiver dom entry in STATE DB transceiver_dom_entry = Namespace.dbs_get_all(self.statedb, mibs.STATE_DB, mibs.transceiver_dom_table(interface)) if not transceiver_dom_entry: return # go over transceiver sensors for sensor in map(bytes.decode, transceiver_dom_entry): if sensor not in SENSOR_NAME_MAP: continue sensor_sub_id = mibs.get_transceiver_sensor_sub_id(ifindex, sensor) sensor_description = get_transceiver_sensor_description(sensor, ifalias) self.physical_classes_map[sensor_sub_id] = PhysicalClass.SENSOR self.physical_description_map[sensor_sub_id] = sensor_description # add to available OIDs list insort_right(self.physical_entities, sensor_sub_id) def get_next(self, sub_id): """ :param sub_id: The 1-based sub-identifier query. :return: the next sub id. """ right = bisect_right(self.physical_entities, sub_id) if right == len(self.physical_entities): return None return self.physical_entities[right] def get_phy_class(self, sub_id): """ :param sub_id: sub OID :return: physical class for this OID """ if sub_id in self.physical_entities: return self.physical_classes_map.get(sub_id, PhysicalClass.UNKNOWN) return None def get_phy_descr(self, sub_id): """ :param sub_id: sub OID :return: description string for this OID """ if sub_id in self.physical_entities: return self.physical_description_map.get(sub_id, "") return None def get_phy_name(self, sub_id): """ :param sub_id: sub OID :return: name string for this OID """ return "" if sub_id in self.physical_entities else None def get_phy_hw_ver(self, sub_id): """ :param sub_id: sub OID :return: hardware version for this OID """ if sub_id in self.physical_entities: return self.physical_hw_version_map.get(sub_id, "") return None def get_phy_fw_ver(self, sub_id): """ :param sub_id: sub OID :return: firmware version for this OID """ return "" if sub_id in self.physical_entities else None def get_phy_sw_rev(self, sub_id): """ :param sub_id: sub OID :return: software version for this OID """ return "" if sub_id in self.physical_entities else None def get_phy_serial_num(self, sub_id): """ :param sub_id: sub OID :return: serial number for this OID """ if sub_id in self.physical_entities: return self.physical_serial_number_map.get(sub_id, "") return None def get_phy_mfg_name(self, sub_id): """ :param sub_id: sub OID :return: manufacture name for this OID """ if sub_id in self.physical_entities: return self.physical_mfg_name_map.get(sub_id, "") return None def get_phy_model_name(self, sub_id): """ :param sub_id: sub OID :return: model name for this OID """ if sub_id in self.physical_entities: return self.physical_model_name_map.get(sub_id, "") return None