Added Alert Transports Mapping (#8660)

Hello all,
I guess this is the second version of a more fully fleshed out alert contact mapping feature. The old one was GH-8507

Transports to convert:

  - [x] API
  - [x] Cisco Spark
  - [x] Elasticsearch
  - [x] GitLab
  - [x] Philips Hue
  - [x] Jira
  - [x] Mail
  - [ ] ~~PagerDuty~~ - Requires a callback so leaving for now
  - [x] Nagios
  - [x] IRC
  - [x] Discord
  - [x] Rocket.chat
  - [x] Hipchat
  - [x] Pushover
  - [x] Boxcar
  - [x] Telegram
  - [x] Pushbullet
  - [x] VictorOps
  - [x] OpsGenie
  - [x] Clickatell
  - [x] PlaySMS
  - [x] Canopsis
  - [x] osTicket
  - [x] Microsoft Teams
  - [x] SMSEagle
  - [x] Syslog
  - [x] Slack

The intention is for this feature to have three different levels to it:
1. Alert rule to an alert contact mapping (where the code is at now)
2. Alert rule to an alert group (made up of alert contacts) mapping
3. Alert contact mapping to different transport configurations.

There will be three transport configuration types.
1. Default (the configuration that is held in the configs table)
2. None (no transport configuration - will explain later)
3. Other (a configuration that will be defined in a different able)

Take Mail transport for example. It can either be of a "default" or "other" configuration. The hope is that in the future, users can send mail from different mail servers if they wish.
However, for ciscospark which requires a room ID and an api-token, I've decided that it has no transport configuration. Most likely, every alert contact will contain a different room-id and an api-token - which is why it has the transport config of "none".
For other transports : I am not familiar with them, so hopefully the community can add support for these. I can definitely help!

To add support for each transport will require several things:
- addition to the UI
- addition to forms/alert-contacts.inc.php
- modifications to its object class

Screenshots
![image](https://user-images.githubusercontent.com/28970851/39594533-2092ce9e-4eca-11e8-9c5d-cd002ece1425.png)
![image](https://user-images.githubusercontent.com/28970851/39594544-276e9856-4eca-11e8-80cc-82789ee0b2b2.png)
![image](https://user-images.githubusercontent.com/28970851/39594553-2fdf528c-4eca-11e8-8a40-4f149e767054.png)

I'm not sure if this is the best way to do things, so please let me know if there's a better way to structure the code! Any comments on code/db schema,/UI etc is welcome and encouraged! 

The UI is heavily based on alert rules (front end is not my strong suit). And parts of the code are based on the code that was written for alert rules.

DO NOT DELETE THIS TEXT

#### Please note

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

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

#### Testers

If you would like to test this pull request then please run: `./scripts/github-apply <pr_id>`, i.e `./scripts/github-apply 5926`
This commit is contained in:
vivia11
2018-07-21 13:34:59 -06:00
committed by Neil Lathwood
parent 54284800c7
commit e1118b628a
57 changed files with 3281 additions and 771 deletions

View File

@@ -0,0 +1,37 @@
<?php
namespace LibreNMS\Alert;
class AlertUtil
{
// Return rule id from alert id
private static function getRuleId($alert_id)
{
$query = "SELECT `rule_id` FROM `alerts` WHERE `id`=?";
return dbFetchCell($query, [$alert_id]);
}
// Return all alert transports mapped to a rule (includies transport groups)
// @retyurn array [$transport_id $transport_type]
public static function getAlertTransports($alert_id)
{
// Query for list of transport ids
$query = "SELECT b.transport_id, b.transport_type FROM alert_transport_map AS a LEFT JOIN alert_transports AS b ON b.transport_id=a.transport_or_group_id WHERE a.target_type='single' AND a.rule_id=? UNION DISTINCT SELECT d.transport_id, d.transport_type FROM alert_transport_map AS a LEFT JOIN alert_transport_groups AS b ON a.transport_or_group_id=b.transport_group_id LEFT JOIN transport_group_transport AS c ON b.transport_group_id=c.transport_group_id LEFT JOIN alert_transports AS d ON c.transport_id=d.transport_id WHERE a.target_type='group' AND a.rule_id=?";
$rule_id = self::getRuleId($alert_id);
return dbFetchRows($query, [$rule_id, $rule_id]);
}
// Return transports configured as default
// @return array [$transport_id $transport_type]
public static function getDefaultAlertTransports()
{
$query = "SELECT transport_id, transport_type FROM alert_transports WHERE is_default=true";
return dbFetchRows($query);
}
// Return list of transport types with a default configured
public static function getDefaultTransportList()
{
$query = "SELECT DISTINCT transport_type FROM alert_transports WHERE is_default=true ";
return dbFetchColumn($query);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace LibreNMS\Alert;
use LibreNMS\Interfaces\Alert\Transport as TransportInterface;
abstract class Transport implements TransportInterface
{
protected $config;
// Sets config field to an associative array of transport config values
public function __construct($transport_id = null)
{
if (!empty($transport_id)) {
$sql = "SELECT `transport_config` FROM `alert_transports` WHERE `transport_id`=?";
$this->config = json_decode(dbFetchCell($sql, [$transport_id]), true);
}
}
}

View File

@@ -1,5 +1,5 @@
<?php
/* Copyright (C) 2014 Daniel Preussker <f0o@devilcode.org>
/* Copyright (C) 2014 Daniel Preussker <f0o>
* 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
@@ -11,11 +11,11 @@
* 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/>. */
* along with this program. If not, see <http>. */
/**
* API Transport
* @author f0o <f0o@devilcode.org>
* @author f0o <f0o>
* @copyright 2014 f0o, LibreNMS
* @license GPL
* @package LibreNMS
@@ -24,16 +24,33 @@
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Api implements Transport
class Api extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$url = $this->config['api-url'];
$method = $this->config['api-method'];
$this->contactAPI($obj, $url, $method);
}
private function deliverAlertOld($obj, $opts)
{
foreach ($opts as $method => $apis) {
// var_dump($method); //FIXME: propper debuging
foreach ($apis as $api) {
// var_dump($api); //FIXME: propper debuging
$this->contactAPI($obj, $api, $method);
}
}
return true;
}
private function contactAPI($obj, $api, $method)
{
$method = strtolower($method);
list($host, $api) = explode("?", $api, 2);
foreach ($obj as $k => $v) {
$api = str_replace("%" . $k, $method == "get" ? urlencode($v) : $v, $api);
@@ -57,7 +74,32 @@ class Api implements Transport
return 'HTTP Status code '.$code;
}
}
}
return true;
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'API Method',
'name' => 'api-method',
'descr' => 'API Method: GET or POST',
'type' => 'select',
'options' => [
'get' => 'get',
'get' => 'get'
]
],
[
'title' => 'API URL',
'name' => 'api-url',
'descr' => 'API URL',
'type' => 'text',
]
],
'validation' => [
'api-method' => 'in:GET,POST',
'api-url' => 'required|url'
]
];
}
}

View File

@@ -38,17 +38,37 @@
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
use LibreNMS\Config;
class Boxcar implements Transport
class Boxcar extends Transport
{
public function deliverAlert($obj, $opts)
{
global $config;
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$boxcar_opts['access_token'] = $this->config['boxcar-token'];
foreach (explode(PHP_EOL, $this->config['options']) as $option) {
list($k,$v) = explode('=', $option);
$boxcar_opts[$k] = $v;
}
return $this->contactBoxcar($obj, $boxcar_opts);
}
public function deliverAlertOld($obj, $opts)
{
foreach ($opts as $api) {
$data = array();
$this->contactBoxcar($obj, $api);
}
return true;
}
public static function contactBoxcar($obj, $api)
{
$data = [];
$data['user_credentials'] = $api['access_token'];
$data['notification[source_name]'] = $config['project_id'];
$data['notification[source_name]'] = Config::get('project_id', 'librenms');
switch ($obj['severity']) {
case "critical":
$severity = "Critical";
@@ -91,13 +111,35 @@ class Boxcar implements Transport
curl_setopt($curl, CURLOPT_URL, 'https://new.boxcar.io/api/notifications');
curl_setopt($curl, CURLOPT_SAFE_UPLOAD, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$ret = curl_exec($curl);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($code != 201) {
var_dump("Boxcar returned error"); //FIXME: proper debugging
return false;
}
}
return true;
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Access Token',
'name' => 'boxcar-token',
'descr' => 'Boxcar Access Token',
'type' => 'text',
],
[
'title' => 'Boxcar Options',
'name' => 'boxcar-options',
'descr' => 'Boxcar Options',
'type' => 'textarea',
]
],
'validation' => [
'boxcar-token' => 'required',
]
];
}
}

View File

@@ -1,11 +1,23 @@
<?php
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Canopsis implements Transport
class Canopsis extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['host'] = $this->config['canopsis-host'];
$opts['port'] = $this->config['canopsis-port'];
$opts['user'] = $this->config['canopsis-user'];
$opts['pass'] = $this->config['canopsis-pass'];
$opts['vhost'] = $this->config['canopsis-vhost'];
}
return $this->contactCanopsis($obj, $opts);
}
public function contactCanopsis($obj, $opts)
{
// Configurations
$host = $opts["host"];
@@ -68,4 +80,49 @@ class Canopsis implements Transport
$conn->close();
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Hostname',
'name' => 'canopsis-host',
'descr' => 'Canopsis Hostname',
'type' => 'text'
],
[
'title' => 'Port Number',
'name' => 'canopsis-port',
'descr' => 'Canopsis Port Number',
'type' => 'text'
],
[
'title' => 'User',
'name' => 'canopsis-user',
'descr' => 'Canopsis User',
'type' => 'text'
],
[
'title' => 'Password',
'name' => 'canopsis-pass',
'descr' => 'Canopsis Password',
'type' => 'text'
],
[
'title' => 'Vhost',
'name' => 'canopsis-vhost',
'descr' => 'Canopsis Vhost',
'type' => 'text'
],
],
'validation' => [
'canopsis-host' => 'required|string',
'canopsis-port' => 'required|numeric',
'canopsis-user' => 'required|string',
'canopsis-pass' => 'required|string',
'canopsis-vhost' => 'required|string',
]
];
}
}

View File

@@ -11,19 +11,31 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Ciscospark implements Transport
class Ciscospark extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
$room_id = $opts['roomid'];
$token = $opts['token'];
$roomId = $opts['roomid'];
} else {
$room_id = $this->config['room-id'];
$token = $this->config['api-token'];
}
return $this->contactCiscospark($obj, $room_id, $token);
}
public function contactCiscospark($obj, $room_id, $token)
{
$text = strip_tags($obj['msg']);
$data = array (
'roomId' => $roomId,
'roomId' => $room_id,
'text' => $text
);
$token = $token;
$curl = curl_init();
set_curl_proxy($curl);
curl_setopt($curl, CURLOPT_URL, 'https://api.ciscospark.com/v1/messages');
@@ -38,10 +50,33 @@ class Ciscospark implements Transport
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($code != 200) {
var_dump("Cisco Spark returned Error, retry later");
echo("Cisco Spark returned Error, retry later\r\n");
return false;
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'API Token',
'name' => 'api-token',
'descr' => 'CiscoSpark API Token',
'type' => 'text',
],
[
'title' => 'RoomID',
'name' => 'room-id',
'descr' => 'CiscoSpark Room ID',
'type' => 'text',
]
],
'validation' => [
'api-token' => 'required|string',
'room-id' => 'required|string'
]
];
}
}

View File

@@ -23,11 +23,26 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Clickatell implements Transport
class Clickatell extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$clickatell_opts['token'] = $this->config['clickatell-token'];
$clickatell_opts['to'] = preg_split('/([,\r\n]+)/', $this->config['clickatell-numbers']);
return $this->contactClickatell($obj, $clickatell_opts);
}
public function deliverAlertOld($obj, $opts)
{
return $this->contactClickatell($obj, $opts);
}
public static function contactClickatell($obj, $opts)
{
$url = 'https://platform.clickatell.com/messages/http/send?apiKey=' . $opts['token'] . '&to=' . implode(',', $opts['to']) . '&content=' . urlencode($obj['title']);
@@ -39,11 +54,32 @@ class Clickatell implements Transport
$ret = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($code > 200) {
if ($debug) {
var_dump($ret);
}
return false;
return var_dump($ret);
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Token',
'name' => 'cickatell-token',
'descr' => 'Clickatell Token',
'type' => 'text',
],
[
'title' => 'Mobile Numbers',
'name' => 'clickatell-numbers',
'descr' => 'Enter mobile numbers, can be new line or comma separated',
'type' => 'textarea',
]
],
'validation' => [
'clickatell-token' => 'required|string',
'clickatell-numbers' => 'required|string',
]
];
}
}

View File

@@ -25,23 +25,46 @@
* Thanks to F0o <f0o@devilcode.org> for creating the Slack transport which is the majority of this code.
* Thanks to sdef2 for figuring out the differences needed to make Discord work.
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Discord implements Transport
class Discord extends Transport
{
public function deliverAlert($obj, $opts)
{
foreach ($opts as $tmp_api) {
$host = $tmp_api['url'];
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$discord_opts['url'] = $this->config['url'];
foreach (explode(PHP_EOL, $this->config['options']) as $option) {
list($k,$v) = explode('=', $option);
$discord_opts['options'][$k] = $v;
}
return $this->contactDiscord($obj, $discord_opts);
}
public function deliverAlertOld($obj, $opts)
{
foreach ($opts as $discord_opts) {
$this->contactDiscord($obj, $discord_opts);
}
return true;
}
public function contactDiscord($obj, $discord_opts)
{
$host = $discord_opts['url'];
$curl = curl_init();
$discord_msg = strip_tags($obj['msg']);
$color = ($obj['state'] == 0 ? '#00FF00' : '#FF0000');
$data = array(
'username'=>$tmp_api['username'],
$data = [
'content' => "". $obj['title'] ."\n" . $discord_msg
);
];
if (!empty($discord_opts['options'])) {
$data = array_merge($data, $discord_opts['options']);
}
$alert_message = json_encode($data);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
set_curl_proxy($curl);
@@ -58,7 +81,29 @@ class Discord implements Transport
var_dump("Return: " . $ret); //FIXME: propper debuging
return 'HTTP Status code ' . $code;
}
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Discord URL',
'name' => 'url',
'descr' => 'Discord URL',
'type' => 'text',
],
[
'title' => 'Options',
'name' => 'options',
'descr' => 'Enter the config options (format: option=value separated by new lines)',
'type' => 'textarea',
]
],
'validation' => [
'url' => 'required|url',
]
];
}
}

View File

@@ -23,13 +23,23 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Dummy implements Transport
class Dummy extends Transport
{
public function deliverAlert($obj, $opts)
{
var_dump($obj);
return true;
}
public function contactDummy()
{
return true;
}
public static function configTemplate()
{
return [];
}
}

View File

@@ -16,11 +16,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Elasticsearch implements Transport
class Elasticsearch extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['es_host'] = $this->config['es-host'];
$opts['es_port'] = $this->config['es-port'];
$opts['es_index'] = $this->config['es-pattern'];
$opts['es_proxy'] = $this-> config['es-proxy'];
}
return $this->contactElasticsearch($obj, $opts);
}
public function contactElasticsearch($obj, $opts)
{
$es_host = '127.0.0.1';
$es_port = 9200;
@@ -184,4 +196,42 @@ class Elasticsearch implements Transport
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Host',
'name' => 'es-host',
'descr' => 'Elasticsearch Host',
'type' => 'text',
],
[
'title' => 'Port',
'name' => 'es-port',
'descr' => 'Elasticsearch Port',
'type' => 'text',
],
[
'title' => 'Index Pattern',
'name' => 'es-pattern',
'descr' => 'Elasticsearch Index Pattern',
'type' => 'text',
],
[
'title' => 'Use proxy if configured?',
'name' => 'es-proxy',
'descr' => 'Elasticsearch Proxy',
'type' => 'checkbox',
'default' => false
]
],
'validation' => [
'es-host' => 'required|string',
'es-port' => 'required|string',
'es-pattern' => 'required|string'
]
];
}
}

View File

@@ -23,11 +23,21 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Gitlab implements Transport
class Gitlab extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['project-id'] = $this->config['gitlab-id'];
$opts['key'] = $this->config['gitlab-key'];
$opts['host'] = $this->config['gitlab-host'];
}
return $this->contactGitlab($obj, $opts);
}
public function contactGitlab($obj, $opts)
{
// Don't create tickets for resolutions
if ($obj['state'] == 0) {
@@ -73,4 +83,35 @@ class Gitlab implements Transport
}
}
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Host',
'name' => 'gitlab-host',
'descr' => 'Gitlab Host',
'type' => 'text',
],
[
'title' => 'Project ID',
'name' => 'gitlab-id',
'descr' => 'Gitlab Prokect ID',
'type'=> 'text',
],
[
'title' => 'Personal Access Token',
'name' => 'gitlab-key',
'descr' => 'Personal Access Token',
'type' => 'text',
]
],
'validation' => [
'gitlab-host' => 'required|string',
'gitlab-id' => 'required|string',
'gitlab-key' => 'required|string'
]
];
}
}

View File

@@ -23,14 +23,36 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Hipchat implements Transport
class Hipchat extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$hipchat_opts['url'] = $this->config['hipchat-url'];
$hipchat_opts['room_id'] = $this->config['hipchat-room-id'];
$hipchat_opts['from'] = $this->config['hipchat-from-name'];
foreach (explode(PHP_EOL, $this->config['hipchat-options']) as $option) {
list($k,$v) = explode('=', $option);
$hipchat_opts[$k] = $v;
}
return $this->contactHipchat($obj, $hipchat_opts);
}
public function deliverAlertOld($obj, $opts)
{
// loop through each room
foreach ($opts as $option) {
$this->contactHipchat($obj, $option);
}
return true;
}
public function contactHipchat($obj, $option)
{
$version = 1;
if (stripos($option['url'], "v2")) {
$version = 2;
@@ -97,6 +119,40 @@ class Hipchat implements Transport
return 'HTTP Status code ' . $code;
}
}
return true;
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'API URL',
'name' => 'hipchat-url',
'descr' => 'Hipchat API URL',
'type' => 'text',
],
[
'title' => 'Room ID',
'name' => 'hipchat-room-id',
'descr' => 'Hipchat Room ID',
'type' => 'text',
],
[
'title' => 'From Name',
'name' => 'hipchat-from-name',
'descr' => 'From Name',
'type' => 'text',
],
[
'title' => 'Hipchat Options',
'name' => 'hipchat-options',
'descr' => 'Hipchat Options',
'type' => 'textarea',
],
],
'validation' => [
'hipchat-url' => 'required|url',
'hipchat-room-id' => 'required|numeric',
]
];
}
}

View File

@@ -23,16 +23,27 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
/**
* The Hue API currently is fairly limited for alerts.
* At it's current implementation we can send ['lselect' => "15 second flash", 'select' => "1 second flash"]
* If a colour request is sent with it it will permenantly change the colour which is less than desired
*/
class Hue implements Transport
class Hue extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['user'] = $this->config['hue-user'];
$opts['bridge'] = $this->config['hue-host'];
$opts['duration'] = $this->config['hue-duration'];
}
return $this->contactHue($obj, $opts);
}
public function contactHue($obj, $opts)
{
// Don't alert on resolve at this time
if ($obj['state'] == 0) {
@@ -68,4 +79,39 @@ class Hue implements Transport
}
}
}
public static function configTemplate()
{
return [
'config'=>[
[
'title'=> 'Host',
'name' => 'hue-host',
'descr' => 'Hue Host',
'type' => 'text',
],
[
'title'=> 'Hue User',
'name' => 'hue-user',
'descr' => 'Phillips Hue Host',
'type' => 'text',
],
[
'title'=> 'Duration',
'name' => 'hue-duration',
'descr' => 'Phillips Hue Duration',
'type' => 'select',
'options' => [
'1 Second' => 'select',
'15 Seconds' => 'lselect'
]
]
],
'validation' => [
'hue-host' => 'required|string',
'hue-user' => 'required|string',
'hue-duration' => 'required|string'
]
];
}
}

View File

@@ -23,14 +23,19 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
use LibreNMS\Config;
class Irc implements Transport
class Irc extends Transport
{
public function deliverAlert($obj, $opts)
{
global $config;
$f = $config['install_dir'] . "/.ircbot.alert";
return $this->contactIrc($obj, $opts);
}
public function contactIrc($obj, $opts)
{
$f = Config::get('install_dir') . "/.ircbot.alert";
if (file_exists($f) && filetype($f) == "fifo") {
$f = fopen($f, "w+");
$r = fwrite($f, json_encode($obj) . "\n");
@@ -42,4 +47,22 @@ class Irc implements Transport
}
}
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'IRC',
'name' => 'irc',
'descr' => 'Enable IRC alerts',
'type' => 'checkbox',
'default' => true,
]
],
'validation' => [
'irc' => 'required'
]
];
}
}

