Files
librenms-librenms/includes/polling/functions.inc.php
Tony Murray eeb3d58f5b Improved Logging and Debugging (#8870)
Use Log facility when Laravel is booted.
Update init.php so we can easily boot Laravel for CLI scripts. (and just Eloquent, but that may go away)
Move all debug setup into set_debug() function and use that across all scripts.
Log Laravel database queries.
Send debug output to librenms log file when enabling debug in the webui.
Allow for colorized Log CLI output. (currently will leave % tags in log file output)

** Needs testing and perhaps tweaking still.

DO NOT DELETE THIS TEXT

#### Please note

> Please read this information carefully. You can run `./scripts/pre-commit.php` to check your code before submitting.

- [x] Have you followed our [code guidelines?](http://docs.librenms.org/Developing/Code-Guidelines/)

#### Testers

If you would like to test this pull request then please run: `./scripts/github-apply <pr_id>`, i.e `./scripts/github-apply 5926`
2018-07-13 23:08:00 +01:00

784 lines
30 KiB
PHP

<?php
use LibreNMS\RRD\RrdDefinition;
use LibreNMS\Exceptions\JsonAppException;
use LibreNMS\Exceptions\JsonAppPollingFailedException;
use LibreNMS\Exceptions\JsonAppParsingFailedException;
use LibreNMS\Exceptions\JsonAppBlankJsonException;
use LibreNMS\Exceptions\JsonAppMissingKeysException;
use LibreNMS\Exceptions\JsonAppWrongVersionException;
use LibreNMS\Exceptions\JsonAppExtendErroredException;
function bulk_sensor_snmpget($device, $sensors)
{
$oid_per_pdu = get_device_oid_limit($device);
$sensors = array_chunk($sensors, $oid_per_pdu);
$cache = array();
foreach ($sensors as $chunk) {
$oids = array_map(function ($data) {
return $data['sensor_oid'];
}, $chunk);
$oids = implode(' ', $oids);
$multi_response = snmp_get_multi_oid($device, $oids, '-OUQnte');
$cache = array_merge($cache, $multi_response);
}
return $cache;
}
/**
* @param $device
* @param string $type type/class of sensor
* @return array
*/
function sensor_precache($device, $type)
{
$sensor_cache = array();
if (file_exists('includes/polling/sensors/pre-cache/'. $device['os'] .'.inc.php')) {
include 'includes/polling/sensors/pre-cache/'. $device['os'] .'.inc.php';
}
return $sensor_cache;
}
function poll_sensor($device, $class)
{
global $config, $memcache, $agent_sensors;
$sensors = array();
$misc_sensors = array();
$all_sensors = array();
foreach (dbFetchRows("SELECT * FROM `sensors` WHERE `sensor_class` = ? AND `device_id` = ?", array($class, $device['device_id'])) as $sensor) {
if ($sensor['poller_type'] == 'agent') {
// Agent sensors are polled in the unix-agent
} elseif ($sensor['poller_type'] == 'ipmi') {
$misc_sensors[] = $sensor;
} else {
$sensors[] = $sensor;
}
}
$snmp_data = bulk_sensor_snmpget($device, $sensors);
$sensor_cache = sensor_precache($device, $class);
foreach ($sensors as $sensor) {
echo 'Checking (' . $sensor['poller_type'] . ") $class " . $sensor['sensor_descr'] . '... '.PHP_EOL;
if ($sensor['poller_type'] == 'snmp') {
$mibdir = null;
$sensor_value = trim(str_replace('"', '', $snmp_data[$sensor['sensor_oid']]));
if (file_exists('includes/polling/sensors/'. $class .'/'. $device['os'] .'.inc.php')) {
require 'includes/polling/sensors/'. $class .'/'. $device['os'] .'.inc.php';
}
if ($class == 'temperature') {
preg_match('/[\d\.\-]+/', $sensor_value, $temp_response);
if (!empty($temp_response[0])) {
$sensor_value = $temp_response[0];
}
} elseif ($class == 'state') {
if (!is_numeric($sensor_value)) {
$state_value = dbFetchCell(
'SELECT `state_value`
FROM `state_translations` LEFT JOIN `sensors_to_state_indexes`
ON `state_translations`.`state_index_id` = `sensors_to_state_indexes`.`state_index_id`
WHERE `sensors_to_state_indexes`.`sensor_id` = ?
AND `state_translations`.`state_descr` LIKE ?',
array($sensor['sensor_id'], $sensor_value)
);
d_echo('State value of ' . $sensor_value . ' is ' . $state_value . "\n");
if (is_numeric($state_value)) {
$sensor_value = $state_value;
}
}
}//end if
unset($mib);
unset($mibdir);
$sensor['new_value'] = $sensor_value;
$all_sensors[] = $sensor;
}
}
foreach ($misc_sensors as $sensor) {
if ($sensor['poller_type'] == 'agent') {
if (isset($agent_sensors)) {
$sensor_value = $agent_sensors[$class][$sensor['sensor_type']][$sensor['sensor_index']]['current'];
$sensor['new_value'] = $sensor_value;
$all_sensors[] = $sensor;
} else {
echo "no agent data!\n";
continue;
}
} elseif ($sensor['poller_type'] == 'ipmi') {
echo " already polled.\n";
// ipmi should probably move here from the ipmi poller file (FIXME)
continue;
} else {
echo "unknown poller type!\n";
continue;
}//end if
}
record_sensor_data($device, $all_sensors);
}//end poll_sensor()
/**
* @param $device
* @param $all_sensors
*/
function record_sensor_data($device, $all_sensors)
{
$supported_sensors = array(
'current' => 'A',
'frequency' => 'Hz',
'runtime' => 'Min',
'humidity' => '%',
'fanspeed' => 'rpm',
'power' => 'W',
'voltage' => 'V',
'temperature' => 'C',
'dbm' => 'dBm',
'charge' => '%',
'load' => '%',
'state' => '#',
'signal' => 'dBm',
'airflow' => 'cfm',
'snr' => 'SNR',
'pressure' => 'kPa',
'cooling' => 'W',
);
foreach ($all_sensors as $sensor) {
$class = ucfirst($sensor['sensor_class']);
$unit = $supported_sensors[$class];
$sensor_value = $sensor['new_value'];
$prev_sensor_value = $sensor['sensor_current'];
if ($sensor_value == -32768) {
echo 'Invalid (-32768) ';
$sensor_value = 0;
}
if ($sensor['sensor_divisor'] && $sensor_value !== 0) {
$sensor_value = ($sensor_value / $sensor['sensor_divisor']);
}
if ($sensor['sensor_multiplier']) {
$sensor_value = ($sensor_value * $sensor['sensor_multiplier']);
}
if (isset($sensor['user_func']) && function_exists($sensor['user_func'])) {
$sensor_value = $sensor['user_func']($sensor_value);
}
$rrd_name = get_sensor_rrd_name($device, $sensor);
$rrd_def = RrdDefinition::make()->addDataset('sensor', 'GAUGE');
echo "$sensor_value $unit\n";
$fields = array(
'sensor' => $sensor_value,
);
$tags = array(
'sensor_class' => $sensor['sensor_class'],
'sensor_type' => $sensor['sensor_type'],
'sensor_descr' => $sensor['sensor_descr'],
'sensor_index' => $sensor['sensor_index'],
'rrd_name' => $rrd_name,
'rrd_def' => $rrd_def
);
data_update($device, 'sensor', $tags, $fields);
// FIXME also warn when crossing WARN level!
if ($sensor['sensor_limit_low'] != '' && $prev_sensor_value > $sensor['sensor_limit_low'] && $sensor_value < $sensor['sensor_limit_low'] && $sensor['sensor_alert'] == 1) {
echo 'Alerting for '.$device['hostname'].' '.$sensor['sensor_descr']."\n";
log_event("$class {$sensor['sensor_descr']} under threshold: $sensor_value $unit (< {$sensor['sensor_limit_low']} $unit)", $device, $class, 4, $sensor['sensor_id']);
} elseif ($sensor['sensor_limit'] != '' && $prev_sensor_value < $sensor['sensor_limit'] && $sensor_value > $sensor['sensor_limit'] && $sensor['sensor_alert'] == 1) {
echo 'Alerting for '.$device['hostname'].' '.$sensor['sensor_descr']."\n";
log_event("$class {$sensor['sensor_descr']} above threshold: $sensor_value $unit (> {$sensor['sensor_limit']} $unit)", $device, $class, 4, $sensor['sensor_id']);
}
if ($sensor['sensor_class'] == 'state' && $prev_sensor_value != $sensor_value) {
$trans = array_column(
dbFetchRows(
"SELECT `state_translations`.`state_value`, `state_translations`.`state_descr` FROM `sensors_to_state_indexes` LEFT JOIN `state_translations` USING (`state_index_id`) WHERE `sensors_to_state_indexes`.`sensor_id`=? AND `state_translations`.`state_value` IN ($sensor_value,$prev_sensor_value)",
array($sensor['sensor_id'])
),
'state_descr',
'state_value'
);
log_event("$class sensor {$sensor['sensor_descr']} has changed from {$trans[$prev_sensor_value]} ($prev_sensor_value) to {$trans[$sensor_value]} ($sensor_value)", $device, $class, 3, $sensor['sensor_id']);
}
if ($sensor_value != $prev_sensor_value) {
dbUpdate(array('sensor_current' => $sensor_value, 'sensor_prev' => $prev_sensor_value, 'lastupdate' => array('NOW()')), 'sensors', "`sensor_class` = ? AND `sensor_id` = ?", array($class, $sensor['sensor_id']));
}
}
}
/**
* @param array $device The device to poll
* @param bool $force_module Ignore device module overrides
* @return bool
*/
function poll_device($device, $force_module = false)
{
global $config, $device;
$device_start = microtime(true);
$attribs = get_dev_attribs($device['device_id']);
$device['attribs'] = $attribs;
load_os($device);
$device['snmp_max_repeaters'] = $attribs['snmp_max_repeaters'];
$device['snmp_max_oid'] = $attribs['snmp_max_oid'];
unset($array);
// Start counting device poll time
echo 'Hostname: ' . $device['hostname'] . PHP_EOL;
echo 'Device ID: ' . $device['device_id'] . PHP_EOL;
echo 'OS: ' . $device['os'];
$ip = dnslookup($device);
$db_ip = isset($ip) ? inet_pton($ip) : null;
if (!empty($db_ip) && inet6_ntop($db_ip) != inet6_ntop($device['ip'])) {
log_event('Device IP changed to ' . $ip, $device, 'system', 3);
dbUpdate(array('ip' => $db_ip), 'devices', 'device_id=?', array($device['device_id']));
}
if ($config['os'][$device['os']]['group']) {
$device['os_group'] = $config['os'][$device['os']]['group'];
echo ' ('.$device['os_group'].')';
}
echo PHP_EOL.PHP_EOL;
unset($poll_update);
unset($poll_update_query);
unset($poll_separator);
$poll_update_array = array();
$update_array = array();
$host_rrd = rrd_name($device['hostname'], '', '');
if ($config['norrd'] !== true && !is_dir($host_rrd)) {
mkdir($host_rrd);
echo "Created directory : $host_rrd\n";
}
$response = device_is_up($device, true);
if ($response['status'] == '1') {
$graphs = array();
$oldgraphs = array();
if ($device['snmp_disable']) {
$config['poller_modules'] = array();
} else {
// we always want the core module to be included, prepend it
$config['poller_modules'] = array('core' => true) + $config['poller_modules'];
}
printChangedStats(true); // don't count previous stats
foreach ($config['poller_modules'] as $module => $module_status) {
$os_module_status = $config['os'][$device['os']]['poller_modules'][$module];
d_echo("Modules status: Global" . (isset($module_status) ? ($module_status ? '+ ' : '- ') : ' '));
d_echo("OS" . (isset($os_module_status) ? ($os_module_status ? '+ ' : '- ') : ' '));
d_echo("Device" . (isset($attribs['poll_' . $module]) ? ($attribs['poll_' . $module] ? '+ ' : '- ') : ' '));
if ($force_module === true ||
$attribs['poll_'.$module] ||
($os_module_status && !isset($attribs['poll_'.$module])) ||
($module_status && !isset($os_module_status) && !isset($attribs['poll_' . $module]))) {
$start_memory = memory_get_usage();
$module_start = microtime(true);
echo "\n#### Load poller module $module ####\n";
include "includes/polling/$module.inc.php";
$module_time = microtime(true) - $module_start;
$module_mem = (memory_get_usage() - $start_memory);
printf("\n>> Runtime for poller module '%s': %.4f seconds with %s bytes\n", $module, $module_time, $module_mem);
printChangedStats();
echo "#### Unload poller module $module ####\n\n";
// save per-module poller stats
$tags = array(
'module' => $module,
'rrd_def' => RrdDefinition::make()->addDataset('poller', 'GAUGE', 0),
'rrd_name' => array('poller-perf', $module),
);
$fields = array(
'poller' => $module_time,
);
data_update($device, 'poller-perf', $tags, $fields);
// remove old rrd
$oldrrd = rrd_name($device['hostname'], array('poller', $module, 'perf'));
if (is_file($oldrrd)) {
unlink($oldrrd);
}
unset($tags, $fields, $oldrrd);
} elseif (isset($attribs['poll_'.$module]) && $attribs['poll_'.$module] == '0') {
echo "Module [ $module ] disabled on host.\n\n";
} elseif (isset($os_module_status) && $os_module_status == '0') {
echo "Module [ $module ] disabled on os.\n\n";
} else {
echo "Module [ $module ] disabled globally.\n\n";
}
}
// Update device_groups
UpdateGroupsForDevice($device['device_id']);
if (!$force_module && !empty($graphs)) {
echo "Enabling graphs: ";
// FIXME EVENTLOGGING -- MAKE IT SO WE DO THIS PER-MODULE?
// This code cycles through the graphs already known in the database and the ones we've defined as being polled here
// If there any don't match, they're added/deleted from the database.
// Ideally we should hold graphs for xx days/weeks/polls so that we don't needlessly hide information.
foreach (dbFetch('SELECT `graph` FROM `device_graphs` WHERE `device_id` = ?', array($device['device_id'])) as $graph) {
if (isset($graphs[$graph['graph']])) {
$oldgraphs[$graph['graph']] = true;
} else {
dbDelete('device_graphs', '`device_id` = ? AND `graph` = ?', array($device['device_id'], $graph['graph']));
}
}
foreach ($graphs as $graph => $value) {
if (!isset($oldgraphs[$graph])) {
echo '+';
dbInsert(array('device_id' => $device['device_id'], 'graph' => $graph), 'device_graphs');
}
echo $graph.' ';
}
echo PHP_EOL;
}//end if
// Ping response
if (can_ping_device($attribs) === true && !empty($response['ping_time'])) {
$tags = array(
'rrd_def' => RrdDefinition::make()->addDataset('ping', 'GAUGE', 0, 65535),
);
$fields = array(
'ping' => $response['ping_time'],
);
$update_array['last_ping'] = array('NOW()');
$update_array['last_ping_timetaken'] = $response['ping_time'];
data_update($device, 'ping-perf', $tags, $fields);
}
$device_time = round(microtime(true) - $device_start, 3);
// Poller performance
if (!empty($device_time)) {
$tags = array(
'rrd_def' => RrdDefinition::make()->addDataset('poller', 'GAUGE', 0),
'module' => 'ALL',
);
$fields = array(
'poller' => $device_time,
);
data_update($device, 'poller-perf', $tags, $fields);
}
if (!$force_module) {
// don't update last_polled time if we are forcing a specific module to be polled
$update_array['last_polled'] = array('NOW()');
$update_array['last_polled_timetaken'] = $device_time;
}
$updated = dbUpdate($update_array, 'devices', '`device_id` = ?', array($device['device_id']));
if ($updated) {
d_echo('Updating ' . $device['hostname'] . PHP_EOL);
}
echo "\nPolled in $device_time seconds\n";
// check if the poll took to long and log an event
if ($device_time > $config['rrd']['step']) {
log_event("Polling took longer than " . round($config['rrd']['step'] / 60, 2) .
' minutes! This will cause gaps in graphs.', $device, 'system', 5);
}
unset($storage_cache);
// Clear cache of hrStorage ** MAYBE FIXME? **
unset($cache);
// Clear cache (unify all things here?)
return true; // device was polled
}
return false; // device not polled
}//end poll_device()
/**
* if no rrd_name parameter is passed, the MIB name is used as the rrd_file_name
*/
function poll_mib_def($device, $mib_name_table, $mib_subdir, $mib_oids, $mib_graphs, &$graphs, $rrd_name = null)
{
echo "This is poll_mib_def Processing\n";
$mib = null;
list($mib, $file) = explode(':', $mib_name_table, 2);
if (is_null($rrd_name)) {
if (str_i_contains($mib_name_table, 'UBNT')) {
$rrd_name = strtolower($mib);
} else {
$rrd_name = strtolower($file);
}
}
$rrd_def = new RrdDefinition();
$oidglist = array();
$oidnamelist = array();
foreach ($mib_oids as $oid => $param) {
$oidindex = $param[0];
$oiddsname = $param[1];
$oiddsdesc = $param[2];
$oiddstype = $param[3];
$oiddsopts = $param[4];
if (empty($oiddsopts)) {
$rrd_def->addDataset($oiddsname, $oiddstype, null, 100000000000);
} else {
$min = array_key_exists('min', $oiddsopts) ? $oiddsopts['min'] : null;
$max = array_key_exists('max', $oiddsopts) ? $oiddsopts['max'] : null;
$heartbeat = array_key_exists('heartbeat', $oiddsopts) ? $oiddsopts['heartbeat'] : null;
$rrd_def->addDataset($oiddsname, $oiddstype, $min, $max, $heartbeat);
}
if ($oidindex != '') {
$fulloid = $oid.'.'.$oidindex;
} else {
$fulloid = $oid;
}
// Add to oid GET list
$oidglist[] = $fulloid;
$oidnamelist[] = $oiddsname;
}//end foreach
// Implde for LibreNMS Version
$oidilist = implode(' ', $oidglist);
$snmpdata = snmp_get_multi($device, $oidilist, '-OQUs', $mib);
if (isset($GLOBALS['exec_status']['exitcode']) && $GLOBALS['exec_status']['exitcode'] !== 0) {
print_debug(' ERROR, bad snmp response');
return false;
}
$oid_count = 0;
$fields = array();
foreach ($oidglist as $fulloid) {
list($splitoid, $splitindex) = explode('.', $fulloid, 2);
$val = $snmpdata[$splitindex][$splitoid];
if (is_numeric($val)) {
$fields[$oidnamelist[$oid_count]] = $val;
} elseif (preg_match("/^\"(.*)\"$/", $val, $number) && is_numeric($number[1])) {
$fields[$oidnamelist[$oid_count]] = $number[1];
} else {
$fields[$oidnamelist[$oid_count]] = 'U';
}
$oid_count++;
}
$tags = compact('rrd_def');
data_update($device, $rrd_name, $tags, $fields);
foreach ($mib_graphs as $graphtoenable) {
$graphs[$graphtoenable] = true;
}
return true;
}//end poll_mib_def()
function get_main_serial($device)
{
if ($device['os_group'] == 'cisco') {
$serial_output = snmp_get_multi($device, 'entPhysicalSerialNum.1 entPhysicalSerialNum.1001', '-OQUs', 'ENTITY-MIB:OLD-CISCO-CHASSIS-MIB');
if (!empty($serial_output[1]['entPhysicalSerialNum'])) {
return $serial_output[1]['entPhysicalSerialNum'];
} elseif (!empty($serial_output[1000]['entPhysicalSerialNum'])) {
return $serial_output[1000]['entPhysicalSerialNum'];
} elseif (!empty($serial_output[1001]['entPhysicalSerialNum'])) {
return $serial_output[1001]['entPhysicalSerialNum'];
}
}
}//end get_main_serial()
function location_to_latlng($device)
{
global $config;
if (function_check('curl_version') !== true) {
d_echo("Curl support for PHP not enabled\n");
return false;
}
$bad_loc = false;
$device_location = $device['location'];
if (!empty($device_location)) {
$new_device_location = preg_replace("/ /", "+", $device_location);
$new_device_location = preg_replace('/[^A-Za-z0-9\-\+]/', '', $new_device_location); // Removes special chars.
// We have a location string for the device.
$loc = parse_location($device_location);
if (!is_array($loc)) {
$loc = dbFetchRow("SELECT `lat`,`lng` FROM `locations` WHERE `location`=? LIMIT 1", array($device_location));
}
if (is_array($loc) === false) {
// Grab data from which ever Geocode service we use.
switch ($config['geoloc']['engine']) {
case "google":
default:
d_echo("Google geocode engine being used\n");
$api_key = ($config['geoloc']['api_key']);
if (!empty($api_key)) {
d_echo("Use Google API key: $api_key\n");
$api_url = "https://maps.googleapis.com/maps/api/geocode/json?address=$new_device_location&key=$api_key";
} else {
$api_url = "https://maps.googleapis.com/maps/api/geocode/json?address=$new_device_location";
}
break;
}
$curl_init = curl_init($api_url);
set_curl_proxy($curl_init);
curl_setopt($curl_init, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_init, CURLOPT_TIMEOUT, 2);
curl_setopt($curl_init, CURLOPT_TIMEOUT_MS, 2000);
curl_setopt($curl_init, CURLOPT_CONNECTTIMEOUT, 5);
$data = json_decode(curl_exec($curl_init), true);
// Parse the data from the specific Geocode services.
switch ($config['geoloc']['engine']) {
case "google":
default:
if ($data['status'] == 'OK') {
$loc = $data['results'][0]['geometry']['location'];
} else {
$bad_loc = true;
}
break;
}
if ($bad_loc === true) {
d_echo("Bad lat / lng received\n");
} else {
$loc['timestamp'] = array('NOW()');
$loc['location'] = $device_location;
if (dbInsert($loc, 'locations')) {
d_echo("Device lat/lng created\n");
} else {
d_echo("Device lat/lng could not be created\n");
}
}
} else {
d_echo("Using cached lat/lng from other device\n");
}
}
}// end location_to_latlng()
/**
* Update the application status and output in the database.
*
* Metric values should have key for of the matching name.
* If you have multiple groups of metrics, you can group them with multiple sub arrays
* The group name (key) will be prepended to each metric in that group, separated by an underscore
* The special group "none" will not be prefixed.
*
* @param array $app app from the db, including app_id
* @param string $response This should be the full output
* @param array $metrics an array of additional metrics to store in the database for alerting
* @param string $status This is the current value for alerting
*/
function update_application($app, $response, $metrics = array(), $status = '')
{
if (!is_numeric($app['app_id'])) {
d_echo('$app does not contain app_id, could not update');
return;
}
$data = array(
'app_state' => 'UNKNOWN',
'app_status' => $status,
'timestamp' => array('NOW()'),
);
if ($response != '' && $response !== false) {
if (str_contains($response, array(
'Traceback (most recent call last):',
))) {
$data['app_state'] = 'ERROR';
} else {
$data['app_state'] = 'OK';
}
}
if ($data['app_state'] != $app['app_state']) {
$data['app_state_prev'] = $app['app_state'];
}
dbUpdate($data, 'applications', '`app_id` = ?', array($app['app_id']));
// update metrics
if (!empty($metrics)) {
$db_metrics = dbFetchRows('SELECT * FROM `application_metrics` WHERE app_id=?', array($app['app_id']));
$db_metrics = array_by_column($db_metrics, 'metric');
// allow two level metrics arrays, flatten them and prepend the group name
if (is_array(current($metrics))) {
$metrics = array_reduce(
array_keys($metrics),
function ($carry, $metric_group) use ($metrics) {
if ($metric_group == 'none') {
$prefix = '';
} else {
$prefix = $metric_group . '_';
}
foreach ($metrics[$metric_group] as $metric_name => $value) {
$carry[$prefix . $metric_name] = $value;
}
return $carry;
},
array()
);
}
echo ': ';
foreach ($metrics as $metric_name => $value) {
if (!isset($db_metrics[$metric_name])) {
// insert new metric
dbInsert(
array(
'app_id' => $app['app_id'],
'metric' => $metric_name,
'value' => (int)$value,
),
'application_metrics'
);
echo '+';
} elseif ((int)$value != (int)$db_metrics[$metric_name]['value']) {
dbUpdate(
array(
'value' => (int)$value,
'value_prev' => $db_metrics[$metric_name]['value'],
),
'application_metrics',
'app_id=? && metric=?',
array($app['app_id'], $metric_name)
);
echo 'U';
} else {
echo '.';
}
unset($db_metrics[$metric_name]);
}
// remove no longer existing metrics (generally should not happen
foreach ($db_metrics as $db_metric) {
dbDelete(
'application_metrics',
'app_id=? && metric=?',
array($app['app_id'], $db_metric['metric'])
);
echo '-';
}
echo PHP_EOL;
}
}
function convert_to_celsius($value)
{
$value = ($value - 32) / 1.8;
return sprintf('%.02f', $value);
}
/**
* This is to make it easier polling apps. Also to help standardize around JSON.
*
* The required keys for the returned JSON are as below.
* version - The version of the snmp extend script. Should be numeric and at least 1.
* error - Error code from the snmp extend script. Should be > 0 (0 will be ignored and negatives are reserved)
* errorString - Text to describe the error.
* data - An key with an array with the data to be used.
*
* If the app returns an error, an exception will be raised.
* Positive numbers will be errors returned by the extend script.
*
* Possible parsing related errors:
* -2 : Failed to fetch data from the device
* -3 : Could not decode the JSON.
* -4 : Empty JSON parsed, meaning blank JSON was returned.
* -5 : Valid json, but missing required keys
* -6 : Returned version is less than the min version.
*
* Error checking may also be done via checking the exceptions listed below.
* JsonAppPollingFailedException, -2 : Empty return from SNMP.
* JsonAppParsingFailedException, -3 : Could not parse the JSON.
* JsonAppBlankJsonException, -4 : Blank JSON.
* JsonAppMissingKeysException, -5 : Missing required keys.
* JsonAppWrongVersionException , -6 : Older version than supported.
* JsonAppExtendErroredException : Polling and parsing was good, but the returned data has an error set.
* This may be checked via $e->getParsedJson() and then checking the
* keys error and errorString.
* The error value can be accessed via $e->getCode()
* The output can be accessed via $->getOutput() Only returned for code -3 or lower.
* The parsed JSON can be access via $e->getParsedJson()
*
* All of the exceptions extend JsonAppException.
*
* If the error is less than -1, you can assume it is a legacy snmp extend script.
*
* @param array $device
* @param string $extend the extend name. For example, if 'zfs' is passed it will be converted to 'nsExtendOutputFull.3.122.102.115'.
* @param integer $min_version the minimum version to accept for the returned JSON. default: 1
*
* @return array The json output data parsed into an array
* @throws JsonAppPollingFailedException
*/
function json_app_get($device, $extend, $min_version = 1)
{
$output = snmp_get($device, 'nsExtendOutputFull.'.string_to_oid($extend), '-Oqv', 'NET-SNMP-EXTEND-MIB');
// make sure we actually get something back
if (empty($output)) {
throw new JsonAppPollingFailedException("Empty return from snmp_get.", -2);
}
// turn the JSON into a array
$parsed_json = json_decode(stripslashes($output), true);
// improper JSON or something else was returned. Populate the variable with an error.
if (json_last_error() !== JSON_ERROR_NONE) {
throw new JsonAppParsingFailedException("Invalid JSON", $output, -3);
}
// There no keys in the array, meaning '{}' was was returned
if (empty($parsed_json)) {
throw new JsonAppBlankJsonException("Blank JSON returned.", $output, -4);
}
// It is a legacy JSON app extend, meaning these are not set
if (!isset($parsed_json['error'], $parsed_json['data'], $parsed_json['errorString'], $parsed_json['version'])) {
throw new JsonAppMissingKeysException("Legacy script or extend error, missing one or more required keys.", $output, $parsed_json, -5);
}
if ($parsed_json['version'] < $min_version) {
throw new JsonAppWrongVersionException("Script,'".$parsed_json['version']."', older than required version of '$min_version'", $output, $parsed_json, -6);
}
if ($parsed_json['error'] != 0) {
throw new JsonAppExtendErroredException("Script returned exception: {$parsed_json['errorString']}", $output, $parsed_json, $parsed_json['error']);
}
return $parsed_json;
}