Refactor alert templates to use Laravel Blade templating engine (#8803)

* Initial start on new templating support

* further updates

* more updates

* More working version

* Last fixes

* Small updates

* modified test to look for dbname.

* Schema update

* Added fix for not being able to disable Philips Hue transport

* Updated sql file

* Updated db_schema

* Set myclabs/deep-copy to be 1.7.x version in composer

* Fixes from murrant

* Forced nikic/php-parser to version 3.1.x in composer

* Updated composer to use custom fork of string-blade-compiler + fixed transport use

* Updated to always use correct template

* Merged legacy and blade templating engines

* Removed template type + fixed test-alert.php

* Added more template placeholders

* Added ability to reference data in templates as $alert->X

* Updated docs for templates

* Updated db_schema.yaml

* Added $alert->builder

* Clarify the use of $alert->builder

* Fixed the use of $alert->transport

* renamed schema file

* Added template validation

* Small update to fix travis issue

* Add Docs text to title bar

* Updated amqp to new of version

* Consistency in alert rule and template title bars
This commit is contained in:
Neil Lathwood
2018-07-14 22:15:43 +01:00
committed by GitHub
parent b56380c261
commit ad6ec7f72a
28 changed files with 1121 additions and 587 deletions

View File

@@ -0,0 +1,37 @@
<?php
/**
* Template.php
*
* Base Template class
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2018 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace LibreNMS\Alert;
class AlertData extends \Illuminate\Support\Collection
{
public function __get($name)
{
if ($this->has($name)) {
return $this->get($name);
}
return "$name is not a valid \$alert data name";
}
}

213
LibreNMS/Alert/Template.php Normal file
View File

@@ -0,0 +1,213 @@
<?php
/**
* Template.php
*
* Base Template class
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2018 Neil Lathwood
* @author Neil Lathwood <gh+n@laf.io>
*/
namespace LibreNMS\Alert;
use App\Models\AlertTemplate;
class Template
{
public $template;
/**
*
* Get the template details
*
* @param null $obj
* @return mixed
*/
public function getTemplate($obj = null)
{
if ($this->template) {
// Return the cached template information.
return $this->template;
}
$this->template = AlertTemplate::whereHas('map', function ($query) use ($obj) {
$query->where('alert_rule_id', '=', $obj['rule_id']);
})->first();
if (!$this->template) {
$this->template = AlertTemplate::where('name', '=', 'Default Alert Template')->first();
}
return $this->template;
}
public function getTitle($data)
{
$data['parsed_title'] = $this->bladeTitle($data);
return $this->legacyTitle($data);
}
public function getBody($data)
{
$data['template']['parsed_template'] = $this->bladeBody($data);
return $this->legacyBody($data);
}
/**
*
* Parse Blade body
*
* @param $data
* @return string
*/
public function bladeBody($data)
{
$alert['alert'] = new AlertData($data['alert']);
try {
return view(['template' => $data['template']->template], $alert)->__toString();
} catch (\Exception $e) {
return view(['template' => $this->getDefaultTemplate($data)], $alert)->__toString();
}
}
/**
*
* Parse Blade title
*
* @param $data
* @return string
*/
public function bladeTitle($data)
{
$alert['alert'] = new AlertData($data['alert']);
try {
return view(['template' => $data['title']], $alert)->__toString();
} catch (\Exception $e) {
return $data['title'] ?: view(['template' => "Template " . $data['name']], $alert)->__toString();
}
}
/**
*
* Parse legacy body
*
* @param $data
* @return mixed|string
*/
public function legacyBody($data)
{
$tpl = $data['template']->parsed_template;
$msg = '$ret .= "'.str_replace(array('{else}', '{/if}', '{/foreach}'), array('"; } else { $ret .= "', '"; } $ret .= "', '"; } $ret .= "'), addslashes($tpl)).'";';
$parsed = $msg;
$s = strlen($msg);
$x = $pos = -1;
$buff = '';
$if = $for = $calc = false;
while (++$x < $s) {
if ($msg[$x] == '{' && $buff == '') {
$buff .= $msg[$x];
} elseif ($buff == '{ ') {
$buff = '';
} elseif ($buff != '') {
$buff .= $msg[$x];
}
if ($buff == '{if') {
$pos = $x;
$if = true;
} elseif ($buff == '{foreach') {
$pos = $x;
$for = true;
} elseif ($buff == '{calc') {
$pos = $x;
$calc = true;
}
if ($pos != -1 && $msg[$x] == '}') {
$orig = $buff;
$buff = '';
$pos = -1;
if ($if) {
$if = false;
$o = 3;
$native = array(
'"; if( ',
' ) { $ret .= "',
);
} elseif ($for) {
$for = false;
$o = 8;
$native = array(
'"; foreach( ',
' as $key=>$value) { $ret .= "',
);
} elseif ($calc) {
$calc = false;
$o = 5;
$native = array(
'"; $ret .= (float) (0+(',
')); $ret .= "',
);
} else {
continue;
}
$cond = trim(populate(substr($orig, $o, -1), false));
$native = $native[0].$cond.$native[1];
$parsed = str_replace($orig, $native, $parsed);
unset($cond, $o, $orig, $native);
}//end if
}//end while
$parsed = populate($parsed);
return RunJail($parsed, $data);
}
/**
*
* Parse legacy title
*
* @param $data
* @return mixed|string
*/
public function legacyTitle($data)
{
if (strstr($data['parsed_title'], '%')) {
return RunJail('$ret = "'.populate(addslashes($data['parsed_title'])).'";', $data);
} else {
return $data['parsed_title'];
}
}
/**
*
* Get the default template
*
* @return string
*/
public function getDefaultTemplate()
{
return '{{ $title }}' . PHP_EOL .
'Severity: {{ $severity }}' . PHP_EOL .
'@if ($state == 0)Time elapsed: {{ $elapsed }} @endif ' . PHP_EOL .
'Timestamp: {{ $timestamp }}' . PHP_EOL .
'Unique-ID: {{ $uid }}' . PHP_EOL .
'Rule: @if ($name) {{ $name }} @else {{ $rule }} @endif ' . PHP_EOL .
'@if ($faults)Faults:' . PHP_EOL .
'@foreach ($faults as $key => $value)' . PHP_EOL .
' #{{ $key }}: {{ $value[\'string\'] }} @endforeach' . PHP_EOL .
'@endif' . PHP_EOL .
'Alert sent to: @foreach ($contacts as $key => $value) {{ $value }} <{{ $key }}> @endforeach';
}
}

View File

@@ -27,7 +27,7 @@
use LibreNMS\Util\FileLock;
$init_modules = array('alerts');
$init_modules = ['alerts', 'alerts-cli'];
require __DIR__ . '/includes/init.php';
$options = getopt('d::');

View File

@@ -0,0 +1,38 @@
<?php
/**
* app/Models/AlertTemplate.php
*
* Model for access to alert_templates table data
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2018 Neil Lathwood
* @author Neil Lathwood <gh+n@laf.io>
*/
namespace App\Models;
class AlertTemplate extends BaseModel
{
public $timestamps = false;
// ---- Define Relationships ----
public function map()
{
return $this->hasMany('App\Models\AlertTemplateMap', 'alert_templates_id', 'id');
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* app/Models/AlertTemplateMap.php
*
* Model for access to alert_template_map table data
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2018 Neil Lathwood
* @author Neil Lathwood <gh+n@laf.io>
*/
namespace App\Models;
class AlertTemplateMap extends BaseModel
{
protected $table = 'alert_template_map';
public $timestamps = false;
// ---- Define Relationships ----
public function template()
{
return $this->belongsTo('App\Models\AlertTemplate', 'alert_templates_id');
}
}

View File

@@ -12,6 +12,13 @@
"issues": "https://github.com/librenms/librenms/issues/",
"irc": "irc://irc.freenode.org/#librenms"
},
"repositories":
[
{
"type": "vcs",
"url": "https://github.com/librenms/StringBladeCompiler"
}
],
"require": {
"php": ">=5.6.4",
"ext-mysqli": "*",
@@ -37,13 +44,15 @@
"influxdb/influxdb-php": "^1.14",
"laravel/laravel": "5.4.*",
"oriceon/toastr-5-laravel": "dev-master",
"doctrine/inflector": "1.1.*",
"symfony/event-dispatcher": "3.*",
"symfony/css-selector": "3.*",
"doctrine/instantiator": "1.0.*",
"phpdocumentor/reflection-docblock": "3.*",
"phpunit/php-token-stream": "1.*"
"phpunit/php-token-stream": "1.*",
"wpb/string-blade-compiler": "3.4.x-dev",
"myclabs/deep-copy": "1.7.*",
"nikic/php-parser": "v3.1.*"
},
"require-dev": {
"squizlabs/php_codesniffer": "^2.9.1",

309
composer.lock generated
View File

@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cbaf71886db021d5c8e7e2a04c06361a",
"content-hash": "1ec95b9b21153d67687370be2181ea01",
"packages": [
{
"name": "amenadiel/jpgraph",
"version": "3.6.19",
"version": "3.6.20",
"source": {
"type": "git",
"url": "https://github.com/HuasoFoundries/jpgraph.git",
"reference": "c9f800a00850706f3fc4b26b6055b55952eb79bc"
"reference": "10a818bc8c1b40deb0063f0f3c3e67c23e3030b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/HuasoFoundries/jpgraph/zipball/c9f800a00850706f3fc4b26b6055b55952eb79bc",
"reference": "c9f800a00850706f3fc4b26b6055b55952eb79bc",
"url": "https://api.github.com/repos/HuasoFoundries/jpgraph/zipball/10a818bc8c1b40deb0063f0f3c3e67c23e3030b7",
"reference": "10a818bc8c1b40deb0063f0f3c3e67c23e3030b7",
"shasum": ""
},
"require": {
@@ -59,7 +59,7 @@
"jpgraph",
"pie"
],
"time": "2018-05-18T17:10:05+00:00"
"time": "2018-06-13T13:59:24+00:00"
},
{
"name": "dapphp/radius",
@@ -602,31 +602,24 @@
},
{
"name": "influxdb/influxdb-php",
"version": "1.14.5",
"version": "1.14.7",
"source": {
"type": "git",
"url": "https://github.com/influxdata/influxdb-php.git",
"reference": "64c79de3f3e0e7f8c20b5fc51f50da8736767de9"
"reference": "570bd5cdedb9b1c1628dceea5785bc40e08f7e48"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/64c79de3f3e0e7f8c20b5fc51f50da8736767de9",
"reference": "64c79de3f3e0e7f8c20b5fc51f50da8736767de9",
"url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/570bd5cdedb9b1c1628dceea5785bc40e08f7e48",
"reference": "570bd5cdedb9b1c1628dceea5785bc40e08f7e48",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0",
"php": "^5.5 || ^7.0",
"symfony/event-dispatcher": "^2.0 || ^3.0 || ^4.0"
"php": "^5.5 || ^7.0"
},
"require-dev": {
"codeclimate/php-test-reporter": "0.*",
"symfony/config": "~2.8|~3.0|~4.0",
"symfony/console": "~2.8|~3.0|~4.0",
"symfony/filesystem": "~2.8|~3.0|~4.0",
"symfony/phpunit-bridge": "^3.4 || ^4.0",
"symfony/stopwatch": "~2.8|~3.0|~4.0",
"symfony/yaml": "~2.8|~3.0|~4.0"
"phpunit/phpunit": "^5.7"
},
"suggest": {
"stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP."
@@ -665,7 +658,7 @@
"influxdb library",
"time series"
],
"time": "2018-01-03T13:09:43+00:00"
"time": "2018-07-06T10:13:39+00:00"
},
{
"name": "jakub-onderka/php-console-color",
@@ -1197,17 +1190,62 @@
"time": "2017-01-23T04:29:33+00:00"
},
{
"name": "nesbot/carbon",
"version": "1.29.2",
"name": "myclabs/deep-copy",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "ed6aa898982f441ccc9b2acdec51490f2bc5d337"
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/ed6aa898982f441ccc9b2acdec51490f2bc5d337",
"reference": "ed6aa898982f441ccc9b2acdec51490f2bc5d337",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0"
},
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
"phpunit/phpunit": "^4.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
},
"files": [
"src/DeepCopy/deep_copy.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Create deep copies (clones) of your objects",
"keywords": [
"clone",
"copy",
"duplicate",
"object",
"object graph"
],
"time": "2017-10-19T19:58:43+00:00"
},
{
"name": "nesbot/carbon",
"version": "1.32.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "64563e2b9f69e4db1b82a60e81efa327a30ff343"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/64563e2b9f69e4db1b82a60e81efa327a30ff343",
"reference": "64563e2b9f69e4db1b82a60e81efa327a30ff343",
"shasum": ""
},
"require": {
@@ -1219,6 +1257,13 @@
"phpunit/phpunit": "^4.8.35 || ^5.7"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Carbon\\Laravel\\ServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"": "src/"
@@ -1242,7 +1287,7 @@
"datetime",
"time"
],
"time": "2018-05-29T15:23:46+00:00"
"time": "2018-07-05T06:59:26+00:00"
},
{
"name": "nikic/php-parser",
@@ -1400,16 +1445,16 @@
},
{
"name": "paragonie/random_compat",
"version": "v2.0.14",
"version": "v2.0.17",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "f6ce7dd93628088e1017fb5dd73b0b9fec7df9e5"
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/f6ce7dd93628088e1017fb5dd73b0b9fec7df9e5",
"reference": "f6ce7dd93628088e1017fb5dd73b0b9fec7df9e5",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
"reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
"shasum": ""
},
"require": {
@@ -1445,7 +1490,7 @@
"pseudorandom",
"random"
],
"time": "2018-06-06T17:40:22+00:00"
"time": "2018-07-04T16:31:37+00:00"
},
{
"name": "pear/console_color2",
@@ -1963,16 +2008,16 @@
},
{
"name": "psy/psysh",
"version": "v0.9.5",
"version": "v0.9.6",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
"reference": "0951e91ac04ca28cf317f3997a0adfc319e80106"
"reference": "4a2ce86f199d51b6e2524214dc06835e872f4fce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/0951e91ac04ca28cf317f3997a0adfc319e80106",
"reference": "0951e91ac04ca28cf317f3997a0adfc319e80106",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/4a2ce86f199d51b6e2524214dc06835e872f4fce",
"reference": "4a2ce86f199d51b6e2524214dc06835e872f4fce",
"shasum": ""
},
"require": {
@@ -2031,7 +2076,7 @@
"interactive",
"shell"
],
"time": "2018-06-02T16:39:22+00:00"
"time": "2018-06-10T17:57:20+00:00"
},
{
"name": "ramsey/uuid",
@@ -2265,16 +2310,16 @@
},
{
"name": "symfony/console",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27"
"reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/36f83f642443c46f3cf751d4d2ee5d047d757a27",
"reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27",
"url": "https://api.github.com/repos/symfony/console/zipball/1b97071a26d028c9bd4588264e101e14f6e7cd00",
"reference": "1b97071a26d028c9bd4588264e101e14f6e7cd00",
"shasum": ""
},
"require": {
@@ -2330,11 +2375,11 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2018-05-16T08:49:21+00:00"
"time": "2018-05-23T05:02:55+00:00"
},
{
"name": "symfony/css-selector",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
@@ -2387,16 +2432,16 @@
},
{
"name": "symfony/debug",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "b28fd73fefbac341f673f5efd707d539d6a19f68"
"reference": "47e6788c5b151cf0cfdf3329116bf33800632d75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/b28fd73fefbac341f673f5efd707d539d6a19f68",
"reference": "b28fd73fefbac341f673f5efd707d539d6a19f68",
"url": "https://api.github.com/repos/symfony/debug/zipball/47e6788c5b151cf0cfdf3329116bf33800632d75",
"reference": "47e6788c5b151cf0cfdf3329116bf33800632d75",
"shasum": ""
},
"require": {
@@ -2439,11 +2484,11 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2018-05-16T14:03:39+00:00"
"time": "2018-06-25T11:10:40+00:00"
},
{
"name": "symfony/dotenv",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/dotenv.git",
@@ -2500,7 +2545,7 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
@@ -2563,16 +2608,16 @@
},
{
"name": "symfony/finder",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "472a92f3df8b247b49ae364275fb32943b9656c6"
"reference": "3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/472a92f3df8b247b49ae364275fb32943b9656c6",
"reference": "472a92f3df8b247b49ae364275fb32943b9656c6",
"url": "https://api.github.com/repos/symfony/finder/zipball/3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394",
"reference": "3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394",
"shasum": ""
},
"require": {
@@ -2608,20 +2653,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2018-05-16T08:49:21+00:00"
"time": "2018-06-19T20:52:10+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "a7b5fc605d1c215cea1122359044b1e682eb70c0"
"reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/a7b5fc605d1c215cea1122359044b1e682eb70c0",
"reference": "a7b5fc605d1c215cea1122359044b1e682eb70c0",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce",
"reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce",
"shasum": ""
},
"require": {
@@ -2662,20 +2707,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2018-05-25T11:07:31+00:00"
"time": "2018-06-21T11:10:19+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "3dac45df55ee0c5134c457a730cd68e2a2ce0445"
"reference": "cb7edcdc47cab3c61c891e6e55337f8dd470d820"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/3dac45df55ee0c5134c457a730cd68e2a2ce0445",
"reference": "3dac45df55ee0c5134c457a730cd68e2a2ce0445",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/cb7edcdc47cab3c61c891e6e55337f8dd470d820",
"reference": "cb7edcdc47cab3c61c891e6e55337f8dd470d820",
"shasum": ""
},
"require": {
@@ -2683,7 +2728,7 @@
"psr/log": "~1.0",
"symfony/debug": "~2.8|~3.0|~4.0",
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
"symfony/http-foundation": "^3.4.4|^4.0.4",
"symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1",
"symfony/polyfill-ctype": "~1.8"
},
"conflict": {
@@ -2751,7 +2796,7 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"time": "2018-05-25T13:16:28+00:00"
"time": "2018-06-25T12:29:19+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2928,16 +2973,16 @@
},
{
"name": "symfony/process",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187"
"reference": "acc5a37c706ace827962851b69705b24e71ca17c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/4cbf2db9abcb01486a21b7a059e03a62fae63187",
"reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187",
"url": "https://api.github.com/repos/symfony/process/zipball/acc5a37c706ace827962851b69705b24e71ca17c",
"reference": "acc5a37c706ace827962851b69705b24e71ca17c",
"shasum": ""
},
"require": {
@@ -2973,7 +3018,7 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2018-05-16T08:49:21+00:00"
"time": "2018-05-30T04:24:30+00:00"
},
{
"name": "symfony/routing",
@@ -3116,16 +3161,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "0e6545672d8c9ce70dd472adc2f8b03155a46f73"
"reference": "e173954a28a44a32c690815fbe4d0f2eac43accb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/0e6545672d8c9ce70dd472adc2f8b03155a46f73",
"reference": "0e6545672d8c9ce70dd472adc2f8b03155a46f73",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/e173954a28a44a32c690815fbe4d0f2eac43accb",
"reference": "e173954a28a44a32c690815fbe4d0f2eac43accb",
"shasum": ""
},
"require": {
@@ -3181,11 +3226,11 @@
"debug",
"dump"
],
"time": "2018-04-26T12:42:15+00:00"
"time": "2018-06-15T07:47:49+00:00"
},
{
"name": "symfony/yaml",
"version": "v2.8.41",
"version": "v2.8.42",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
@@ -3344,28 +3389,28 @@
},
{
"name": "vlucas/phpdotenv",
"version": "v2.4.0",
"version": "v2.5.0",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c"
"reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c",
"reference": "3cc116adbe4b11be5ec557bf1d24dc5e3a21d18c",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/6ae3e2e6494bb5e58c2decadafc3de7f1453f70a",
"reference": "6ae3e2e6494bb5e58c2decadafc3de7f1453f70a",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.0"
"phpunit/phpunit": "^4.8.35 || ^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@@ -3375,7 +3420,7 @@
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause-Attribution"
"BSD-3-Clause"
],
"authors": [
{
@@ -3390,7 +3435,7 @@
"env",
"environment"
],
"time": "2016-09-01T10:05:43+00:00"
"time": "2018-07-01T10:25:50+00:00"
},
{
"name": "webmozart/assert",
@@ -3442,6 +3487,50 @@
],
"time": "2018-01-29T19:49:41+00:00"
},
{
"name": "wpb/string-blade-compiler",
"version": "3.4.x-dev",
"source": {
"type": "git",
"url": "https://github.com/librenms/StringBladeCompiler.git",
"reference": "30c974a305c14f412af3a8c515b74528429c4f5f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/librenms/StringBladeCompiler/zipball/30c974a305c14f412af3a8c515b74528429c4f5f",
"reference": "30c974a305c14f412af3a8c515b74528429c4f5f",
"shasum": ""
},
"require": {
"laravel/framework": "5.4.*",
"php": ">=5.6.4"
},
"type": "library",
"autoload": {
"psr-4": {
"Wpb\\String_Blade_Compiler\\": "src/"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Terre Porter",
"email": "tporter@webpage-builders.com"
}
],
"description": "Laravel Blade String Compiler, render string as blade templates",
"keywords": [
"blade",
"compiler",
"laravel"
],
"support": {
"source": "https://github.com/librenms/StringBladeCompiler/tree/3.4"
},
"time": "2018-06-21T21:13:41+00:00"
},
{
"name": "xjtuwangke/passwordhash",
"version": "dev-master",
@@ -3883,51 +3972,6 @@
],
"time": "2017-01-05T08:46:19+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0"
},
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
"phpunit/phpunit": "^4.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
},
"files": [
"src/DeepCopy/deep_copy.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Create deep copies (clones) of your objects",
"keywords": [
"clone",
"copy",
"duplicate",
"object",
"object graph"
],
"time": "2017-10-19T19:58:43+00:00"
},
{
"name": "phpspec/prophecy",
"version": "1.7.6",
@@ -4925,7 +4969,7 @@
},
{
"name": "symfony/class-loader",
"version": "v3.4.11",
"version": "v3.4.12",
"source": {
"type": "git",
"url": "https://github.com/symfony/class-loader.git",
@@ -5029,7 +5073,8 @@
"minimum-stability": "stable",
"stability-flags": {
"xjtuwangke/passwordhash": 20,
"oriceon/toastr-5-laravel": 20
"oriceon/toastr-5-laravel": 20,
"wpb/string-blade-compiler": 20
},
"prefer-stable": false,
"prefer-lowest": false,

View File

@@ -161,7 +161,7 @@ return [
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Wpb\String_Blade_Compiler\ViewServiceProvider::class,
/*
* Package Service Providers...
@@ -182,7 +182,6 @@ return [
* Vendor Service Providers...
*/
Kamaln7\Toastr\ToastrServiceProvider::class,
],
/*

View File

@@ -0,0 +1,266 @@
source: Alerting/Templates.md
# Templates
Templates can be assigned to a single or a group of rules and can contain any kind of text. There is also a default template which is used for any rule that isn't associated with a template. This template can be found under `Alert Templates` page and can be edited. It also has an option revert it back to its default content.
The template-parser understands `if` and `foreach` controls and replaces certain placeholders with information gathered about the alert.
## Syntax
Controls:
- if-else (Else can be omitted):
`{if %placeholder == value}Some Text{else}Other Text{/if}`
- foreach-loop:
`{foreach %faults}Key: %key<br/>Value: %value{/foreach}`
Placeholders:
Placeholders are special variables that if used within the template will be replaced with the relevant data, I.e:
`The device %hostname has been up for %uptime seconds` would result in the following `The device localhost has been up for 30344 seconds`.
- Device ID: `%device_id`
- Hostname of the Device: `%hostname`
- sysName of the Device: `%sysName`
- sysDescr of the Device: `%sysDescr`
- hardware of the Device: `%hardware`
- Software version of the Device: `%version`
- location of the Device: `%location`
- uptime of the Device (in seconds): `%uptime`
- short uptime of the Device (28d 22h 30m 7s): `%uptime_short`
- long uptime of the Device (28 days, 22h 30m 7s): `%uptime_long`
- description (purpose db field) of the Device: `%description`
- notes of the Device: `%notes`
- notes of the alert: `%alert_notes`
- ping timestamp (if icmp enabled): `%ping_timestamp`
- ping loss (if icmp enabled): `%ping_loss`
- ping min (if icmp enabled): `%ping_min`
- ping max (if icmp enabled): `%ping_max`
- ping avg (if icmp enabled): `%ping_avg`
- Title for the Alert: `%title`
- Time Elapsed, Only available on recovery (`%state == 0`): `%elapsed`
- Alert-ID: `%id`
- Unique-ID: `%uid`
- Faults, Only available on alert (`%state != 0`), must be iterated in a foreach (`{foreach %faults}`). Holds all available information about the Fault, accessible in the format `%value.Column`, for example: `%value.ifDescr`. Special field `%value.string` has most Identification-information (IDs, Names, Descrs) as single string, this is the equivalent of the default used.
- State: `%state`
- Severity: `%severity`
- Rule: `%rule`
- Rule-Name: `%name`
- Timestamp: `%timestamp`
- Transport name: `%transport`
- Contacts, must be iterated in a foreach, `%key` holds email and `%value` holds name: `%contacts`
Placeholders can be used within the subjects for templates as well although %faults is most likely going to be worthless.
> NOTE: Placeholder names which are contained within another need to be ordered correctly. As an example:
```text
Limit: %value.sensor_limit / %value.sensor_limit_low
```
Should be done as:
```text
Limit: %value.sensor_limit_low / %value.sensor_limit
```
The Default Template is a 'one-size-fit-all'. We highly recommend defining your own templates for your rules to include more specific information.
## Examples
Default Template:
```text
%title
Severity: %severity
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Unique-ID: %uid
Rule: {if %name}%name{else}%rule{/if}
{if %faults}Faults:
{foreach %faults} #%key: %value.string{/foreach}{/if}
Alert sent to: {foreach %contacts}%value <%key> {/foreach}
```
Ports Utilization Template:
```text
%title
Device Name: %hostname
Severity: %severity
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Rule: {if %name}%name{else}%rule{/if}
{foreach %faults}
Physical Interface: %value.ifDescr
Interface Description: %value.ifAlias
Interface Speed: {calc (%value.ifSpeed/1000000000)} Gbs
Inbound Utilization: {calc ((%value.ifInOctets_rate*8)/%value.ifSpeed)*100}%
Outbound Utilization: {calc ((%value.ifOutOctets_rate*8)/%value.ifSpeed)*100}%
{/foreach}
```
Storage:
```text
%title
Device Name: %hostname
Severity: %severity
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Location: %location
Description: %description
Features: %features
Purpose: %purpose
Notes: %notes
Server: %sysName {foreach %faults}Mount Point: %value.storage_descr Percent Utilized: %value.storage_perc{/foreach}
```
Temperature Sensors:
```text
%title
Device Name: %hostname
Severity: %severity
Timestamp: %timestamp
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Location: %location
Description: %description
Features: %features
Purpose: %purpose
Notes: %notes
Rule: {if %name}%name{else}%rule{/if}
{if %faults}Faults:
{foreach %faults}
#%key: Temperature: %value.sensor_current°C
** {calc(%value.sensor_current-%value.sensor_limit)}°C over limit
Previous Measurement: %value.sensor_prev°C
High Temperature Limit: %value.sensor_limit°C
{/foreach}
{/if}
```
Value Sensors:
```text
%title
Device Name: %hostname
Severity: %severity
Timestamp: %timestamp
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Location: %location
Description: %description
Features: %features
Purpose: %purpose
Notes: %notes
Rule: {if %name}%name{else}%rule{/if}
{if %faults}Faults:
{foreach %faults}
#%key: Sensor%value.sensor_current
** {calc(%value.sensor_current-%value.sensor_limit)}over limit
Previous Measurement: %value.sensor_prev
Limit: %value.sensor_limit
{/foreach}
{/if}
```
Memory Alert:
```text
%title
Device Name: %hostname
Severity: %severity
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Location: %location
Description: %description
Notes: %notes
Server: %hostname {foreach %faults}
Memory Description: %value.mempool_descr
Percent Utilized: %value.mempool_perc{/foreach}
```
Conditional formatting example, will display a link to the host in email or just the hostname in any other transport:
```text
{if %transport == mail}<a href="https://my.librenms.install/device/device=%hostname/">%hostname</a>
{else}
%hostname
{/if}
```
Note the use of double-quotes. Single quotes (`'`) in templates will be escaped (replaced with `\'`) in the output and should therefore be avoided.
## Examples HTML
Note: To use HTML emails you must set HTML email to Yes in the WebUI under Global Settings > Alerting Settings > Email transport > Use HTML emails
Note: To include Graphs you must enable unauthorized graphs in config.php. Allow_unauth_graphs_cidr is optional, but more secure.
```
$config['allow_unauth_graphs_cidr'] = array(127.0.0.1/32');
$config['allow_unauth_graphs'] = true;
```
Service Alert:
```
<div style="font-family:Helvetica;">
<h2>{if %state == 1}<span style="color:red;">%severity{/if}
{if %state == 2}<span style="color:goldenrod;">acknowledged{/if}</span>
{if %state == 3}<span style="color:green;">recovering{/if}</span>
{if %state == 0}<span style="color:green;">recovered{/if}</span>
</h2>
<b>Host:</b> %hostname<br>
<b>Duration:</b> %elapsed<br>
<br>
{if %faults}
{foreach %faults}<b>%value.service_desc - %value.service_type</b><br>
%value.service_message<br>
<br>
{/foreach}
{/if}
</div>
```
Processor Alert with Graph:
```
%title <br>
Severity: %severity <br>
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp <br>
Alert-ID: %id <br>
Rule: {if %name}%name{else}%rule{/if} <br>
{if %faults}Faults:
{foreach %faults}
#%key: %value.string <br>
{/foreach}
{if %faults}<b>Faults:</b><br>
{foreach %faults}<img src="https://server/graph.php?device=%value.device_id&type=device_processor&width=459&height=213&lazy_w=552&from=end-72h%22/%3E<br>
https://server/graphs/id=%value.device_id/type=device_processor/<br>
{/foreach}
Template: CPU alert <br>
{/if}
{/if}
```
## Included
We include a few templates for you to use, these are specific to the type of alert rules you are creating. For example if you create a rule that would alert on BGP sessions then you can
assign the BGP template to this rule to provide more information.
The included templates apart from the default template are:
- BGP Sessions
- Ports
- Temperature

View File

@@ -2,70 +2,66 @@ source: Alerting/Templates.md
# Templates
> This page is for installs running version 1.41 or later. You can find the older docs [here](Old_Templates.md)
Templates can be assigned to a single or a group of rules and can contain any kind of text. There is also a default template which is used for any rule that isn't associated with a template. This template can be found under `Alert Templates` page and can be edited. It also has an option revert it back to its default content.
The template-parser understands `if` and `foreach` controls and replaces certain placeholders with information gathered about the alert.
The templating engine in use is Laravel Blade. We will cover some of the basics here, however the official Laravel docs will have more information [here](https://laravel.com/docs/5.4/blade)
## Syntax
Controls:
- if-else (Else can be omitted):
`{if %placeholder == value}Some Text{else}Other Text{/if}`
`@if ($alert->placeholder == 'value') Some Text @else Other Text @endif`
- foreach-loop:
`{foreach %faults}Key: %key<br/>Value: %value{/foreach}`
`@foreach ($alert->faults as $key => $value) Key: $key<br/>Value: $value @endforeach`
Placeholders:
Placeholders are special variables that if used within the template will be replaced with the relevant data, I.e:
`The device %hostname has been up for %uptime seconds` would result in the following `The device localhost has been up for 30344 seconds`.
`The device {{ $alert->hostname }} has been up for {{ $alert->uptime }} seconds` would result in the following `The device localhost has been up for 30344 seconds`.
- Device ID: `%device_id`
- Hostname of the Device: `%hostname`
- sysName of the Device: `%sysName`
- sysDescr of the Device: `%sysDescr`
- hardware of the Device: `%hardware`
- Software version of the Device: `%version`
- location of the Device: `%location`
- uptime of the Device (in seconds): `%uptime`
- short uptime of the Device (28d 22h 30m 7s): `%uptime_short`
- long uptime of the Device (28 days, 22h 30m 7s): `%uptime_long`
- description (purpose db field) of the Device: `%description`
- notes of the Device: `%notes`
- notes of the alert: `%alert_notes`
- ping timestamp (if icmp enabled): `%ping_timestamp`
- ping loss (if icmp enabled): `%ping_loss`
- ping min (if icmp enabled): `%ping_min`
- ping max (if icmp enabled): `%ping_max`
- ping avg (if icmp enabled): `%ping_avg`
- Title for the Alert: `%title`
- Time Elapsed, Only available on recovery (`%state == 0`): `%elapsed`
- Alert-ID: `%id`
- Unique-ID: `%uid`
- Faults, Only available on alert (`%state != 0`), must be iterated in a foreach (`{foreach %faults}`). Holds all available information about the Fault, accessible in the format `%value.Column`, for example: `%value.ifDescr`. Special field `%value.string` has most Identification-information (IDs, Names, Descrs) as single string, this is the equivalent of the default used.
- State: `%state`
- Severity: `%severity`
- Rule: `%rule`
- Rule-Name: `%name`
- Timestamp: `%timestamp`
- Transport name: `%transport`
> When using placeholders to echo data, you need to wrap the placeholder in `{{ }}`. I.e `{{ $alert->hostname }}`.
- Contacts, must be iterated in a foreach, `%key` holds email and `%value` holds name: `%contacts`
- Device ID: `$alert->device_id`
- Hostname of the Device: `$alert->hostname`
- sysName of the Device: `$alert->sysName`
- sysDescr of the Device: `$alert->sysDescr`
- sysContact of the Device: `$alert->sysContact`
- OS of the Device: `$alert->os`
- Type of Device: `$alert->type`
- IP of the Device: `$alert->ip`
- hardware of the Device: `$alert->hardware`
- Software version of the Device: `$alert->version`
- location of the Device: `$alert->location`
- uptime of the Device (in seconds): `$alert->uptime`
- short uptime of the Device (28d 22h 30m 7s): `$alert->uptime_short`
- long uptime of the Device (28 days, 22h 30m 7s): `$alert->uptime_long`
- description (purpose db field) of the Device: `$alert->description`
- notes of the Device: `$alert->notes`
- notes of the alert: `$alert->alert_notes`
- ping timestamp (if icmp enabled): `$alert->ping_timestamp`
- ping loss (if icmp enabled): `$alert->ping_loss`
- ping min (if icmp enabled): `$alert->ping_min`
- ping max (if icmp enabled): `$alert->ping_max`
- ping avg (if icmp enabled): `$alert->ping_avg`
- Title for the Alert: `$alert->title`
- Time Elapsed, Only available on recovery (`$alert->state == 0`): `$alert->elapsed`
- Rule Builder (the actual rule) (use `{!! $alert->builder !!}`): `$alert->builder`
- Alert-ID: `$alert->id`
- Unique-ID: `$alert->uid`
- Faults, Only available on alert (`$alert->state != 0`), must be iterated in a foreach (`@foreach ($alert->faults as $key => $value) @endforeach`). Holds all available information about the Fault, accessible in the format `$value['Column']`, for example: `$value['ifDescr']`. Special field `$value['string']` has most Identification-information (IDs, Names, Descrs) as single string, this is the equivalent of the default used.
- State: `$alert->state`
- Severity: `$alert->severity`
- Rule: `$alert->rule`
- Rule-Name: `$alert->name`
- Timestamp: `$alert->timestamp`
- Transport name: `$alert->transport`
- Contacts, must be iterated in a foreach, `$key` holds email and `$value` holds name: `$alert->contacts`
Placeholders can be used within the subjects for templates as well although %faults is most likely going to be worthless.
> NOTE: Placeholder names which are contained within another need to be ordered correctly. As an example:
```text
Limit: %value.sensor_limit / %value.sensor_limit_low
```
Should be done as:
```text
Limit: %value.sensor_limit_low / %value.sensor_limit
```
Placeholders can be used within the subjects for templates as well although $faults is most likely going to be worthless.
The Default Template is a 'one-size-fit-all'. We highly recommend defining your own templates for your rules to include more specific information.
@@ -73,135 +69,138 @@ The Default Template is a 'one-size-fit-all'. We highly recommend defining your
Default Template:
```text
%title
Severity: %severity
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Unique-ID: %uid
Rule: {if %name}%name{else}%rule{/if}
{if %faults}Faults:
{foreach %faults} #%key: %value.string{/foreach}{/if}
Alert sent to: {foreach %contacts}%value <%key> {/foreach}
{{ $alert->title }}
Severity: {{ $alert->severity }}
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Timestamp: {{ $alert->timestamp }}
Unique-ID: {{ $alert->uid }}
Rule: @if ($alert->name) {{ $alert->name }} @else {{ $alert->rule }} @endif
@if ($alert->faults) Faults:
@foreach ($alert->faults as $key => $value)
#{{ $key }}: {{ $value['string'] }}
@endforeach
@endif
Alert sent to:
@foreach ($alert->contacts as $key => $value)
{{ $value }} <{{ $key }}>
@endforeach
```
Ports Utilization Template:
```text
%title
Device Name: %hostname
Severity: %severity
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Rule: {if %name}%name{else}%rule{/if}
{foreach %faults}
Physical Interface: %value.ifDescr
Interface Description: %value.ifAlias
Interface Speed: {calc (%value.ifSpeed/1000000000)} Gbs
Inbound Utilization: {calc ((%value.ifInOctets_rate*8)/%value.ifSpeed)*100}%
Outbound Utilization: {calc ((%value.ifOutOctets_rate*8)/%value.ifSpeed)*100}%
{/foreach}
{{ $alert->title }}
Device Name: {{ $alert->hostname }}
Severity: {{ $alert->severity }}
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Timestamp: {{ $alert->timestamp }}
Rule: @if ($alert->name) {{ $alert->name }} @else {{ $alert->rule }} @endif
@foreach ($alert->faults as $key => $value)
Physical Interface: $value['ifDescr']
Interface Description: $value['ifAlias']
Interface Speed: @php echo ($value['ifSpeed']/1000000000); @endphp Gbs
Inbound Utilization: @php echo (($value['ifInOctets_rate']*8)/$value['ifSpeed'])*100; @endphp%
Outbound Utilization: @php echo (($value['ifOutOctets_rate']*8)/$value['ifSpeed'])*100; @endphp%
@endforeach
```
Storage:
```text
{{ $alert->title }}
%title
Device Name: {{ $alert->hostname }}
Severity: {{ $alert->severity }}
Uptime: {{ $alert->uptime_short }}
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Timestamp: {{ $alert->timestamp }}
Location: {{ $alert->location }}
Description: {{ $alert->description }}
Features: {{ $alert->features }}
Purpose: {{ $alert->purpose }}
Notes: {{ $alert->notes }}
Device Name: %hostname
Severity: %severity
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Location: %location
Description: %description
Features: %features
Purpose: %purpose
Notes: %notes
Server: %sysName {foreach %faults}Mount Point: %value.storage_descr Percent Utilized: %value.storage_perc{/foreach}
Server: {{ $alert->sysName }} @foreach ($alert->faults as $key => $value)Mount Point: $value['storage_descr'] Percent Utilized: $value['storage_perc']@endforeach
```
Temperature Sensors:
```text
{{ $alert->title }}
%title
Device Name: {{ $alert->hostname }}
Severity: {{ $alert->severity }}
Timestamp: {{ $alert->timestamp }}
Uptime: {{ $alert->uptime_short }}
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Location: {{ $alert->location }}
Description: {{ $alert->description }}
Features: {{ $alert->features }}
Purpose: {{ $alert->purpose }}
Notes: {{ $alert->notes }}
Device Name: %hostname
Severity: %severity
Timestamp: %timestamp
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Location: %location
Description: %description
Features: %features
Purpose: %purpose
Notes: %notes
Rule: {if %name}%name{else}%rule{/if}
{if %faults}Faults:
{foreach %faults}
#%key: Temperature: %value.sensor_current°C
** {calc(%value.sensor_current-%value.sensor_limit)}°C over limit
Previous Measurement: %value.sensor_prev°C
High Temperature Limit: %value.sensor_limit°C
{/foreach}
{/if}
Rule: @if ($alert->name) {{ $alert->name }} @else {{ $alert->rule }} @endif
@if ($alert->faults) Faults:
@foreach ($faults as $key => $value)
#{{ $key }}: Temperature: $value['sensor_current']°C
** @php echo ($value['sensor_current']-$value['sensor_limit']); @endphp°C over limit
Previous Measurement: $value['sensor_prev']°C
High Temperature Limit: $value['sensor_limit']°C
@endforeach
@endif
```
Value Sensors:
```text
{{ $alert->title }}
%title
Device Name: {{ $alert->hostname }}
Severity: {{ $alert->severity }}
Timestamp: {{ $alert->timestamp }}
Uptime: {{ $alert->uptime_short }}
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Location: {{ $alert->location }}
Description: {{ $alert->description }}
Features: {{ $alert->features }}
Purpose: {{ $alert->purpose }}
Notes: {{ $alert->notes }}
Device Name: %hostname
Severity: %severity
Timestamp: %timestamp
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Location: %location
Description: %description
Features: %features
Purpose: %purpose
Notes: %notes
Rule: {if %name}%name{else}%rule{/if}
{if %faults}Faults:
{foreach %faults}
#%key: Sensor%value.sensor_current
** {calc(%value.sensor_current-%value.sensor_limit)}over limit
Previous Measurement: %value.sensor_prev
Limit: %value.sensor_limit
{/foreach}
{/if}
Rule: @if ($alert->name) {{ $alert->name }} @else {{ $alert->rule }} @endif
@if ($alert->faults) Faults:
@foreach ($alert->faults as $key => $value)
#{{ $key }}: Sensor {{ $value['sensor_current'] }}
** @php echo ($value['sensor_current']-$value['sensor_limit']); @endphp over limit
Previous Measurement: {{ $value['sensor_prev'] }}
Limit: {{ $value['sensor_limit'] }}
@endforeach
@endif
```
Memory Alert:
```text
%title
{{ $alert->title }}
Device Name: %hostname
Severity: %severity
Uptime: %uptime_short
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp
Location: %location
Description: %description
Notes: %notes
Device Name: {{ $alert->hostname }}
Severity: {{ $alert->severity }}
Uptime: {{ $alert->uptime_short }}
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Timestamp: {{ $alert->timestamp }}
Location: {{ $alert->location }}
Description: {{ $alert->description }}
Notes: {{ $alert->notes }}
Server: %hostname {foreach %faults}
Memory Description: %value.mempool_descr
Percent Utilized: %value.mempool_perc{/foreach}
Server: {{ $alert->hostname }}
@foreach ($alert->faults as $key => $value)
Memory Description: {{ $value['mempool_descr'] }}
Percent Utilized: {{ $value['mempool_perc'] }}
@endforeach
```
Conditional formatting example, will display a link to the host in email or just the hostname in any other transport:
```text
{if %transport == mail}<a href="https://my.librenms.install/device/device=%hostname/">%hostname</a>
{else}
%hostname
{/if}
@if ($alert->transport == mail)<a href="https://my.librenms.install/device/device={{ $alert->hostname }}/">{{ $alert->hostname }}</a>
@else
{{ $alert->hostname }}
@endif
```
Note the use of double-quotes. Single quotes (`'`) in templates will be escaped (replaced with `\'`) in the output and should therefore be avoided.
## Examples HTML
Note: To use HTML emails you must set HTML email to Yes in the WebUI under Global Settings > Alerting Settings > Email transport > Use HTML emails
@@ -215,43 +214,43 @@ $config['allow_unauth_graphs'] = true;
Service Alert:
```
<div style="font-family:Helvetica;">
<h2>{if %state == 1}<span style="color:red;">%severity{/if}
{if %state == 2}<span style="color:goldenrod;">acknowledged{/if}</span>
{if %state == 3}<span style="color:green;">recovering{/if}</span>
{if %state == 0}<span style="color:green;">recovered{/if}</span>
<h2>@if ($alert->state == 1) <span style="color:red;">{{ $alert->severity }} @endif
@if ($alert->state == 2) <span style="color:goldenrod;">acknowledged @endif</span>
@if ($alert->state == 3) <span style="color:green;">recovering @endif</span>
@if ($alert->state == 0) <span style="color:green;">recovered @endif</span>
</h2>
<b>Host:</b> %hostname<br>
<b>Duration:</b> %elapsed<br>
<b>Host:</b> {{ $alert->hostname }}<br>
<b>Duration:</b> {{ $alert->elapsed }}<br>
<br>
{if %faults}
{foreach %faults}<b>%value.service_desc - %value.service_type</b><br>
%value.service_message<br>
@if ($alert->faults)
@foreach ($alert->faults as $key => $value)<b>$value['service_desc'] - $value['service_type']</b><br>
$value['service_message']<br>
<br>
{/foreach}
{/if}
@endforeach
@endif
</div>
```
Processor Alert with Graph:
```
%title <br>
Severity: %severity <br>
{if %state == 0}Time elapsed: %elapsed{/if}
Timestamp: %timestamp <br>
Alert-ID: %id <br>
Rule: {if %name}%name{else}%rule{/if} <br>
{if %faults}Faults:
{foreach %faults}
#%key: %value.string <br>
{/foreach}
{if %faults}<b>Faults:</b><br>
{foreach %faults}<img src="https://server/graph.php?device=%value.device_id&type=device_processor&width=459&height=213&lazy_w=552&from=end-72h%22/%3E<br>
https://server/graphs/id=%value.device_id/type=device_processor/<br>
{/foreach}
{{ $alert->title }} <br>
Severity: {{ $alert->severity }} <br>
@if ($alert->state == 0) Time elapsed: {{ $alert->elapsed }} @endif
Timestamp: {{ $alert->timestamp }} <br>
Alert-ID: {{ $alert->id }} <br>
Rule: @if ($alert->name) {{ $alert->name }} @else {{ $alert->rule }} @endif <br>
@if ($alert->faults) Faults:
@foreach ($alert->faults as $key => $value)
#{{ $key }}: {{ $value['string'] }}<br>
@endforeach
@if ($alert->faults) <b>Faults:</b><br>
@foreach ($alert->faults as $key => $value)<img src="https://server/graph.php?device={{ $value['device_id'] }}&type=device_processor&width=459&height=213&lazy_w=552&from=end-72h><br>
https://server/graphs/id={{ $value['device_id'] }}/type=device_processor/<br>
@endforeach
Template: CPU alert <br>
{/if}
{/if}
@endif
@endif
```
## Included

View File

@@ -16,10 +16,10 @@
use LibreNMS\Authentication\Auth;
$init_modules = array('web', 'auth', 'alerts');
$init_modules = array('web', 'auth', 'alerts', 'alerts-cli');
require realpath(__DIR__ . '/..') . '/includes/init.php';
set_debug($_REQUEST['debug']);
set_debug(isset($_REQUEST['debug']) ? $_REQUEST['debug'] : false);
if (!Auth::check()) {
echo 'unauthenticated';

View File

@@ -44,8 +44,8 @@ $rule_id = $_POST['rule_id'];
$count = mres($_POST['count']);
$delay = mres($_POST['delay']);
$interval = mres($_POST['interval']);
$mute = mres($_POST['mute']);
$invert = mres($_POST['invert']);
$mute = mres(isset($_POST['mute']) ? $_POST['mute'] : null);
$invert = mres(isset($_POST['invert']) ? $_POST['invert'] : null);
$name = mres($_POST['name']);
$proc = mres($_POST['proc']);
$recovery = ($vars['recovery']);
@@ -138,7 +138,7 @@ if (is_numeric($rule_id) && $rule_id > 0) {
if (is_numeric($rule_id) && $rule_id > 0) {
$devices = [];
$groups = [];
foreach ((array)$_POST['maps'] as $item) {
foreach ((array)$vars['maps'] as $item) {
if (starts_with($item, 'g')) {
$groups[] = (int)substr($item, 1);
} else {

View File

@@ -24,48 +24,50 @@
use LibreNMS\Authentication\Auth;
header('Content-type: text/plain');
$status = 'error';
if (!Auth::user()->hasGlobalAdmin()) {
die('ERROR: You need to be admin');
header('Content-Type: application/json');
$response = array('status' => $status, 'message' => 'You need to be admin');
die(_json_encode($response));
}
$status = 'error';
$template_id = 0;
$name = mres($_POST['name']);
if (!empty($name)) {
if (is_numeric($_REQUEST['template_id']) && $_REQUEST['rule_id']) {
$name = mres($vars['name']);
if (isset($vars['template']) && empty(view(['template' => $vars['template']], [])->__toString())) {
$message = 'Template failed to be parsed, please check the syntax';
} elseif (!empty($name)) {
if ((isset($vars['template_id']) && is_numeric($vars['template_id'])) && (isset($vars['rule_id']) && $vars['rule_id'])) {
//Update the template/rule mapping
if (is_array($_REQUEST['rule_id'])) {
$_REQUEST['rule_id'] = implode(",", $_REQUEST['rule_id']);
if (is_array($vars['rule_id'])) {
$vars['rule_id'] = implode(",", $vars['rule_id']);
}
if (substr($_REQUEST['rule_id'], 0, 1) != ",") {
$_REQUEST['rule_id'] = ",".$_REQUEST['rule_id'];
if (substr($vars['rule_id'], 0, 1) != ",") {
$vars['rule_id'] = ",".$vars['rule_id'];
}
if (substr($_REQUEST['rule_id'], -1, 1) != ",") {
$_REQUEST['rule_id'] .= ",";
if (substr($vars['rule_id'], -1, 1) != ",") {
$vars['rule_id'] .= ",";
}
if (dbUpdate(array('rule_id' => mres($_REQUEST['rule_id']), 'name' => $name), "alert_templates", "id = ?", array($_REQUEST['template_id'])) >= 0) {
if (dbUpdate(array('rule_id' => mres($vars['rule_id']), 'name' => $name), "alert_templates", "id = ?", array($vars['template_id'])) >= 0) {
$message = "Updated template and rule id mapping";
} else {
$message ="Failed to update the template and rule id mapping";
}
} elseif ($_REQUEST['template'] && is_numeric($_REQUEST['template_id'])) {
} elseif ($vars['template'] && is_numeric($vars['template_id'])) {
//Update template-text
if (dbUpdate(array('template' => $_REQUEST['template'], 'name' => $name, 'title' => $_REQUEST['title'], 'title_rec' => $_REQUEST['title_rec']), "alert_templates", "id = ?", array($_REQUEST['template_id'])) >= 0) {
if (dbUpdate(array('template' => $vars['template'], 'name' => $name, 'title' => $vars['title'], 'title_rec' => $vars['title_rec']), "alert_templates", "id = ?", array($vars['template_id'])) >= 0) {
$status = 'ok';
$message = "Alert template updated";
} else {
$message = "Failed to update the template";
}
} elseif ($_REQUEST['template']) {
} elseif ($vars['template']) {
//Create new template
if ($name != 'Default Alert Template') {
$template_id = dbInsert(array('template' => $_REQUEST['template'], 'name' => $name, 'title' => $_REQUEST['title'], 'title_rec' => $_REQUEST['title_rec']), "alert_templates");
$template_id = dbInsert(array('template' => $vars['template'], 'name' => $name, 'title' => $vars['title'], 'title_rec' => $vars['title_rec']), "alert_templates");
if ($template_id != false) {
$status = 'ok';
$message = "Alert template has been created.";
@@ -84,5 +86,4 @@ if (!empty($name)) {
$response = array('status' => $status, 'message' => $message, 'newid' => $template_id);
header('Content-Type: application/json');
echo _json_encode($response);

View File

@@ -19,7 +19,7 @@ if (!Auth::user()->hasGlobalAdmin()) {
die('ERROR: You need to be admin');
}
$template_id = ($_POST['template_id']);
$template_id = ($vars['template_id']);
if (is_numeric($template_id) && $template_id > 0) {
$template = dbFetchRow('SELECT * FROM `alert_templates` WHERE `id` = ? LIMIT 1', array($template_id));
@@ -28,6 +28,7 @@ if (is_numeric($template_id) && $template_id > 0) {
'name' => $template['name'],
'title' => $template['title'],
'title_rec' => $template['title_rec'],
'type' => $template['type'],
);
header('Content-type: application/json');
echo _json_encode($output);

View File

@@ -24,75 +24,30 @@ if (!Auth::user()->hasGlobalAdmin()) {
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="Create">Alert Rules</h4>
<h4 class="modal-title" id="Create">Alert Template :: <a href="https://docs.librenms.org/Alerting/Templates/"><i class="fa fa-book fa-1x"></i> Docs</a></h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<span id="response"></span>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="template" class="control-label">Template:</label><br />
<div class="alert alert-danger" role="alert">You can enter text for your template directly below if you're feeling brave enough :)</div>
<label for="name">Template name: </label>
<input type="text" class="form-control input-sm" id="name" name="name">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="designer" class="control-label">Designer:</label><br />
<div class="alert alert-warning" role="alert">The designer below will help you create a template - be warned, it's beta :)</div>
<label for="template">Template: </label>
<textarea class="form-control" id="template" name="template" rows="15"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<textarea class="form-control" id="template" name="template" rows="15"></textarea><br /><br />
<strong><em>Give your template a name: </em></strong><br />
<input type="text" class="form-control input-sm" id="name" name="name"><br />
<em>Optionally, add custom titles: </em><br />
<input type="text" class="form-control input-sm" id="title" name="title" placeholder="Alert Title"><input type="text" class="form-control input-sm" id="title_rec" name="title_rec" placeholder="Recovery Title"><br />
<span id="error"></span><br />
<label for="title">Alert title: </label>
<input type="text" class="form-control input-sm" id="title" name="title" placeholder="Alert Title">
</div>
<div class="form-group">
<label for="title_rec">Recovery title: </label>
<input type="text" class="form-control input-sm" id="title_rec" name="title_rec" placeholder="Recovery Title">
</div>
<button type="button" class="btn btn-primary btn-sm" name="create-template" id="create-template">Create template</button>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<span><strong>Controls:</strong><br />
<?php
$controls = array('if','endif','else','foreach', 'endforeach');
foreach ($controls as $control) {
echo ' <button type="button" class="btn btn-primary btn-sm" data-target="#control-add" id="control-add" name="control-add" data-type="control" data-value="'.$control.'">'.$control.'</button>';
}
?>
</span><br /><br />
<span><strong>Placeholders:</strong><br />
<?php
$placeholders = array('hostname', 'sysName', 'location', 'uptime', 'description', 'notes', 'title','elapsed','id','uid','faults','state','severity','rule','timestamp','contacts','key','value','new line');
foreach ($placeholders as $placeholder) {
echo ' <button type="button" class="btn btn-success btn-sm" data-target="#placeholder-add" id="placeholder-add" name="placeholder-add" data-type="placeholder" data-value="'.$placeholder.'">'.$placeholder.'</button>';
}
?>
</span><br /><br />
<span><strong>Operator:</strong><br />
<?php
$operators = array('==','!=','>=','>','<=','<','&&','||','blank');
foreach ($operators as $operator) {
echo ' <button type="button" class="btn btn-warning btn-sm" data-target="#operator-add" id="operator-add" name="operator-add" data-type="operator" data-value="'.$operator.'">'.$operator.'</button>';
}
?>
<br /><br />
<small><em>Free text - press enter to add</em></small><br />
<input type="text" class="form-control" id="value" name="value" autocomplete="off"><br /><br />
<input type="text" class="form-control" id="line" name="line"><br /><br />
<input type="hidden" name="template_id" id="template_id">
<input type="hidden" name="default_template" id="default_template" value="0">
<button type="button" class="btn btn-primary" id="add_line" name="add_line">Add line</button>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -120,6 +75,9 @@ $('#alert-template').on('show.bs.modal', function (event) {
$('#name').val(output['name']);
$('#title').val(output['title']);
$('#title_rec').val(output['title_rec']);
if(output['template'].indexOf("{/if}")>=0){
toastr.info('The old template syntax is no longer supported. Please see https://docs.librenms.org/Alerting/Old-Templates/');
}
}
});
}
@@ -148,75 +106,6 @@ $('#create-template').click('', function(e) {
alertTemplateAjaxOps(template, name, template_id, title, title_rec);
});
$('#add_line').click('', function(e) {
e.preventDefault();
var line = $('#line').val();
$('#template').append(line + '\r\n');
$('#line').val('');
});
$('button[name="control-add"],button[name="placeholder-add"],button[name="operator-add"]').click('', function(e) {
e.preventDefault();
var type = $(this).data("type");
var value = $(this).data("value");
var line = $('#line').val();
var new_line = '';
if(type == 'control') {
$('button[name="control-add"]').prop('disabled',true);
if(value == 'if') {
new_line = '{if ';
} else if(value == 'endif') {
new_line = '{/if}';
$('button[name="control-add"]').prop('disabled',false);
} else if(value == 'else') {
new_line = ' {else} ';
} else if(value == 'foreach') {
new_line = '{foreach ';
} else if(value == 'endforeach') {
new_line = '{/foreach} ';
$('button[name="control-add"]').prop('disabled',false);
}
} else if(type == 'placeholder') {
if($('button[name="control-add"]').prop('disabled') === true) {
$('button[name="placeholder-add"]').prop('disabled',true);
}
if(value == 'new line') {
new_line = '\\r\\n ';
} else {
new_line = '%'+value+' ';
}
if(value == 'key' || value == 'value' || value == 'new line') {
$('button[name="placeholder-add"]').prop('disabled',false);
}
} else if(type == 'operator') {
if(value == 'blank') {
$('button[name="control-add"]').prop('disabled',false);
$('button[name="placeholder-add"]').prop('disabled',false);
new_line = '}';
} else {
$('button[name="operator-add"]').prop('disabled',true);
new_line = value+' ';
}
}
$('#line').val(line + new_line);
$('#valuee').focus();
});
$('#value').keypress(function (e) {
if(e.which == 13) {
updateLine($('#value').val());
$('#value').val('');
}
});
$('div').on('click', 'button#reset-default', function(e) {
e.preventDefault();
var template_id = $("#template_id").val();
var template = '%title\r\nSeverity: %severity\r\n{if %state == 0}Time elapsed: %elapsed\r\n{/if}Timestamp: %timestamp\r\nUnique-ID: %uid\r\nRule: {if %name}%name{else}%rule{/if}\r\n{if %faults}Faults:\r\n{foreach %faults} #%key: %value.string\r\n{/foreach}{/if}Alert sent to: {foreach %contacts}%value <%key> {/foreach}';
var name = 'Default Alert Template';
alertTemplateAjaxOps(template, name, template_id, '', '');
});
function alertTemplateAjaxOps(template, name, template_id, title, title_rec)
{
$.ajax({
@@ -225,6 +114,7 @@ function alertTemplateAjaxOps(template, name, template_id, title, title_rec)
data: { type: "alert-templates", template: template, name: name, template_id: template_id, title: title, title_rec: title_rec},
dataType: "json",
success: function(output) {
console.log(output);
if(output.status == 'ok') {
toastr.success(output.message);
$("#alert-template").modal('hide');
@@ -242,29 +132,14 @@ function alertTemplateAjaxOps(template, name, template_id, title, title_rec)
$('#templatetable').bootgrid("append", newrow);
}
} else {
$("#error").html('<div class="alert alert-danger">'+msg+'</div>');
toastr.error(output.message);
}
},
error: function(){
$("#error").html('<div class="alert alert-danger">An error occurred updating this alert template.</div>');
toastr.error('An error occurred updating this alert template.');
}
});
}
function updateLine(value) {
var line = $('#line').val();
//$('#value').prop('disabled',true);
if($('button[name="placeholder-add"]').prop('disabled') === true) {
value = '"'+value+'" } ';
//$('#value').prop('disabled',false);
} else {
value = value + ' ';
}
$('#line').val(line + value);
$('button[name="control-add"]').prop('disabled',false);
$('button[name="placeholder-add"]').prop('disabled',false);
$('button[name="operator-add"]').prop('disabled',false);
}
</script>

View File

@@ -26,7 +26,7 @@ if (Auth::user()->hasGlobalAdmin()) {
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h5 class="modal-title" id="Create">Alert Rules :: <a href="https://docs.librenms.org/Alerting/">Docs <i class="fa fa-book fa-1x"></i></a> </h5>
<h5 class="modal-title" id="Create">Alert Rule :: <a href="https://docs.librenms.org/Alerting/"><i class="fa fa-book fa-1x"></i> Docs</a> </h5>
</div>
<div class="modal-body">

View File

@@ -1568,6 +1568,7 @@ echo '
<label for="hue_duration" class="col-sm-4 control-label">Philips Hue Duration</label>
<div class="col-sm-4">
<select id="hue_duration" class="form-control" name="global-config-select" data-config_id="'.$hue_duration['config_id'].'">
<option '.( $hue_duration['config_value'] == "" ? "selected" : ""). ' value=""></option>
<option '.( $hue_duration['config_value'] == "select" ? "selected" : ""). ' value="select">1 Second</option>
<option '.( $hue_duration['config_value'] == "lselect" ? "selected" : ""). ' value="lselect">15 Seconds</option>
</select>

View File

@@ -13,3 +13,87 @@
*/
require_once 'includes/print-alert-templates.php';
?>
<form action="" method="post">
<input type="hidden" name="tool" value="convert">
<input type="submit" class="btn btn-primary btn-sm" value="Convert template">
</form>
<br />
<?php
if ($vars['tool'] === 'convert' || $vars['old']) {
if ($vars['old']) {
foreach (explode(PHP_EOL, $vars['old']) as $line) {
if (str_contains($line, '{calc')) {
$new .= preg_replace(
[
'/{calc[ ]*([\w\d\s\%\.\(\)\*\/\-\+\/]+)}/',// Replaces {calc (something*100)}
'/%([\w\d]+)\.([\w\d]+)/',// Replaces %something.anything
],
[
'@php echo \1; @endphp ',
'$value[\'\2\']',
],
$line
);
} else {
$new .= convert_template($line);
}
}
}
echo "
<form action='' method='post'>
<div class='row'>
<div class='col-sm-6'>
Paste old template here<br />
<textarea name='old' cols='80' rows='30'>{$vars['old']}</textarea>
</div>
<div class='col-sm-6'>
New<br />
<textarea name='old' cols='80' rows='30' disabled>$new</textarea>
</div>
</div>
<button type='submit' class='btn btn-success btn-sm' name='submit' value='convert'>Convert</button>
</form>
";
}
function convert_template($old)
{
$find = [
'/{if %([\w=\s]+)}/',// Replaces {if %something == else}
'/{else}/',// Replaces {else}
'/{\/if}/',// Replaces {/if}
'/{foreach %faults}/',// Replaces {foreach %faults}
'/{foreach %contacts}/',// Replaces {foreach %contacts}
'/{\/foreach}/',// Replaces {/foreach}
'/{calc[ ]*([\w\d\s\%\.\(\)\*\/\-\+\/]+)}/',// Replaces {calc (something*100)}
'/%value.string/',// Replaces %value.string
'/%([\w\d]+)\.([\w\d]+)/',// Replaces %something.anything
'/%([\w\d]+)/',// Replaces %anything
];
$replace = [
'@if ($alert->\1) ',
' @else ',
' @endif',
'@foreach ($alert->faults as $key => $value)',
'@foreach ($alert->contacts as $key => $value)',
'@endforeach ',
'@php echo \1; @endphp ',
'$value[\'string\']',
'{{ $\1[\'\2\'] }}',
'{{ $alert->\1 }}',
];
$old = preg_replace($find, $replace, $old);
// Revert some over-zealous changes:
$find = [
'/\$alert->key/',
'/\$alert->value/',
];
$replace = [
'$key',
'$value',
];
return preg_replace($find, $replace, $old);
}

View File

@@ -22,6 +22,7 @@
* @subpackage Alerts
*/
use LibreNMS\Alert\Template;
use LibreNMS\Alerting\QueryBuilderParser;
use LibreNMS\Authentication\Auth;
@@ -345,80 +346,6 @@ function GetContacts($results)
return $tmp_contacts;
}
/**
* Format Alert
* @param array $obj Alert-Array
* @return string
*/
function FormatAlertTpl($obj)
{
$tpl = $obj["template"];
$msg = '$ret .= "'.str_replace(array('{else}', '{/if}', '{/foreach}'), array('"; } else { $ret .= "', '"; } $ret .= "', '"; } $ret .= "'), addslashes($tpl)).'";';
$parsed = $msg;
$s = strlen($msg);
$x = $pos = -1;
$buff = '';
$if = $for = $calc = false;
while (++$x < $s) {
if ($msg[$x] == '{' && $buff == '') {
$buff .= $msg[$x];
} elseif ($buff == '{ ') {
$buff = '';
} elseif ($buff != '') {
$buff .= $msg[$x];
}
if ($buff == '{if') {
$pos = $x;
$if = true;
} elseif ($buff == '{foreach') {
$pos = $x;
$for = true;
} elseif ($buff == '{calc') {
$pos = $x;
$calc = true;
}
if ($pos != -1 && $msg[$x] == '}') {
$orig = $buff;
$buff = '';
$pos = -1;
if ($if) {
$if = false;
$o = 3;
$native = array(
'"; if( ',
' ) { $ret .= "',
);
} elseif ($for) {
$for = false;
$o = 8;
$native = array(
'"; foreach( ',
' as $key=>$value) { $ret .= "',
);
} elseif ($calc) {
$calc = false;
$o = 5;
$native = array(
'"; $ret .= (float) (0+(',
')); $ret .= "',
);
} else {
continue;
}
$cond = trim(populate(substr($orig, $o, -1), false));
$native = $native[0].$cond.$native[1];
$parsed = str_replace($orig, $native, $parsed);
unset($cond, $o, $orig, $native);
}//end if
}//end while
$parsed = populate($parsed);
return RunJail($parsed, $obj);
}//end FormatAlertTpl()
/**
* Populate variables
* @param string $txt Text with variables
@@ -476,19 +403,19 @@ function DescribeAlert($alert)
{
$obj = array();
$i = 0;
$device = dbFetchRow('SELECT hostname, sysName, sysDescr, hardware, version, location, purpose, notes, uptime FROM devices WHERE device_id = ?', array($alert['device_id']));
$device = dbFetchRow('SELECT hostname, sysName, sysDescr, sysContact, os, type, ip, hardware, version, location, purpose, notes, uptime FROM devices WHERE device_id = ?', array($alert['device_id']));
$attribs = get_dev_attribs($alert['device_id']);
if (can_ping_device($attribs)) {
$ping_stats = dbFetchRow('SELECT `timestamp`, `loss`, `min`, `max`, `avg` FROM `device_perf` WHERE `device_id` = ? ORDER BY `timestamp` LIMIT 1', [$alert['device_id']]);
}
$tpl = dbFetchRow('SELECT `template`,`title`,`title_rec` FROM `alert_templates` JOIN `alert_template_map` ON `alert_template_map`.`alert_templates_id`=`alert_templates`.`id` WHERE `alert_template_map`.`alert_rule_id`=?', array($alert['rule_id']));
if (!$tpl) {
$tpl = dbFetchRow("SELECT `template`,`title`,`title_rec` FROM `alert_templates` WHERE `name`='Default Alert Template'");
}
$default_tpl = "%title\r\nSeverity: %severity\r\n{if %state == 0}Time elapsed: %elapsed\r\n{/if}Timestamp: %timestamp\r\nUnique-ID: %uid\r\nRule: {if %name}%name{else}%rule{/if}\r\n{if %faults}Faults:\r\n{foreach %faults} #%key: %value.string\r\n{/foreach}{/if}Alert sent to: {foreach %contacts}%value <%key> {/foreach}";
$obj['hostname'] = $device['hostname'];
$obj['sysName'] = $device['sysName'];
$obj['sysDescr'] = $device['sysDescr'];
$obj['sysContact'] = $device['sysContact'];
$obj['os'] = $device['os'];
$obj['type'] = $device['type'];
$obj['ip'] = inet6_ntop($device['ip']);
$obj['hardware'] = $device['hardware'];
$obj['version'] = $device['version'];
$obj['location'] = $device['location'];
@@ -499,6 +426,7 @@ function DescribeAlert($alert)
$obj['notes'] = $device['notes'];
$obj['alert_notes'] = $alert['note'];
$obj['device_id'] = $alert['device_id'];
$obj['rule_id'] = $alert['rule_id'];
if (can_ping_device($attribs)) {
$obj['ping_timestamp'] = $ping_stats['template'];
$obj['ping_loss'] = $ping_stats['loss'];
@@ -507,17 +435,12 @@ function DescribeAlert($alert)
$obj['ping_avg'] = $ping_stats['avg'];
}
$extra = $alert['details'];
if (!isset($tpl['template'])) {
$obj['template'] = $default_tpl;
} else {
$obj['template'] = $tpl['template'];
}
$tpl = new Template;
$template = $tpl->getTemplate($obj);
if ($alert['state'] >= 1) {
if (!empty($tpl['title'])) {
$obj['title'] = $tpl['title'];
} else {
$obj['title'] = 'Alert for device '.$device['hostname'].' - '.($alert['name'] ? $alert['name'] : $alert['rule']);
}
$obj['title'] = $template->title ?: 'Alert for device '.$device['hostname'].' - '.($alert['name'] ? $alert['name'] : $alert['rule']);
if ($alert['state'] == 2) {
$obj['title'] .= ' got acknowledged';
} elseif ($alert['state'] == 3) {
@@ -529,9 +452,10 @@ function DescribeAlert($alert)
foreach ($extra['rule'] as $incident) {
$i++;
$obj['faults'][$i] = $incident;
$obj['faults'][$i]['string'] = null;
foreach ($incident as $k => $v) {
if (!empty($v) && $k != 'device_id' && (stristr($k, 'id') || stristr($k, 'desc') || stristr($k, 'msg')) && substr_count($k, '_') <= 1) {
$obj['faults'][$i]['string'] .= $k.' => '.$v.'; ';
$obj['faults'][$i]['string'] .= $k.' = '.$v.'; ';
}
}
}
@@ -552,11 +476,7 @@ function DescribeAlert($alert)
$extra['count'] = 0;
dbUpdate(array('details' => gzcompress(json_encode($id['details']), 9)), 'alert_log', 'id = ?', array($alert['id']));
if (!empty($tpl['title_rec'])) {
$obj['title'] = $tpl['title_rec'];
} else {
$obj['title'] = 'Device '.$device['hostname'].' recovered from '.($alert['name'] ? $alert['name'] : $alert['rule']);
}
$obj['title'] = $template->title_rec ?: 'Device '.$device['hostname'].' recovered from '.($alert['name'] ? $alert['name'] : $alert['rule']);
$obj['elapsed'] = TimeFormat(strtotime($alert['time_logged']) - strtotime($id['time_logged']));
$obj['id'] = $id['id'];
foreach ($extra['rule'] as $incident) {
@@ -571,6 +491,7 @@ function DescribeAlert($alert)
} else {
return 'Unknown State';
}//end if
$obj['builder'] = $alert['builder'];
$obj['uid'] = $alert['id'];
$obj['alert_id'] = $alert['alert_id'];
$obj['severity'] = $alert['severity'];
@@ -579,9 +500,7 @@ function DescribeAlert($alert)
$obj['timestamp'] = $alert['time_logged'];
$obj['contacts'] = $extra['contacts'];
$obj['state'] = $alert['state'];
if (strstr($obj['title'], '%')) {
$obj['title'] = RunJail('$ret = "'.populate(addslashes($obj['title'])).'";', $obj);
}
$obj['template'] = $template;
return $obj;
}//end DescribeAlert()
@@ -886,6 +805,7 @@ function ExtTransports($obj)
{
global $config;
$tmp = false;
$type = new Template;
// To keep scrutinizer from naging because it doesnt understand eval
foreach ($config['alert']['transports'] as $transport => $opts) {
if (is_array($opts)) {
@@ -894,8 +814,10 @@ function ExtTransports($obj)
$class = 'LibreNMS\\Alert\\Transport\\' . ucfirst($transport);
if (($opts === true || !empty($opts)) && $opts != false && class_exists($class)) {
$obj['transport'] = $transport;
$msg = FormatAlertTpl($obj);
$obj['msg'] = $msg;
$obj['alert'] = collect($obj);
$obj['title'] = $type->getTitle($obj);
$obj['alert']['title'] = $obj['title'];
$obj['msg'] = $type->getBody($obj);
echo $transport.' => ';
$instance = new $class;
$tmp = $instance->deliverAlert($obj, $opts);

View File

@@ -1030,7 +1030,7 @@ function object_is_cached($section, $obj)
function can_ping_device($attribs)
{
global $config;
if ($config['icmp_check'] === true && $attribs['override_icmp_disable'] != "true") {
if ($config['icmp_check'] === true && (isset($attribs['override_icmp_disable']) && $attribs['override_icmp_disable'] != "true")) {
return true;
} else {
return false;
@@ -1569,7 +1569,7 @@ function load_os(&$device)
}
// Set type to a predefined type for the OS if it's not already set
if ($device['attribs']['override_device_type'] != 1 && $config['os'][$device['os']]['type'] != $device['type']) {
if ((!isset($device['attribs']['override_device_type']) && $device['attribs']['override_device_type'] != 1) && $config['os'][$device['os']]['type'] != $device['type']) {
log_event('Device type changed ' . $device['type'] . ' => ' . $config['os'][$device['os']]['type'], $device, 'system', 3);
$device['type'] = $config['os'][$device['os']]['type'];
dbUpdate(array('type' => $device['type']), 'devices', 'device_id=?', array($device['device_id']));

View File

@@ -175,10 +175,6 @@ function dbInsert($data, $table)
dbCommitTransaction();
// return $id;
} else {
if ($table != 'Contact') {
trigger_error('QDB - Insert failed.', E_USER_WARNING);
}
dbRollbackTransaction();
$id = null;
}
@@ -285,7 +281,7 @@ function dbUpdate($data, $table, $where = null, $parameters = array())
$return = mysqli_affected_rows($database_link);
} else {
// echo("$fullSql");
trigger_error('QDB - Update failed.', E_USER_WARNING);
//trigger_error('QDB - Update failed.', E_USER_WARNING);
$return = false;
}

View File

@@ -42,7 +42,7 @@ function set_debug($state = true, $silence = false)
restore_error_handler(); // disable Laravel error handler
if ($debug) {
if (isset($debug) && $debug) {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
ini_set('log_errors', 0);
@@ -1025,7 +1025,7 @@ function log_event($text, $device = null, $type = null, $severity = 2, $referenc
'datetime' => array("NOW()"),
'severity' => $severity,
'message' => $text,
'username' => Auth::user()->username ?: '',
'username' => isset(Auth::user()->username) ? Auth::user()->username : '',
);
dbInsert($insert, 'eventlog');

View File

@@ -90,6 +90,13 @@ if (module_selected('polling', $init_modules)) {
if (module_selected('alerts', $init_modules)) {
require_once $install_dir . '/includes/device-groups.inc.php';
require_once $install_dir . '/includes/alerts.inc.php';
if (module_selected('alerts-cli', $init_modules)) {
// Boot Laravel - we only use it in alerting at present
require $install_dir . '/bootstrap/autoload.php';
$app = require_once $install_dir . '/bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
}
}
// Display config.php errors instead of http 500

View File

@@ -103,7 +103,6 @@ alert_schedule_items:
alert_templates:
Columns:
- { Field: id, Type: int(11), 'Null': false, Extra: auto_increment }
- { Field: rule_id, Type: varchar(255), 'Null': false, Extra: '', Default: ',' }
- { Field: name, Type: varchar(255), 'Null': false, Extra: '' }
- { Field: template, Type: longtext, 'Null': false, Extra: '' }
- { Field: title, Type: varchar(255), 'Null': true, Extra: '' }

View File

@@ -154,6 +154,7 @@ pages:
- Intro: Support/Device-Notes/index.md
- Carel pCOweb: Support/Device-Notes/Carel-pCOweb-Devices.md
- hidden:
- Alerting/Old_Templates.md
- API/API-Docs.md
- Installation/Installing-LibreNMS.md
- Extensions/Alerting.md

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
$init_modules = array('alerts');
$init_modules = ['alerts', 'alerts-cli'];
require __DIR__ . '/../includes/init.php';
$options = getopt('t:h:r:p:s:d::');
@@ -20,6 +20,7 @@ if ($options['r'] && $options['h']) {
$alert = $alerts[0];
$alert['details']['delay'] = 0;
$alert['note'] = 'Testing';
IssueAlert($alert);
} else {
c_echo("

1
sql-schema/254.sql Normal file
View File

@@ -0,0 +1 @@
ALTER TABLE `alert_templates` DROP `rule_id`;

View File

@@ -60,7 +60,7 @@ class DBSetupTest extends DBTestCase
}
// cannot assume user use the librenms database name
if (str_contains($line, 'librenms')) {
if (str_contains($line, 'librenms.')) {
throw new PHPUnitException("Do not include the database name in schema files\n$file: $line");
}
}