View File

@@ -23,11 +23,23 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Jira implements Transport
class Jira extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['username'] = $this->config['jira-username'];
$opts['password'] = $this->config['jira-password'];
$opts['prjkey'] = $this->config['jira-key'];
$opts['issuetype'] = $this->config['jira-type'];
$opts['url'] = $this->config['jira-url'];
}
return $this->contactJira($obj, $opts);
}
public function contactJira($obj, $opts)
{
// Don't create tickets for resolutions
if ($obj['severity'] == 'recovery' && $obj['msg'] != 'This is a test alert') {
@@ -75,4 +87,49 @@ class Jira implements Transport
return false;
}
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'URL',
'name' => 'jira-url',
'descr' => 'Jira URL',
'type' => 'text'
],
[
'title' => 'Project Key',
'name' => 'jira-key',
'descr' => 'Jira Project Key',
'type' => 'text'
],
[
'title' => 'Issue Type',
'name' => 'jira-type',
'descr' => 'Jira Issue Type',
'type' => 'text'
],
[
'title' => 'Jira Username',
'name' => 'jira-username',
'descr' => 'Jira Username',
'type' => 'text'
],
[
'title' => 'Jira Password',
'name' => 'jira-password',
'descr' => 'Jira Password',
'type' => 'text'
],
],
'validation' => [
'jira-key' => 'required|string',
'jira-url' => 'required|string',
'jira-type' => 'required|string',
'jira-username' => 'required|string',
'jira-password' => 'required|string',
]
];
}
}

View File

@@ -23,13 +23,40 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
use LibreNMS\Config;
class Mail implements Transport
class Mail extends Transport
{
public function deliverAlert($obj, $opts)
{
global $config;
return send_mail($obj['contacts'], $obj['title'], $obj['msg'], ($config['email_html'] == 'true') ? true : false);
return $this->contactMail($obj, $opts);
}
public function contactMail($obj, $opts)
{
if (empty($this->config['email'])) {
$email = $obj['contacts'];
} else {
$email = $this->config['email'];
}
return send_mail($email, $obj['title'], $obj['msg'], (Config::get('email_html') == 'true') ? true : false);
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Email',
'name' => 'email',
'descr' => 'Email address of contact',
'type' => 'text',
]
],
'validation' => [
'email' => 'required|email'
]
];
}
}

View File

@@ -11,11 +11,20 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Msteams implements Transport
class Msteams extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['url'] = $this->config['msteam-url'];
}
return $this->contactMsteams($obj, $opts);
}
public function contactMsteams($obj, $opts)
{
$url = $opts['url'];
$color = ($obj['state'] == 0 ? '#00FF00' : '#FF0000');
@@ -40,7 +49,23 @@ class Msteams implements Transport
var_dump("Microsoft Teams returned Error, retry later");
return false;
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Webhook URL',
'name' => 'msteam-url',
'descr' => 'Microsoft Teams Webhook URL',
'type' => 'text',
]
],
'validation' => [
'msteam-url' => 'required|url'
]
];
}
}

View File

