Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Added support for sensors to be discovered from yaml #6859

Merged
merged 12 commits into from Jun 26, 2017
60 changes: 60 additions & 0 deletions doc/Developing/Sensor-State-Support.md
Expand Up @@ -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: otherStateSensorValue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the correct name is:
otherStateSensorErrorStatus

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, don't think it matters though.

num_oid: .1.3.6.1.4.1.5528.100.4.2.10.1.3.
descr: otherStateSensorLabel
index: '{{ $index }}'
state_name: otherStateSensorErrorStatus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about otherStateSensor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's how it is in the old code so replicating that.

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.

Expand Down
53 changes: 53 additions & 0 deletions 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:
Expand All @@ -17,10 +19,61 @@ 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.

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.
Expand Down
80 changes: 80 additions & 0 deletions 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: tempSensorValue
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: otherStateSensorValue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the correct name is:
otherStateSensorErrorStatus

num_oid: .1.3.6.1.4.1.5528.100.4.2.10.1.3.
descr: otherStateSensorLabel
index: '{{ $index }}'
state_name: otherStateSensorErrorStatus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about otherStateSensor?

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 }
80 changes: 75 additions & 5 deletions includes/discovery/functions.inc.php
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1032,6 +1044,63 @@ 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) {
$value = $snmp_data[$data['value']];
if ($snmp_data[$data['value']]) {
$oid = $data['num_oid'] . $index;
if (isset($snmp_data[$data['descr']])) {
$descr = $snmp_data[$data['descr']];
} else {
$descr = $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
Expand All @@ -1055,6 +1124,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";
Expand Down
11 changes: 11 additions & 0 deletions includes/discovery/sensors.inc.php
Expand Up @@ -12,6 +12,17 @@
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 = $data['oid'];
$pre_cache[$tmp_name] = snmpwalk_cache_oid($device, $oid, $pre_cache[$tmp_name], $device['dynamic_discovery']['mib'], null, '-OeQUs');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check if the oid has already been fetched here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And send an empty array to the walk function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Walking a single entry is ok but I'm not sure how well the code will work against that yet. I've added support for passing a description as a string as well as saying which key in the array contains the description so it's possibly ok with single OIDs.

}
}
}
}

// Run custom sensors
require 'includes/discovery/sensors/cisco-entity-sensor.inc.php';
require 'includes/discovery/sensors/entity-sensor.inc.php';
Expand Down