mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
add json_app_get and convert fail2ban over to JSON (#8571)
* add json_app_get function * add numeric testing and version support * now use json_app_get * remove some unneeded code * update the docs for json_app_get some more * make the format checker happy * add in min version support and now take extend name instead of the partial OID * hmm... don't make min version optional * add Exception usage for this all make min version actually work * minor formatting cleanup * minor style cleanup * update json_app_get with $throw_me setting * Use exceptions to fully handle errors. Always update the application. Include error message for use in UI. Move data to data key for easier parsing. Add test data * make a few changes to the lovely changes from @murrant * style cleanup * now attempt parsing it the old way if a error of -5 is returned * add new exceptions and rework them all * add new exceptions and min version 0 no longer bypasses the key checks * redo the error codes a bit and improve the comment about it all * fix a a bit of formatting * added JsonAppException and make the other JsonApp stuff a sub of it * note JsonAppException * fix class creation * JsonAppBlank now extends JsonApp * doh! add <?php * update the poller to properly use the new exceptions * no longer check for error twice and make sure the data key is present * cleanup processing of legacy scripts * tweak this a bit * white space fix * fix the tests for fail2ban
This commit is contained in:
21
LibreNMS/Exceptions/JsonAppBlankJsonException.php
Normal file
21
LibreNMS/Exceptions/JsonAppBlankJsonException.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class JsonAppBlankJsonException extends JsonAppException
|
||||
{
|
||||
private $output;
|
||||
|
||||
public function __construct($message, $output, $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
}
|
8
LibreNMS/Exceptions/JsonAppException.php
Normal file
8
LibreNMS/Exceptions/JsonAppException.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
class JsonAppException extends \Exception
|
||||
{
|
||||
|
||||
}
|
28
LibreNMS/Exceptions/JsonAppExtendErroredException.php
Normal file
28
LibreNMS/Exceptions/JsonAppExtendErroredException.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class JsonAppExtendErroredException extends JsonAppException
|
||||
{
|
||||
private $output;
|
||||
private $parsed_json;
|
||||
|
||||
public function __construct($message, $output, $parsed_json = [], $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->output = $output;
|
||||
$this->parsed_json = $parsed_json;
|
||||
}
|
||||
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
public function getParsedJson()
|
||||
{
|
||||
return $this->parsed_json;
|
||||
}
|
||||
}
|
28
LibreNMS/Exceptions/JsonAppMissingKeysException.php
Normal file
28
LibreNMS/Exceptions/JsonAppMissingKeysException.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class JsonAppMissingKeysException extends JsonAppException
|
||||
{
|
||||
private $output;
|
||||
private $parsed_json;
|
||||
|
||||
public function __construct($message, $output, $parsed_json = [], $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->output = $output;
|
||||
$this->parsed_json = $parsed_json;
|
||||
}
|
||||
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
public function getParsedJson()
|
||||
{
|
||||
return $this->parsed_json;
|
||||
}
|
||||
}
|
21
LibreNMS/Exceptions/JsonAppParsingFailedException.php
Normal file
21
LibreNMS/Exceptions/JsonAppParsingFailedException.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class JsonAppParsingFailedException extends JsonAppException
|
||||
{
|
||||
private $output;
|
||||
|
||||
public function __construct($message, $output, $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
}
|
8
LibreNMS/Exceptions/JsonAppPollingFailedException.php
Normal file
8
LibreNMS/Exceptions/JsonAppPollingFailedException.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
class JsonAppPollingFailedException extends JsonAppException
|
||||
{
|
||||
|
||||
}
|
28
LibreNMS/Exceptions/JsonAppWrongVersionException.php
Normal file
28
LibreNMS/Exceptions/JsonAppWrongVersionException.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace LibreNMS\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class JsonAppWrongVersionException extends JsonAppException
|
||||
{
|
||||
private $output;
|
||||
private $parsed_json;
|
||||
|
||||
public function __construct($message, $output, $parsed_json = [], $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->output = $output;
|
||||
$this->parsed_json = $parsed_json;
|
||||
}
|
||||
|
||||
public function getOutput()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
public function getParsedJson()
|
||||
{
|
||||
return $this->parsed_json;
|
||||
}
|
||||
}
|
@@ -1,21 +1,41 @@
|
||||
<?php
|
||||
|
||||
use LibreNMS\Exceptions\JsonAppParsingFailedException;
|
||||
use LibreNMS\Exceptions\JsonAppException;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
|
||||
echo "fail2ban";
|
||||
|
||||
$name = 'fail2ban';
|
||||
$app_id = $app['app_id'];
|
||||
|
||||
$options = '-O qv';
|
||||
$oid = '.1.3.6.1.4.1.8072.1.3.2.3.1.2.8.102.97.105.108.50.98.97.110';
|
||||
$f2b = snmp_walk($device, $oid, $options);
|
||||
$f2b = trim($f2b, '"');
|
||||
echo $name;
|
||||
|
||||
$metrics = array();
|
||||
$bannedStuff = explode("\n", $f2b);
|
||||
try {
|
||||
$f2b = json_app_get($device, $name);
|
||||
} catch (JsonAppParsingFailedException $e) {
|
||||
// Legacy script, build compatible array
|
||||
$legacy = explode("\n", $e->getOutput());
|
||||
$f2b = [
|
||||
'data' => [
|
||||
'total' => array_shift($legacy), // total was first line in legacy app
|
||||
'jails' => []
|
||||
]
|
||||
];
|
||||
|
||||
$total_banned=$bannedStuff[0];
|
||||
foreach ($legacy as $jail_data) {
|
||||
list($jail, $banned) = explode(" ", $jail_data);
|
||||
if (isset($jail) && isset($banned)) {
|
||||
$f2b['data']['jails'][$jail] = $banned;
|
||||
}
|
||||
}
|
||||
} catch (JsonAppException $e) {
|
||||
echo PHP_EOL . $name . ':' .$e->getCode().':'. $e->getMessage() . PHP_EOL;
|
||||
update_application($app, $e->getCode().':'.$e->getMessage(), []); // Set empty metrics and error message
|
||||
return;
|
||||
}
|
||||
|
||||
$f2b = $f2b[data];
|
||||
|
||||
$metrics = [];
|
||||
|
||||
$rrd_name = array('app', $name, $app_id);
|
||||
$rrd_def = RrdDefinition::make()
|
||||
@@ -24,23 +44,15 @@ $rrd_def = RrdDefinition::make()
|
||||
|
||||
|
||||
$fields = array(
|
||||
'banned' =>$total_banned,
|
||||
'firewalled'=>'U',
|
||||
'banned' => $f2b['total'],
|
||||
'firewalled'=>'U', // legacy ds
|
||||
);
|
||||
$metrics['total'] = $fields;
|
||||
|
||||
$tags = array('name' => $name, 'app_id' => $app_id, 'rrd_def' => $rrd_def, 'rrd_name' => $rrd_name);
|
||||
data_update($device, 'app', $tags, $fields);
|
||||
|
||||
$int=1;
|
||||
$jails=array();
|
||||
|
||||
while (isset($bannedStuff[$int])) {
|
||||
list($jail, $banned) = explode(" ", $bannedStuff[$int]);
|
||||
|
||||
if (isset($jail) && isset($banned)) {
|
||||
$jails[] = $jail;
|
||||
|
||||
foreach ($f2b['jails'] as $jail => $banned) {
|
||||
$rrd_name = array('app', $name, $app_id, $jail);
|
||||
$rrd_def = RrdDefinition::make()->addDataset('banned', 'GAUGE', 0);
|
||||
$fields = array('banned' => $banned);
|
||||
@@ -48,17 +60,14 @@ while (isset($bannedStuff[$int])) {
|
||||
$metrics["jail_$jail"] = $fields;
|
||||
$tags = array('name' => $name, 'app_id' => $app_id, 'rrd_def' => $rrd_def, 'rrd_name' => $rrd_name);
|
||||
data_update($device, 'app', $tags, $fields);
|
||||
}
|
||||
|
||||
$int++;
|
||||
}
|
||||
|
||||
update_application($app, $f2b, $metrics);
|
||||
update_application($app, 'ok', $metrics);
|
||||
|
||||
//
|
||||
// component processing for fail2ban
|
||||
//
|
||||
$device_id=$device['device_id'];
|
||||
$device_id = $device['device_id'];
|
||||
|
||||
$options=array(
|
||||
'filter' => array(
|
||||
@@ -85,7 +94,7 @@ if (empty($jails)) {
|
||||
|
||||
$id = $component->getFirstComponentID($f2bc);
|
||||
$f2bc[$id]['label'] = 'Fail2ban Jails';
|
||||
$f2bc[$id]['jails'] = json_encode($jails);
|
||||
$f2bc[$id]['jails'] = json_encode(array_keys($f2b['jails']));
|
||||
|
||||
$component->setComponentPrefs($device_id, $f2bc);
|
||||
}
|
||||
|
@@ -1,6 +1,13 @@
|
||||
<?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)
|
||||
{
|
||||
@@ -690,3 +697,85 @@ 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;
|
||||
}
|
||||
|
49
tests/data/linux_fail2ban-legacy.json
Normal file
49
tests/data/linux_fail2ban-legacy.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"applications": {
|
||||
"discovery": {
|
||||
"applications": [
|
||||
{
|
||||
"app_type": "fail2ban",
|
||||
"app_state": "UNKNOWN",
|
||||
"discovered": "1",
|
||||
"app_state_prev": null,
|
||||
"app_status": "",
|
||||
"app_instance": ""
|
||||
}
|
||||
],
|
||||
"application_metrics": []
|
||||
},
|
||||
"poller": {
|
||||
"applications": [
|
||||
{
|
||||
"app_type": "fail2ban",
|
||||
"app_state": "OK",
|
||||
"discovered": "1",
|
||||
"app_state_prev": "UNKNOWN",
|
||||
"app_status": "",
|
||||
"app_instance": ""
|
||||
}
|
||||
],
|
||||
"application_metrics": [
|
||||
{
|
||||
"metric": "jail_dovecot",
|
||||
"value": "0",
|
||||
"value_prev": null,
|
||||
"app_type": "fail2ban"
|
||||
},
|
||||
{
|
||||
"metric": "jail_sshd",
|
||||
"value": "0",
|
||||
"value_prev": null,
|
||||
"app_type": "fail2ban"
|
||||
},
|
||||
{
|
||||
"metric": "total",
|
||||
"value": "0",
|
||||
"value_prev": null,
|
||||
"app_type": "fail2ban"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
49
tests/data/linux_fail2ban-v1.json
Normal file
49
tests/data/linux_fail2ban-v1.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"applications": {
|
||||
"discovery": {
|
||||
"applications": [
|
||||
{
|
||||
"app_type": "fail2ban",
|
||||
"app_state": "UNKNOWN",
|
||||
"discovered": "1",
|
||||
"app_state_prev": null,
|
||||
"app_status": "",
|
||||
"app_instance": ""
|
||||
}
|
||||
],
|
||||
"application_metrics": []
|
||||
},
|
||||
"poller": {
|
||||
"applications": [
|
||||
{
|
||||
"app_type": "fail2ban",
|
||||
"app_state": "OK",
|
||||
"discovered": "1",
|
||||
"app_state_prev": "UNKNOWN",
|
||||
"app_status": "",
|
||||
"app_instance": ""
|
||||
}
|
||||
],
|
||||
"application_metrics": [
|
||||
{
|
||||
"metric": "jail_dovecot",
|
||||
"value": "0",
|
||||
"value_prev": null,
|
||||
"app_type": "fail2ban"
|
||||
},
|
||||
{
|
||||
"metric": "jail_sshd",
|
||||
"value": "0",
|
||||
"value_prev": null,
|
||||
"app_type": "fail2ban"
|
||||
},
|
||||
{
|
||||
"metric": "total",
|
||||
"value": "0",
|
||||
"value_prev": null,
|
||||
"app_type": "fail2ban"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
11
tests/snmpsim/linux_fail2ban-legacy.snmprec
Normal file
11
tests/snmpsim/linux_fail2ban-legacy.snmprec
Normal file
@@ -0,0 +1,11 @@
|
||||
1.3.6.1.2.1.1.1.0|4|Linux server 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64
|
||||
1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.8072.3.2.10
|
||||
1.3.6.1.2.1.1.3.0|67|77550514
|
||||
1.3.6.1.2.1.1.4.0|4|<private>
|
||||
1.3.6.1.2.1.1.5.0|4|<private>
|
||||
1.3.6.1.2.1.1.6.0|4|<private>
|
||||
1.3.6.1.2.1.25.1.1.0|67|77552962
|
||||
1.3.6.1.4.1.8072.1.3.2.2.1.21.6.100.105.115.116.114.111|2|1
|
||||
1.3.6.1.4.1.8072.1.3.2.2.1.21.8.102.97.105.108.50.98.97.110|2|1
|
||||
1.3.6.1.4.1.8072.1.3.2.3.1.2.8.102.97.105.108.50.98.97.110|4x|300a646f7665636f7420300a7373686420300a
|
||||
1.3.6.1.6.3.10.2.1.3.0|2|775505
|
11
tests/snmpsim/linux_fail2ban-v1.snmprec
Normal file
11
tests/snmpsim/linux_fail2ban-v1.snmprec
Normal file
@@ -0,0 +1,11 @@
|
||||
1.3.6.1.2.1.1.1.0|4|Linux server 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64
|
||||
1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.8072.3.2.10
|
||||
1.3.6.1.2.1.1.3.0|67|77550514
|
||||
1.3.6.1.2.1.1.4.0|4|<private>
|
||||
1.3.6.1.2.1.1.5.0|4|<private>
|
||||
1.3.6.1.2.1.1.6.0|4|<private>
|
||||
1.3.6.1.2.1.25.1.1.0|67|77552962
|
||||
1.3.6.1.4.1.8072.1.3.2.2.1.21.6.100.105.115.116.114.111|2|1
|
||||
1.3.6.1.4.1.8072.1.3.2.2.1.21.8.102.97.105.108.50.98.97.110|2|1
|
||||
1.3.6.1.4.1.8072.1.3.2.3.1.2.8.102.97.105.108.50.98.97.110|4x|7b2264617461223a7b226a61696c73223a7b2273736864223a2230222c22646f7665636f74223a2230227d2c22746f74616c223a307d2c226572726f72223a2230222c2276657273696f6e223a312c226572726f72537472696e67223a226661696c3262616e2d636c69656e742065786974656420776974682030227d0a
|
||||
1.3.6.1.6.3.10.2.1.3.0|2|775505
|
Reference in New Issue
Block a user