@@ -23,11 +23,25 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Nagios implements Transport
class Nagios extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$opts = $this->config['nagios-fifo'];
return $this->contactNagios($obj, $opts);
}
public function deliverAlertOld($obj, $opts)
{
return $this->contactNagios($obj, $opts);
}
public static function contactNagios($obj, $opts)
{
/*
host_perfdata_file_template=
@@ -55,4 +69,21 @@ class Nagios implements Transport
$format .= "\n";
return file_put_contents($opts, $format);
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Nagios FIFO',
'name' => 'nagios-fifo',
'descr' => 'Nagios compatible FIFO',
'type' => 'text',
],
],
'validation' => [
'nagios-fifo' => 'required',
]
];
}
}

View File

@@ -23,11 +23,19 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Opsgenie implements Transport
class Opsgenie extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['url'] = $this->config['genie-url'];
}
return $this->contactOpsgenie($obj, $opts);
}
public function contactOpsgenie($obj, $opts)
{
$url = $opts['url'];
@@ -50,4 +58,21 @@ class Opsgenie implements Transport
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Webhook URL',
'name' => 'genie-url',
'descr' => 'OpsGenie Webhook URL',
'type' => 'text'
]
],
'validation' => [
'genie-url' => 'required|url'
]
];
}
}

View File

@@ -11,11 +11,20 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Osticket implements Transport
class Osticket extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['url'] = $this->config['os-url'];
$opts['token'] = $this->config['os-token'];
}
return $this->contactOsticket($obj, $opts);
}
public function contactOsticket($obj, $opts)
{
global $config;
@@ -55,4 +64,28 @@ class Osticket implements Transport
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'API URL',
'name' => 'os-url',
'descr' => 'osTicket API URL',
'type' => 'text'
],
[
'title' => 'API Token',
'name' => 'os-token',
'descr' => 'osTicket API Token',
'type' => 'text'
]
],
'validation' => [
'os-url' => 'required|url',
'os-token' => 'required|string'
]
];
}
}

View File

@@ -23,9 +23,9 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Pagerduty implements Transport
class Pagerduty extends Transport
{
public function deliverAlert($obj, $opts)
{
@@ -59,4 +59,14 @@ class Pagerduty implements Transport
}
return true;
}
public function contactPagerduty()
{
return [];// Pagerduty is a custom transport.
}
public static function configTemplate()
{
return [];// Pagerduty is a custom transport.
}
}

View File

@@ -23,11 +23,29 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Playsms implements Transport
class Playsms extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$playsms_opts['url'] = $this->config['playsms-url'];
$playsms_opts['user'] = $this->config['playsms-user'];
$playsms_opts['token'] = $this->config['playsms-token'];
$playsms_opts['from'] = $this->config['playsms-from'];
$playsms_opts['to'] = preg_split('/([,\r\n]+)/', $this->config['playsms-mobiles']);
return $this->contactDiscord($obj, $playsms_opts);
}
public function deliverAlertOld($obj, $opts)
{
return $this->contactPlaysms($obj, $opts);
}
public static function contactPlaysms($obj, $opts)
{
$data = array("u" => $opts['user'], "h" => $opts['token'], "to" => implode(',', $opts['to']), "msg" => $obj['title']);
if (!empty($opts['from'])) {
@@ -43,11 +61,52 @@ class Playsms implements Transport
$ret = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($code > 202) {
if ($debug) {
var_dump($ret);
}
return false;
return var_dump($ret);
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'PlaySMS URL',
'name' => 'playsms-url',
'descr' => 'PlaySMS URL',
'type' => 'text',
],
[
'title' => 'User',
'name' => 'playsms-user',
'descr' => 'PlaySMS User',
'type' => 'text',
],
[
'title' => 'Token',
'name' => 'playsms-token',
'descr' => 'PlaySMS Token',
'type' => 'text',
],
[
'title' => 'From',
'name' => 'playsms-from',
'descr' => 'PlaySMS From',
'type' => 'text',
],
[
'title' => 'Mobiles',
'name' => 'playsms-mobiles',
'descr' => 'PlaySMS Mobiles, can be new line or comma separated',
'type' => 'textarea',
],
],
'validation' => [
'playsms-url' => 'required|url',
'playsms-user' => 'required|string',
'playsms-token' => 'required|string',
'playsms-mobiles' => 'required',
]
];
}
}

View File

@@ -23,11 +23,19 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Pushbullet implements Transport
class Pushbullet extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts = $this->config['pushbullet-token'];
}
return contactPushbullet($obj, $opts);
}
public function contactPushbullet($obj, $opts)
{
// Note: At this point it might be useful to iterate through $obj['contacts'] and send each of them a note ?
@@ -55,4 +63,21 @@ class Pushbullet implements Transport
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Access Token',
'name' => 'pushbullet-token',
'descr' => 'Pushbullet Access Token',
'type' => 'text'
]
],
'validation' => [
'pushbullet-token' => 'required|string'
]
];
}
}

View File

@@ -37,13 +37,37 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Pushover implements Transport
class Pushover extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$pushover_opts = $this->config;
unset($pushover_opts['options']);
foreach (explode(PHP_EOL, $this->config['options']) as $option) {
list($k,$v) = explode('=', $option);
$pushover_opts['options'][$k] = $v;
}
return $this->contactPushover($obj, $pushover_opts);
}
public function deliverAlertOld($obj, $opts)
{
foreach ($opts as $api) {
$response = $this->contactPushover($obj, $api);
if ($response !== true) {
return $response;
}
}
return true;
}
public function contactPushover($obj, $api)
{
$data = array();
$data['token'] = $api['appkey'];
$data['user'] = $api['userkey'];
@@ -71,9 +95,13 @@ class Pushover implements Transport
}
$data['title'] = $obj['title'];
$data['message'] = $obj['msg'];
if ($api['options']) {
$data = array_merge($data, $api['options']);
}
$curl = curl_init();
set_curl_proxy($curl);
curl_setopt($curl, CURLOPT_URL, 'https://api.pushover.net/1/messages.json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SAFE_UPLOAD, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$ret = curl_exec($curl);
@@ -82,7 +110,36 @@ class Pushover implements Transport
var_dump("Pushover returned error"); //FIXME: proper debugging
return 'HTTP Status code ' . $code;
}
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Api Key',
'name' => 'appkey',
'descr' => 'Api Key',
'type' => 'text',
],
[
'title' => 'User Key',
'name' => 'userkey',
'descr' => 'User Key',
'type' => 'text',
],
[
'title' => 'Pushover Options',
'name' => 'options',
'descr' => 'Pushover options',
'type' => 'textarea',
],
],
'validation' => [
'appkey' => 'required',
'userkey' => 'required',
]
];
}
}

View File

@@ -23,14 +23,34 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Rocket implements Transport
class Rocket extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$rocket_opts['url'] = $this->config['rocket-url'];
foreach (explode(PHP_EOL, $this->config['rocket-options']) as $option) {
list($k,$v) = explode('=', $option);
$rocket_opts[$k] = $v;
}
return $this->contactRocket($obj, $rocket_opts);
}
public function deliverAlertOld($obj, $opts)
{
foreach ($opts as $tmp_api) {
$host = $tmp_api['url'];
$this->contactRocket($obj, $tmp_api);
}
return true;
}
public static function contactRocket($obj, $api)
{
$host = $api['url'];
$curl = curl_init();
$rocket_msg = strip_tags($obj['msg']);
$color = ($obj['state'] == 0 ? '#00FF00' : '#FF0000');
@@ -43,10 +63,10 @@ class Rocket implements Transport
'text' => $rocket_msg,
)
),
'channel' => $tmp_api['channel'],
'username' => $tmp_api['username'],
'icon_url' => $tmp_api['icon_url'],
'icon_emoji' => $tmp_api['icon_emoji'],
'channel' => $api['channel'],
'username' => $api['username'],
'icon_url' => $api['icon_url'],
'icon_emoji' => $api['icon_emoji'],
);
$alert_message = json_encode($data);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
@@ -64,7 +84,29 @@ class Rocket implements Transport
var_dump("Return: " . $ret); //FIXME: propper debuging
return 'HTTP Status code ' . $code;
}
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Webhook URL',
'name' => 'rocket-url',
'descr' => 'Rocket.chat Webhook URL',
'type' => 'text',
],
[
'title' => 'Rocket.chat Options',
'name' => 'rocket-options',
'descr' => 'Rocket.chat Options',
'type' => 'textarea',
]
],
'validation' => [
'rocket-url' => 'required|url',
]
];
}
}

View File

@@ -23,34 +23,59 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Slack implements Transport
class Slack extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$slack_opts['url'] = $this->config['slack-url'];
foreach (explode(PHP_EOL, $this->config['options']) as $option) {
list($k,$v) = explode('=', $option);
$slack_opts[$k] = $v;
}
return $this->contactSlack($obj, $slack_opts);
}
public function deliverAlertOld($obj, $opts)
{
foreach ($opts as $tmp_api) {
$host = $tmp_api['url'];
$this->contactSlack($obj, $tmp_api);
}
return true;
}
public static function contactSlack($obj, $api)
{
$host = $api['url'];
$curl = curl_init();
$slack_msg = strip_tags($obj['msg']);
$color = ($obj['state'] == 0 ? '#00FF00' : '#FF0000');
$data = array(
'attachments' => array(
0 => array(
$data = [
'attachments' => [
0 => [
'fallback' => $slack_msg,
'color' => $color,
'title' => $obj['title'],
'text' => $slack_msg,
'mrkdwn_in' => array('text', 'fallback')
)
),
'channel' => $tmp_api['channel'],
'username' => $tmp_api['username'],
'icon_url' => $tmp_api['icon_url'],
'icon_emoji' => $tmp_api['icon_emoji'],
);
'mrkdwn_in' => ['text', 'fallback'],
'author_name' => $api['author_name'],
'fields' => [
[
'title' => 'Priority',
'value' => ucfirst($obj['severity']),
'short' => false,
]
],
],
],
'channel' => $api['channel'],
];
$alert_message = json_encode($data);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
set_curl_proxy($curl);
curl_setopt($curl, CURLOPT_URL, $host);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
@@ -65,7 +90,29 @@ class Slack implements Transport
var_dump("Return: " . $ret); //FIXME: propper debuging
return 'HTTP Status code ' . $code;
}
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Webhook URL',
'name' => 'slack-url',
'descr' => 'Slack Webhook URL',
'type' => 'text',
],
[
'title' => 'Slack Options',
'name' => 'options',
'descr' => 'Slack Options',
'type' => 'textarea',
]
],
'validation' => [
'slack-url' => 'required|url',
]
];
}
}

View File

@@ -23,18 +23,35 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Smseagle implements Transport
class Smseagle extends Transport
{
public function deliverAlert($obj, $opts)
{
$params = array(
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$smseagle_opts['url'] = $this->config['playsms-url'];
$smseagle_opts['user'] = $this->config['playsms-user'];
$smseagle_opts['token'] = $this->config['playsms-pass'];
$smseagle_opts['to'] = preg_split('/([,\r\n]+)/', $this->config['playsms-mobiles']);
return $this->contactSmseagle($obj, $smseagle_opts);
}
public function deliverAlertOld($obj, $opts)
{
return $this->contactSmseagle($obj, $opts);
}
public static function contactSmseagle($obj, $opts)
{
$params = [
'login' => $opts['user'],
'pass' => $opts['token'],
'to' => implode(',', $opts['to']),
'message' => $obj['title'],
);
];
$url = 'http://' . $opts['url'] . '/index.php/http_api/send_sms?' . http_build_query($params);
$curl = curl_init($url);
@@ -49,4 +66,42 @@ class Smseagle implements Transport
return false;
}
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'SMSEagle URL',
'name' => 'smseagle-url',
'descr' => 'SMSEagle URL',
'type' => 'text',
],
[
'title' => 'User',
'name' => 'smseagle-user',
'descr' => 'SMSEagle User',
'type' => 'text',
],
[
'title' => 'Password',
'name' => 'smseagle-pass',
'descr' => 'SMSEagle Password',
'type' => 'text',
],
[
'title' => 'Mobiles',
'name' => 'smseagle-mobiles',
'descr' => 'SMSEagle Mobiles, can be new line or comma separated',
'type' => 'textarea',
],
],
'validation' => [
'smseagle-url' => 'required|url',
'smseagle-user' => 'required|string',
'smseagle-pass' => 'required|string',
'smseagle-mobiles' => 'required',
]
];
}
}

View File

@@ -16,11 +16,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Syslog implements Transport
class Syslog extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['syslog_host'] = $this->config['syslog-host'];
$opts['syslog_port'] = $this->config['syslog-port'];
$opts['syslog_facility'] = $this->config['syslog-facility'];
}
return $this->contactSyslog($obj, $opts);
}
public function contactSyslog($obj, $opts)
{
$syslog_host = '127.0.0.1';
$syslog_port = 514;
@@ -116,4 +126,35 @@ class Syslog implements Transport
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Host',
'name' => 'syslog-host',
'descr' => 'Syslog Host',
'type' => 'text'
],
[
'title' => 'Port',
'name' => 'syslog-port',
'descr' => 'Syslog Port',
'type' => 'text'
],
[
'title' => 'Facility',
'name' => 'syslog-facility',
'descr' => 'Syslog Facility',
'type' => 'text'
]
],
'validation' => [
'syslog-host' => 'required|string',
'syslog-port' => 'required|numeric',
'syslog-facility' => 'required|string'
]
];
}
}

View File

@@ -24,17 +24,33 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Telegram implements Transport
class Telegram extends Transport
{
public function deliverAlert($obj, $opts)
{
if (empty($this->config)) {
return $this->deliverAlertOld($obj, $opts);
}
$telegram_opts['chat_id'] = $this->config['telegram-chat-id'];
$telegram_opts['token'] = $this->config['telegram-token'];
return $this->contactTelegram($obj, $telegram_opts);
}
public function deliverAlertOld($obj, $opts)
{
foreach ($opts as $chat_id => $data) {
$this->contactTelegram($obj, $data);
}
return true;
}
public static function contactTelegram($obj, $data)
{
$curl = curl_init();
set_curl_proxy($curl);
$text = urlencode($obj['msg']);
print_r($data);
curl_setopt($curl, CURLOPT_URL, ("https://api.telegram.org/bot{$data['token']}/sendMessage?chat_id={$data['chat_id']}&text=$text"));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$ret = curl_exec($curl);
@@ -45,7 +61,30 @@ class Telegram implements Transport
var_dump("Return: " . $ret); //FIXME: propper debuging
return 'HTTP Status code ' . $code;
}
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Chat ID',
'name' => 'telegram-chat-id',
'descr' => 'Telegram Chat ID',
'type' => 'text',
],
[
'title' => 'Token',
'name' => 'telegram-token',
'descr' => 'Telegram Token',
'type' => 'text',
]
],
'validation' => [
'telegram-chat-id' => 'required|string',
'telegram-token' => 'required|string',
]
];
}
}

View File

@@ -24,11 +24,19 @@
*/
namespace LibreNMS\Alert\Transport;
use LibreNMS\Interfaces\Alert\Transport;
use LibreNMS\Alert\Transport;
class Victorops implements Transport
class Victorops extends Transport
{
public function deliverAlert($obj, $opts)
{
if (!empty($this->config)) {
$opts['url'] = $this->config['victorops-url'];
}
return $this->contactVictorops($obj, $opts);
}
public function contactVictorops($obj, $opts)
{
$url = $opts['url'];
@@ -65,4 +73,21 @@ class Victorops implements Transport
}
return true;
}
public static function configTemplate()
{
return [
'config' => [
[
'title' => 'Post URL',
'name' => 'victorops-url',
'descr' => 'Victorops Post URL',
'type' => 'text'
]
],
'validation' => [
'victorops-url' => 'required|string'
]
];
}
}

View File

