diff --git a/doc/Developing/Sensor-State-Support.md b/doc/Developing/Sensor-State-Support.md index d27170168a50..c76c55a0130a 100644 --- a/doc/Developing/Sensor-State-Support.md +++ b/doc/Developing/Sensor-State-Support.md @@ -48,6 +48,66 @@ We also map these values to the actual state sensor(state_index) where these val *Is as you might have guessed, where the sensor_id is mapped to a state_index_id.* ### Example + +For YAML based state discovery: + +```yaml +mib: NETBOTZV2-MIB +modules: + sensors: + state: + - + oid: dryContactSensorTable + value: dryContactSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.2.1.1.2. + descr: dryContactSensorLabel + index: 'dryContactSensor.{{ $index }}' + state_name: dryContactSensor + states: + - { descr: 'null', graph: 0, value: -1, generic: 3 } + - { descr: open, graph: 0, value: 0, generic: 0 } + - { descr: closed, graph: 0, value: 1, generic: 2 } + - + oid: doorSwitchSensorTable + value: doorSwitchSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.2.2.1.2. + descr: doorSwitchSensorLabel + index: 'doorSwitchSensor.{{ $index }}' + state_name: doorSwitchSensor + states: + - { descr: 'null', graph: 0, value: -1, generic: 3 } + - { descr: open, graph: 0, value: 0, generic: 0 } + - { descr: closed, graph: 0, value: 1, generic: 2 } + - + oid: cameraMotionSensorTable + value: cameraMotionSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.2.3.1.2. + descr: cameraMotionSensorLabel + index: 'cameraMotionSensor.{{ $index }}' + state_name: cameraMotionSensor + states: + - { descr: 'null', graph: 0, value: -1, generic: 3 } + - { descr: noMotion, graph: 0, value: 0, generic: 0 } + - { descr: motionDetected, graph: 0, value: 1, generic: 2 } + - + oid: otherStateSensorTable + value: otherStateSensorErrorStatus + num_oid: .1.3.6.1.4.1.5528.100.4.2.10.1.3. + descr: otherStateSensorLabel + index: '{{ $index }}' + state_name: otherStateSensorErrorStatus + states: + - { descr: normal, graph: 0, value: 0, generic: 0 } + - { descr: info, graph: 0, value: 1, generic: 1 } + - { descr: warning, graph: 0, value: 2, generic: 1 } + - { descr: error, graph: 0, value: 3, generic: 2 } + - { descr: critical, graph: 0, value: 4, generic: 2 } + - { descr: failure, graph: 0, value: 5, generic: 2 } + +``` + +For advanced state discovery: + This example will be based on a Cisco power supply sensor and is all it takes to have sensor state support for Cisco power supplys in Cisco switches. The file should be located in /includes/discovery/sensors/state/cisco.inc.php. diff --git a/doc/Developing/os/Health-Information.md b/doc/Developing/os/Health-Information.md index 9bdcb007964b..a14bfff2a3d1 100644 --- a/doc/Developing/os/Health-Information.md +++ b/doc/Developing/os/Health-Information.md @@ -1,5 +1,7 @@ source: Developing/os/Health-Information.md +#### Sensors + This document will guide you through adding health / sensor information for your new device. Currently we have support for the following health metrics along with the values we expect to see the data in: @@ -17,10 +19,62 @@ Currently we have support for the following health metrics along with the values | power | W | | runtime | Min | | signal | dBm | +| snr | SNR | | state | # | | temperature | C | | voltage | V | +#### Simple health discovery + +We have support for defining health / sensor discovery using YAML files so that you don't need to know how to write PHP. + +All yaml files are located in `includes/definitions/discovery/$os.yaml`. Defining the information hear is not always +possible and is heavily reliant on vendors being sensible with the MIBs they generate. Only snmp walks are supported +and you must provide a sane table that can be traversed and contains all of the data you need. We will use netbotz as +an example here. + +`includes/definitions/discovery/netbotz.yaml` + +```yaml +mib: NETBOTZV2-MIB +modules: + sensors: + airflow: + - + oid_name: netbotz_airflow + oid: airFlowSensorTable + value: airFlowSensorValue + divisor: 10 + num_oid: .1.3.6.1.4.1.5528.100.4.1.5.1.2. + descr: airFlowSensorLabel + index: 'airFlowSensorValue.{{ $index }}' +``` + +At the top you can define one or more mibs to be used in the lookup of data: + +`mib: NETBOTZV2-MIB` + +The only sensor we have defined here is airflow. The available options are as follows: + + - `oid` (required): This is the name of the table you want to do the snmp walk on. + - `value` (required): This is the key within the table that contains the value. + - `num_oid` (required): This is the numerical OID that contains `value`. This should always be without the appended `index`. + - `divisor` (optional): This is the divisor to use against the returned `value`. + - `multiplier` (optional): This is the multiplier to use against the returned `value`. + - `low_limit` (optional): This is the critical low threshold that `value` should be (used in alerting). + - `low_warn_limit` (optional): This is the warning low threshold that `value` should be (used in alerting). + - `warn_limit` (optional): This is the warning high threshold that `value` should be (used in alerting). + - `high_limit` (optional): This is the critical high threshold that `value` should be (used in alerting). + - `descr` (required): This is the key within the table that contains the description of this sensor. + - `index` (optional): This is the index value we use to uniquely identify this sensor. `{{ $index }}` will be replaced by the `index` from the snmp walk. + - `skip_values` (optional): This is an array of values we should skip over. + +If you aren't able to use yaml to perform the sensor discovery, you will most likely need to use Advanced health discovery. + +#### Advanced health discovery + +If you can't use the yaml files as above, then you will need to create the discovery code in php. + The directory structure for sensor information is `includes/discovery/sensors/$class/$os.inc.php`. The format of all of the sensors follows the same code format which is to call the `discover_sensor()` function - with the exception of state which requires additional code. diff --git a/includes/definitions/discovery/netbotz.yaml b/includes/definitions/discovery/netbotz.yaml new file mode 100644 index 000000000000..7e7c10ddecd7 --- /dev/null +++ b/includes/definitions/discovery/netbotz.yaml @@ -0,0 +1,80 @@ +mib: NETBOTZV2-MIB +modules: + sensors: + airflow: + - + oid: airFlowSensorTable + value: airFlowSensorValue + divisor: 10 + num_oid: .1.3.6.1.4.1.5528.100.4.1.5.1.2. + descr: airFlowSensorLabel + index: 'airFlowSensorValue.{{ $index }}' + temperature: + - + oid: dewPointSensorTable + value: dewPointSensorValue + divisor: 10 + num_oid: .1.3.6.1.4.1.5528.100.4.1.3.1.2. + descr: dewPointSensorLabel + index: 'dewPointSensorValue.{{ $index }}' + - + oid: tempSensorTable + value: tempSensorValueInt + num_oid: .1.3.6.1.4.1.5528.100.4.1.1.1.8. + descr: tempSensorLabel + index: '{{ $index }}' + humidity: + - + oid: humiSensorTable + value: humiSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.1.2.1.8. + descr: humiSensorLabel + index: '{{ $index }}' + state: + - + oid: dryContactSensorTable + value: dryContactSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.2.1.1.2. + descr: dryContactSensorLabel + index: 'dryContactSensor.{{ $index }}' + state_name: dryContactSensor + states: + - { descr: 'null', graph: 0, value: -1, generic: 3 } + - { descr: open, graph: 0, value: 0, generic: 0 } + - { descr: closed, graph: 0, value: 1, generic: 2 } + - + oid: doorSwitchSensorTable + value: doorSwitchSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.2.2.1.2. + descr: doorSwitchSensorLabel + index: 'doorSwitchSensor.{{ $index }}' + state_name: doorSwitchSensor + states: + - { descr: 'null', graph: 0, value: -1, generic: 3 } + - { descr: open, graph: 0, value: 0, generic: 0 } + - { descr: closed, graph: 0, value: 1, generic: 2 } + - + oid: cameraMotionSensorTable + value: cameraMotionSensorValue + num_oid: .1.3.6.1.4.1.5528.100.4.2.3.1.2. + descr: cameraMotionSensorLabel + index: 'cameraMotionSensor.{{ $index }}' + state_name: cameraMotionSensor + states: + - { descr: 'null', graph: 0, value: -1, generic: 3 } + - { descr: noMotion, graph: 0, value: 0, generic: 0 } + - { descr: motionDetected, graph: 0, value: 1, generic: 2 } + - + oid: otherStateSensorTable + value: otherStateSensorErrorStatus + num_oid: .1.3.6.1.4.1.5528.100.4.2.10.1.3. + descr: otherStateSensorLabel + index: '{{ $index }}' + state_name: otherStateSensorErrorStatus + states: + - { descr: normal, graph: 0, value: 0, generic: 0 } + - { descr: info, graph: 0, value: 1, generic: 1 } + - { descr: warning, graph: 0, value: 2, generic: 1 } + - { descr: error, graph: 0, value: 3, generic: 2 } + - { descr: critical, graph: 0, value: 4, generic: 2 } + - { descr: failure, graph: 0, value: 5, generic: 2 } diff --git a/includes/discovery/functions.inc.php b/includes/discovery/functions.inc.php index 469ba5ea312c..70138d908bd0 100644 --- a/includes/discovery/functions.inc.php +++ b/includes/discovery/functions.inc.php @@ -85,9 +85,23 @@ function discover_new_device($hostname, $device = '', $method = '', $interface = d_echo("$ip not in a matched network - skipping\n"); }//end if } - //end discover_new_device() +/** + * @param $device + */ +function load_discovery(&$device) +{ + global $config; + $yaml_discovery = $config['install_dir'] . '/includes/definitions/discovery/' . $device['os'] . '.yaml'; + if (file_exists($yaml_discovery)) { + $device['dynamic_discovery'] = Symfony\Component\Yaml\Yaml::parse( + file_get_contents($yaml_discovery) + ); + } + unset($yaml_discovery); +} + function discover_device(&$device, $options = null) { global $config, $valid; @@ -119,6 +133,7 @@ function discover_device(&$device, $options = null) } load_os($device); + load_discovery($device); if (is_array($config['os'][$device['os']]['register_mibs'])) { register_mibs($device, $config['os'][$device['os']]['register_mibs'], 'includes/discovery/os/' . $device['os'] . '.inc.php'); } @@ -182,14 +197,11 @@ function discover_device(&$device, $options = null) echo "\n"; $discovered_devices++; } - //end discover_device() -// Discover sensors - +// Discover sensors function discover_sensor(&$valid, $class, $device, $oid, $index, $type, $descr, $divisor = 1, $multiplier = 1, $low_limit = null, $low_warn_limit = null, $warn_limit = null, $high_limit = null, $current = null, $poller_type = 'snmp', $entPhysicalIndex = null, $entPhysicalIndex_measured = null, $user_func = null) { - $low_limit = set_null($low_limit); $low_warn_limit = set_null($low_warn_limit); $warn_limit = set_null($warn_limit); @@ -1032,6 +1044,70 @@ function ignore_storage($descr) return $deny; } +function discovery_process(&$valid, $device, $sensor_type, $pre_cache) +{ + if ($device['dynamic_discovery']['modules']['sensors'][$sensor_type]) { + foreach ($device['dynamic_discovery']['modules']['sensors'][$sensor_type] as $data) { + $tmp_name = $data['oid']; + $raw_data = $pre_cache[$tmp_name]; + foreach ($raw_data as $index => $snmp_data) { + $skip = false; + $value = $snmp_data[$data['value']]; + foreach ((array)$data['skip_values'] as $skip_value) { + echo "Here $value and $skip_value END\n"; + if ($value == $skip_value) { + $skip = true; + } + } + if ($skip === false && is_numeric($value)) { + $oid = $data['num_oid'] . $index; + if (isset($snmp_data[$data['descr']])) { + $descr = $snmp_data[$data['descr']]; + } else { + $descr = str_replace('{{ $index }}', $index, $data['descr']); + } + $divisor = $data['divisor'] ?: 1; + $multiplier = $data['multiplier'] ?: 1; + $low_limit = $data['low_limit'] ?: 'null'; + $low_warn_limit = $data['low_warn_limit'] ?: 'null'; + $warn_limit = $data['warn_limit'] ?: 'null'; + $high_limit = $data['high_limit'] ?: 'null'; + $state_name = ''; + if ($sensor_type !== 'state') { + if (is_numeric($divisor)) { + $value = $value / $divisor; + } + if (is_numeric($multiplier)) { + $value = $value * $multiplier; + } + } else { + $state_name = $data['descr']; + $state_index_id = create_state_index($state_name); + foreach ($data['states'] as $state) { + $insert = array( + 'state_index_id' => $state_index_id, + 'state_descr' => $state['descr'], + 'state_draw_graph' => $state['graph'], + 'state_value' => $state['value'], + 'state_generic_value' => $state['generic'] + ); + dbInsert($insert, 'state_translations'); + } + } + $tmp_index = $data['index'] ?: $index; + $uindex = str_replace('{{ $index }}', $index, $tmp_index); + if ($sensor_type === 'state') { + discover_sensor($valid['sensor'], $sensor_type, $device, $oid, $uindex, $state_name, $descr, $divisor, $multiplier, $low_limit, $low_warn_limit, $warn_limit, $high_limit, $value); + create_sensor_to_state_index($device, $state_name, $uindex); + } else { + discover_sensor($valid['sensor'], $sensor_type, $device, $oid, $uindex, $device['os'], $descr, $divisor, $multiplier, $low_limit, $low_warn_limit, $warn_limit, $high_limit, $value); + } + } + } + } + } +} + /** * @param $types * @param $device @@ -1055,6 +1131,7 @@ function sensors($types, $device, $valid, $pre_cache = array()) include $dir . '/rfc1628.inc.php'; } } + discovery_process($valid, $device, $sensor_type, $pre_cache); d_echo($valid['sensor'][$sensor_type]); check_valid_sensors($device, $sensor_type, $valid['sensor']); echo "\n"; diff --git a/includes/discovery/sensors.inc.php b/includes/discovery/sensors.inc.php index 47b66660919e..08af8c4e83a6 100644 --- a/includes/discovery/sensors.inc.php +++ b/includes/discovery/sensors.inc.php @@ -12,6 +12,19 @@ d_echo($pre_cache); } +if (isset($device['dynamic_discovery']['modules']['sensors'])) { + foreach ($device['dynamic_discovery']['modules']['sensors'] as $key => $data_array) { + foreach ($data_array as $data) { + foreach ((array)$data['oid'] as $oid) { + $tmp_name = $oid; + if (!isset($pre_cache[$tmp_name])) { + $pre_cache[$tmp_name] = snmpwalk_cache_oid($device, $oid, array(), $device['dynamic_discovery']['mib'], null, '-OeQUs'); + } + } + } + } +} + // Run custom sensors require 'includes/discovery/sensors/cisco-entity-sensor.inc.php'; require 'includes/discovery/sensors/entity-sensor.inc.php'; diff --git a/includes/discovery/sensors/airflow/netbotz.inc.php b/includes/discovery/sensors/airflow/netbotz.inc.php deleted file mode 100644 index 80ee1baa5668..000000000000 --- a/includes/discovery/sensors/airflow/netbotz.inc.php +++ /dev/null @@ -1,43 +0,0 @@ -. - * - * @package LibreNMS - * @link http://librenms.org - * @copyright 2017 Neil Lathwood - * @author Neil Lathwood - */ - -d_echo($pre_cache['netbotz_airflow']); - -if (is_array($pre_cache['netbotz_airflow'])) { - echo 'NetBotz '; - foreach ($pre_cache['netbotz_airflow'] as $index => $data) { - if ($data['airFlowSensorValue']) { - $divisor = 10; - $multiplier = 1; - $value = $data['airFlowSensorValue'] / $divisor; - $oid = '.1.3.6.1.4.1.5528.100.4.1.5.1.2.' . $index; - $index = 'airFlowSensorValue.' . $index; - $descr = $data['airFlowSensorLabel']; - if (is_numeric($value)) { - discover_sensor($valid['sensor'], 'airflow', $device, $oid, $index, 'netbotz', $descr, $divisor, $multiplier, null, null, null, null, $value); - } - } - } -} diff --git a/includes/discovery/sensors/humidity/netbotz.inc.php b/includes/discovery/sensors/humidity/netbotz.inc.php deleted file mode 100644 index 98bbd25ad939..000000000000 --- a/includes/discovery/sensors/humidity/netbotz.inc.php +++ /dev/null @@ -1,26 +0,0 @@ -= 0) { - discover_sensor($valid['sensor'], 'humidity', $device, $humidity_oid, $humidity_id, 'netbotz', $descr, '1', '1', null, null, null, null, $humidity); - } - } - - unset($data); -} - -unset($oids); diff --git a/includes/discovery/sensors/pre-cache/netbotz.inc.php b/includes/discovery/sensors/pre-cache/netbotz.inc.php deleted file mode 100644 index de8ec4b9c007..000000000000 --- a/includes/discovery/sensors/pre-cache/netbotz.inc.php +++ /dev/null @@ -1,30 +0,0 @@ -. - * - * @package LibreNMS - * @link http://librenms.org - * @copyright 2017 Neil Lathwood - * @author Neil Lathwood - */ - -$pre_cache['netbotz_airflow'] = snmpwalk_cache_oid($device, 'airFlowSensorTable', array(), 'NETBOTZV2-MIB'); -$pre_cache['netbotz_temperature'] = snmpwalk_cache_oid($device, 'dewPointSensorTable', array(), 'NETBOTZV2-MIB'); -$pre_cache['netbotz_state'] = snmpwalk_cache_oid($device, 'dryContactSensorTable', array(), 'NETBOTZV2-MIB', null, '-OeQUs'); -$pre_cache['netbotz_state'] = snmpwalk_cache_oid($device, 'doorSwitchSensorTable', $pre_cache['netbotz_state'], 'NETBOTZV2-MIB', null, '-OeQUs'); -$pre_cache['netbotz_state'] = snmpwalk_cache_oid($device, 'cameraMotionSensorTable', $pre_cache['netbotz_state'], 'NETBOTZV2-MIB', null, '-OeQUs'); diff --git a/includes/discovery/sensors/state/netbotz.inc.php b/includes/discovery/sensors/state/netbotz.inc.php index 855f374cee9f..552e3fa1d0ee 100644 --- a/includes/discovery/sensors/state/netbotz.inc.php +++ b/includes/discovery/sensors/state/netbotz.inc.php @@ -9,94 +9,3 @@ * option) any later version. Please see LICENSE.txt at the top level of * the source code distribution for details. */ - -$temp = snmpwalk_cache_multi_oid($device, 'otherStateSensorTable', array(), 'NETBOTZ410-MIB'); -$cur_oid = '.1.3.6.1.4.1.5528.100.4.2.10.1.3.'; - -if (is_array($temp)) { - //Create State Index - $state_name = 'otherStateSensorErrorStatus'; - $state_index_id = create_state_index($state_name); - - //Create State Translation - if ($state_index_id !== null) { - $states = array( - array($state_index_id, 'normal', 0, 0, 0), - array($state_index_id, 'info', 0, 1, 1), - array($state_index_id, 'warning', 0, 2, 1), - array($state_index_id, 'error', 0, 3, 2), - array($state_index_id, 'critical', 0, 4, 2), - array($state_index_id, 'failure', 0, 5, 2) - ); - foreach ($states as $value) { - $insert = array( - 'state_index_id' => $value[0], - 'state_descr' => $value[1], - 'state_draw_graph' => $value[2], - 'state_value' => $value[3], - 'state_generic_value' => $value[4] - ); - dbInsert($insert, 'state_translations'); - } - } - - foreach ($temp as $index => $entry) { - $descr = $temp[$index]['otherStateSensorLabel']; - //Discover Sensors - discover_sensor($valid['sensor'], 'state', $device, $cur_oid . $index, $index, $state_name, $descr, '1', '1', null, null, null, null, $temp[$index][' otherStateSensorErrorStatus'], 'snmp', $index); - - //Create Sensor To State Index - create_sensor_to_state_index($device, $state_name, $index); - } -} - - -foreach ($pre_cache['netbotz_state'] as $index => $data) { - if (is_array($data)) { - $tmp_keys = array_keys($data); - $state_name = str_replace('Id', '', $tmp_keys[0]); - $state_index_id = create_state_index($state_name); - if ($state_name === 'cameraMotionSensor') { - $states = array( - array($state_index_id, 'null', 0, -1, 3), - array($state_index_id, 'noMotion', 0, 0, 0), - array($state_index_id, 'motionDetected', 0, 1, 2), - ); - $oid = '.1.3.6.1.4.1.5528.100.4.2.3.1.2.'; - } elseif ($state_name === 'doorSwitchSensor') { - $states = array( - array($state_index_id, 'null', 0, -1, 3), - array($state_index_id, 'open', 0, 0, 0), - array($state_index_id, 'closed', 0, 1, 2), - ); - $oid = '.1.3.6.1.4.1.5528.100.4.2.2.1.2.'; - } elseif ($state_name === 'dryContactSensor') { - $states = array( - array($state_index_id, 'null', 0, -1, 3), - array($state_index_id, 'open', 0, 0, 0), - array($state_index_id, 'closed', 0, 1, 2), - ); - $oid = '.1.3.6.1.4.1.5528.100.4.2.1.1.2.'; - } - if ($state_index_id !== null) { - foreach ($states as $value) { - $insert = array( - 'state_index_id' => $value[0], - 'state_descr' => $value[1], - 'state_draw_graph' => $value[2], - 'state_value' => $value[3], - 'state_generic_value' => $value[4] - ); - dbInsert($insert, 'state_translations'); - } - } - $cur_oid = $oid . $index; - $index = $state_name . '.' . $index; - $descr = $data[$state_name . 'Label']; - $value = $data[$state_name . 'Value']; - if (isset($value)) { - discover_sensor($valid['sensor'], 'state', $device, $cur_oid, $index, $state_name, $descr, 1, 1, null, null, null, null, $value); - create_sensor_to_state_index($device, $state_name, $index); - } - } -} diff --git a/includes/discovery/sensors/temperature/netbotz.inc.php b/includes/discovery/sensors/temperature/netbotz.inc.php deleted file mode 100644 index 4c302937df89..000000000000 --- a/includes/discovery/sensors/temperature/netbotz.inc.php +++ /dev/null @@ -1,40 +0,0 @@ - $data) { - if ($data['dewPointSensorValue']) { - $divisor = 10; - $multiplier = 1; - $value = $data['dewPointSensorValue'] / $divisor; - $oid = '.1.3.6.1.4.1.5528.100.4.1.3.1.2.' . $index; - $index = 'dewPointSensorValue.' . $index; - $descr = $data['dewPointSensorLabel']; - if (is_numeric($value)) { - discover_sensor($valid['sensor'], 'temperature', $device, $oid, $index, 'netbotz', $descr, $divisor, $multiplier, null, null, null, null, $value); - } - } - } -} diff --git a/tests/YamlTest.php b/tests/YamlTest.php index 2d7a14613675..e0d371915fc4 100644 --- a/tests/YamlTest.php +++ b/tests/YamlTest.php @@ -32,7 +32,7 @@ class YamlTest extends \PHPUnit_Framework_TestCase { - public function testYaml() + public function testOSYaml() { global $config; @@ -49,4 +49,28 @@ public function testYaml() $this->assertArrayHasKey('text', $data, $file); } } + + public function testDiscoveryYaml() + { + global $config; + + $pattern = $config['install_dir'] . '/includes/definitions/discovery/*.yaml'; + foreach (glob($pattern) as $file) { + try { + $data = Yaml::parse(file_get_contents($file)); + } catch (ParseException $e) { + throw new PHPUnitException("$file Could not be parsed"); + } + + foreach ($data['modules'] as $module => $sub_modules) { + foreach ($sub_modules as $sub_module) { + foreach ($sub_module as $sensor) { + $this->assertArrayHasKey('oid', $sensor, $file); + $this->assertArrayHasKey('num_oid', $sensor, $file); + $this->assertArrayHasKey('value', $sensor, $file); + } + } + } + } + } }