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)
Example #2
0
class HpIcfPowerSupplyMib(mibretriever.MibRetriever):
    """A MibRetriever for collecting power supply states from HP netboxes."""

    mib = get_mib("POWERSUPPLY-MIB")

    def __init__(self, agent_proxy):
        super(HpIcfPowerSupplyMib, self).__init__(agent_proxy)
        self.entity_mib = EntityMib(agent_proxy)
        self.psu_status_table = None

    @defer.inlineCallbacks
    def _get_psu_status_table(self):
        """Returns the power supply status table from this netbox."""
        df = self.retrieve_table("hpicfPsTable")
        df.addCallback(self.translate_result)
        df.addCallback(reduce_index)
        psu_table = yield df
        self._logger.debug("psu_table: %r", psu_table)
        defer.returnValue(psu_table)

    @staticmethod
    def _translate_psu_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 PSU_STATUS_MAP.get(psu_status, PSU.STATE_UNKNOWN)

    @defer.inlineCallbacks
    def get_power_supply_status(self, internal_id):
        """Returns the status of the powersupply with the given internal id."""
        if not self.psu_status_table:
            self.psu_status_table = yield self._get_psu_status_table()

        index = _psu_index_from_internal_id(internal_id)
        psu_status_row = self.psu_status_table.get(index, {})
        psu_status = psu_status_row.get("hpicfPsState")

        self._logger.debug("hpicfPsState.%s = %r", index, psu_status)
        defer.returnValue(self._translate_psu_status(psu_status))

    @defer.inlineCallbacks
    def get_power_supplies(self):
        """Retrieves a list of power supply objects"""
        hp_psus = yield self._get_psu_status_table()
        entities = yield self.entity_mib.get_power_supplies()
        if len(hp_psus) != len(entities):
            self._logger.warning(
                "Number of power supplies in ENTITY-MIB (%d) and POWERSUPPLY-MIB (%d) "
                "do not match",
                len(entities),
                len(hp_psus),
            )

        # Power supplies are always numbered from 1 and up in POWERSUPPLY-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)