class CiscoEntityFruControlMib(mibretriever.MibRetriever): """A MibRetriever to collect inventory and status information for field-replaceable units (such as power supplies and fans) on Cisco netboxes. """ mib = get_mib("CISCO-ENTITY-FRU-CONTROL-MIB") def __init__(self, agent_proxy): super(CiscoEntityFruControlMib, self).__init__(agent_proxy) self.entity_mib = EntityMib(self.agent_proxy) self.fan_status_table = None self.psu_status_table = None def _get_fantray_status_table(self): """Retrieve the whole table of fan-sensors.""" return self.retrieve_table("cefcFanTrayStatusTable").addCallback( reduce_index) def _get_power_status_table(self): """Retrieve the whole table of PSU-sensors.""" self.retrieve_table("cefcFRUPowerStatusTable").addCallback( reduce_index) @staticmethod def _translate_fan_status(oper_status): """Translates the fan status value from the MIB to a NAV PSU status value. :returns: A state value from nav.models.manage.PowerSupplyOrFan.STATE_CHOICES """ return FAN_STATUS_MAP.get(oper_status, PowerSupplyOrFan.STATE_UNKNOWN) @staticmethod def _translate_power_supply_status_value(oper_status): """Translates the PSU status value from the MIB to a NAV PSU status value. :returns: A state value from nav.models.manage.PowerSupplyOrFan.STATE_CHOICES """ return PSU_STATUS_MAP.get(oper_status, PowerSupplyOrFan.STATE_UNKNOWN) @defer.inlineCallbacks def get_fan_status(self, internal_id): """Returns the operational status for a fan with the given internal id.""" oper_status = yield self.retrieve_column_by_index( "cefcFanTrayOperStatus", (int(internal_id), )) self._logger.debug("cefcFanTrayOperStatus.%s = %r", internal_id, oper_status) defer.returnValue(self._translate_fan_status(oper_status)) @defer.inlineCallbacks def get_power_supply_status(self, internal_id): """Returns the operational status for a PSU with the given internal id.""" oper_status = yield self.retrieve_column_by_index( "cefcFRUPowerOperStatus", (int(internal_id), )) self._logger.debug("cefcFRUPowerOperStatus.%s = %r", internal_id, oper_status) defer.returnValue( self._translate_power_supply_status_value(oper_status)) @defer.inlineCallbacks def get_fan_status_table(self): """Retrieve the whole table of fan-sensors and cache the result.""" if not self.fan_status_table: self.fan_status_table = yield self._get_fantray_status_table() defer.returnValue(self.fan_status_table) @defer.inlineCallbacks def get_psu_status_table(self): """Retrieve the whole table of PSU-sensors and cache the result.""" if not self.psu_status_table: self.psu_status_table = yield self._get_power_status_table() defer.returnValue(self.psu_status_table) def get_power_supplies(self): """Retrieves a list of power supply objects""" return self.entity_mib.get_power_supplies() @defer.inlineCallbacks def get_fans(self): """Retrieves a list of fan objects. A Cisco device reports fan trays and individual fans in entPhysicalTable, but only the status of entire fan trays can be queried from this MIB, so this filters away any non-FRU units. """ fans = yield self.entity_mib.get_fans() status = yield self.get_fan_status_table() self._logger.debug("found %d/%d field-replaceable fan entities", len(status), len(fans)) fans = [fan for fan in fans if fan.internal_id in status] defer.returnValue(fans)
class HpIcfFanMib(mibretriever.MibRetriever): """A MibRetriever for collecting fan states from HP netboxes.""" mib = get_mib("FAN-MIB") def __init__(self, agent_proxy): super(HpIcfFanMib, self).__init__(agent_proxy) self.entity_mib = EntityMib(agent_proxy) self.fan_status_table = None @defer.inlineCallbacks def _get_fan_status_table(self): """Returns the fan status from this netbox.""" df = self.retrieve_table("hpicfFanTable") df.addCallback(self.translate_result) df.addCallback(reduce_index) fan_table = yield df self._logger.debug("fan_table: %r", fan_table) defer.returnValue(fan_table) @staticmethod def _translate_fan_status(psu_status): """Translates the PSU status value from the MIB to a NAV PSU status value. :returns: A state value from nav.models.manage.PowerSupplyOrFan.STATE_CHOICES """ return FAN_STATUS_MAP.get(psu_status, FAN.STATE_UNKNOWN) @defer.inlineCallbacks def get_fan_status(self, internal_id): """Returns the status of the fan with the given internal id.""" if not self.fan_status_table: self.fan_status_table = yield self._get_fan_status_table() index = _fan_index_from_internal_id(internal_id) fan_status_row = self.fan_status_table.get(index, {}) fan_status = fan_status_row.get("hpicfFanState") self._logger.debug("hpicfFanState.%s = %r", index, fan_status) defer.returnValue(self._translate_fan_status(fan_status)) @defer.inlineCallbacks def get_fans(self): """Retrieves a list of fan objects""" hp_fans = yield self._get_fan_status_table() entities = yield self.entity_mib.get_fans() if len(hp_fans) != len(entities): self._logger.warning( "Number of fans in ENTITY-MIB (%d) and FAN-MIB (%d) do not match", len(entities), len(hp_fans), ) # Fans always numbered from 1 and up in FAN-MIB, # and there is no official way to map their IDs to # ENTITY-MIB::entPhysicalTable - therefore, this code naively assumes they at # least appear in the same order in the two MIBS for index, ent in enumerate(sorted(entities, key=attrgetter("internal_id")), start=1): ent.internal_id = "{}:{}".format(ent.internal_id, index) defer.returnValue(entities)