diff --git a/agent_based/dell_powerconnect_psu.py b/agent_based/dell_powerconnect_psu.py new file mode 100644 index 0000000000000000000000000000000000000000..9e3a7141a210190f41b47a4d2bf8e594300b71b7 --- /dev/null +++ b/agent_based/dell_powerconnect_psu.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2 +# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and +# conditions defined in the file COPYING, which is part of this source code package. + +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2021-08-20 + +# 2023-03-30: Rewritten for cmk 2.x +# added current power consumption metric + +# Tested with Dell PowerConnect 5448 and 5424 models. +# Relevant SNMP OIDs: +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.1.67109185 = INTEGER: 67109185 +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.1.67109186 = INTEGER: 67109186 +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.2.67109185 = STRING: "ps1_unit1" +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.2.67109186 = STRING: "ps2_unit1" +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.3.67109185 = INTEGER: 1 +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.3.67109186 = INTEGER: 5 +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.4.67109185 = INTEGER: 5 +# .1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1.4.67109186 = INTEGER: 4 + +# Status codes: +# 1 => normal, +# 2 => warning, +# 3 => critical, +# 4 => shutdown, +# 5 => notPresent, +# 6 => notFunctioning + +# Supply Source Codes: +# 1 => unknown +# 2 => ac +# 3 => dc +# 4 => externalPowerSupply +# 5 => internalRedundant + +# GENERAL MAPS: + +from typing import Dict, Optional, List, Tuple + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + Service, + Result, + check_levels, + State, + SNMPTree, + contains, + any_of, +) +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + DiscoveryResult, + CheckResult, + StringTable, +) + +dell_powerconnect_psu_status_map = { + "1": "normal", + "2": "warning", + "3": "critical", + "4": "shutdown", + "5": "notPresent", + "6": "notFunctioning", +} + +dell_powerconnect_psu_supply_map = { + "1": "Unknown", + "2": "Alternating Current", + "3": "Direct Current", + "4": "External Power Supply", + "5": "Internal Redundant", +} + +dell_powerconnect_psu_status2nagios_map = { + "normal": 0, + "warning": 1, + "critical": 2, + "shutdown": 3, + "notPresent": 1, + "notFunctioning": 2, +} + + +# sample string_table +# [ +# [ +# ['Dell EMC Networking S4048 switch/router'] +# ], +# [ +# ['11', 'S4048-ON PSU 1', '1', '2', '49'], +# ['12', 'S4048-ON PSU 2', '1', '2', '41'], +# ['21', 'S4048-ON PSU 1', '1', '2', '50'], +# ['22', 'S4048-ON PSU 2', '1', '2', '39'] +# ] +# ] +# + + +def parse_dell_powerconnect_psu(string_table: List[StringTable]) -> Optional[Dict[str, Tuple[str, str, int]]]: + try: + hw_ident = string_table[0][0][0] + except IndexError: + return + + section = {} + for device_id, name, state, supply, current_power in string_table[1]: + # M6220 are blade switches which report valid values only for the "Main" + # sensor. The other one is reported as notFunctioning, but this is wrong. + # Simply ignore the "System" sensor for those devices. + if dell_powerconnect_psu_status_map[state] != "notPresent" and ( + "M6220" not in hw_ident or name != "System" + ): + section[f'{device_id} {name}'] = ( + state, + dell_powerconnect_psu_supply_map[supply], + int(current_power) if current_power.isdigit() else 0, + ) + + return section + + +def discovery_dell_powerconnect_psu(section: Dict[str, Tuple[str, str, int]]) -> DiscoveryResult: + for item in section.keys(): + yield Service(item=item) + + +def check_dell_powerconnect_psu(item, params, section: Dict[str, Tuple[str, str, int]]) -> CheckResult: + try: + state, supply, current_power = section[item] + except KeyError: + yield Result(state=State.UNKNOW, summary='ITEM not found in SNMP data') + return + + dell_powerconnect_status = dell_powerconnect_psu_status_map[state] + status = dell_powerconnect_psu_status2nagios_map[dell_powerconnect_status] + + yield Result(state=State(status), summary=f'State: {dell_powerconnect_status}') + + if current_power > 0: # 0 - indicates that Current power is not available for related supply + yield from check_levels( + value=current_power, + levels_upper=params['levels_abs_upper'], + levels_lower=params['levels_abs_lower'], + label='Power consumption', + render_func=lambda v: f'{v} W', + metric_name='power_usage', + ) + + yield Result(state=State.OK, summary=f'Source: {supply}') + + +register.snmp_section( + name='dell_powerconnect_psu', + parse_function=parse_dell_powerconnect_psu, + fetch=[ + SNMPTree( + base='.1.3.6.1.4.1.674.10895.3000.1.2.100.1', # + oids=[ + '0', # productIdentificationDisplayName + ]), + SNMPTree( + base='.1.3.6.1.4.1.674.10895.3000.1.2.110.7.2.1', # + oids=[ + '1', # envMonSupplyStatusIndex + '2', # envMonSupplyStatusDescr + '3', # envMonSupplyState + '4', # envMonSupplySource + '5', # envMonSupplyCurrentPower (assume W) + ]), + ], + detect=any_of( + contains('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.674.10895'), + contains('.1.3.6.1.2.1.1.2.0', '.1.3.6.1.4.1.6027.1.3.22'), + ) +) + +register.check_plugin( + name='dell_powerconnect_psu', + service_name='Sensor %s', + discovery_function=discovery_dell_powerconnect_psu, + check_function=check_dell_powerconnect_psu, + check_default_parameters={ + 'levels_abs_upper': None, + 'levels_abs_lower': None + }, + check_ruleset_name='psu_wattage', +) diff --git a/checks/dell_powerconnect_psu b/checks/dell_powerconnect_psu new file mode 100644 index 0000000000000000000000000000000000000000..6dd7e54ce453f0f07a3d62394ce831bd74302b6a --- /dev/null +++ b/checks/dell_powerconnect_psu @@ -0,0 +1 @@ +# dummy for dell_powerconnect_psu \ No newline at end of file diff --git a/dell_powerconnect_psu.mkp b/dell_powerconnect_psu.mkp new file mode 100644 index 0000000000000000000000000000000000000000..8a58a3bd0030a796501a823d76529ea34d93cba1 Binary files /dev/null and b/dell_powerconnect_psu.mkp differ diff --git a/gui/metrics/psu_wattage.py b/gui/metrics/psu_wattage.py new file mode 100644 index 0000000000000000000000000000000000000000..eeda089b1a5bcee79262ea8e5f6965e090b8ca28 --- /dev/null +++ b/gui/metrics/psu_wattage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2023-03-30 +# + +from cmk.gui.plugins.metrics.utils import ( + perfometer_info, +) + +perfometer_info.append( + { + 'type': 'logarithmic', + 'metric': 'power_usage', + 'half_value': 200.0, + 'exponent': 2, + } +) diff --git a/packages/dell_powerconnect_psu b/packages/dell_powerconnect_psu new file mode 100644 index 0000000000000000000000000000000000000000..d670335b0b094f10dc515c0360161f6b96f579ac --- /dev/null +++ b/packages/dell_powerconnect_psu @@ -0,0 +1,16 @@ +{'author': 'thl-cmk[at]outlook[dot]com', + 'description': 'Rewrite of CMKs dell_powerconnect_psu check for CMK2.1\n' + '\n' + '- fixes missing PSUs if there are different PSUs with the ' + 'same name\n' + '- adds current power usage as perfdata \n', + 'download_url': 'https://thl-cmk.hopto.org', + 'files': {'agent_based': ['dell_powerconnect_psu.py'], + 'checks': ['dell_powerconnect_psu'], + 'gui': ['metrics/psu_wattage.py']}, + 'name': 'dell_powerconnect_psu', + 'title': 'Dell Power connect PSU', + 'version': '20230330.v0.0.2', + 'version.min_required': '2.1.0', + 'version.packaged': '2.1.0p21', + 'version.usable_until': None} \ No newline at end of file