@@ -0,0 +1,42 @@
source: Alerting/Creating-Transport.md
# Creating a new Transport.
### File location
All transports are located in `LibreNMS\Alert\Transport` and the files are named after the Transport name. I.e
`Discord.php` for Discord.
### Transport structure.
The following functions are required for a new transport to pass the unit tests:
`deliverAlert()` - This is function called within alerts to invoke the transport. Here you should do any post processing
of the transport config to get it ready for use.
`contact$Transport()` - This is named after the transport so for Discord it would be `contactDiscord()`. This is what
actually interacts with the 3rd party API, invokes the mail command or whatever you want your alert to do.
`configTemplate()` - This is used to define the form that will accept the transport config in the webui and then what
data should be validated and how. Validation is done using [Laravel validation](https://laravel.com/docs/5.4/validation)
The following function is __not__ required for new Transports and is for legacy reasons only. `deliverAlertOld()`.
### WebUI
At present you will also need to add the new transport to the select part of the form in
`html/includes/modal/edit_alert_transport.inc.php`. I.e:
`<option value="discord-form">Discord</option>`
Please ensure you add the new entry in the correct location sorted alphabetically.
### Documentation
Please don't forget to update the [Transport](Transports.md) file to include details of your new transport.
A table should be provided to indicate the form values that we ask for and examples. I.e:
```
**Example:**
Config | Example
------ | -------
Discord URL | https://discordapp.com/api/webhooks/4515489001665127664/82-sf4385ysuhfn34u2fhfsdePGLrg8K7cP9wl553Fg6OlZuuxJGaa1d54fe
Options | username=myname
```

View File

@@ -2,237 +2,151 @@ source: Alerting/Transports.md
# Transports
Transports are located within `LibreNMS/Alert/Transport/` and defined as well as configured via ~~`$config['alert']['transports']['Example'] = 'Some Options'`~~.
Transports are located within `LibreNMS/Alert/Transport/` and can be configured within the WebUI under Alerts -> Alert Transports.
Contacts will be gathered automatically and passed to the configured transports.
By default the Contacts will be only gathered when the alert triggers and will ignore future changes in contacts for the incident. If you want contacts to be re-gathered before each dispatch, please set ~~`$config['alert']['fixed-contacts'] = false;`~~ in your config.php.
By default the Contacts will be only gathered when the alert triggers and will ignore future changes in contacts for the incident.
If you want contacts to be re-gathered before each dispatch, please set 'Updates to contact email addresses not honored' to Off in the WebUI.
The contacts will always include the `SysContact` defined in the Device's SNMP configuration and also every LibreNMS-User that has at least `read`-permissions on the entity that is to be alerted.
The contacts will always include the `SysContact` defined in the Device's SNMP configuration and also every LibreNMS user that has at least `read`-permissions on the entity that is to be alerted.
At the moment LibreNMS only supports Port or Device permissions.
You can exclude the `SysContact` by setting:
You can exclude the `SysContact` by toggling 'Issue alerts to sysContact'.
```php
$config['alert']['syscontact'] = false;
```
To include users that have `Global-Read`, `Administrator` or `Normal-User` permissions it is required to toggle the options:
To include users that have `Global-Read` or `Administrator` permissions it is required to add these additions to the `config.php` respectively:
- Issue alerts to admins.
- Issue alerts to read only users
- Issue alerts to normal users.
```php
$config['alert']['globals'] = true; //Include Global-Read into alert-contacts
$config['alert']['admins'] = true; //Include Administrators into alert-contacts
```
## Using a Proxy?
[Proxy Configuration](../Support/Configuration.md#proxy-support)
## API
> You can configure these options within the WebUI now, please avoid setting these options within config.php
API transports definitions are a bit more complex than the E-Mail configuration.
The basis for configuration is ~~`$config['alert']['transports']['api'][METHOD]`~~ where `METHOD` can be `get`,`post` or `put`.
This basis has to contain an array with URLs of each API to call.
The URL can have the same placeholders as defined in the [Template-Syntax](Templates#syntax).
If the `METHOD` is `get`, all placeholders will be URL-Encoded.
The API transport uses cURL to call the APIs, therefore you might need to install `php5-curl` or similar in order to make it work.
__Note__: it is highly recommended to define own [Templates](Templates) when you want to use the API transport. The default template might exceed URL-length for GET requests and therefore cause all sorts of errors.
Example:
If the `Api Method` is `get`, all placeholders will be URL-Encoded.
```php
$config['alert']['transports']['api']['get'][] = "https://api.thirdparti.es/issue?apikey=abcdefg&subject=%title";
```
The API transport uses cURL to call the APIs, therefore you might need to install `php curl` to make it work.
__Note__: it is highly recommended to define your own [Templates](Templates) when you want to use the API transport. The default template might exceed URL-length for GET requests and therefore cause all sorts of errors.
**Example:**
Config | Example
------ | -------
API Method | get
API URL | http://my.example.com/api
## Boxcar
Copy your access token from the Boxcar app or from the Boxcar.io website and setup the transport.
[Using a proxy?](../Support/Configuration.md#proxy-support)
[Boxcar Docs](http://developer.boxcar.io/api/publisher/)
Enabling Boxcar support is super easy.
Copy your access token from the Boxcar app or from the Boxcar.io website and setup the transport in your config.php like:
```php
$config['alert']['transports']['boxcar'][] = array(
"access_token" => 'ACCESSTOKENGOESHERE',
);
```
To modify the Critical alert sound, add the 'sound_critical' parameter, example:
```php
$config['alert']['transports']['boxcar'][] = array(
"access_token" => 'ACCESSTOKENGOESHERE',
"sound_critical" => 'detonator-charge',
);
```
**Example:**
Config | Example
------ | -------
Access Token | i23f23mr23rwerw
## Canopsis
Canopsis is a hypervision tool. LibreNMS can send alerts to Canopsis which are then converted to canopsis events.
Canopsis is a hypervision tool. LibreNMS can send alerts to Canopsis which are then converted to canopsis events. To configure the transport, go to:
[Canopsis Docs](http://www.canopsis.org/wp-content/themes/canopsis/doc/sakura/user-guide/event-spec.html)
Global Settings -> Alerting Settings -> Canopsis Transport.
You will need to fill this paramaters :
```php
$config['alert']['transports']['canopsis']['host'] = 'www.xxx.yyy.zzz';
$config['alert']['transports']['canopsis']['port'] = '5672';
$config['alert']['transports']['canopsis']['user'] = 'admin';
$config['alert']['transports']['canopsis']['passwd'] = 'my_password';
$config['alert']['transports']['canopsis']['vhost'] = 'canopsis';
```
For more information about canopsis and its events, take a look here :
http://www.canopsis.org/
http://www.canopsis.org/wp-content/themes/canopsis/doc/sakura/user-guide/event-spec.html
**Example:**
Config | Example
------ | -------
Hostname | www.xxx.yyy.zzz
Port Number | 5672
User | admin
Password | my_password
Vhost | canopsis
## Cisco Spark
[Using a proxy?](../Support/Configuration.md#proxy-support)
Cisco Spark. LibreNMS can send alerts to a Cisco Spark room. To make this possible you need to have a RoomID and a token.
For more information about Cisco Spark RoomID and token, take a look here :
https://developer.ciscospark.com/getting-started.html
https://developer.ciscospark.com/resource-rooms.html
To configure the transport, go to:
- [Getting started](https://developer.ciscospark.com/getting-started.html)
- [Rooms](https://developer.ciscospark.com/resource-rooms.html)
Global Settings -> Alerting Settings -> Cisco Spark transport.
This can also be done manually in config.php :
```php
$config['alert']['transports']['ciscospark']['token'] = '1234567890QWERTYUIOP';
$config['alert']['transports']['ciscospark']['roomid'] = '1234567890QWERTYUIOP';
```
**Example:**
Config | Example
------ | -------
API Token | ASd23r23edewda
RoomID | 34243243251
## Clickatell
[Using a proxy?](../Support/Configuration.md#proxy-support)
Clickatell provides a REST-API requiring an Authorization-Token and at least one Cellphone number.
Please consult Clickatell's documentation regarding number formatting.
[Clickatell Docs](https://www.clickatell.com/developers/api-documentation/rest-api-request-parameters/)
Here an example using 3 numbers, any amount of numbers is supported:
```php
$config['alert']['transports']['clickatell']['token'] = 'MYFANCYACCESSTOKEN';
$config['alert']['transports']['clickatell']['to'][] = '+1234567890';
$config['alert']['transports']['clickatell']['to'][] = '+1234567891';
$config['alert']['transports']['clickatell']['to'][] = '+1234567892';
```
**Example:**
Config | Example
------ | -------
Token | dsaWd3rewdwea
Mobile Numbers | +1234567890,+1234567891,+1234567892
## Discord
The Discord transport will POST the alert message to your Discord Incoming WebHook. Simple html tags are stripped from
the message.
The Discord transport will POST the alert message to your Discord Incoming WebHook (https://discordapp.com/developers/docs/resources/webhook). Simple html tags are stripped from the message. The only required value is for url, without this no call to Discord will be made. Below is an example webhook url:
The only required value is for url, without this no call to Discord will be made. The Options field supports the
JSON/Form Params listed in the Discord Docs below.
```
https://discordapp.com/api/webhooks/4515489001665127664/82-sf4385ysuhfn34u2fhfsdePGLrg8K7cP9wl553Fg6OlZuuxJGaa1d54fe
```
[Discord Docs](https://discordapp.com/developers/docs/resources/webhook#execute-webhook)
**Example:**
Config | Example
------ | -------
Discord URL | https://discordapp.com/api/webhooks/4515489001665127664/82-sf4385ysuhfn34u2fhfsdePGLrg8K7cP9wl553Fg6OlZuuxJGaa1d54fe
Options | username=myname
## Elasticsearch
You can have LibreNMS send alerts to an elasticsearch database. Each fault will be sent as a separate document.
You can have LibreNMS emit alerts to an elasticsearch database. Each fault will be sent as a separate document.
The index pattern uses strftime() formatting.
The proxy setting uses the proxy set in config.php if true and does not if false; this allows you to use local servers.
```php
$config['alert']['transports']['elasticsearch']['es_host'] = '127.0.0.1';
$config['alert']['transports']['elasticsearch']['es_port'] = 9200;
$config['alert']['transports']['elasticsearch']['es_index'] = 'librenms-%Y.%m.%d';
$config['alert']['transports']['elasticsearch']['es_proxy'] = false;
```
## E-Mail
> You can configure these options within the WebUI now, please avoid setting these options within config.php
For all but the default contact, we support setting multiple email addresses separated by a comma. So you can
set the devices sysContact, override the sysContact or have your users emails set like:
`email@domain.com, alerting@domain.com`
E-Mail transport is enabled with adding the following to your `config.php`:
```php
$config['alert']['transports']['mail'] = true;
```
The E-Mail transports uses the same email-configuration like the rest of LibreNMS.
As a small reminder, here is it's configuration directives including defaults:
```php
$config['email_backend'] = 'mail'; // Mail backend. Allowed: "mail" (PHP's built-in), "sendmail", "smtp".
$config['email_from'] = NULL; // Mail from. Default: "ProjectName" <projectid@`hostname`>
$config['email_user'] = $config['project_id'];
$config['email_sendmail_path'] = '/usr/sbin/sendmail'; // The location of the sendmail program.
$config['email_html'] = FALSE; // Whether to send HTML email as opposed to plaintext
$config['email_smtp_host'] = 'localhost'; // Outgoing SMTP server name.
$config['email_smtp_port'] = 25; // The port to connect.
$config['email_smtp_timeout'] = 10; // SMTP connection timeout in seconds.
$config['email_smtp_secure'] = NULL; // Enable encryption. Use 'tls' or 'ssl'
$config['email_smtp_auth'] = FALSE; // Whether or not to use SMTP authentication.
$config['email_smtp_username'] = NULL; // SMTP username.
$config['email_smtp_password'] = NULL; // Password for SMTP authentication.
$config['alert']['default_only'] = false; //Only issue to default_mail
$config['alert']['default_mail'] = ''; //Default email
```
**Example:**
Config | Example
------ | -------
Host | 127.0.0.1
Port | 9200
Index Patter | librenms-%Y.%m.%d
## Gitlab
LibreNMS will create issues for warning and critical level alerts however only title and description are set.
Uses Personal access tokens to authenticate with Gitlab and will store the token in cleartext.
LibreNMS will create issues for warning and critical level alerts however only title and description are set. Uses Personal access tokens to authenticate with Gitlab and will store the token in cleartext.
```php
$config['alert']['transports']['gitlab']['host'] = 'http://gitlab.host.tld';
$config['alert']['transports']['gitlab']['project_id'] = '1';
$config['alert']['transports']['gitlab']['key'] = 'AbCdEf12345';
```
**Example:**
Config | Example
------ | -------
Host | http://gitlab.host.tld
Project ID | 1
Personal Access Token | AbCdEf12345
## HipChat
> You can configure these options within the WebUI now, please avoid setting these options within config.php
[Using a proxy?](../Support/Configuration.md#proxy-support)
The HipChat transport requires the following:
__room_id__ = HipChat Room ID
__url__ = HipChat API URL+API Key
__from__ = The name that will be displayed
The HipChat transport makes the following optional:
__color__ = Any of HipChat's supported message colors
__message_format__ = Any of HipChat's supported message formats
__notify__ = 0 or 1
See the HipChat API Documentation for
[rooms/message](https://www.hipchat.com/docs/api/method/rooms/message)
See the HipChat API Documentation for [rooms/message](https://www.hipchat.com/docs/api/method/rooms/message)
for details on acceptable values.
> You may notice that the link points at the "deprecated" v1 API. This is
> because the v2 API is still in beta.
Below are two examples of sending messages to a HipChat room.
**Example:**
Config | Example
------ | -------
API URL | https://api.hipchat.com/v1/rooms/message?auth_token=109jawregoaihj
Room ID | 7654321
From Name | LibreNMS
Options | color = red
| notify = 1
| message_format = text
```php
$config['alert']['transports']['hipchat'][] = array("url" => "https://api.hipchat.com/v1/rooms/message?auth_token=9109jawregoaih",
"room_id" => "1234567",
"from" => "LibreNMS");
$config['alert']['transports']['hipchat'][] = array("url" => "https://api.hipchat.com/v1/rooms/message?auth_token=109jawregoaihj",
"room_id" => "7654321",
"from" => "LibreNMS",
"color" => "red",
"notify" => 1,
"message_format" => "text");
```
These settings can also be configured from the WebUI, here's an example used for the HipChat V2 API:
![HipChat V2 WebUI Example](/img/hipchatv2-webui.png)
At present the following options are supported: `color`, `notify` and `message_format`.
> Note: The default message format for HipChat messages is HTML. It is
> recommended that you specify the `text` message format to prevent unexpected
@@ -240,102 +154,95 @@ These settings can also be configured from the WebUI, here's an example used for
> `>`).
## IRC
> You can configure these options within the WebUI now, please avoid setting these options within config.php
The IRC transports only works together with the LibreNMS IRC-Bot.
Configuration of the LibreNMS IRC-Bot is described [here](https://github.com/librenms/librenms/blob/master/doc/Extensions/IRC-Bot.md).
```php
$config['alert']['transports']['irc'] = true;
```
**Example:**
Config | Example
------ | -------
IRC | enabled
## JIRA
You can have LibreNMS create issues on a Jira instance for critical and warning alerts. The Jira transport only sets
summary and description fields. Therefore your Jira project must not have any other mandatory field for the provided
issuetype. The config fields that need to set are Jira URL, Jira username, Jira password, Project key, and issue type.
Currently http authentication is used to access Jira and Jira username and password will be stored as cleartext in the
LibreNMS database.
You can have LibreNMS create issues on a Jira instance for critical and warning alerts. The Jira transport only sets summary and description fiels. Therefore your Jira project must not have any other mandatory field for the provided issuetype. The config fields that need to set are Jira URL, Jira username, Jira password, Project key, and issue type.
Currently http authentication is used to access Jira and Jira username and password will be stored as cleartext in the LibreNMS database.
[Jira Issue Types](https://confluence.atlassian.com/adminjiracloud/issue-types-844500742.html)
```php
$config['alert']['transports']['jira']['url'] = 'https://myjira.mysite.com';
$config['alert']['transports']['jira']['username'] = 'myjirauser';
$config['alert']['transports']['jira']['password'] = 'myjirapass';
$config['alert']['transports']['jira']['prjkey'][] = 'JIRAPROJECTKEY';
$config['alert']['transports']['jira']['issuetype'][] = 'Myissuetype';
```
**Example:**
Config | Example
------ | -------
URL | https://myjira.mysite.com
Project Key | JIRAPROJECTKEY
Issue Type | Myissuetype
Jira Username | myjirauser
Jira Password | myjirapass
## Mail
For all but the default contact, we support setting multiple email addresses separated by a comma. So you can
set the devices sysContact, override the sysContact or have your users emails set like:
`email@domain.com, alerting@domain.com`
The E-Mail transports uses the same email-configuration like the rest of LibreNMS.
As a small reminder, here is it's configuration directives including defaults:
**Example:**
Config | Example
------ | -------
Email | me@example.com
## Microsoft Teams
Microsoft Teams. LibreNMS can send alerts to Microsoft Teams Connector API which are then posted to a specific channel.
[Using a proxy?](../Support/Configuration.md#proxy-support)
Microsoft Teams. LibreNMS can send alerts to Microsoft Teams Connector API which are then posted to a specific channel. To configure the transport, go to:
Global Settings -> Alerting Settings -> Microsoft Teams Transport.
This can also be done manually in config.php :
```php
$config['alert']['transports']['msteams']['url'] = 'https://outlook.office365.com/webhook/123456789';
```
**Example:**
Config | Example
------ | -------
WebHook URL | https://outlook.office365.com/webhook/123456789
## Nagios Compatible
> You can configure these options within the WebUI now, please avoid setting these options within config.php
The nagios transport will feed a FIFO at the defined location with the same format that nagios would.
This allows you to use other Alerting-Systems to work with LibreNMS, for example [Flapjack](http://flapjack.io).
```php
$config['alert']['transports']['nagios'] = "/path/to/my.fifo"; //Flapjack expects it to be at '/var/cache/nagios3/event_stream.fifo'
```
This allows you to use other alerting systems with LibreNMS, for example [Flapjack](http://flapjack.io).
**Example:**
Config | Example
------ | -------
Nagios FIFO | /path/to/my.fifo
## OpsGenie
Using OpsGenie LibreNMS integration, LibreNMS forwards alerts to OpsGenie with detailed information.
OpsGenie acts as a dispatcher for LibreNMS alerts, determines the right people to notify based on on-call
schedules and notifies via email, text messages (SMS), phone calls and iOS & Android push notifications.
Then escalates alerts until the alert is acknowledged or closed.
> You can configure these options within the WebUI now, please avoid setting these options within config.php
Create a [LibreNMS Integration](https://docs.opsgenie.com/docs/librenms-integration) from the integrations page
once you signup. Then copy the API key from OpsGenie to LibreNMS.
[Using a proxy?](../Support/Configuration.md#proxy-support)
If you want to automatically ack and close alerts, leverage Marid integration. More detail with screenshots is
available in [OpsGenie LibreNMS Integration page](https://docs.opsgenie.com/docs/librenms-integration).
Using OpsGenie LibreNMS integration, LibreNMS forwards alerts to OpsGenie with detailed information. OpsGenie acts as a dispatcher for LibreNMS alerts, determines the right people to notify based on on-call schedules notifies via email, text messages (SMS), phone calls and iOS & Android push notifications, and escalates alerts until the alert is acknowledged or closed.
Create a [LibreNMS Integration](https://docs.opsgenie.com/docs/librenms-integration) from the integrations page once you signup. Then, copy the API key from OpsGenie to LibreNMS.
If you want to automatically ack and close alerts, leverage Marid integration. More detail with screenshots is available in [OpsGenie LibreNMS Integration page](https://docs.opsgenie.com/docs/librenms-integration).
**Example:**
Config | Example
------ | -------
WebHook URL | https://url/path/to/webhook
## osTicket
LibreNMS can send alerts to osTicket API which are then converted to osTicket tickets.
[Using a proxy?](../Support/Configuration.md#proxy-support)
osTicket, open source ticket system. LibreNMS can send alerts to osTicket API which are then converted to osTicket tickets. To configure the transport, go to:
Global Settings -> Alerting Settings -> osTicket Transport.
This can also be done manually in config.php :
```php
$config['alert']['transports']['osticket']['url'] = 'http://osticket.example.com/api/http.php/tickets.json';
$config['alert']['transports']['osticket']['token'] = '123456789';
```
**Example:**
Config | Example
------ | -------
API URL | http://osticket.example.com/api/http.php/tickets.json
API Token | 123456789
## PagerDuty
PagerDuty setup is currently done by a two way integration. Start this process from Settings -> Alerting Settings from within LibreNMS.
> You can configure these options within the WebUI now, please avoid setting these options within config.php
[Using a proxy?](../Support/Configuration.md#proxy-support)
Enabling PagerDuty transports is almost as easy as enabling email-transports.
All you need is to create a Service with type Generic API on your PagerDuty dashboard.
Now copy your API-Key from the newly created Service and setup the transport like:
```php
$config['alert']['transports']['pagerduty'] = 'MYAPIKEYGOESHERE';
```
That's it!
__Note__: Currently ACK notifications are not transported to PagerDuty, This is going to be fixed within the next major version (version by date of writing: 2015.05)
[PagerDuty Docs](https://www.pagerduty.com/docs/guides/librenms-integration-guide/)
## Philips Hue
Want to spice up your noc life? LibreNMS will flash all lights connected to your philips hue bridge whenever an alert is triggered.
To setup, go to the you http://`your-bridge-ip`/debug/clip.html
@@ -346,137 +253,127 @@ To setup, go to the you http://`your-bridge-ip`/debug/clip.html
- Click on `POST`
- In the `Command Response` You should see output with your username. Copy this without the quotes
More Info: [Philips Hue Documentation](https://www.developers.meethue.com/documentation/getting-started)
```php
$config['alert']['transports']['hue']['bridge'] = 'http://bridge.example.com';
$config['alert']['transports']['hue']['user'] = 'af89jauaf98aj34r';
$config['alert']['transports']['hue']['duration'] = 'lselect';
```
**Example:**
Config | Example
------ | -------
Host | http://your-bridge-ip
Hue User | username
Duration | 1 Second
## PlaySMS
[Using a proxy?](../Support/Configuration.md#proxy-support)
PlaySMS is an open source SMS-Gateway that can be used via their HTTP-API using a Username and WebService-Token.
PlaySMS is an open source SMS-Gateway that can be used via their HTTP API using a Username and WebService Token.
Please consult PlaySMS's documentation regarding number formatting.
[PlaySMS Docs](https://github.com/antonraharja/playSMS/blob/master/documents/development/WEBSERVICES.md)
Here an example using 3 numbers, any amount of numbers is supported:
```php
$config['alert']['transports']['playsms']['url'] = 'https://localhost/index.php?app=ws';
$config['alert']['transports']['playsms']['user'] = 'user1';
$config['alert']['transports']['playsms']['token'] = 'MYFANCYACCESSTOKEN';
$config['alert']['transports']['playsms']['from'] = '+1234567892'; //Optional
$config['alert']['transports']['playsms']['to'][] = '+1234567890';
$config['alert']['transports']['playsms']['to'][] = '+1234567891';
```
**Example:**
Config | Example
------ | -------
PlaySMS | https://localhost/index.php?app=ws
User | user1
Token | MYFANCYACCESSTOKEN
From | My Name
Mobiles | +1234567892,+1234567890,+1234567891
## Pushbullet
Get your Access Token from your Pushbullet's settings page and set it in your transport:
[Using a proxy?](../Support/Configuration.md#proxy-support)
Enabling Pushbullet is a piece of cake.
Get your Access Token from your Pushbullet's settings page and set it in your config like:
```php
$config['alert']['transports']['pushbullet'] = 'MYFANCYACCESSTOKEN';
```
**Example:**
Config | Example
------ | -------
Access Token | MYFANCYACCESSTOKEN
## Pushover
If you want to change the [notification sounds](https://pushover.net/api#sounds) then add it in Pushover Options:
[Using a proxy?](../Support/Configuration.md#proxy-support)
> You can configure these options within the WebUI now, please avoid setting these options within config.php
If you want to change the [notification sounds](https://pushover.net/api#sounds) then add it in config options as:
```php
sound_critical=falling
```
`sound_critical=falling`
Enabling Pushover support is fairly easy, there are only two required parameters.
Firstly you need to create a new Application (called LibreNMS, for example) in your account on the Pushover website (https://pushover.net/apps)
Now copy your API Token/Key from the newly created Application and setup the transport in your config.php like:
Now copy your API Key and obtain your User Key from the newly created Application and setup the transport.
```php
$config['alert']['transports']['pushover'][] = array(
"appkey" => 'APPLICATIONAPIKEYGOESHERE',
"userkey" => 'USERKEYGOESHERE',
);
```
[Pushover Docs](https://pushover.net/api)
To modify the Critical alert sound, add the 'sound_critical' parameter, example:
```php
$config['alert']['transports']['pushover'][] = array(
"appkey" => 'APPLICATIONAPIKEYGOESHERE',
"userkey" => 'USERKEYGOESHERE',
"sound_critical" => 'siren',
);
```
**Example:**
Config | Example
------ | -------
Api Key | APPLICATIONAPIKEYGOESHERE
User Key | USERKEYGOESHERE
Pushover Options | sound_critical=falling
## Rocket.chat
The Rocket.chat transport will POST the alert message to your Rocket.chat Incoming WebHook using the
attachments option. Simple html tags are stripped from the message. All options are optional, the only
required value is for url, without this then no call to Rocket.chat will be made.
[Using a proxy?](../Support/Configuration.md#proxy-support)
[Rocket.chat Docs](https://rocket.chat/docs/developer-guides/rest-api/chat/postmessage)
The Rocket.chat transport will POST the alert message to your Rocket.chat Incoming WebHook using the [attachments](https://rocket.chat/docs/developer-guides/rest-api/chat/postmessage) option, you are able to specify multiple webhooks along with the relevant options to go with it. Simple html tags are stripped from the message. All options are optional, the only required value is for url, without this then no call to Rocket.chat will be made. Below is an example of how to send alerts to two channels with different customised options:
```php
$config['alert']['transports']['rocket'][] = array('url' => "https://rocket.url/api/v1/chat.postMessage", 'channel' => '#Alerting');
$config['alert']['transports']['rocket'][] = array('url' => "https://rocket.url/api/v1/chat.postMessage", 'channel' => '@john', 'username' => 'LibreNMS', 'icon_emoji' => ':ghost:');
```
**Example:**
Config | Example
------ | -------
Webhook URL | https://rocket.url/api/v1/chat.postMessage
Rocket.chat Options | channel=#Alerting
| username=myname
| icon_url=http://someurl/image.gif
| icon_emoji=:smirk:
## Slack
The Slack transport will POST the alert message to your Slack Incoming WebHook using the attachments option,
you are able to specify multiple webhooks along with the relevant options to go with it. Simple html tags
are stripped from the message. All options are optional, the only required value is for url, without this
then no call to Slack will be made.
> You can configure these options within the WebUI now, please avoid setting these options within config.php
We currently support the following attachment options:
[Using a proxy?](../Support/Configuration.md#proxy-support)
`author_name`
The Slack transport will POST the alert message to your Slack Incoming WebHook using the [attachments](https://api.slack.com/docs/message-attachments) option, you are able to specify multiple webhooks along with the relevant options to go with it. Simple html tags are stripped from the message. All options are optional, the only required value is for url, without this then no call to Slack will be made. Below is an example of how to send alerts to two channels with different customised options:
[Slack docs](https://api.slack.com/docs/message-attachments)
```php
$config['alert']['transports']['slack'][] = array('url' => "https://hooks.slack.com/services/A12B34CDE/F56GH78JK/L901LmNopqrSTUVw2w3XYZAB4C", 'channel' => '#Alerting');
$config['alert']['transports']['slack'][] = array('url' => "https://hooks.slack.com/services/A12B34CDE/F56GH78JK/L901LmNopqrSTUVw2w3XYZAB4C", 'channel' => '@john', 'username' => 'LibreNMS', 'icon_emoji' => ':ghost:');
```
**Example:**
Config | Example
------ | -------
Webhook URL | https://slack.com/url/somehook
Slack Options | author_name=Me
## SMSEagle
SMSEagle is a hardware SMS Gateway that can be used via their HTTP API using a Username and password.
[Using a proxy?](../Support/Configuration.md#proxy-support)
SMSEagle is a hardware SMS Gateway that can be used via their HTTP-API using a Username and password
Please consult their documentation at [www.smseagle.eu](http://www.smseagle.eu)
Destination numbers are one per line, with no spaces. They can be in either local or international dialling format.
```php
$config['alert']['transports']['smseagle']['url'] = 'ip.add.re.ss';
$config['alert']['transports']['smseagle']['user'] = 'smseagle_user';
$config['alert']['transports']['smseagle']['token'] = 'smseagle_user_password';
$config['alert']['transports']['smseagle']['to'][] = '+3534567890';
$config['alert']['transports']['smseagle']['to'][] = '0834567891';
```
[SMSEagle Docs](http://www.smseagle.eu)
**Example:**
Config | Example
------ | -------
SMSEagle URL | ip.add.re.ss
User | smseagle_user
Password | smseagle_user_password
Mobiles | +3534567890
| 0834567891
## Syslog
You can have LibreNMS emit alerts as syslogs complying with RFC 3164.
More information on RFC 3164 can be found here: https://tools.ietf.org/html/rfc3164
Example output: `<26> Mar 22 00:59:03 librenms.host.net librenms[233]: [Critical] network.device.net: Port Down - port_id => 98939; ifDescr => xe-1/1/0;`
Each fault will be sent as a separate syslog.
```php
$config['alert']['transports']['syslog']['syslog_host'] = '127.0.0.1';
$config['alert']['transports']['syslog']['syslog_port'] = 514;
$config['alert']['transports']['syslog']['syslog_facility'] = 3;
```
**Example:**
Config | Example
------ | -------
Host | 127.0.0.1
Port | 514
Facility | 3
## Telegram
[Using a proxy?](../Support/Configuration.md#proxy-support)
> Thank you to [snis](https://github.com/snis) for these instructions.
1. First you must create a telegram account and add BotFather to you list. To do this click on the following url: https://telegram.me/botfather
@@ -485,7 +382,8 @@ $config['alert']['transports']['syslog']['syslog_facility'] = 3;
3. Add your bot to telegram with the following url: `http://telegram.me/<botname>` and send some text to the bot.
4. Now copy your token code and go to the following page in chrome: `https://api.telegram.org/bot<tokencode>/getUpdates`
4. The BotFather should have responded with a token, copy your token code and go to the following page in chrome: `https://api.telegram.org/bot<tokencode>/getUpdates`
(this could take a while so continue to refresh until you see something similar to below)
5. You see a json code with the message you sent to the bot. Copy the Chat id. In this example that is “-9787468”
`"message":{"message_id":7,"from":"id":656556,"first_name":"Joo","last_name":"Doo","username":"JohnDoo"},"chat":{"id":-9787468,"title":"Telegram Group"},"date":1435216924,"text":"Hi"}}]}`
@@ -493,19 +391,26 @@ $config['alert']['transports']['syslog']['syslog_facility'] = 3;
6. Now create a new "Telegram transport" in LibreNMS (Global Settings -> Alerting Settings -> Telegram transport).
Click on 'Add Telegram config' and put your chat id and token into the relevant box.
[Telegram Docs](https://core.telegram.org/api)
**Example:**
Config | Example
------ | -------
Chat ID | 34243432
Token | 3ed32wwf235234
## VictorOps
[Using a proxy?](../Support/Configuration.md#proxy-support)
VictorOps provide a webHook url to make integration extremely simple. To get the URL required login to your VictorOps account and go to:
VictorOps provide a webHook url to make integration extremely simple. To get the URL required login to your VictorOps
account and go to:
Settings -> Integrations -> REST Endpoint -> Enable Integration.
The URL provided will have $routing_key at the end, you need to change this to something that is unique to the system sending the alerts such as librenms. I.e:
The URL provided will have $routing_key at the end, you need to change this to something that is unique to the system
sending the alerts such as librenms. I.e:
`https://alert.victorops.com/integrations/generic/20132414/alert/2f974ce1-08fc-4dg8-a4f4-9aee6cf35c98/librenms`
```php
$config['alert']['transports']['victorops']['url'] = 'https://alert.victorops.com/integrations/generic/20132414/alert/2f974ce1-08fc-4dg8-a4f4-9aee6cf35c98/librenms';
```
**Example:**
Config | Example
------ | -------
Post URL | https://alert.victorops.com/integrations/generic/20132414/alert/2f974ce1-08fc-4dg8-a4f4-9aee6cf35c98/librenms

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -148,9 +148,60 @@ if (is_numeric($rule_id) && $rule_id > 0) {
dbSyncRelationship('alert_device_map', 'rule_id', $rule_id, 'device_id', $devices);
dbSyncRelationship('alert_group_map', 'rule_id', $rule_id, 'group_id', $groups);
//Update transport groups and transports - can't use dbSyncRelationship
$transports = [];
$groups = [];
foreach ((array)$vars['transports'] as $item) {
if (starts_with($item, 'g')) {
$groups[] = (int)substr($item, 1);
} else {
$transports[] = (int)$item;
}
}
// Fetch transport/group mappings already in db
$sql = "SELECT `transport_or_group_id` FROM `alert_transport_map` WHERE `target_type`='single' AND `rule_id`=?";
$db_transports = dbFetchColumn($sql, [$rule_id]);
$sql = "SELECT `transport_or_group_id` FROM `alert_transport_map` WHERE `target_type`='group' AND `rule_id`=?";
$db_groups = dbFetchColumn($sql, [$rule_id]);
// Compare arrays to get add and removed transports/groups
$t_add = array_diff($transports, $db_transports);
$t_del = array_diff($db_transports, $transports);
$g_add = array_diff($groups, $db_groups);
$g_del = array_diff($db_groups, $groups);
// Insert any new mappings
$insert = [];
foreach ($t_add as $transport_id) {
$insert[] = array (
'transport_or_group_id' => $transport_id,
'target_type' => 'single',
'rule_id' => $rule_id
);
}
foreach ($g_add as $group_id) {
$insert[] = array(
'transport_or_group_id' => $group_id,
'target_type' => 'group',
'rule_id' => $rule_id
);
}
if (!empty($insert)) {
$res = dbBulkInsert($insert, 'alert_transport_map');
}
// Remove old mappings
if (!empty($t_del)) {
dbDelete('alert_transport_map', 'target_type="single" AND transport_or_group_id IN (?)', array(array(implode(',', $t_del))));
}
if (!empty($g_del)) {
dbDelete('alert_transport_map', 'target_type="group" AND transport_or_group_id IN (?)', array(array(implode(',', $g_del))));
}
}
die(json_encode([
'status' => $status,
'message' => $message,
'message' => $message
]));

View File

@@ -0,0 +1,137 @@
<?php
/**
* alert-transports.inc.php
*
* LibreNMS alert-transports.inc.php for processor
*
* 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 Vivia Nguyen-Tran
* @author Vivia Nguyen-Tran <vivia@ualberta.ca>
*/
use LibreNMS\Authentication\Auth;
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Container\Container;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Translation\FileLoader;
use Illuminate\Translation\Translator;
use Illuminate\Validation\DatabasePresenceVerifier;
use Illuminate\Validation\Factory;
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
die(json_encode([
'status' => 'error',
'message' => 'You need to be admin'
]));
}
$status = 'ok';
$message = '';
$transport_id = $vars['transport_id'];
$name = $vars['name'];
$is_default = isset($vars['is_default']) ? $vars['is_default'] : null;
$transport_type = $vars['transport-type'];
if ($is_default == 'on') {
$is_default = true;
} else {
$is_default = false;
}
if (empty($name)) {
$status = 'error';
$message = 'No transport name provided';
} elseif (empty($transport_type)) {
$status = 'error';
$message = 'Missing transport information';
} else {
$details = array(
'transport_name' => $name,
'is_default' => $is_default
);
if (is_numeric($transport_id) && $transport_id > 0) {
// Update the fields -- json config field will be updated later
dbUpdate($details, 'alert_transports', 'transport_id=?', [$transport_id]);
} else {
// Insert the new alert transport
$newEntry = true;
$transport_id = dbInsert($details, 'alert_transports');
}
if ($transport_id) {
$class = 'LibreNMS\\Alert\\Transport\\'.ucfirst($transport_type);
if (!method_exists($class, 'configTemplate')) {
die(json_encode([
'status' => 'error',
'message' => 'This transport type is not yet supported'
]));
}
// Build config values
$result = call_user_func_array($class.'::configTemplate', []);
$loader = new FileLoader(new Filesystem, "$install_dir/resources/lang");
$translator = new Translator($loader, 'en');
$validation = new Factory($translator, new Container);
$validator = $validation->make($vars, $result['validation']);
if ($validator->fails()) {
$errors = $validator->errors();
foreach ($errors->all() as $error) {
$message .= "$error<br>";
}
$status = 'error';
} else {
foreach ($result['config'] as $tmp_config) {
$transport_config[$tmp_config['name']] = $vars[$tmp_config['name']];
}
//Update the json config field
if ($transport_config) {
$transport_config = json_encode($transport_config);
$detail = array(
'transport_type' => $transport_type,
'transport_config' => $transport_config
);
$where = 'transport_id=?';
dbUpdate($detail, 'alert_transports', $where, [$transport_id]);
$status = 'ok';
$message = 'Updated alert transports';
} else {
$status = 'error';
$message = 'There was an issue with the transport config';
}
}
if ($status == 'error' && $newEntry) {
//If error, we will have to delete the new entry in alert_transports tbl
$where = '`transport_id`=?';
dbDelete('alert_transports', $where, [$transport_id]);
}
} else {
$status = 'error';
$message = 'Failed to update transport';
}
}
die(json_encode([
'status' => $status,
'message' => $message
]));

View File

@@ -20,14 +20,14 @@ if (!Auth::user()->hasGlobalAdmin()) {
die('ERROR: You need to be admin');
}
if (!is_numeric($_POST['alert_id'])) {
if (!is_numeric($vars['alert_id'])) {
echo 'ERROR: No alert selected';
exit;
} else {
if (dbDelete('alert_rules', '`id` = ?', array($_POST['alert_id']))) {
dbDelete('alert_device_map', 'rule_id=?', [$_POST['alert_id']]);
dbDelete('alert_group_map', 'rule_id=?', [$_POST['alert_id']]);
if (dbDelete('alert_rules', '`id` = ?', array($vars['alert_id']))) {
dbDelete('alert_device_map', 'rule_id=?', [$vars['alert_id']]);
dbDelete('alert_group_map', 'rule_id=?', [$vars['alert_id']]);
dbDelete('alert_transport_map', 'rule_id=?', [$vars['alert_id']]);
echo 'Alert rule has been deleted.';
exit;
} else {

View File

@@ -0,0 +1,44 @@
<?php
/*
* LibreNMS
*
* 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. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Authentication\Auth;
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
die(json_encode([
'status' => 'error',
'message' => 'ERROR: You need to be admin.'
]));
}
$status = 'ok';
$message = '';
if (!is_numeric($vars['transport_id'])) {
$status = 'error';
$message = 'ERROR: No transport selected';
} else {
if (dbDelete('alert_transports', '`transport_id` = ?', [$vars['transport_id']])) {
dbDelete('alert_transport_map', '`target_type` = "single" AND `transport_or_group_id` = ?', [$vars['transport_id']]);
dbDelete('transport_group_transport', '`transport_id`=?', [$vars['transport_id']]);
$message = 'Alert transport has been deleted';
} else {
$message = 'ERROR: Alert transport has not been deleted';
}
}
die(json_encode([
'status' => $status,
'message'=> $message
]));

View File

@@ -0,0 +1,45 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2014 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
*
* 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. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Authentication\Auth;
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
die(json_encode([
'status' => 'error',
'message' => 'ERROR: You need to be admin.'
]));
}
$status = 'ok';
$message = '';
if (!is_numeric($vars['group_id'])) {
$status = 'error';
$message = 'ERROR: No transport group selected';
} else {
if (dbDelete('alert_transport_groups', '`transport_group_id` = ?', [$vars['group_id']])) {
dbDelete('transport_group_transport', '`transport_group_id`=?', [$vars['group_id']]);
dbDelete('alert_transport_map', '`target_type`="group" AND `transport_or_group_id`=?', [$vars['group_id']]);
$message = 'Alert transport group has been deleted';
} else {
$message = 'ERROR: Alert transport group has not been deleted';
}
}
die(json_encode([
'status' => $status,
'message'=> $message
]));

View File

@@ -19,9 +19,8 @@ if (!Auth::user()->hasGlobalAdmin()) {
header('Content-type: text/plain');
die('ERROR: You need to be admin');
}
$alert_id = $_POST['alert_id'];
$template_id = $_POST['template_id'];
$alert_id = $vars['alert_id'];
$template_id = $vars['template_id'];
if (is_numeric($alert_id) && $alert_id > 0) {
$rule = dbFetchRow('SELECT * FROM `alert_rules` WHERE `id` = ? LIMIT 1', [$alert_id]);
@@ -37,6 +36,24 @@ if (is_numeric($alert_id) && $alert_id > 0) {
foreach ($groups as $group) {
$maps[] = ['id' => 'g' . $group['group_id'], 'text' => $group['name']];
}
$transports = [];
$members = dbFetchRows('SELECT `transport_or_group_id`, `transport_name`, `transport_type` FROM `alert_transport_map` LEFT JOIN `alert_transports` ON `transport_or_group_id` = `transport_id` WHERE `target_type`="single" AND `rule_id`=?', [$alert_id]);
foreach ($members as $member) {
$transports[] = [
'id' => $member['transport_or_group_id'],
'text' => ucfirst($member['transport_type']).": ".$member['transport_name']
];
}
$t_groups = dbFetchRows('SELECT `transport_or_group_id`, `transport_group_name` FROM `alert_transport_map` LEFT JOIN `alert_transport_groups` ON `transport_or_group_id`=`transport_group_id` WHERE `target_type`="group" AND `rule_id`=?', [$alert_id]);
foreach ($t_groups as $group) {
$transports[] = [
'id' => 'g'.$group['transport_or_group_id'],
'text' => 'Group: '.$group['transport_group_name']
];
}
} elseif (is_numeric($template_id) && $template_id >= 0) {
$tmp_rules = get_rules_from_json();
$rule = $tmp_rules[$template_id];
@@ -55,6 +72,7 @@ if (is_array($rule)) {
echo json_encode([
'extra' => isset($rule['extra']) ? json_decode($rule['extra']) : null,
'maps' => $maps,
'transports' => $transports,
'name' => $rule['name'],
'proc' => $rule['proc'],
'builder' => $builder,

View File

@@ -0,0 +1,56 @@
<?php
/*
* LibreNMS
*
* 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. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Authentication\Auth;
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
die(json_encode([
'status' => 'error',
'message' => 'ERROR: You need to be admin'
]));
}
$transport_id = $vars['transport_id'];
// Retrieve alert transport
if (is_numeric($transport_id) && $transport_id > 0) {
$transport = dbFetchRow('SELECT * FROM `alert_transports` WHERE `transport_id` =? LIMIT 1', [$transport_id]);
if ($transport['is_default'] == true) {
$is_default = true;
} else {
$is_default = false;
}
$details = [];
// Get alert transport configuration details
foreach (json_decode($transport['transport_config'], true) as $key => $value) {
$details[] = [
'name' => $key,
'value' => $value
];
}
}
if (is_array($transport)) {
die(json_encode([
'name' => $transport['transport_name'],
'type' => $transport['transport_type'],
'is_default' => $is_default,
'details' => $details
]));
} else {
die(json_encode([
'status' => 'error',
'message' => 'ERROR: No alert transport found'
]));
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* LibreNMS
*
* 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. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Authentication\Auth;
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
die(json_encode([
'status' => 'error',
'message' => 'ERROR: You need to be admin'
]));
}
$group_id = $vars['group_id'];
// Retrieve alert transport
if (is_numeric($group_id) && $group_id > 0) {
$name = dbFetchCell('SELECT `transport_group_name` FROM `alert_transport_groups` WHERE `transport_group_id`=? LIMIT 1', [$group_id]);
$query = "SELECT `a`.`transport_id`, `transport_type`, `transport_name` FROM `transport_group_transport` AS `a` LEFT JOIN `alert_transports` AS `b` ON `a`.`transport_id`=`b`.`transport_id` WHERE `transport_group_id`=?";
$members = [];
foreach (dbFetchRows($query, [$group_id]) as $member) {
$members[] = [
'id' => $member['transport_id'],
'text' => ucfirst($member['transport_type']).": ".$member['transport_name']
];
}
}
if (is_array($members)) {
die(json_encode([
'name' => $name,
'members' => $members
]));
} else {
die(json_encode([
'status' => 'error',
'message' => 'ERROR: No transport group found'
]));
}

View File

@@ -19,7 +19,8 @@ if (!Auth::user()->hasGlobalAdmin()) {
die('ERROR: You need to be admin');
}
$transport = mres($_POST['transport']);
$transport = $vars['transport'] ?: null;
$transport_id = $vars['transport_id'] ?: null;
require_once $config['install_dir'].'/includes/alerts.inc.php';
$tmp = array(dbFetchRow('select device_id,hostname,sysDescr,version,hardware,location from devices order by device_id asc limit 1'));
@@ -37,7 +38,7 @@ $obj = array(
"faults" => false,
"uid" => "000",
"severity" => "critical",
"rule" => "%macros.device = 1",
"rule" => "macros.device = 1",
"name" => "Test-Rule",
"string" => "#1: test => string;",
"timestamp" => date("Y-m-d H:i:s"),
@@ -48,11 +49,14 @@ $obj = array(
$status = 'error';
if ($transport_id) {
$transport = dbFetchCell("SELECT `transport_type` FROM `alert_transports` WHERE `transport_id` = ?", [$transport_id]);
}
$class = 'LibreNMS\\Alert\\Transport\\' . ucfirst($transport);
if (class_exists($class)) {
$opts = $config['alert']['transports'][$transport];
if ($opts) {
$instance = new $class;
$instance = new $class($transport_id);
$tmp = $instance->deliverAlert($obj, $opts);
if ($tmp) {
$status = 'ok';

View File

@@ -0,0 +1,101 @@
<?php
/**
* transport-groups.inc.php
*
* LibreNMS alert-transportsinc.php for processor
*
* 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 Vivia Nguyen-Tran
* @author Vivia Nguyen-Tran <vivia@ualberta.ca>
*/
use LibreNMS\Authentication\Auth;
header('Content-type: application/json');
if (!Auth::user()->hasGlobalAdmin()) {
die(json_encode([
'status' => 'error',
'message' => 'You need to be admin'
]));
}
$status = 'ok';
$message = '';
$group_id = $vars['group_id'];
$name = $vars['name'];
$target_members = [];
foreach ((array)$vars['members'] as $target) {
$target_members[] = (int)$target;
}
if (empty($name)) {
$status = 'error';
$message = 'No transport group name provided';
} elseif (sizeof($target_members) < 1) {
// Not enough members for a group; requires 1 at least
$status = 'error';
$message = 'Not enough group members';
} else {
if (is_numeric($group_id) && $group_id > 0) {
dbUpdate(array(
'transport_group_name' => $name
), 'alert_transport_groups', "`transport_group_id`=?", [$group_id]);
} else {
// Insert into db
$group_id = dbInsert(array(
'transport_group_name' => $name
), 'alert_transport_groups');
}
if (is_numeric($group_id) && $group_id > 0) {
$sql = "SELECT `transport_id` FROM `transport_group_transport` WHERE `transport_group_id`=?";
$db_members = dbFetchColumn($sql, [$group_id]);
// Compare arrays to get added and removed transports
$add = array_diff($target_members, $db_members);
$remove = array_diff($db_members, $target_members);
// Insert new transport group members
$insert = [];
foreach ($add as $transport_id) {
$insert[] = array(
'transport_id' => $transport_id,
'transport_group_id' => $group_id
);
}
if (!empty($insert)) {
dbBulkInsert($insert, 'transport_group_transport');
}
// Remove old transport group members
if (!empty($remove)) {
dbDelete('transport_group_transport', 'transport_group_id=? AND `transport_id` IN (?)', array($group_id, array(implode(',', $remove))));
}
$message = 'Updated alert transport group';
} else {
$status = 'error';
$message = 'Did not update alert transport group';
}
}
die(json_encode([
'status' => $status,
'message' => $message
]));

View File

@@ -0,0 +1,66 @@
<?php
/**
* transport-groups.inc.php
*
* List transports and transport groups
*
* 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 Vivia Nguyen-Tran
* @author Vivia Nguyen-Tran <vivia@ualberta>
*/
use LibreNMS\Authentication\Auth;
if (!Auth::user()->hasGlobalRead()) {
return [];
}
list($transports, $t_more) = include 'transports.inc.php';
$query = '';
$params = [];
if (!empty($_REQUEST['search'])) {
$query .= ' WHERE `transport_group_name` LIKE ?';
$params[] = '%' . mres($_REQUEST['search']) . '%';
}
$total = dbFetchCell("SELECT COUNT(*) FROM `alert_transport_groups` $query", $params);
$more = false;
if (!empty($_REQUEST['limit'])) {
$limit = (int) $_REQUEST['limit'];
$page = isset($_REQUEST['page']) ? (int) $_REQUEST['page'] : 1;
$offset = ($page - 1) * $limit;
$query .= " LIMIT $offset, $limit";
} else {
$offset = 0;
}
$sql = "SELECT `transport_group_id` AS `id`, `transport_group_name` AS `text` FROM `alert_transport_groups` $query";
$groups = dbFetchRows($sql, $params);
$more = ($offset + count($groups))<$total;
$groups = array_map(function ($group) {
$group['text'] = "Group: ".$group['text'];
$group['id'] = "g".$group['id'];
return $group;
}, $groups);
$data = [['text' => 'Transport Groups', 'children' => $groups], $transports[0]];
return[$data, $more || $c_more];

View File

@@ -0,0 +1,64 @@
<?php
/**
* transports.inc.php
*
* List transports
*
* 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 Vivia Nguyen-Tran
* @author Vivia Nguyen-Tran <vivia@ualberta>
*/
use LibreNMS\Authentication\Auth;
if (!Auth::user()->hasGlobalRead()) {
return [];
}
$query = '';
$params = [];
if (!empty($_REQUEST['search'])) {
$query .= ' WHERE `transport_name` LIKE ?';
$params[] = '%' . mres($_REQUEST['search']) . '%';
}
$total = dbFetchCell("SELECT COUNT(*) FROM `alert_transports` $query", $params);
$more = false;
if (!empty($_REQUEST['limit'])) {
$limit = (int) $_REQUEST['limit'];
$page = isset($_REQUEST['page']) ? (int) $_REQUEST['page'] : 1;
$offset = ($page - 1) * $limit;
$query .= " LIMIT $offset, $limit";
} else {
$offset = 0;
}
$sql = "SELECT `transport_id` AS `id`, `transport_name` AS `text`, `transport_type` AS `type` FROM `alert_transports` $query";
$transports = dbFetchRows($sql, $params);
$more = ($offset + count($transports))<$total;
$transports = array_map(function ($transport) {
$transport['text'] = ucfirst($transport['type']).": ".$transport['text'];
unset($transport['type']);
return $transport;
}, $transports);
$data = [['text' => 'Transports', 'children' => $transports]];
return[$data, $more];

View File

@@ -0,0 +1,306 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2018 Vivia Nguyen-Tran <vivia@ualberta.ca>
*
* 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. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Authentication\Auth;
use LibreNMS\Config;
if (Auth::user()->hasGlobalAdmin()) {
?>
<!--Modal for adding or updating an alert transport -->
<div class="modal fade" id="edit-alert-transport" tabindex="-1" role="dialog"
aria-labelledby="Edit-transport" aria-hidden="true">
<div class="modal-dialog modal-lg">
<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="Edit-transport">Alert Transport :: <a href="https://docs.librenms.org/Alerting/">Docs <i class="fa fa-book fa-1x"></i></a> </h5>
</div>
<div class="modal-body">
<form method="post" role="form" id="transports" class="form-horizontal transports-form">
<input type="hidden" name="transport_id" id="transport_id" value="">
<input type="hidden" name="type" id="type" value="alert-transports">
<div class='form-group' title="The description of this alert transport.">
<label for='name' class='col-sm-3 col-md-2 control-label'>Transport name: </label>
<div class='col-sm-9 col-md-10'>
<input type='text' id='name' name='name' class='form-control validation' maxlength='200' required>
</div>
</div>
<div class="form-group" title="The type of transport.">
<label for='transport-choice' class='col-sm-3 col-md-2 control-label'>Transport type: </label>
<div class="col-sm-3">
<select name='transport-choice' id='transport-choice' class='form-control'>
<option value="api-form">API</option>
<option value="boxcar-form">Boxcar</option>
<option value="canopsis-form">Canopsis</option>
<option value="ciscospark-form">Cisco Spark</option>
<option value="clickatell-form">Clickatell</option>
<option value="discord-form">Discord</option>
<option value="elasticsearch-form">Elasticsearch</option>
<option value="gitlab-form">Gitlab</option>
<option value="hipchat-form">Hipchat</option>
<option value="irc-form">IRC</option>
<option value="jira-form">Jira</option>
<option value="mail-form" selected>Mail</option>
<option value="msteams-form">Microsoft Teams</option>
<option value="nagios-form">Nagios</option>
<option value="opsgenie-form">OpsGenie</option>
<option value="osticket-form">osTicket</option>
<option value="hue-form">Phillips Hue</option>
<option value="playsms-form">PlaySMS</option>
<option value="pushbullet-form">Pushbullet</option>
<option value="pushover-form">Pushover</option>
<option value="rocket-form">Rocket.chat</option>
<option value="slack-form">Slack</option>
<option value="smseagle-form">SMSEagle</option>
<option value="syslog-form">Syslog</option>
<option value="telegram-form">Telegram</option>
<option value="victorops-form">Victorops</option>
<!--Insert more transport type options here has support is added. Value should be: [transport_name]-form -->
</select>
</div>
</div>
<div class="form-group" title="The transport is default.">
<label for="default" class="col-sm-3 col-md-2 control-label">Default Alert: </label>
<div class="col-sm-2">
<input type="checkbox" name="is_default" id="is_default">
</div>
</div>
</form>
<?php
// Fetch list of transport classes
$transport_dir = Config::get('install_dir').'/LibreNMS/Alert/Transport';
$switches = []; // store names of bootstrap switches
foreach (scandir($transport_dir) as $transport) {
$transport = strstr($transport, '.', true);
if (empty($transport)) {
continue;
}
$class = 'LibreNMS\\Alert\\Transport\\'.$transport;
if (!method_exists($class, 'configTemplate')) {
// Skip since support has not been added
continue;
}
echo '<form method="post" role="form" id="'.strtolower($transport).'-form" class="form-horizontal transport">';
echo '<input type="hidden" name="transport-type" id="transport-type" value="'.strtolower($transport).'">';
$tmp = call_user_func($class.'::configTemplate');
foreach ($tmp['config'] as $item) {
echo '<div class="form-group" title="'.$item['descr'].'">';
echo '<label for="'.$item['name'].'" class="col-sm-3 col-md-2 control-label">'.$item['title'].': </label>';
if ($item['type'] == 'text') {
echo '<div class="col-sm-9 col-md-10">';
echo '<input type="'.$item['type'].'" id="'.$item['name'].'" name="'.$item['name'].'" class="form-control" ';
if ($item['required']) {
echo 'required>';
} else {
echo '>';
}
echo '</div>';
} elseif ($item['type'] == 'checkbox') {
echo '<div class="col-sm-2">';
echo '<input type="checkbox" name="'.$item['name'].'" id="'.$item['name'].'">';
echo '</div>';
$switches[$item['name']] = $item['default'];
} elseif ($item['type'] == 'select') {
echo '<div class="col-sm-3">';
echo '<select name="'.$item['name'].'" id="'.$item['name'].'" class="form-control">';
foreach ($item['options'] as $descr => $opt) {
echo '<option value="'.$opt.'">'.$descr.'</option>';
}
echo '</select>';
echo '</div>';
} elseif ($item['type'] === 'textarea') {
echo '<div class="col-sm-9 col-md-10">';
echo '<textarea name="' . $item['name'] . '" id="' . $item['name'] . '" class="form-control" placeholder="'.$item['descr'].'">';
echo '</textarea>';
echo '</div>';
}
echo '</div>';
}
echo '<div class="form-group">';
echo '<div class="col-sm-12 text-center">';
echo '<button type="button" class="btn btn-success btn-save" name="save-transport">';
echo 'Save Transport';
echo '</button>';
echo '</div>';
echo '</div>';
echo '</form>';
}
?>
</div>
</div>
</div>
</div>
<!-- Modal end for adding or updating an alert tramsport-->
<!--Modal for deleting an alert transport -->
<div class="modal fade" id="delete-alert-transport" tabindex="-1" role="dialog"
aria-labelledby="Delete" aria-hidden="true">
<div class="modal-dialog modal-sm">
<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="Delete">Confirm Transport Delete</h5>
</div>
<div class="modal-body">
<p>If you would like to remove this alert transport then please click Delete.</p>
</div>
<div class="modal-footer">
<form role="form" class="remove_transport_form">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger danger" id="remove-alert-transport" data-target="remove-alert-transport">Delete</button>
<input type="hidden" name="transport_id" id="delete_transport_id" value="">
<input type="hidden" name="confirm" id="confirm" value="yes">
</form>
</div>
</div>
</div>
</div>
<!--Modal end for deleting an alert transport -->
<script>
// Scripts related to editing/updating alert transports
// Display different form on selection
$("#transport-choice").change(function (){
$(".transport").hide();
$("#" + $(this).val()).show().find("input:text").val("");
});
$("#edit-alert-transport").on("show.bs.modal", function(e) {
// Get transport id of clicked element
var transport_id = $(e.relatedTarget).data("transport_id");
$("#transport_id").val(transport_id);
if(transport_id > 0) {
$.ajax({
type: "POST",
url: "ajax_form.php",
data: { type: "show-alert-transport", transport_id: transport_id },
success: function (data) {
loadTransport(data);
},
error: function () {
toastr.error("Failed to process alert transport");
}
});
} else {
// Resetting to default
$("#name").val("");
$("#transport-choice").val("mail-form");
$(".transport").hide();
$("#" + $("#transport-choice").val()).show().find("input:text").val("");
$("#is_default").bootstrapSwitch('state', false);
// Turn on all switches in form
var switches = <?php echo json_encode($switches);?>;
$.each(switches, function(name, state) {
$("input[name="+name+"]").bootstrapSwitch('state', state);
});
}
});
function loadTransport(transport) {
$("#name").val(transport.name);
$("#transport-choice").val(transport.type+"-form");
$("#is_default").bootstrapSwitch('state', transport.is_default);
$(".transport").hide();
$("#" + $("#transport-choice").val()).show().find("input:text").val("");
// Populate the field values
transport.details.forEach(function(config) {
var $field = $("#" + config.name);
if ($field.prop('type') == 'checkbox') {
$field.bootstrapSwitch('state', config.value);
} else {
$field.val(config.value);
$("#options").val('dasdsaddasdsa');
}
});
}
// Save alert transport
$(".btn-save").on("click", function (e) {
e.preventDefault();
//Combine form data (general and transport specific)
data = $("form.transports-form").serializeArray();
data = data.concat($("#" + $("#transport-choice").val()).serializeArray());
if (data !== null) {
//post data to ajax form
$.ajax({
type: "POST",
url: "ajax_form.php",
data: data,
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
toastr.success(data.message);
setTimeout(function (){
$("#edit-alert-transports").modal("hide");
window.location.reload();
}, 500);
} else {
toastr.error(data.message);
}
},
error: function () {
toastr.error("Failed to process alert transport");
}
});
}
});
// Scripts related to deleting an alert transport
// Populate transport id value
$("#delete-alert-transport").on("show.bs.modal", function(event) {
transport_id = $(event.relatedTarget).data("transport_id");
$("#delete_transport_id").val(transport_id);
});
// Delete the alert transport
$("#remove-alert-transport").click('', function(event) {
event.preventDefault();
var transport_id = $("#delete_transport_id").val();
$.ajax({
type: "POST",
url: "ajax_form.php",
data: { type: "delete-alert-transport", transport_id: transport_id },
dataType: "json",
success: function(data) {
if (data.status == 'ok') {
toastr.success(data.message);
$("#alert-transport-" + transport_id).remove();
$("#delete-alert-transport").modal("hide");
} else {
toastr.error(data.message);
}
},
error: function() {
toastr.error("The alert transport could not be deleted.");
}
});
});
</script>
<?php
}

View File

@@ -0,0 +1,187 @@
<?php
/*
* LibreNMS
*
* Copyright (c) 2018 Vivia Nguyen-Tran <vivia@ualberta.ca>
*
* 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. Please see LICENSE.txt at the top level of
* the source code distribution for details.
*/
use LibreNMS\Authentication\Auth;
if (Auth::user()->hasGlobalAdmin()) {
?>
<!--Modal for adding or updating a transport group -->
<div class="modal fade" id="edit-transport-group" tabindex="-1" role="dialog"
aria-labelledby="Edit-transport" aria-hidden="true">
<div class="modal-dialog modal-lg">
<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="Edit-transport">Alert Transport Groups :: <a href="https://docs.librenms.org/Alerting/">Docs <i class="fa fa-book fa-1x"></i></a> </h5>
</div>
<div class="modal-body">
<form method="post" role="form" id="transport-group" class="form-horizontal transport-group-form">
<input type="hidden" name="group_id" id="group_id" value="">
<input type="hidden" name="type" id="type" value="transport-groups">
<div class='form-group' title="The description of this transport group.">
<label for='name' class='col-sm-3 col-md-2 control-label'>Group Name: </label>
<div class='col-sm-9 col-md-10'>
<input type='text' id='group-name' name='name' class='form-control validation' maxlength='200' required>
</div>
</div>
<div class="form-group" title="The members for this transport group.">
<label for='transport-choice' class='col-sm-3 col-md-2 control-label'>Group Members: </label>
<div class="col-sm-9 col-md-10">
<select name='members[]' id='members' class='form-control' multiple="multiple"></select>
</div>
</div>
<div class="form-group">
<div class="col-sm-12 text-center">
<button type="button" class="btn btn-success" id="save-group" name="save-group">
Save Transport Group
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Modal end for adding or updating an alert transport-->
<!-- Modal for deleting transport group -->
<div class="modal fade" id="delete-transport-group" tabindex="-1" role=dialog"
aria-labelledby="Delete" aria-hidden="true">
<div class="modal-dialog modal-sm">
<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="Delete">Confirm Group Delete</h5>
</div>
<div class="modal-body">
<p>If you would like to remove this transport group then please click Delete.</p>
</div>
<div class="modal-footer">
<form role="form" class="remove_contract_group">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger danger" id="remove-transport-group" data-target="remove-transport-group">Delete</button>
<input type="hidden" name="group_id" id="delete_group_id" value="">
</form>
</div>
</div>
</div>
</div>
<!-- Modal end for deleting transport group-->
<script>
$("#edit-transport-group").on("show.bs.modal", function (e) {
// Get group-id of the clicked element
var group_id = $(e.relatedTarget).data("group_id");
$("#group_id").val(group_id);
// Reset form
$(this).find("input[type=text]").val("");
var $members = $("#members");
$members.empty();
$members.val(null).trigger('change');
if (group_id > 0) {
$.ajax({
type: "POST",
url: "ajax_form.php",
data: { type: "show-transport-group", group_id: group_id},
success: function (group) {
$("#group-name").val(group.name);
$.each(group.members, function(index, value) {
var option = new Option(value.text, value.id, true, true);
$members.append(option).trigger("change");
});
},
error: function () {
toastr.error("Failed to process transport group");
}
});
}
});
$("#save-group").on("click", function (e) {
e.preventDefault();
data = $("form.transport-group-form").serializeArray();
if (data != null) {
$.ajax({
type: "POST",
url: "ajax_form.php",
data: data,
dataType: "json",
success: function (data) {
if (data.status == 'ok') {
toastr.success(data.message);
setTimeout(function () {
$("edit-transport-group").modal("hide");
window.location.reload();
}, 500);
} else {
toastr.error(data.message);
}
},
error: function () {
toastr.error("Failed to proccess transport group");
}
});
}
});
$("#members").select2({
width: "100%",
placeholder: "Transport Name",
ajax: {
url: 'ajax_list.php',
delay: 250,
data: function(params) {
return {
type: "transports",
search: params.term
}
}
}
});
// Populate group id value
$("#delete-transport-group").on("show.bs.modal", function (event) {
group_id = $(event.relatedTarget).data("group_id");
$("#delete_group_id").val(group_id);
});
// Delete the transport group
$("#remove-transport-group").click('', function (event) {
event.preventDefault();
var group_id = $("#delete_group_id").val();
$.ajax({
type: "POST",
url: "ajax_form.php",
data: { type: "delete-transport-group", group_id: group_id},
dataType: "json",
success: function(data) {
if (data.status == 'ok') {
toastr.success(data.message);
$("#alert-transport-group-" + group_id).remove();
$("#delete-transport-group").modal("hide");
} else {
toastr.error(data.message);
}
},
error: function() {
toastr.error("The alert transport could not be deleted.");
}
});
});
</script>
<?php
}

View File

@@ -109,6 +109,12 @@ if (Auth::user()->hasGlobalAdmin()) {
<select id="maps" name="maps[]" class="form-control" multiple="multiple"></select>
</div>
</div>
<div class="form-group" title="Restricts this alert rule to specified transports.">
<label for="transports" class="col-sm-3 col-md-2 control-label">Transports: </label>
<div class="col-sm-9 col-md-10">
<select id="transports" name="transports[]" class="form-control" multiple="multiple"></select>
</div>
</div>
<div class='form-group' title="A link to some documentation on how to handle this alert. This will be included in notifications.">
<label for='proc' class='col-sm-3 col-md-2 control-label'>Procedure URL: </label>
<div class='col-sm-9 col-md-10'>
@@ -123,8 +129,6 @@ if (Auth::user()->hasGlobalAdmin()) {
</div>
</div>
</form>
</div>
</div>
</div>
@@ -274,6 +278,11 @@ if (Auth::user()->hasGlobalAdmin()) {
$maps.empty();
$maps.val(null).trigger('change');
setRuleDevice() // pre-populate device in the maps if this is a per-device rule
var $transports = $("#transports");
$transports.empty();
$transports.val(null).trigger('change');
$("#transport-choice").val("email");
}
});
@@ -295,6 +304,15 @@ if (Auth::user()->hasGlobalAdmin()) {
$maps.append(option).trigger('change')
});
}
var $transports = $("#transports");
$transports.empty();
$transports.val(null).trigger('change');
if(rule.transports != null) {
$.each(rule.transports, function(index, value) {
var option = new Option(value.text, value.id, true, true);
$transports.append(option).trigger("change");
});
}
if (rule.extra != null) {
var extra = rule.extra;
@@ -354,6 +372,21 @@ if (Auth::user()->hasGlobalAdmin()) {
}
}
});
$("#transports").select2({
width: "100%",
placeholder: "Transport/Group Name",
ajax: {
url: 'ajax_list.php',
delay: 250,
data: function(params) {
return {
type: "transport_groups",
search: params.term
}
}
}
});
</script>
<?php
}

View File

@@ -0,0 +1,163 @@
<?php
use LibreNMS\Authentication\Auth;
$no_refresh = true;
?>
<div class="row">
<div class="col-sm-12">
<span id="message"></span>
</div>
</div>
<?php
require_once 'includes/modal/edit_alert_transport.inc.php';
require_once 'includes/modal/edit_transport_group.inc.php';
?>
<div class="table-responsive">
<table class="table table-hover table-condensed">
<tr>
<th>#</th>
<th>Transport Name</th>
<th>Transport Type</th>
<th>Default</th>
<th>Details</th>
<th style="width:126px;">Action</th>
</tr>
<td colspan="6">
<?php
if (Auth::user()->hasGlobalAdmin()) {
echo "<button type='button' class='btn btn-primary btn-sm' data-toggle='modal' data-target='#edit-alert-transport'><i class='fa fa-plus'></i> Create alert transport</button>";
}
echo "</td>";
// Iterate through each alert transport
$query = "SELECT `transport_id` AS `id`, `transport_name` AS `name`, `transport_type` AS `type`, `is_default`, `transport_config` AS `config` FROM `alert_transports`";
foreach (dbFetchRows($query) as $transport) {
echo "<tr id=\"alert-transport-{$transport['id']}\">";
echo "<td><i>#".((int)$transport['id'])."</i></td>";
echo "<td>".$transport['name']."</td>";
echo "<td>".$transport['type']."</td>";
if ($transport['is_default'] == true) {
echo "<td>Yes</td>";
} else {
echo "<td>No</td>";
}
echo "<td class='col-sm-4'>";
// Iterate through transport config template to display config details
$class = 'LibreNMS\\Alert\\Transport\\'.ucfirst($transport['type']);
if (!method_exists($class, 'configTemplate')) {
//skip
continue;
}
$tmp = call_user_func($class.'::configTemplate');
$transport_config = json_decode($transport['config'], true);
foreach ($tmp['config'] as $item) {
$val = $transport_config[$item['name']];
// Match value to key name for select inputs
if ($item['type'] == 'select') {
$val = array_search($val, $item['options']);
}
echo "<i>".$item['title'].": ".$val."<br/></i>";
}
echo "</td>";
echo "<td>";
// Add action buttons for admin users only
if (Auth::user()->hasGlobalAdmin()) {
echo "<div class='btn-group btn-group-sm' role='group'>";
echo "<button type='button' class='btn btn-primary' data-toggle='modal' data-target='#edit-alert-transport' data-transport_id='".$transport['id']."' name='edit-alert-rule' data-container='body' data-toggle='popover' data-content='Edit transport'><i class='fa fa-lg fa-pencil' aria-hidden='true'></i></button> ";
echo "<button type='button' class='btn btn-danger' aria-label='Delete' data-toggle='modal' data-target='#delete-alert-transport' data-transport_id='".$transport['id']."' name='delete-alert-transport' data-container='body' data-toggle='popover' data-content='Delete transport'><i class='fa fa-lg fa-trash' aria-hidden='true'></i></button>";
echo "<button type='button' class='btn btn-warning' data-transport_id='".$transport['id']."' data-transport='{$transport['type']}' name='test-transport' id='test-transport' data-toggle='popover' data-content='Test transport'><i class='fa fa-lg fa-check' aria-hidden='true'></i></button> ";
echo "</div>";
}
echo "</td>";
echo "</tr>\r\n";
}
?>
</table>
</div>
<div class="table-responsive">
<table class="table table-hover table-condensed">
<tr>
<th>#</th>
<th>Transport Group</th>
<th>Size</th>
<th>Members</th>
<th style="width:86px;">Action</th>
</tr>
<td colspan="5">
<?php
if (Auth::user()->hasGlobalAdmin()) {
echo "<button type='button' class='btn btn-primary btn-sm' data-toggle='modal' data-target='#edit-transport-group'><i class='fa fa-plus'></i> Create transport group</button>";
}
echo "</td>";
//Iterate through alert groups
$query = "SELECT `transport_group_id` AS `id`, `transport_group_name` AS `name` FROM `alert_transport_groups`";
foreach (dbFetchRows($query) as $group) {
echo "<tr id=\"alert-transport-group-{$group['id']}\">";
echo "<td><i>#".((int)$group['id'])."</i></td>";
echo "<td>".$group['name']."</td>";
//List out the members of each group
$query = "SELECT `transport_type`, `transport_name` FROM `transport_group_transport` AS `a` LEFT JOIN `alert_transports` AS `b` ON `a`.`transport_id`=`b`.`transport_id` WHERE `transport_group_id`=?";
$members = dbFetchRows($query, [$group['id']]);
echo "<td>".sizeof($members)."</td>";
echo "<td>";
foreach ($members as $member) {
echo "<i>".ucfirst($member['transport_type']).": ".$member['transport_name']."<br /></i>";
}
echo "</td>";
echo "<td>";
if (Auth::user()->hasGlobalAdmin()) {
echo "<div class='btn-group btn-group-sm' role='group'>";
echo "<button type='button' class='btn btn-primary' data-toggle='modal' data-target='#edit-transport-group' data-group_id='".$group['id']."' data-container='body' data-toggle='popover' data-content='Edit transport group'><i class='fa fa-lg fa-pencil' aria-hidden='true'></i></button> ";
echo "<button type='button' class='btn btn-danger' aria-label='Delete' data-toggle='modal' data-target='#delete-transport-group' data-group_id='".$group['id']."' data-container='body' data-toggle='popover' data-content='Delete transport group'><i class='fa fa-lg fa-trash' aria-hidden='true'></i></button>";
echo "</div>";
}
echo "</td>";
echo "</tr>";
}
?>
</table>
</div>
<script>
$("button#test-transport").click(function() {
var $this = $(this);
var transport_id = $this.data("transport_id");
var transport = $this.data("transport");
$.ajax({
type: 'POST',
url: 'ajax_form.php',
data: { type: "test-transport", transport_id: transport_id },
dataType: "json",
success: function(data){
if (data.status == 'ok') {
toastr.success('Test to ' + transport + ' ok');
} else {
toastr.error('Test to ' + transport + ' failed');
}
},
error: function(){
toastr.error('Test to ' + transport + ' failed - general error');
}
});
});
$("[data-toggle='popover']").popover({
trigger: 'hover',
placement: 'top'
});
</script>

View File

@@ -611,6 +611,7 @@ if ($alerts['active_count'] > 0) {
<li><a href="<?php echo(generate_url(array('page'=>'alert-rules'))); ?>"><i class="fa fa-list fa-fw fa-lg" aria-hidden="true"></i> Alert Rules</a></li>
<li><a href="<?php echo(generate_url(array('page'=>'alert-schedule'))); ?>"><i class="fa fa-calendar fa-fw fa-lg" aria-hidden="true"></i> Scheduled Maintenance</a></li>
<li><a href="<?php echo(generate_url(array('page'=>'templates'))); ?>"><i class="fa fa-file fa-fw fa-lg" aria-hidden="true"></i> Alert Templates</a></li>
<li><a href="<?php echo(generate_url(array('page'=>'alert-transports'))); ?>"><i class="fa fa-bus fa-fw fa-lg" aria-hidden="true"></i> Alert Transports</a></li>
<?php } ?>
</ul>
</li>

View File

@@ -0,0 +1,3 @@
<?php
require_once 'includes/print-alert-transports.php';

View File

@@ -398,6 +398,7 @@ $mail_conf = array(
);
echo '
<div class="well"><strong>DEPRECATED</strong>: Please use the new Alert Transports section under Alerts to configure transports - <a href="https://docs.librenms.org/Alerting/Transports/" target="_blank">docs</a>.</div>
<div class="panel-group" id="accordion">
<form class="form-horizontal" role="form" action="" method="post">
';
@@ -1726,23 +1727,13 @@ echo '
dataType: "json",
success: function(data){
if (data.status == 'ok') {
$this.removeClass('btn-primary').addClass('btn-success');
setTimeout(function(){
$this.removeClass('btn-success').addClass('btn-primary');
}, 2000);
}
else {
$this.removeClass('btn-primary').addClass('btn-danger');
setTimeout(function(){
$this.removeClass('btn-danger').addClass('btn-primary');
}, 2000);
toastr.success('Test to ' + transport + ' ok');
} else {
toastr.error('Test to ' + transport + ' failed');
}
},
error: function(){
$this.removeClass('btn-primary').addClass('btn-danger');
setTimeout(function(){
$this.removeClass('btn-danger').addClass('btn-primary');
}, 2000);
toastr.error('Test to ' + transport + ' failed - general error');
}
});
});

View File

@@ -23,8 +23,10 @@
*/
use LibreNMS\Alert\Template;
use LibreNMS\Alert\AlertData;
use LibreNMS\Alerting\QueryBuilderParser;
use LibreNMS\Authentication\Auth;
use LibreNMS\Alert\AlertUtil;
/**
* @param $rule
@@ -806,39 +808,81 @@ function ExtTransports($obj)
global $config;
$tmp = false;
$type = new Template;
// To keep scrutinizer from naging because it doesnt understand eval
// If alert transport mapping exists, override the default transports
$transport_maps = AlertUtil::getAlertTransports($obj['alert_id']);
if (!$transport_maps) {
$transport_maps = AlertUtil::getDefaultAlertTransports();
$legacy_transports = AlertUtil::getDefaultTransportList();
foreach ($config['alert']['transports'] as $transport => $opts) {
if (in_array($transport, $legacy_transports)) {
// If it is a default transport type, then the alert has already been sent out, so skip
continue;
}
if (is_array($opts)) {
$opts = array_filter($opts);
}
$class = 'LibreNMS\\Alert\\Transport\\' . ucfirst($transport);
if (($opts === true || !empty($opts)) && $opts != false && class_exists($class)) {
$obj['transport'] = $transport;
$obj['alert'] = collect($obj);
$transport_maps[] = [
'transport_id' => null,
'transport_type' => $transport,
'opts' => $opts,
'legacy' => true,
];
}
}
unset($legacy_transports);
}
foreach ($transport_maps as $item) {
$class = 'LibreNMS\\Alert\\Transport\\'.ucfirst($item['transport_type']);
if (class_exists($class)) {
$transport_title = ($item['legacy'] === true) ? "{$item['transport_type']} (legacy)" : $item['transport_type'];
$obj['transport'] = $item['transport_type'];
$obj['alert'] = new AlertData($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);
$prefix = array( 0=>"recovery", 1=>$obj['severity']." alert", 2=>"acknowledgment" );
$prefix[3] = &$prefix[0];
$prefix[4] = &$prefix[0];
if ($tmp === true) {
echo 'OK';
log_event('Issued ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "'", $obj['device_id'], 'alert', 1);
} elseif ($tmp === false) {
echo 'ERROR';
log_event('Could not issue ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "'", $obj['device_id'], null, 5);
} else {
echo "ERROR: $tmp\r\n";
log_event('Could not issue ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "' Error: " . $tmp, $obj['device_id'], 'error', 5);
}
echo "$transport_title => ";
$instance = new $class($item['transport_id']);
$tmp = $instance->deliverAlert($obj, $item['opts']);
AlertLog($tmp, $obj, $obj['transport']);
unset($instance);
echo '; ';
}
}
if (count($transport_maps) === 0) {
echo 'No configured transports';
}
}//end ExtTransports()
// Log alert event
function AlertLog($result, $obj, $transport)
{
$prefix = [
0 => "recovery",
1 => $obj['severity']." alert",
2 => "acknowledgment"
];
$prefix[3] = &$prefix[0];
$prefix[4] = &$prefix[0];
if ($result === true) {
echo 'OK';
log_event('Issued ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "'", $obj['device_id'], 'alert', 1);
} elseif ($result === false) {
echo 'ERROR';
log_event('Could not issue ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "'", $obj['device_id'], null, 5);
} else {
echo "ERROR: $result\r\n";
log_event('Could not issue ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "' Error: " . $result, $obj['device_id'], 'error', 5);
}
return;
}//end AlertLog()
/**
* Check if a device's all parent are down
* Returns true if all parents are down

View File

@@ -117,6 +117,29 @@ alert_template_map:
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [id], Unique: true, Type: BTREE }
alert_templates_id: { Name: alert_templates_id, Columns: [alert_templates_id, alert_rule_id], Unique: false, Type: BTREE }
alert_transports:
Columns:
- { Field: transport_id, Type: int(11), 'Null': false, Extra: auto_increment }
- { Field: transport_name, Type: varchar(30), 'Null': false, Extra: '' }
- { Field: transport_type, Type: varchar(20), 'Null': false, Extra: '', Default: mail }
- { Field: is_default, Type: tinyint(1), 'Null': false, Extra: '', Default: '0' }
- { Field: transport_config, Type: text, 'Null': true, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [transport_id], Unique: true, Type: BTREE }
alert_transport_groups:
Columns:
- { Field: transport_group_id, Type: int(11), 'Null': false, Extra: auto_increment }
- { Field: transport_group_name, Type: varchar(30), 'Null': false, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [transport_group_id], Unique: true, Type: BTREE }
alert_transport_map:
Columns:
- { Field: id, Type: int(11), 'Null': false, Extra: auto_increment }
- { Field: rule_id, Type: int(11), 'Null': false, Extra: '' }
- { Field: transport_or_group_id, Type: int(11), 'Null': false, Extra: '' }
- { Field: target_type, Type: varchar(16), 'Null': false, Extra: '' }
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [id], Unique: true, Type: BTREE }
api_tokens:
Columns:
- { Field: id, Type: int(11), 'Null': false, Extra: auto_increment }
@@ -1566,6 +1589,10 @@ toner:
Indexes:
PRIMARY: { Name: PRIMARY, Columns: [toner_id], Unique: true, Type: BTREE }
device_id: { Name: device_id, Columns: [device_id], Unique: false, Type: BTREE }
transport_group_transport:
Columns:
- { Field: transport_group_id, Type: int(11), 'Null': false, Extra: '' }
- { Field: transport_id, Type: int(11), 'Null': false, Extra: '' }
ucd_diskio:
Columns:
- { Field: diskio_id, Type: int(11), 'Null': false, Extra: auto_increment }

View File

@@ -105,6 +105,7 @@ pages:
- Alerting/Rules.md
- Alerting/Templates.md
- Alerting/Transports.md
- Alerting/Creating-Transport.md
- Alerting/Entities.md
- Alerting/Macros.md
- Alerting/Testing.md

4
sql-schema/255.sql Normal file
View File

@@ -0,0 +1,4 @@
CREATE TABLE IF NOT EXISTS alert_transports (transport_id INT(11) NOT NULL AUTO_INCREMENT, transport_name VARCHAR(30) NOT NULL COLLATE utf8_unicode_ci, transport_type VARCHAR(20) NOT NULL DEFAULT 'mail', is_default BOOL NOT NULL DEFAULT false, transport_config TEXT, PRIMARY KEY(transport_id));
CREATE TABLE IF NOT EXISTS alert_transport_map (id INT(11) NOT NULL AUTO_INCREMENT, rule_id INT(11) NOT NULL, transport_or_group_id INT(11) NOT NULL, target_type VARCHAR(16) NOT NULL, PRIMARY KEY(id));
CREATE TABLE IF NOT EXISTS alert_transport_groups (transport_group_id INT(11) NOT NULL AUTO_INCREMENT, transport_group_name VARCHAR(30) NOT NULL COLLATE utf8_unicode_ci, PRIMARY KEY(transport_group_id));
CREATE TABLE IF NOT EXISTS transport_group_transport (transport_group_id INT(11) NOT NULL, transport_id INT(11) NOT NULL);

View File

@@ -25,6 +25,11 @@
namespace LibreNMS\Tests;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
use RegexIterator;
class AlertTest extends TestCase
{
public function testJsonAlertCollection()
@@ -35,4 +40,29 @@ class AlertTest extends TestCase
$this->assertInternalType('array', $rule);
}
}
public function testTransports()
{
foreach ($this->getTransportFiles() as $file => $_unused) {
$parts = explode('/', $file);
$transport = ucfirst(str_replace('.php', '', array_pop($parts)));
$class = 'LibreNMS\\Alert\\Transport\\'.$transport;
if (!class_exists($class)) {
$this->assertTrue(false, "The transport $transport does not exist");
} else {
$methods = ['deliverAlert', 'configTemplate', 'contact'.$transport];
foreach ($methods as $method) {
if (!method_exists($class, $method)) {
$this->assertTrue(false, "The transport $transport does not have the method $method");
}
}
}
}
}
private function getTransportFiles()
{
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('LibreNMS/Alert/Transport'));
return new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
}
}