diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 8c14a87e20..58db63e4de 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -241,6 +241,8 @@ input: Incoming line/power information of full) | 25 | input.realpower | Current sum value of all (ePDU) phases real power (W) | 300 +| input.realpower.nominal | Nominal sum value of all (ePDU) + phases real power (W) | 850 | input.power | Current sum value of all (ePDU) phases apparent power (VA) | 500 | input.source | The current input power source | 1 @@ -632,6 +634,8 @@ Some specific data to outlet groups exists: |================================================================================= | Name | Description | Example value | outlet.group.n.type | Type of outlet group (OPAQUE) | outlet-section +| outlet.group.n.color | Color-coding of the outlets + in this group (OPAQUE) | yellow | outlet.group.n.count | Number of outlets in the group | 12 | outlet.group.n.phase | Electrical phase to which the physical outlet group (Gang) is diff --git a/drivers/Makefile.am b/drivers/Makefile.am index fc1f2f4e6f..6bc4f4262d 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -232,7 +232,7 @@ snmp_ups_SOURCES = snmp-ups.c snmp-ups-helpers.c \ baytech-mib.c bestpower-mib.c \ compaq-mib.c cyberpower-mib.c \ delta_ups-mib.c \ - eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c \ + eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c eaton-pdu-marlin-helpers.c \ eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c \ eaton-ats16-nmc-mib.c eaton-ats16-nm2-mib.c apc-ats-mib.c eaton-ats30-mib.c \ emerson-avocent-pdu-mib.c \ @@ -313,7 +313,7 @@ nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS) # tracking (which is automatic), but to ensure these files are # distributed by "make dist". -dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ +dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ dummy-ups.h explore-hid.h gamatronic.h genericups.h \ hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h nut_libusb.h liebert-hid.h \ @@ -329,7 +329,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_hunnox.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ - apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ + apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h eaton-pdu-marlin-helpers.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h diff --git a/drivers/apc-iem-mib.h b/drivers/apc-iem-mib.h new file mode 100644 index 0000000000..65904e68c0 --- /dev/null +++ b/drivers/apc-iem-mib.h @@ -0,0 +1,19 @@ +#ifndef APC_IEM_MIB_H +#define APC_IEM_MIB_H + +/* + * FIXME: The below is needed because the main driver body uses this to determine + * whether a conversion from Fahrenheit to Celsius is needed (which really should + * be solved in subdriver specific formatting functions, like we do in usbhid-ups + * This is used in both snmp-ups.c and apc.c logics. + */ + +/* IEM ambient variables */ +/* IEM: integrated environment monitor probe */ + +#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" +#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" +#define APCC_IEM_FAHRENHEIT 2 +#define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" + +#endif /* APC_IEM_MIB_H */ diff --git a/drivers/apc-mib.c b/drivers/apc-mib.c index d640d2c6ef..1ac0a27816 100644 --- a/drivers/apc-mib.c +++ b/drivers/apc-mib.c @@ -276,13 +276,7 @@ static snmp_info_t apcc_mib[] = { { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.2.0", "", SU_FLAG_OK, NULL }, { "ambient.1.humidity.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL }, { "ambient.1.humidity.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL }, - - /* IEM ambient variables */ /* IEM: integrated environment monitor probe */ -#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" -#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" -#define APCC_IEM_FAHRENHEIT 2 -#define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" { "ambient.temperature", 0, 1, APCC_OID_IEM_TEMP, "", SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 1, APCC_OID_IEM_HUMID, "", SU_FLAG_OK, NULL }, diff --git a/drivers/apc-mib.h b/drivers/apc-mib.h index e5aeb07220..0b510ea4f6 100644 --- a/drivers/apc-mib.h +++ b/drivers/apc-mib.h @@ -3,15 +3,7 @@ #include "main.h" #include "snmp-ups.h" - -/* - * FIXME: The below is needed because the main driver body uses this to determine - * whether a conversion from Fahrenheit to Celsius is needed (which really should - * be solved in subdriver specific formatting functions, like we do in usbhid-ups - */ -#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" -#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" -#define APCC_IEM_FAHRENHEIT 2 +#include "apc-iem-mib.h" extern mib2nut_info_t apc; diff --git a/drivers/eaton-ats16-nm2-mib.c b/drivers/eaton-ats16-nm2-mib.c index 256fd4359e..1980c30039 100644 --- a/drivers/eaton-ats16-nm2-mib.c +++ b/drivers/eaton-ats16-nm2-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nm2-mib.h" -#define EATON_ATS16_NM2_MIB_VERSION "0.21" +#define EATON_ATS16_NM2_MIB_VERSION "0.22" #define EATON_ATS16_NM2_SYSOID ".1.3.6.1.4.1.534.10.2" /* newer Network-M2 */ #define EATON_ATS16_NM2_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" @@ -78,6 +78,15 @@ static info_lkp_t eaton_ats16_nm2_output_status_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t eaton_ats16_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + /* EATON_ATS Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nm2_mib[] = { @@ -167,6 +176,15 @@ static snmp_info_t eaton_ats16_nm2_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* Dry contacts on EMP001 TH module */ + /* ats2ContactState.1 = INTEGER: open(1) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, #if 0 /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c index 3b75c0a5dd..8cf2496bfd 100644 --- a/drivers/eaton-ats16-nmc-mib.c +++ b/drivers/eaton-ats16-nmc-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nmc-mib.h" -#define EATON_ATS16_NMC_MIB_VERSION "0.20" +#define EATON_ATS16_NMC_MIB_VERSION "0.21" #define EATON_ATS16_NMC_SYSOID ".1.3.6.1.4.1.705.1" /* legacy NMC */ #define EATON_ATS16_NMC_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" @@ -78,9 +78,17 @@ static info_lkp_t eaton_ats16_nmc_output_status_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t eaton_ats16_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + /* EATON_ATS_NMC Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nmc_mib[] = { - /* standard MIB items */ { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, @@ -167,6 +175,15 @@ static snmp_info_t eaton_ats16_nmc_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* Dry contacts on EMP001 TH module */ + /* ats2ContactState.1 = INTEGER: open(1) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0]}, #if 0 /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ @@ -236,10 +253,6 @@ static snmp_info_t eaton_ats16_nmc_mib[] = { { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, /* ats2ContactType.2 = INTEGER: notUsed(4) */ { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL }, - /* ats2ContactState.1 = INTEGER: open(1) */ - { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", NULL, SU_FLAG_OK, NULL }, - /* ats2ContactState.2 = INTEGER: open(1) */ - { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", NULL, SU_FLAG_OK, NULL }, /* ats2ContactDescr.1 = STRING: Input #1 */ { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL }, /* ats2ContactDescr.2 = STRING: Input #2 */ diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c new file mode 100644 index 0000000000..421045f4c8 --- /dev/null +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -0,0 +1,91 @@ +/* eaton-pdu-marlin-helpers.c - helper routines for eaton-pdu-marlin-mib.c + * to monitor Eaton ePDUs branded as: + * G2 Marlin SW / MI / MO / MA + * G3 Shark SW / MI / MO / MA + * + * Copyright (C) 2017-2019 + * Arnaud Quette + * Copyright (C) 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" /* must be the first header */ + +#include +#include +#include + +#include "eaton-pdu-marlin-helpers.h" +#include "dstate.h" +#include "common.h" +/* Allow access to temperature_unit */ +#include "snmp-ups.h" + +/* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ +long marlin_device_count_fun(const char *daisy_dev_list) +{ + long count = 0, i; + + for (i = 0; daisy_dev_list[i] != '\0'; i++) { + if (daisy_dev_list[i] == ',') { + /* Each comma means a new device in the list */ + count ++; + } + } + if (i > 0 && (daisy_dev_list[i - 1] != ',') ) { + /* Non-empty string => at least one device, and no trailing commas */ + count ++; + } + + upsdebugx(3, "%s: counted devices in '%s', got %ld", + __func__, daisy_dev_list, count); + return count; +} + +/* Temperature unit consideration: + * only store the device unit, for converting to Celsius. + * Don't publish the device unit, since NUT will publish + * as Celsius in all cases */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) +{ + long snmp_value = *((long*)raw_snmp_value); + switch (snmp_value) { + case 0: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_KELVIN; + break; + case 1: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_CELSIUS; + break; + case 2: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_FAHRENHEIT; + break; + default: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_UNKNOWN; + break; + } + return "celsius"; +} diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h new file mode 100644 index 0000000000..fc9890e519 --- /dev/null +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -0,0 +1,29 @@ +/* eaton-pdu-marlin-helpers.h - helper for subdriver to monitor certain + * Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2017-2019 Arnaud Quette + * 2017 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_MARLIN_HELPERS_H +#define EATON_EPDU_MARLIN_HELPERS_H + +long marlin_device_count_fun(const char *daisy_dev_list); +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value); + +#endif /* EATON_EPDU_MARLIN_HELPERS_H */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 729c98f1d0..bed4bbef53 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -29,12 +29,14 @@ */ #include "eaton-pdu-marlin-mib.h" -#include "dstate.h" +#if WITH_SNMP_LKP_FUN +#include "eaton-pdu-marlin-helpers.h" +#endif /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.59" +#define EATON_MARLIN_MIB_VERSION "0.69" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -110,6 +112,13 @@ static info_lkp_t marlin_ambient_presence_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t marlin_emp002_ambient_presence_info[] = { + { 0, "unknown", NULL, NULL }, + { 2, "yes", NULL, NULL }, /* communicationOK */ + { 3, "no", NULL, NULL }, /* communicationLost */ + { 0, NULL, NULL, NULL } +}; + static info_lkp_t marlin_threshold_status_info[] = { { 0, "good", NULL, NULL }, /* No threshold triggered */ { 1, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ @@ -127,7 +136,7 @@ static info_lkp_t marlin_threshold_frequency_status_info[] = { static info_lkp_t marlin_ambient_drycontacts_info[] = { { -1, "unknown", NULL, NULL }, - { 0, "open", NULL, NULL }, + { 0, "opened", NULL, NULL }, { 1, "closed", NULL, NULL }, { 0, NULL, NULL, NULL } }; @@ -192,78 +201,168 @@ static info_lkp_t marlin_input_type_info[] = { { 0, NULL, NULL, NULL } }; -static char marlin_scratch_buf[20]; - -/* Compute the phase to which an outlet group is connected - * WRT the number of phase(s) and the outlet group number. - * Note that the group type (marlin_outlet_group_type_info) is - * not considered since this applies to any kind of group */ -static const char *marlin_outlet_group_phase_fun(void *raw_outlet_group_nb) -{ - int outlet_group_nb = *((int *)raw_outlet_group_nb); - const char* str_phases_nb = dstate_getinfo("input.phases"); - int phases_nb = 1; - if (str_phases_nb && (outlet_group_nb >= 0) ) { - phases_nb = atoi(str_phases_nb); - if (phases_nb == 1) { - return "L1"; - } - else { /* 3ph assumed, 2ph PDU don't exist! */ - if (outlet_group_nb > 3) - phases_nb = (outlet_group_nb - 3); - else - phases_nb = outlet_group_nb; - - snprintf(marlin_scratch_buf, sizeof(marlin_scratch_buf), "L%i", phases_nb); - if (phases_nb < 1 || phases_nb > 3) - upsdebugx(3, "WARNING: %s got %i phases which is an unexpected amount", - __func__, phases_nb); - - return marlin_scratch_buf; - } - } - return NULL; -} static info_lkp_t marlin_outlet_group_phase_info[] = { - { 1, "dummy", marlin_outlet_group_phase_fun, NULL }, + { 0, "unknown", NULL, NULL }, /* unknown */ + { 1, "1", NULL, NULL }, /* singlePhase */ + { 2, "1-N", NULL, NULL }, /* phase1toN */ + { 3, "2-N", NULL, NULL }, /* phase2toN */ + { 4, "3-N", NULL, NULL }, /* phase3toN */ + { 5, "1-2", NULL, NULL }, /* phase1to2 */ + { 6, "2-3", NULL, NULL }, /* phase2to3 */ + { 7, "3-1", NULL, NULL }, /* phase3to1 */ + { 0, NULL, NULL, NULL } +}; + +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. + */ + +#if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +} +#endif // WITH_SNMP_LKP_FUN_DUMMY + +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_sensor_temperature_read_info[] = { + { 0, "dummy", su_temperature_read_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +#else // if not WITH_SNMP_LKP_FUN: + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "kelvin", NULL, NULL }, + { 1, "celsius", NULL, NULL }, + { 2, "fahrenheit", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#endif // WITH_SNMP_LKP_FUN + +/* Extracted from powerware-mib.c ; try to commonalize */ +static info_lkp_t marlin_ambient_drycontacts_polarity_info[] = { + { 0, "normal-opened", NULL, NULL }, + { 1, "normal-closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_ambient_drycontacts_state_info[] = { + { 0, "inactive", NULL, NULL }, + { 1, "active", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#if WITH_SNMP_LKP_FUN +/* Note: marlin_device_count_fun() is defined in eaton-pdu-marlin-helpers.c + * Future work for DMF might provide a same-named routine via LUA-C gateway. + */ + +# if WITH_SNMP_LKP_FUN_DUMMY +long marlin_device_count_fun(const char *daisy_dev_list) + { return 1; } +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ + +static info_lkp_t marlin_device_count_info[] = { + { 1, "dummy", NULL, marlin_device_count_fun }, { 0, NULL, NULL, NULL } }; +#else /* if not WITH_SNMP_LKP_FUN: */ + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ + +#endif /* WITH_SNMP_LKP_FUN */ + + /* Snmp2NUT lookup table for Eaton Marlin MIB */ static snmp_info_t eaton_marlin_mib[] = { /* standard MIB items */ - { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, - { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, - { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.1.0", + NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.4.0", + NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.6.0", + NULL, SU_FLAG_OK, NULL }, /* Device collection */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.3.%i", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* For daisychain, there is only 1 physical interface! */ - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.2.1.2.2.1.6.2", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - /* Daisychained devices support - * Notes: this definition is used to: + + /* Daisychained devices support */ + /* FIXME : Should this be a static value, or can we expect the amount of + * daisy-chained devices to change without restart of the driver by user? + * If this is a critical matter, should a detected change of amount of + * daisy-chained devices, outlet counts, etc. cause restart/reinit of + * this running driver instance? + */ +#if WITH_SNMP_LKP_FUN + /* Number of daisychained units is processed according to present units + * in the chain with new G3 firmware (02.00.0051, since autumn 2017): + * Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ + /* FIXME: inline func */ + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.1.0", + "0", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + &marlin_device_count_info[0] /* devices_count */ }, +#endif + /* Notes: this older/fallback definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ - { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", - "1", SU_FLAG_STATIC, NULL }, + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "1", SU_FLAG_STATIC +#if WITH_SNMP_LKP_FUN + | SU_FLAG_UNIQUE +#endif + , NULL /* devices_count */ }, /* UPS collection */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, @@ -278,8 +377,9 @@ static snmp_info_t eaton_marlin_mib[] = { { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.%i", "", SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* FIXME: needs a date reformating callback * 2011-8-29,16:27:25.0,+1:0 * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 @@ -292,39 +392,60 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Input collection */ + /* Note: a larger ePDU can have several inputs. The "%i" iterators + * in key names are currently available for daisychain devs, outlets, + * and groups - but not for inputs. These would likely evolve later + * to "input.%i.something" with default (non-%i) same as .1 instance. + * At this time only a single-input (or first of several inputs) is + * supported by this mapping. + */ /* Historically, some of these data were previously published as * outlet.{realpower,...} * However, it's more suitable and logic to have these on input.{...} */ /* Note: the below gives the number of input, not the number of phase(s)! */ /* inputCount.0; Value (Integer): 1 - { "input.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", + { "input.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", NULL, SU_FLAG_STATIC, NULL }, */ - /* Note: for daisychain mode, we must handle phase(s) per device, not as a whole */ + /* Note: for daisychain mode, we must handle phase(s) per device, + * not as a whole. In case of daisychain, support of the UNIQUE + * field is not yet implemented (FIXME) so the last resolved OID + * value wins. If a more-preferable OID is not implemented by device, + * this is ok - the previous available value remains in place. */ /* inputType.%i.1 = INTEGER: singlePhase (1) */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", - NULL, SU_FLAG_STATIC, &marlin_input_type_info[0] }, + { "input.phases", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", + NULL, SU_FLAG_STATIC, + &marlin_input_type_info[0] }, /* Frequency is measured globally */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + { "input.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", NULL, 0, NULL }, { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_alarm_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_alarm_info[0] }, /* inputCurrentPercentLoad (measured globally) * Current percent load, based on the rated current capacity */ /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ - { "input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + { "input.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + { "input.L1.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", + { "input.L2.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", + { "input.L3.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* FIXME: @@ -334,15 +455,18 @@ static snmp_info_t eaton_marlin_mib[] = { * Voltage has to be expressed either phase-phase or phase-neutral * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) - * => RFC input.Lx.voltage.context */ - { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + * => RFC input.Lx.voltage.context */ + { "input.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -360,10 +484,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -381,10 +507,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.2", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -402,10 +530,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -428,10 +558,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -452,10 +584,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -476,10 +610,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.2", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -500,10 +636,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -517,47 +655,98 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, /* Sum of all phases realpower, valid for Shark 1ph/3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", + { "input.L1.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + { "input.L2.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", + { "input.L3.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L1.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", + { "input.L2.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + { "input.L3.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", + + /* Input feed: a feed (A or B) is tied to an input, and this + * sub-collection describes the properties of an actual cable. + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ + /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ + /* Feed name(s) of the ePDU power input(s), can be set by user (FIXME: rename to .desc?) + * inputFeedName.0.1 = Value (OctetString): Feed A + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ +/* { "input.%i.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", + * NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + */ + { "input.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.1", + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* Feed color (integer RGB) + * inputFeedColor.0.1 = Gauge32: 0 (black) + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ +/* { "input.%i.feed.color", 0, 1, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + */ + { "input.feed.color", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* inputPowerCapacity.0.1 = INTEGER: 2300 */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "input.realpower.nominal", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Ambient collection */ + /* EMP001 (legacy) mapping */ + /* Note: this is still published, beside from the new daisychained version! */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_presence_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_presence_info[0] }, { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_temperature_alarms_info[0] }, - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_temperature_alarms_info[0] }, + { "ambient.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", NULL, SU_FLAG_OK, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.temperature.low", ST_FLAG_RW, 0.1, @@ -580,11 +769,14 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_humidity_alarms_info[0] }, - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_humidity_alarms_info[0] }, + { "ambient.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", NULL, SU_FLAG_OK, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.humidity.low", ST_FLAG_RW, 0.1, @@ -608,59 +800,241 @@ static snmp_info_t eaton_marlin_mib[] = { /* Dry contacts on TH module */ { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.2", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, + + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + { "ambient.count", ST_FLAG_RW, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.1.0", + "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* CommunicationStatus.n */ + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_emp002_ambient_presence_info[0] }, + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + { "ambient.%i.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorManufacturer.n */ + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorModel.n */ + { "ambient.%i.model", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorSerialNumber.n */ + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorUuid.n */ + { "ambient.%i.id", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorAddress.n */ + { "ambient.%i.address", 0, 1, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorMonitoredBy.n */ + { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorFirmwareVersion.n */ + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + { "ambient.%i.temperature.unit", 0, 1.0, + ".1.3.6.1.4.1.534.6.8.1.2.5.0", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &eaton_sensor_temperature_unit_info[0] }, + /* temperatureValue.n.1 */ + { "ambient.%i.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, +#if WITH_SNMP_LKP_FUN + &eaton_sensor_temperature_read_info[0] +#else + NULL +#endif + }, + { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_temperature_alarms_info[0] }, + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdLowWarning.n.1 */ + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdHighWarning.n.1 */ + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdHighCritical.n.1 */ + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityValue.n.1 */ + { "ambient.%i.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_humidity_alarms_info[0] }, + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdLowWarning.n.1 */ + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdHighWarning.n.1 */ + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdHighCritical.n.1 */ + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* digitalInputName.n.{1,2} */ + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* digitalInputPolarity.n */ + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, /* Outlet collection */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", + { "outlet.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "outlet.id", 0, 1, NULL, + { "outlet.id", 0, 1, + NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, + NULL, + "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* UnitType * used to depict the overall outlets switchability of the unit on G3 and newer ePDU*/ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.10.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, &marlin_unit_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + &marlin_unit_switchability_info[0] }, /* Ugly hack for older G2 ePDU: check the first outlet to determine unit switchability */ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.1", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ - { "outlet.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + { "outlet.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", NULL, 0, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + { "outlet.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, - { "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", + { "outlet.current", 0, 0.01, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", NULL, 0, NULL }, - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + { "outlet.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", NULL, 0, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + { "outlet.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", NULL, 0, NULL }, /* outlet template definition * Indexes start from 1, ie outlet.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ - /* outletName: Outlet friendly name, which can be modified by the user */ + /* Outlet friendly name, which can be modified by the user + * outletName: = OctetString: "Outlet A16" + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", - NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_status_info[0] }, + NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_status_info[0] }, + /* Numeric identifier of the outlet, tied to the whole unit */ + /* NOTE: For daisychain devices ATM the last listed value presented by + * the SNMP device is kept by the driver - no SU_FLAG_UNIQUE here yet. + * Verified that a non-implemented OID does not publish empty values. */ + /* Fallback in firmwares issued before Sep 2017 is to use the + * outletID: Outlet physical name, related to its number in the group + * ex: first outlet of the second group (B) is B1, or can default to + * the outlet number (represented as string) and is a read-only string + * outletID.0.8 = Value (OctetString): "8" + */ { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* outletID: Outlet physical name, related to its number in the group - * ex: first outlet of the second group (B) is B1 */ + NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + + /* Fallback in firmwares issued before Sep 2017 (outletID): */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + /* Preferred: Outlet physical name OID in new G3 firmware (02.00.0051) + * is named outletDesignator (other MIBs outletPhysicalName) + * and is a read-only string provided by firmware + * outletDesignator.0.1 = Value (OctetString): "A1" + * outletPhysicalName.0.16 = Value (OctetString): "A16" + */ + { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, @@ -681,14 +1055,17 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.6", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", + { "outlet.%i.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_current_alarms_info[0] }, { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.5.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, @@ -701,16 +1078,20 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.8.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", + { "outlet.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", + { "outlet.%i.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_voltage_alarms_info[0] }, { "outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.4.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, @@ -723,19 +1104,23 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.7.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", + { "outlet.%i.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* outletControlSwitchable */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.9.%i.%i", - "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, &marlin_outlet_switchability_info[0] }, + "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, + &marlin_outlet_switchability_info[0] }, /* FIXME: handle non switchable units (only measurements), which do not expose this OID */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.5.%i.%i", - "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_type_info[0] }, + "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_type_info[0] }, /* TODO: handle statistics * outletWh.0.1 @@ -743,7 +1128,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Outlet groups collection */ - { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", + { "outlet.group.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, /* outlet groups template definition * Indexes start from 1, ie outlet.group.1 => .1 */ @@ -752,18 +1138,45 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - /* groupName.0.1 = OctetString: Factory Group 1 */ - /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ - { "outlet.group.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + /* User-friendly (writeable) description of the outlet group: + * groupName.0.1 = OctetString: Factory Group 1 + * groupName.0.2 = OctetString: Branch Circuit B + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ + { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_SEMI_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* Outlet-group physical name, a read-only string, + * is named groupDesignator (other MIBs groupPhysicalName) + * groupPhysicalName.0.1 = Value (OctetString): A + * groupDesignator.0.2 = Value (OctetString): B + */ + { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.8.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* Outlet-group color: groupColor (other MIBs groupBkgColor) + * groupColor.0.1 = Value (Gauge32): 16051527 (0xF4ED47) + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.7.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* groupType.0.1 = Integer: outletSection (4) */ { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_type_info[0] }, + /* Phase to which an outlet-group is connected: + * We use the following OID, which gives the voltage measurement type + * groupVoltageMeasType.0.1; Value (Integer): singlePhase (1) + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ { "outlet.group.%i.phase", 0, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", + ".1.3.6.1.4.1.534.6.6.7.5.3.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_phase_info[0] }, /* groupControlStatus.0.1 = Integer: on (1) */ @@ -844,7 +1257,16 @@ static snmp_info_t eaton_marlin_mib[] = { /* groupVA.0.1 = Integer: 3132 */ { "outlet.group.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* Input to which an outlet-group is connected + * groupInputIndex.0.1 = Integer: 1 + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "outlet.group.%i.input", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.9.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* instant commands. */ /* Notes: @@ -855,29 +1277,59 @@ static snmp_info_t eaton_marlin_mib[] = { * we currently use "0", so instant On | Off | Reboot... */ /* no counterpart found! { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, */ + NULL, SU_TYPE_CMD, NULL }, */ /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + { "outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + { "outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + { "outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* Per-outlet shutdown / startup delay (configuration point, not the timers) + * outletControlShutoffDelay.0.3 = INTEGER: 120 + * outletControlSequenceDelay.0.8 = INTEGER: 8 + * (by default each output socket startup is delayed by its number in seconds) + */ + { "outlet.%i.delay.shutdown", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.10.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.delay.start", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + { "outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + { "outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + { "outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* Per-outlet shutdown / startup timers + * outletControlOffCmd.0.1 = INTEGER: -1 + * outletControlOnCmd.0.1 = INTEGER: -1 + */ + { "outlet.%i.timer.shutdown", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.timer.start", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index 4430112b0b..d0a844d84f 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -27,7 +27,7 @@ #include "mge-mib.h" -#define MGE_MIB_VERSION "0.54" +#define MGE_MIB_VERSION "0.55" /* TODO: * - MGE PDU MIB and sysOID (".1.3.6.1.4.1.705.2") */ @@ -132,6 +132,13 @@ static info_lkp_t mge_power_source_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t mge_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "closed", NULL, NULL }, + { 2, "opened", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + /* Parameters default values */ #define DEFAULT_ONDELAY "30" /* Delay between return of utility power */ /* and powering up of load, in seconds */ @@ -244,6 +251,10 @@ static snmp_info_t mge_mib[] = { /* Ambient page: Environment Sensor (ref 66 846) */ { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.705.1.8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.705.1.8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsmgEnvironmentInput1State.1 */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.9.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, + /* upsmgEnvironmentInput1State.1 */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.10.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index 8a1a3cc497..4177b693d6 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,7 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.32" +#define MGE_XML_VERSION "MGEXML/0.36" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" @@ -577,6 +577,25 @@ static const char *mge_ambient_info(const char *arg_val) } } +static const char *mge_drycontact_info(const char *arg_val) +{ + /* these values should theoretically be obtained through + * Environment.Input[1].State[x].Description + * Examples: + * open + * closed + */ + switch (atoi(arg_val)) + { + case 0: + return "opened"; + case 1: + return "closed"; + default: + return NULL; + } +} + static const char *mge_timer_shutdown(const char *delay_before_shutoff) { if (atoi(delay_before_shutoff) > -1 ) { @@ -1059,6 +1078,8 @@ static xml_info_t mge_xml2nut[] = { { "ambient.temperature.low", ST_FLAG_RW, 0, "Environment.Temperature.LowThreshold", 0, 0, NULL }, { "ambient.temperature.maximum", 0, 0, "Environment.PresentStatus.HighTemperature", 0, 0, mge_ambient_info }, { "ambient.temperature.minimum", 0, 0, "Environment.PresentStatus.LowTemperature", 0, 0, mge_ambient_info }, + { "ambient.contacts.1.status", 0, 0, "Environment.Input[1].PresentStatus.State", 0, 0, mge_drycontact_info }, + { "ambient.contacts.2.status", 0, 0, "Environment.Input[2].PresentStatus.State", 0, 0, mge_drycontact_info }, /* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */ { "outlet.id", 0, 0, "UPS.OutletSystem.Outlet[1].OutletID", 0, 0, NULL }, diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 2d739347e0..e4b0296010 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -4,7 +4,7 @@ * Copyright (C) * 2005-2006 Olli Savia * 2005-2006 Niels Baggesen - * 2015-2021 Eaton (author: Arnaud Quette ) + * 2015-2019 Eaton (author: Arnaud Quette ) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +24,12 @@ */ #include "powerware-mib.h" +#if WITH_SNMP_LKP_FUN +/* FIXME: shared helper code, need to be put in common */ +#include "eaton-pdu-marlin-helpers.h" +#endif -#define PW_MIB_VERSION "0.95" +#define PW_MIB_VERSION "0.104" /* TODO: more sysOID and MIBs support: * @@ -35,7 +39,8 @@ * PXGX 1000 cards (PDU/RPP/RPM): Get pduNumPanels ".1.3.6.1.4.1.534.6.6.4.1.1.1.4.0" */ -/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) */ +/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) + * Eaton Gigabit Network Card (Genepi) */ #define POWERWARE_SYSOID ".1.3.6.1.4.1.534.1" /* Powerware UPS newer PXGX UPS cards (BladeUPS, ...) */ #define EATON_PXGX_SYSOID ".1.3.6.1.4.1.534.2.12" @@ -72,8 +77,8 @@ #define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ -#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1" /* XUPS-MIB::xupsControlOutputOffDelay */ -#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2" /* XUPS-MIB::xupsControlOutputOnDelay */ +#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1.0" /* XUPS-MIB::xupsControlOutputOffDelay */ +#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2.0" /* XUPS-MIB::xupsControlOutputOnDelay */ #define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ #define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ #define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ @@ -135,6 +140,20 @@ static info_lkp_t pw_pwr_info[] = { { 0, NULL, NULL, NULL } }; +/* FIXME: mapped to (experimental.)ups.type, but + * should be output.source or ups.mode (need RFC) + * to complement the above ups.status + * along with having ups.type as described hereafter*/ +/* FIXME: should be used by ups.mode or output.source (need RFC); + * Note: this define is not set via project options; code was hidden with + * original commit to "snmp-ups: support newer Genepi management cards"; + * un-hidden to make it "experimental.*" namespace during backporting + */ +#ifndef USE_PW_MODE_INFO +# define USE_PW_MODE_INFO 1 +#endif + +#if USE_PW_MODE_INFO static info_lkp_t pw_mode_info[] = { { 1, "", NULL, NULL }, { 2, "", NULL, NULL }, @@ -146,7 +165,8 @@ static info_lkp_t pw_mode_info[] = { { 8, "parallel capacity", NULL, NULL }, { 9, "parallel redundancy", NULL, NULL }, { 10, "high efficiency", NULL, NULL }, - /* Extended status values */ + /* Extended status values, + * FIXME: check for source and completion */ { 240, "" /* battery (0xF0) */, NULL, NULL }, { 100, "" /* maintenanceBypass (0x64) */, NULL, NULL }, { 96, "" /* Bypass (0x60) */, NULL, NULL }, @@ -156,6 +176,34 @@ static info_lkp_t pw_mode_info[] = { { 16, "" /* none (0x10) */, NULL, NULL }, { 0, NULL, NULL, NULL } }; +#endif /* USE_PW_MODE_INFO */ + +/* FIXME: may be standardized + * extracted from bcmxcp.c->BCMXCP_TOPOLOGY_*, Make some common definitions */ +static info_lkp_t pw_topology_info[] = { + { 0x0000, "", NULL, NULL }, /* None; use the Table of Elements */ + { 0x0010, "Off-line switcher, Single Phase", NULL, NULL }, + { 0x0020, "Line-Interactive UPS, Single Phase", NULL, NULL }, + { 0x0021, "Line-Interactive UPS, Two Phase", NULL, NULL }, + { 0x0022, "Line-Interactive UPS, Three Phase", NULL, NULL }, + { 0x0030, "Dual AC Input, On-Line UPS, Single Phase", NULL, NULL }, + { 0x0031, "Dual AC Input, On-Line UPS, Two Phase", NULL, NULL }, + { 0x0032, "Dual AC Input, On-Line UPS, Three Phase", NULL, NULL }, + { 0x0040, "On-Line UPS, Single Phase", NULL, NULL }, + { 0x0041, "On-Line UPS, Two Phase", NULL, NULL }, + { 0x0042, "On-Line UPS, Three Phase", NULL, NULL }, + { 0x0050, "Parallel Redundant On-Line UPS, Single Phase", NULL, NULL }, + { 0x0051, "Parallel Redundant On-Line UPS, Two Phase", NULL, NULL }, + { 0x0052, "Parallel Redundant On-Line UPS, Three Phase", NULL, NULL }, + { 0x0060, "Parallel for Capacity On-Line UPS, Single Phase", NULL, NULL }, + { 0x0061, "Parallel for Capacity On-Line UPS, Two Phase", NULL, NULL }, + { 0x0062, "Parallel for Capacity On-Line UPS, Three Phase", NULL, NULL }, + { 0x0102, "System Bypass Module, Three Phase", NULL, NULL }, + { 0x0122, "Hot-Tie Cabinet, Three Phase", NULL, NULL }, + { 0x0200, "Outlet Controller, Single Phase", NULL, NULL }, + { 0x0222, "Dual AC Input Static Switch Module, 3 Phase", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; /* Legacy implementation */ static info_lkp_t pw_battery_abm_status[] = { @@ -194,6 +242,120 @@ static info_lkp_t pw_yes_no_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t pw_outlet_status_info[] = { + { 1, "on", NULL, NULL }, + { 2, "off", NULL, NULL }, + { 3, "on", NULL, NULL }, /* pendingOff, transitional status */ + { 4, "off", NULL, NULL }, /* pendingOn, transitional status */ + /* { 5, "", NULL, NULL }, unknown */ + /* { 6, "", NULL, NULL }, reserved */ + { 7, "off", NULL, NULL }, /* Failed in Closed position */ + { 8, "on", NULL, NULL }, /* Failed in Open position */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. + */ + +# if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +}; +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ + +static info_lkp_t pw_sensor_temperature_unit_info[] = { + { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_sensor_temperature_read_info[] = { + { 0, "dummy", su_temperature_read_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +#else /* if not WITH_SNMP_LKP_FUN: */ + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t pw_sensor_temperature_unit_info[] = { + { 0, "kelvin", NULL, NULL }, + { 1, "celsius", NULL, NULL }, + { 2, "fahrenheit", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#endif /* WITH_SNMP_LKP_FUN */ + +static info_lkp_t pw_ambient_drycontacts_polarity_info[] = { + { 0, "normal-opened", NULL, NULL }, + { 1, "normal-closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_ambient_drycontacts_state_info[] = { + { 0, "inactive", NULL, NULL }, + { 1, "active", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_emp002_ambient_presence_info[] = { + { 0, "unknown", NULL, NULL }, + { 2, "yes", NULL, NULL }, /* communicationOK */ + { 3, "no", NULL, NULL }, /* communicationLost */ + { 0, NULL, NULL, NULL } +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ +static info_lkp_t pw_threshold_status_info[] = { + { 0, "good", NULL, NULL }, /* No threshold triggered */ + { 1, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "critical-low", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "warning-high", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "critical-high", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ +static info_lkp_t pw_threshold_temperature_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low temperature warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low temperature critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high temperature warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high temperature critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_threshold_humidity_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low humidity warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low humidity critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high humidity warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high humidity critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + /* Snmp2NUT lookup table */ static snmp_info_t pw_mib[] = { @@ -218,9 +380,15 @@ static snmp_info_t pw_mib[] = { { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", SU_FLAG_STATIC, NULL }, { "ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", - SU_OUTPUT_1, NULL }, + 0, NULL }, + /* FIXME: should be removed in favor of output.power */ { "ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", 0, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputWatts.1.0; Value (Integer): 300 */ + { "ups.power", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, &pw_pwr_info[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", @@ -229,10 +397,20 @@ static snmp_info_t pw_mib[] = { SU_STATUS_BATT, &pw_alarm_lb[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", SU_STATUS_BATT, &pw_battery_abm_status[0] }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", +#if USE_PW_MODE_INFO + /* FIXME: should be ups.mode or output.source (need RFC) */ + /* Note: this define is not set via project options; code hidden with + * commit to "snmp-ups: support newer Genepi management cards" */ + { "experimental.ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", SU_FLAG_STATIC | SU_FLAG_OK, &pw_mode_info[0] }, +#endif /* USE_PW_MODE_INFO */ + /* xupsTopologyType.0; Value (Integer): 32 */ + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, &pw_topology_info[0] }, + /* FIXME: should be removed in favor of their output. equivalent! */ { "ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", 0, NULL }, + /* FIXME: should be removed in favor of output.power.nominal */ { "ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", 0, NULL }, /* XUPS-MIB::xupsEnvAmbientTemp.0 */ @@ -269,7 +447,10 @@ static snmp_info_t pw_mib[] = { /* XUPS-MIB::xupsConfigOutputFreq.0 */ { "output.frequency.nominal", 0, 0.1, "1.3.6.1.4.1.534.1.10.4.0", "", 0, NULL }, /* XUPS-MIB::xupsOutputVoltage.1 */ - { "output.voltage", 0, 1.0, ".1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, + { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputVoltage.1.0; Value (Integer): 230 */ + { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1.0", "", SU_OUTPUT_1, NULL }, /* XUPS-MIB::xupsConfigOutputVoltage.0 */ { "output.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.1.0", "", 0, NULL }, /* XUPS-MIB::xupsConfigLowOutputVoltageLimit.0 */ @@ -278,8 +459,20 @@ static snmp_info_t pw_mib[] = { { "output.voltage.high", 0, 1.0, ".1.3.6.1.4.1.534.1.10.7.0", "", 0, NULL }, { "output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputCurrent.1.0; Value (Integer): 0 */ + { "output.current", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.3.1.0", "", + SU_OUTPUT_1, NULL }, { "output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* Name/OID: xupsOutputWatts.1.0; Value (Integer): 1200 */ + { "output.realpower", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL }, + /* Duplicate of "ups.realpower.nominal" + * FIXME: map either ups or output, but not both (or have an auto-remap) */ + { "output.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL }, { "output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", @@ -315,6 +508,11 @@ static snmp_info_t pw_mib[] = { 0, NULL }, { "input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", SU_INPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsInputVoltage.1[.0]; Value (Integer): 245 */ + { "input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1", "", + SU_INPUT_1, NULL }, + /* XUPS-MIB::xupsConfigInputVoltage.0 */ { "input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL }, { "input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", @@ -345,6 +543,10 @@ static snmp_info_t pw_mib[] = { { "input.bypass.frequency", 0, 0.1, PW_OID_BY_FREQUENCY, "", 0, NULL }, { "input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", SU_INPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsBypassVoltage.1.0; Value (Integer): 244 */ + { "input.bypass.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.5.3.1.2.1.0", "", + SU_INPUT_1, NULL }, { "input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", @@ -352,7 +554,23 @@ static snmp_info_t pw_mib[] = { { "input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", SU_INPUT_3, NULL }, - /* Ambient page */ + /* Outlet page */ + /* Master outlet id always equal to 0 */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC , NULL }, + /* XUPS-MIB:: xupsSwitchable.0 */ + { "outlet.switchable", 0, 1, ".1.3.6.1.4.1.534.1.9.7.0", NULL, SU_FLAG_STATIC , &pw_yes_no_info[0] }, + /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ + { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL }, + /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, + /* This MIB does not provide outlets switchability info. So map to a nearby + OID, for data activation, and map all values to "yes" */ + { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepStatus.X; Value (Integer): 1 */ + { "outlet.%i.status", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.2.%i", NULL, SU_OUTLET, &pw_outlet_status_info[0] }, + + /* Ambient collection */ + /* EMP001 (legacy) mapping */ /* XUPS-MIB::xupsEnvRemoteTemp.0 */ { "ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ @@ -365,6 +583,86 @@ static snmp_info_t pw_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, + /* XUPS-MIB::xupsContactDescr.n */ + { "ambient.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.1", "", 0, NULL }, + { "ambient.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.2", "", 0, NULL }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, + { "ambient.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL }, + /* CommunicationStatus.n */ + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE, &pw_emp002_ambient_presence_info[0] }, + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorManufacturer.n */ + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorModel.n */ + { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorSerialNumber.n */ + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorUuid.n */ + { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorAddress.n */ + { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorFirmwareVersion.n */ + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0] }, + /* temperatureValue.n.1 */ + { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, +#if WITH_SNMP_LKP_FUN + &pw_sensor_temperature_read_info[0] +#else + NULL +#endif + }, + { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_temperature_alarms_info[0] }, + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdLowWarning.n.1 */ + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdHighWarning.n.1 */ + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdHighCritical.n.1 */ + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityValue.n.1 */ + { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_humidity_alarms_info[0] }, + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdLowWarning.n.1 */ + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdHighWarning.n.1 */ + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdHighCritical.n.1 */ + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* digitalInputName.n.{1,2} */ + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL }, + /* digitalInputPolarity.n */ + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", @@ -375,17 +673,37 @@ static snmp_info_t pw_mib[] = { /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ { "shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* XUPS-MIB::xupsControlOutputOffDelay */ /* load off after 1 sec, shortest possible delay; 0 cancels */ - { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "", + { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "1", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "load.off.delay", 0, DEFAULT_OFFDELAY, PW_OID_CONT_OFFDELAY, "", + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "load.off.delay", 0, 1, PW_OID_CONT_OFFDELAY, NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* XUPS-MIB::xupsControlOutputOnDelay */ /* load on after 1 sec, shortest possible delay; 0 cancels */ - { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "", + { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "1", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "load.on.delay", 0, DEFAULT_ONDELAY, PW_OID_CONT_ONDELAY, "", + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "load.on.delay", 0, 1, PW_OID_CONT_ONDELAY, NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* Delays handling: + * 0-n :Time in seconds until the command is issued + * -1:Cancel a pending Off/On command */ + /* XUPS-MIB::xupsRecepOffDelaySecs.n */ + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, + { "ups.alarms", 0, 1.0, PW_OID_ALARMS, "", 0, NULL }, diff --git a/drivers/snmp-ups-helpers.c b/drivers/snmp-ups-helpers.c index ce3f2abcff..b4116ff6f8 100644 --- a/drivers/snmp-ups-helpers.c +++ b/drivers/snmp-ups-helpers.c @@ -45,6 +45,9 @@ static char su_scratch_buf[255]; +/* Temperature handling, to convert back to Celsius */ +int temperature_unit = TEMPERATURE_UNKNOWN; + /* Convert a US formated date (mm/dd/yyyy) to an ISO 8601 Calendar date (yyyy-mm-dd) */ const char *su_usdate_to_isodate_info_fun(void *raw_date) { @@ -72,3 +75,32 @@ info_lkp_t su_convert_to_iso_date_info[] = { { 1, "dummy", su_usdate_to_isodate_info_fun, NULL }, { 0, NULL, NULL, NULL } }; + +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(void *raw_snmp_value) +{ + const long snmp_value = *((long*)raw_snmp_value); + long celsius_value = snmp_value; + + memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); + + switch (temperature_unit) { + case TEMPERATURE_KELVIN: + celsius_value = (snmp_value / 10) - 273.15; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_CELSIUS: + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", (snmp_value / 10)); + break; + case TEMPERATURE_FAHRENHEIT: + celsius_value = (((snmp_value / 10) - 32) * 5) / 9; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_UNKNOWN: + default: + upsdebugx(1, "%s: not a known temperature unit for conversion!", __func__); + break; + } + upsdebugx(2, "%s: %.1ld => %s", __func__, (snmp_value / 10), su_scratch_buf); + return su_scratch_buf; +} diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 9d40fc57cd..9cbcf9c9af 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -133,6 +133,9 @@ struct snmp_session g_snmp_sess, *g_snmp_sess_p; const char *OID_pwr_status; int g_pwr_battery; int pollfreq; /* polling frequency */ +int semistaticfreq; /* semistatic entry update frequency */ +static int semistatic_countdown = 0; + static int quirk_symmetra_threephase = 0; /* Number of device(s): standard is "1", but talking @@ -163,7 +166,7 @@ static const char *mibname; static const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" -#define DRIVER_VERSION "1.19" +#define DRIVER_VERSION "1.21" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -183,6 +186,12 @@ upsdrv_info_t upsdrv_info = { static time_t lastpoll = 0; +/* Communication status handling */ +#define COMM_UNKNOWN 0 +#define COMM_OK 1 +#define COMM_LOST 2 +static int comm_status = COMM_UNKNOWN; + /* template OIDs index start with 0 or 1 (estimated stable for a MIB), * automatically guessed at the first pass */ static int template_index_base = -1; @@ -190,6 +199,7 @@ static int template_index_base = -1; static int device_template_index_base = -1; /* OID index of the 1rst daisychained device */ static int outlet_template_index_base = -1; static int outletgroup_template_index_base = -1; +static int ambient_template_index_base = -1; static int device_template_offset = -1; /* sysOID location */ @@ -241,8 +251,8 @@ void upsdrv_initinfo(void) && !(su_info_p->flags & SU_OUTLET_GROUP)) { /* first check that this OID actually exists */ -/* FIXME: daisychain commands support! */ -su_addcmd(su_info_p); + /* FIXME: daisychain commands support! */ + su_addcmd(su_info_p); /* if (nut_snmp_get(su_info_p->OID) != NULL) { dstate_addcmd(su_info_p->info_type); @@ -263,9 +273,11 @@ su_addcmd(su_info_p); /* initialize all other INFO_ fields from list */ if (snmp_ups_walk(SU_WALKMODE_INIT) == TRUE) { dstate_dataok(); + comm_status = COMM_OK; } else { dstate_datastale(); + comm_status = COMM_LOST; } /* setup handlers for instcmd and setvar functions */ @@ -288,10 +300,12 @@ void upsdrv_updateinfo(void) if (snmp_ups_walk(SU_WALKMODE_UPDATE)) { upsdebugx(1, "%s: pollfreq: Data OK", __func__); dstate_dataok(); + comm_status = COMM_OK; } else { upsdebugx(1, "%s: pollfreq: Data STALE", __func__); dstate_datastale(); + comm_status = COMM_LOST; } /* Commit status first, otherwise in daisychain mode, "device.0" may @@ -307,8 +321,11 @@ void upsdrv_updateinfo(void) lastpoll = time(NULL); } else { - /* Just tell everything is ok to upsd */ - dstate_dataok(); + /* Just tell the same status to upsd */ + if (comm_status == COMM_OK) + dstate_dataok(); + else + dstate_datastale(); } } @@ -368,6 +385,8 @@ void upsdrv_makevartable(void) "Set SNMP version (default=v1, allowed: v2c,v3)"); addvar(VAR_VALUE, SU_VAR_POLLFREQ, "Set polling frequency in seconds, to reduce network flow (default=30)"); + addvar(VAR_VALUE, SU_VAR_SEMISTATICFREQ, + "Set semistatic value update frequency in update cycles, to reduce network flow (default=10)"); addvar(VAR_VALUE, SU_VAR_RETRIES, "Specifies the number of Net-SNMP retries to be used in the requests (default=5)"); addvar(VAR_VALUE, SU_VAR_TIMEOUT, @@ -597,6 +616,17 @@ void upsdrv_initups(void) else pollfreq = DEFAULT_POLLFREQ; + /* init semistatic update frequency */ + if (getval(SU_VAR_SEMISTATICFREQ)) + semistaticfreq = atoi(getval(SU_VAR_SEMISTATICFREQ)); + else + semistaticfreq = DEFAULT_SEMISTATICFREQ; + if (semistaticfreq < 1) { + upsdebugx(1, "Bad %s value provided, setting to default", SU_VAR_SEMISTATICFREQ); + semistaticfreq = DEFAULT_SEMISTATICFREQ; + } + semistatic_countdown = semistaticfreq; + /* Get UPS Model node to see if there's a MIB */ /* FIXME: extend and use match_model_OID(char *model) */ su_info_p = su_find_info("ups.model"); @@ -1251,8 +1281,6 @@ static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_l snprintf(buf, buf_len, "%s", oid_leaf+1); upsdebugx(3, "Fallback value: %s", buf); } - else - snprintf(buf, buf_len, "%s", tmp_buf); break; default: return FALSE; @@ -2069,6 +2097,7 @@ long su_find_valinfo(info_lkp_t *oid2info, const char* value) /* String reformating function */ const char *su_find_strval(info_lkp_t *oid2info, void *value) { +#if WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { upsdebugx(2, "%s: using generic lookup function (string reformatting)", __func__); @@ -2077,6 +2106,10 @@ const char *su_find_strval(info_lkp_t *oid2info, void *value) return retvalue; } upsdebugx(1, "%s: no result value for this OID string value (%s)", __func__, (char*)value); +#else + NUT_UNUSED_VARIABLE(oid2info); + upsdebugx(1, "%s: no mapping function for this OID string value (%s)", __func__, (char*)value); +#endif // WITH_SNMP_LKP_FUN return NULL; } @@ -2086,6 +2119,7 @@ const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) info_lkp_t *info_lkp; long value = *((long *)raw_value); +#if WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { upsdebugx(2, "%s: using generic lookup function", __func__); @@ -2093,6 +2127,7 @@ const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) upsdebugx(2, "%s: got value '%s'", __func__, retvalue); return retvalue; } +#endif // WITH_SNMP_LKP_FUN /* Otherwise, use the simple values mapping */ for (info_lkp = oid2info; (info_lkp != NULL) && @@ -2190,7 +2225,7 @@ static bool_t is_multiple_template(const char *OID_template) } /* Instantiate an snmp_info_t from a template. - * Useful for outlet and outlet.group templates. + * Useful for device, outlet, outlet.group and ambient templates. * Note: remember to adapt info_type, OID and optionaly dfl */ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) { @@ -2274,6 +2309,9 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) case SU_DAISY: template_index_base = device_template_index_base; break; + case SU_AMBIENT_TEMPLATE: + template_index_base = ambient_template_index_base; + break; default: /* we should never fall here! */ upsdebugx(3, "%s: unknown template type '%" PRI_SU_FLAGS "' for %s", @@ -2338,6 +2376,8 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) outlet_template_index_base = base_index; else if (su_info_p->flags & SU_OUTLET_GROUP) outletgroup_template_index_base = base_index; + else if (su_info_p->flags & SU_AMBIENT_TEMPLATE) + ambient_template_index_base = base_index; else device_template_index_base = base_index; } @@ -2446,6 +2486,7 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ else { template_count = atoi(dstate_getinfo(template_count_var)); } + upsdebugx(1, "%i instances found...", template_count); /* Only instantiate templates if needed! */ if (template_count > 0) { @@ -2458,6 +2499,7 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ cur_template_number < (template_count + base_snmp_index) ; cur_template_number++) { + upsdebugx(1, "Processing instance %i/%i...", cur_template_number, template_count); /* Special processing for daisychain: * append 'device.x' to the NUT variable name, except for the * whole daisychain ("device.0") */ @@ -2497,7 +2539,7 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ #endif } } - else /* Outlet and outlet groups templates */ + else if (!strncmp(type, "outlet", 6)) /* Outlet and outlet groups templates */ { /* Get the index of the current template instance */ cur_nut_index = cur_template_number; @@ -2534,6 +2576,44 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ su_info_p->info_type, cur_nut_index); } } + else if (!strncmp(type, "ambient", 7)) + { + /* FIXME: can be grouped with outlet* above */ + /* Get the index of the current template instance */ + cur_nut_index = cur_template_number; + + /* Special processing for daisychain */ + if (daisychain_enabled == TRUE) { + /* Only publish on the daisychain host */ + if ( (su_info_p->flags & SU_TYPE_DAISY_MASTER_ONLY) + && (current_device_number != 1) ) { + upsdebugx(2, "discarding variable due to daisychain master flag"); + continue; + } + + /* Device(s) 1-N (master + slave(s)) need to append 'device.x' */ + if ((devices_count > 1) && (current_device_number > 0)) { + memset(&tmp_buf[0], 0, SU_INFOSIZE); + strcat(&tmp_buf[0], "device.%i."); + strcat(&tmp_buf[0], su_info_p->info_type); + + upsdebugx(4, "FORMATTING STRING = %s", &tmp_buf[0]); + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + &tmp_buf[0], current_device_number, cur_nut_index); + } + else { + /* FIXME: daisychain-whole, what to do? */ + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else { + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else + upsdebugx(4, "Error: unknown template type '%s", type); /* check if default value is also a template */ if ((cur_info_p.dfl != NULL) && @@ -2562,9 +2642,14 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, current_device_number + device_template_offset, cur_template_number); } - else { + else if (su_info_p->flags & SU_TYPE_DAISY_2) { snprintf((char *)cur_info_p.OID, SU_INFOSIZE, - su_info_p->OID, cur_template_number + device_template_offset, current_device_number - device_template_offset); + su_info_p->OID, cur_template_number + device_template_offset, + current_device_number - device_template_offset); + } + else { + /* Note: no device daisychain templating (SU_TYPE_DAISY_MASTER_ONLY)! */ + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, cur_template_number); } } else { @@ -2620,6 +2705,10 @@ snmp_info_flags_t get_template_type(const char* varname) upsdebugx(4, "device template"); return SU_DAISY; } + else if (!strncmp(varname, "ambient", 7)) { + upsdebugx(4, "ambient template"); + return SU_AMBIENT_TEMPLATE; + } else { upsdebugx(2, "Unknown template type: %s", varname); return 0; @@ -2639,6 +2728,8 @@ int extract_template_number(snmp_info_flags_t template_type, const char* varname item_number_ptr = &varname[6]; else if (template_type & SU_DAISY) item_number_ptr = &varname[6]; + else if (template_type & SU_AMBIENT_TEMPLATE) + item_number_ptr = &varname[7]; else return -1; @@ -2725,17 +2816,43 @@ bool_t daisychain_init() daisychain_enabled = TRUE; /* Try to get the OID value, if it's not a template */ + upsdebugx(3, "OID for device.count is %s", + su_info_p->OID ? su_info_p->OID : ""); if ((su_info_p->OID != NULL) && (strstr(su_info_p->OID, "%i") == NULL)) { - if (nut_snmp_get_int(su_info_p->OID, &devices_count) == TRUE) - upsdebugx(1, "There are %ld device(s) present", devices_count); - else - { - upsdebugx(1, "Error: can't get the number of device(s) present!"); - upsdebugx(1, "Falling back to 1 device!"); - devices_count = 1; +#if WITH_SNMP_LKP_FUN + devices_count = -1; + /* First test if we have a generic lookup function + * FIXME: Check if the field type is a string? + */ + /* TODO: backport the 2x2 mapping function support + * and this would be "fun_s2l" in resulting codebase + */ + if ( (su_info_p->oid2info != NULL) && (su_info_p->oid2info->nuf_s2l != NULL) ) { + char buf[1024]; + upsdebugx(2, "%s: using generic string-to-long lookup function", __func__); + if (TRUE == nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info)) { + devices_count = su_info_p->oid2info->nuf_s2l(buf); + upsdebugx(2, "%s: got value '%ld'", __func__, devices_count); + } } + + if (devices_count == -1) { +#endif /* WITH_SNMP_LKP_FUN */ + + if (nut_snmp_get_int(su_info_p->OID, &devices_count) == TRUE) + upsdebugx(1, "There are %ld device(s) present", devices_count); + else + { + upsdebugx(1, "Error: can't get the number of device(s) present!"); + upsdebugx(1, "Falling back to 1 device!"); + devices_count = 1; + } + +#if WITH_SNMP_LKP_FUN + } +#endif /* WITH_SNMP_LKP_FUN */ } /* Otherwise (template), use the guesstimation function to get * the number of devices present */ @@ -2751,10 +2868,9 @@ bool_t daisychain_init() daisychain_enabled = FALSE; upsdebugx(1, "Devices count is less than 1!"); upsdebugx(1, "Falling back to 1 device and disabling daisychain support!"); - } - - /* Publish the device(s) count */ - if (devices_count > 1) { + } else { + /* Publish the device(s) count - even if just one + * device was recognized at this moment */ dstate_setinfo("device.count", "%ld", devices_count); /* Also publish the default value for mfr and a forged model @@ -2956,6 +3072,12 @@ bool_t snmp_ups_walk(int mode) snmp_info_t *su_info_p; bool_t status = FALSE; + if (mode == SU_WALKMODE_UPDATE) { + semistatic_countdown--; + if (semistatic_countdown < 0) + semistatic_countdown = semistaticfreq; + } + /* Loop through all device(s) */ /* Note: considering "unitary" and "daisy-chained" devices, we have * several variables (and their values) that can come into play: @@ -3063,6 +3185,13 @@ bool_t snmp_ups_walk(int mode) if ((mode == SU_WALKMODE_UPDATE) && !(su_info_p->flags & SU_FLAG_OK)) continue; + /* skip semi-static elements in update mode: only parse when countdown reaches 0 */ + if ((mode == SU_WALKMODE_UPDATE) && (su_info_p->flags & SU_FLAG_SEMI_STATIC)) { + if (semistatic_countdown != 0) + continue; + upsdebugx(1, "Refreshing semi-static entry %s", su_info_p->OID); + } + /* skip static elements in update mode */ if ((mode == SU_WALKMODE_UPDATE) && (su_info_p->flags & SU_FLAG_STATIC)) continue; @@ -3072,7 +3201,8 @@ bool_t snmp_ups_walk(int mode) * Not applicable to outlets (need SU_FLAG_STATIC tagging) */ if ((su_info_p->flags & SU_FLAG_ABSENT) && !(su_info_p->flags & SU_OUTLET) - && !(su_info_p->flags & SU_OUTLET_GROUP)) + && !(su_info_p->flags & SU_OUTLET_GROUP) + && !(su_info_p->flags & SU_AMBIENT_TEMPLATE)) { if (mode == SU_WALKMODE_INIT) { @@ -3144,6 +3274,13 @@ bool_t snmp_ups_walk(int mode) else status = process_template(mode, "outlet.group", su_info_p); } + else if (su_info_p->flags & SU_AMBIENT_TEMPLATE) { + /* Skip commands after init */ + if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) + continue; + else + status = process_template(mode, "ambient", su_info_p); + } else { /* if (daisychain_enabled == TRUE) { status = process_template(mode, "device", su_info_p); @@ -3187,7 +3324,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) upsdebugx(2, "%s: %s %s", __func__, su_info_p->info_type, su_info_p->OID); /* Check if this is a daisychain template */ - if ((format_char = strchr(su_info_p->OID, '%')) != NULL) { + if (su_info_p->OID != NULL + && (format_char = strchr(su_info_p->OID, '%')) != NULL + ) { upsdebugx(3, "%s: calling instantiate_info() for " "daisy-chain template", __func__); tmp_info_p = instantiate_info(su_info_p, tmp_info_p); @@ -3407,7 +3546,14 @@ bool_t su_ups_get(snmp_info_t *su_info_p) return TRUE; } - if (su_info_p->info_flags & ST_FLAG_STRING) { + /* special treatment for element without oid but with default value */ + if (su_info_p->OID == NULL && su_info_p->dfl != NULL) { + status = TRUE; + /* FIXME: strlcpy() would fit here safer; not used in NUT yet */ + strncpy(buf, su_info_p->dfl, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + } + else if (su_info_p->info_flags & ST_FLAG_STRING) { upsdebugx(2, "%s: requesting nut_snmp_get_str(), " "with%s daisy template originally", __func__, (format_char!=NULL ? "" : "out")); diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 4c870138d3..c1549870ff 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -90,6 +90,7 @@ #define DEFAULT_POLLFREQ 30 /* in seconds */ #define DEFAULT_NETSNMP_RETRIES 5 #define DEFAULT_NETSNMP_TIMEOUT 1 /* in seconds */ +#define DEFAULT_SEMISTATICFREQ 10 /* in snmpwalk update cycles */ /* use explicit booleans */ #ifndef FALSE @@ -108,10 +109,34 @@ typedef int bool_t; /* typedef void (*interpreter)(char *, char *, int); */ +#ifndef WITH_SNMP_LKP_FUN +/* Recent addition of fun/nuf hooks in info_lkp_t is not well handled by + * all corners of the codebase, e.g. not by DMF. So at least until that + * is fixed, (TODO) we enable those bits of code only optionally during + * a build for particular usage. Conversely, experimenters can define + * this macro to a specific value while building the codebase and see + * what happens under different conditions ;) + */ +# if (defined WITH_DMFMIB) && (WITH_DMFMIB != 0) +# define WITH_SNMP_LKP_FUN 0 +# else +# define WITH_SNMP_LKP_FUN 1 +# endif +#endif + +#ifndef WITH_SNMP_LKP_FUN_DUMMY +# define WITH_SNMP_LKP_FUN_DUMMY 0 +#endif + /* for lookup between OID values and INFO_ value */ typedef struct { int oid_value; /* SNMP OID value */ const char *info_value; /* NUT INFO_* value */ +#if WITH_SNMP_LKP_FUN +/* FIXME: Currently we do not have a way to provide custom C code + * via DMF - keep old approach until we get the ability, e.g. by + * requiring a LUA implementation to be passed alongside C lookups. + */ /* * Currently there are a few cases using a "fun_vp2s" type of lookup * function, while the "nuf_s2l" type was added for completeness but @@ -123,6 +148,7 @@ typedef struct { */ const char *(*fun_vp2s)(void *snmp_value); /* optional SNMP to NUT mapping function, converting a pointer to SNMP data (e.g. numeric or string) into a NUT string */ long (*nuf_s2l)(const char *nut_value); /* optional NUT to SNMP mapping function, converting a NUT string into SNMP numeric data */ +#endif /* WITH_SNMP_LKP_FUN */ } info_lkp_t; /* Structure containing info about one item that can be requested @@ -157,7 +183,7 @@ typedef struct { info_lkp_t *oid2info; /* lookup table between OID and NUT values */ } snmp_info_t; -/* "flags" bits 0..8 (and 9 reserved for DMF) */ +/* "flags" bits 0..9 */ #define SU_FLAG_OK (1UL << 0) /* show element to upsd - * internal to snmp driver */ #define SU_FLAG_STATIC (1UL << 1) /* retrieve info only once. */ @@ -175,9 +201,9 @@ typedef struct { #define SU_FLAG_NAINVALID (1UL << 7) /* Invalid if "N/A" value */ #define SU_CMD_OFFSET (1UL << 8) /* Add +1 to the OID index */ -/* Reserved slot -- to import from DMF branch codebase: -//#define SU_FLAG_SEMI_STATIC (1UL << 9)*/ /* Refresh this entry once in several walks -// * (for R/W values user can set on device, like descriptions or contacts) */ +#define SU_FLAG_SEMI_STATIC (1UL << 9) /* Refresh this entry once in several walks + * (for R/W values user can set on device, + * like descriptions or contacts) */ /* Notes on outlet templates usage: * - outlet.count MUST exist and MUST be declared before any outlet template @@ -222,17 +248,14 @@ typedef struct { /* "flags" bits 21..23 (and 24 reserved for DMF) */ #define SU_TYPE_DAISY_1 (1UL << 21) /* Daisychain index is the 1st specifier */ #define SU_TYPE_DAISY_2 (1UL << 22) /* Daisychain index is the 2nd specifier */ -#define SU_TYPE_DAISY(t) ((t)->flags & (3UL << 21)) /* Mask the 2 SU_TYPE_DAISY_* but not SU_DAISY */ +#define SU_TYPE_DAISY(t) ((t)->flags & (11UL << 21)) /* Mask the SU_TYPE_DAISY_{1,2,MASTER_ONLY} but not SU_DAISY */ #define SU_DAISY (1UL << 23) /* Daisychain template definition - set at run-time for devices with detected "device.count" over 1 */ -/* NOTE: Previously SU_DAISY had same bit-flag value as SU_TYPE_DAISY_2*/ -/* Reserved slot -- to import from DMF branch codebase -// (and change SU_TYPE_DAISY to 11UL<<21 for the 3 types then): -//#define SU_TYPE_DAISY_MASTER_ONLY (1UL << 24)*/ /* Only valid for daisychain master (device.1) */ +/* NOTE: Previously SU_DAISY had same bit-flag value as SU_TYPE_DAISY_2 */ +#define SU_TYPE_DAISY_MASTER_ONLY (1UL << 24) /* Only valid for daisychain master (device.1) */ /* Free slot: (1UL << 25) */ -/* Reserved slot -- to import from DMF branch codebase: -//#define SU_AMBIENT_TEMPLATE (1UL << 26)*/ /* ambient template definition */ +#define SU_AMBIENT_TEMPLATE (1UL << 26) /* ambient template definition */ /* Reserved slot -- to import from DMF branch codebase: //#define SU_FLAG_FUNCTION (1UL << 27) @@ -255,6 +278,7 @@ typedef struct { #define SU_VAR_VERSION "snmp_version" #define SU_VAR_RETRIES "snmp_retries" #define SU_VAR_TIMEOUT "snmp_timeout" +#define SU_VAR_SEMISTATICFREQ "semistaticfreq" #define SU_VAR_MIBS "mibs" #define SU_VAR_POLLFREQ "pollfreq" /* SNMP v3 related parameters */ @@ -349,6 +373,17 @@ extern info_lkp_t su_convert_to_iso_date_info[]; /* Name the mapping location in that array for consumers to reference */ #define FUNMAP_USDATE_TO_ISODATE 0 +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(void *raw_snmp_value); + +/* Temperature handling, to convert back to Celsius (NUT standard) */ +extern int temperature_unit; + +#define TEMPERATURE_UNKNOWN 0 +#define TEMPERATURE_CELSIUS 1 +#define TEMPERATURE_KELVIN 2 +#define TEMPERATURE_FAHRENHEIT 3 + /***************************************************** * End of Subdrivers shared helpers functions *****************************************************/ @@ -366,6 +401,7 @@ extern const char *OID_pwr_status; extern int g_pwr_battery; extern int pollfreq; /* polling frequency */ extern int input_phases, output_phases, bypass_phases; +extern int semistaticfreq; /* semistatic entry update frequency */ /* pointer to the Snmp2Nut lookup table */ extern mib2nut_info_t *mib2nut_info;