mirror of
https://github.com/librenms/librenms.git
synced 2024-09-21 10:28:13 +00:00
Alert transport cleanup, no_proxy support and other proxy cleanups (#14763)
* Add no_proxy and other proxy related settings Set user agent on all http client requests Unify http client usage * Style fixes * Remove useless use statements * Correct variable, good job phpstan * Add tests fix https_proxy bug add tcp:// to the config settings format * style and lint fixes * Remove guzzle from the direct dependencies * Use built in Laravel testing functionality * update baseline
This commit is contained in:
parent
02896172bd
commit
04bb75f5f3
@ -25,6 +25,11 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Alert;
|
namespace LibreNMS\Alert;
|
||||||
|
|
||||||
|
use App\Models\Device;
|
||||||
|
use LibreNMS\Config;
|
||||||
|
use LibreNMS\Enum\AlertState;
|
||||||
|
use LibreNMS\Util\Time;
|
||||||
|
|
||||||
class AlertData extends \Illuminate\Support\Collection
|
class AlertData extends \Illuminate\Support\Collection
|
||||||
{
|
{
|
||||||
public function __get($name)
|
public function __get($name)
|
||||||
@ -35,4 +40,50 @@ class AlertData extends \Illuminate\Support\Collection
|
|||||||
|
|
||||||
return "$name is not a valid \$alert data name";
|
return "$name is not a valid \$alert data name";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function testData(Device $device, array $faults = []): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'hostname' => $device->hostname,
|
||||||
|
'device_id' => $device->device_id,
|
||||||
|
'sysDescr' => $device->sysDescr,
|
||||||
|
'sysName' => $device->sysName,
|
||||||
|
'sysContact' => $device->sysContact,
|
||||||
|
'os' => $device->os,
|
||||||
|
'type' => $device->type,
|
||||||
|
'ip' => $device->ip,
|
||||||
|
'display' => $device->displayName(),
|
||||||
|
'version' => $device->version,
|
||||||
|
'hardware' => $device->hardware,
|
||||||
|
'features' => $device->features,
|
||||||
|
'serial' => $device->serial,
|
||||||
|
'status' => $device->status,
|
||||||
|
'status_reason' => $device->status_reason,
|
||||||
|
'location' => (string) $device->location,
|
||||||
|
'description' => $device->purpose,
|
||||||
|
'notes' => $device->notes,
|
||||||
|
'uptime' => $device->uptime,
|
||||||
|
'uptime_short' => Time::formatInterval($device->uptime, true),
|
||||||
|
'uptime_long' => Time::formatInterval($device->uptime),
|
||||||
|
'title' => 'Testing transport from ' . Config::get('project_name'),
|
||||||
|
'elapsed' => '11s',
|
||||||
|
'alerted' => 0,
|
||||||
|
'alert_id' => '000',
|
||||||
|
'alert_notes' => 'This is the note for the test alert',
|
||||||
|
'proc' => 'This is the procedure for the test alert',
|
||||||
|
'rule_id' => '000',
|
||||||
|
'id' => '000',
|
||||||
|
'faults' => $faults,
|
||||||
|
'uid' => '000',
|
||||||
|
'severity' => 'critical',
|
||||||
|
'rule' => 'macros.device = 1',
|
||||||
|
'name' => 'Test-Rule',
|
||||||
|
'string' => '#1: test => string;',
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'contacts' => AlertUtil::getContacts($device->toArray()),
|
||||||
|
'state' => AlertState::ACTIVE,
|
||||||
|
'msg' => 'This is a test alert',
|
||||||
|
'builder' => '{}',
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ namespace LibreNMS\Alert;
|
|||||||
|
|
||||||
use App\Models\AlertTransport;
|
use App\Models\AlertTransport;
|
||||||
use App\View\SimpleTemplate;
|
use App\View\SimpleTemplate;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
@ -12,11 +11,8 @@ use LibreNMS\Interfaces\Alert\Transport as TransportInterface;
|
|||||||
|
|
||||||
abstract class Transport implements TransportInterface
|
abstract class Transport implements TransportInterface
|
||||||
{
|
{
|
||||||
protected $config;
|
protected ?array $config;
|
||||||
/**
|
protected string $name = '';
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
public static function make(string $type): TransportInterface
|
public static function make(string $type): TransportInterface
|
||||||
{
|
{
|
||||||
@ -43,25 +39,9 @@ abstract class Transport implements TransportInterface
|
|||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function __construct(?AlertTransport $transport = null)
|
||||||
* Transport constructor.
|
|
||||||
*
|
|
||||||
* @param null $transport
|
|
||||||
*/
|
|
||||||
public function __construct($transport = null)
|
|
||||||
{
|
{
|
||||||
if (! empty($transport)) {
|
$this->config = $transport ? $transport->transport_config : [];
|
||||||
if ($transport instanceof AlertTransport) {
|
|
||||||
$this->config = $transport->transport_config;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$model = \App\Models\AlertTransport::findOrFail($transport); /** @var AlertTransport $model */
|
|
||||||
$this->config = $model->transport_config;
|
|
||||||
} catch (ModelNotFoundException $e) {
|
|
||||||
$this->config = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,7 +49,7 @@ abstract class Transport implements TransportInterface
|
|||||||
*/
|
*/
|
||||||
public function name(): string
|
public function name(): string
|
||||||
{
|
{
|
||||||
if ($this->name !== null) {
|
if ($this->name !== '') {
|
||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +138,7 @@ abstract class Transport implements TransportInterface
|
|||||||
return 'LibreNMS\\Alert\\Transport\\' . ucfirst($type);
|
return 'LibreNMS\\Alert\\Transport\\' . ucfirst($type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isHtmlContent($content): bool
|
protected function isHtmlContent(string $content): bool
|
||||||
{
|
{
|
||||||
return $content !== strip_tags($content);
|
return $content !== strip_tags($content);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
* 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
|
* option) any later version. Please see LICENSE.txt at the top level of
|
||||||
* the source code distribution for details. */
|
* the source code distribution for details. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API Transport
|
* API Transport
|
||||||
*
|
*
|
||||||
@ -16,76 +17,53 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Config;
|
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
use LibreNMS\Util\Url;
|
||||||
|
|
||||||
class Alerta extends Transport
|
class Alerta extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$opts['url'] = $this->config['alerta-url'];
|
$severity = ($alert_data['state'] == AlertState::RECOVERED ? $this->config['recoverstate'] : $this->config['alertstate']);
|
||||||
$opts['environment'] = $this->config['environment'];
|
|
||||||
$opts['apikey'] = $this->config['apikey'];
|
|
||||||
$opts['alertstate'] = $this->config['alertstate'];
|
|
||||||
$opts['recoverstate'] = $this->config['recoverstate'];
|
|
||||||
|
|
||||||
return $this->contactAlerta($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactAlerta($obj, $opts)
|
|
||||||
{
|
|
||||||
$host = $opts['url'];
|
|
||||||
$curl = curl_init();
|
|
||||||
$text = strip_tags($obj['msg']);
|
|
||||||
$severity = ($obj['state'] == AlertState::RECOVERED ? $opts['recoverstate'] : $opts['alertstate']);
|
|
||||||
$deviceurl = (Config::get('base_url') . 'device/device=' . $obj['device_id']);
|
|
||||||
$devicehostname = $obj['hostname'];
|
|
||||||
$data = [
|
$data = [
|
||||||
'resource' => $devicehostname,
|
'resource' => $alert_data['display'],
|
||||||
'event' => $obj['name'],
|
'event' => $alert_data['name'],
|
||||||
'environment' => $opts['environment'],
|
'environment' => $this->config['environment'],
|
||||||
'severity' => $severity,
|
'severity' => $severity,
|
||||||
'service' => [$obj['title']],
|
'service' => [$alert_data['title']],
|
||||||
'group' => $obj['name'],
|
'group' => $alert_data['name'],
|
||||||
'value' => $obj['state'],
|
'value' => $alert_data['state'],
|
||||||
'text' => $text,
|
'text' => strip_tags($alert_data['msg']),
|
||||||
'tags' => [$obj['title']],
|
'tags' => [$alert_data['title']],
|
||||||
'attributes' => [
|
'attributes' => [
|
||||||
'sysName' => $obj['sysName'],
|
'sysName' => $alert_data['sysName'],
|
||||||
'sysDescr' => $obj['sysDescr'],
|
'sysDescr' => $alert_data['sysDescr'],
|
||||||
'os' => $obj['os'],
|
'os' => $alert_data['os'],
|
||||||
'type' => $obj['type'],
|
'type' => $alert_data['type'],
|
||||||
'ip' => $obj['ip'],
|
'ip' => $alert_data['ip'],
|
||||||
'uptime' => $obj['uptime_long'],
|
'uptime' => $alert_data['uptime_long'],
|
||||||
'moreInfo' => '<a href=' . $deviceurl . '>' . $devicehostname . '</a>',
|
'moreInfo' => '<a href=' . Url::deviceUrl($alert_data['device_id']) . '>' . $alert_data['display'] . '</a>',
|
||||||
],
|
],
|
||||||
'origin' => $obj['rule'],
|
'origin' => $alert_data['rule'],
|
||||||
'type' => $obj['title'],
|
'type' => $alert_data['title'],
|
||||||
];
|
];
|
||||||
$alert_message = json_encode($data);
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
$headers = ['Content-Type: application/json', 'Authorization: Key ' . $opts['apikey']];
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 201) {
|
|
||||||
var_dump("API '$host' returned Error");
|
|
||||||
var_dump('Params: ' . $alert_message);
|
|
||||||
var_dump('Return: ' . $ret);
|
|
||||||
var_dump('Headers: ', $headers);
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
$res = Http::client()
|
||||||
|
->withHeaders([
|
||||||
|
'Authorization' => 'Key ' . $this->config['apikey'],
|
||||||
|
])
|
||||||
|
->post($this->config['alerta-url'], $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['text'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -23,116 +23,67 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Config;
|
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
use LibreNMS\Util\Url;
|
||||||
|
|
||||||
class Alertmanager extends Transport
|
class Alertmanager extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'Alert Manager';
|
protected string $name = 'Alert Manager';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$alertmanager_opts = $this->parseUserOptions($this->config['alertmanager-options']);
|
$url = $this->config['alertmanager-url'];
|
||||||
$alertmanager_opts['url'] = $this->config['alertmanager-url'];
|
$username = $this->config['alertmanager-username'];
|
||||||
|
$password = $this->config['alertmanager-password'];
|
||||||
|
|
||||||
$alertmanager_username = $this->config['alertmanager-username'];
|
$alertmanager_status = $alert_data['state'] == AlertState::RECOVERED ? 'endsAt' : 'startsAt';
|
||||||
$alertmanager_password = $this->config['alertmanager-password'];
|
$alertmanager_msg = strip_tags($alert_data['msg']);
|
||||||
|
|
||||||
return $this->contactAlertmanager($obj, $alertmanager_opts, $alertmanager_username, $alertmanager_password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactAlertmanager($obj, $api, string $username, string $password)
|
|
||||||
{
|
|
||||||
if ($obj['state'] == AlertState::RECOVERED) {
|
|
||||||
$alertmanager_status = 'endsAt';
|
|
||||||
} else {
|
|
||||||
$alertmanager_status = 'startsAt';
|
|
||||||
}
|
|
||||||
$gen_url = (Config::get('base_url') . 'device/device=' . $obj['device_id']);
|
|
||||||
$alertmanager_msg = strip_tags($obj['msg']);
|
|
||||||
$data = [[
|
$data = [[
|
||||||
$alertmanager_status => date('c'),
|
$alertmanager_status => date('c'),
|
||||||
'generatorURL' => $gen_url,
|
'generatorURL' => Url::deviceUrl($alert_data['device_id']),
|
||||||
'annotations' => [
|
'annotations' => [
|
||||||
'summary' => $obj['name'],
|
'summary' => $alert_data['name'],
|
||||||
'title' => $obj['title'],
|
'title' => $alert_data['title'],
|
||||||
'description' => $alertmanager_msg,
|
'description' => $alertmanager_msg,
|
||||||
],
|
],
|
||||||
'labels' => [
|
'labels' => [
|
||||||
'alertname' => $obj['name'],
|
'alertname' => $alert_data['name'],
|
||||||
'severity' => $obj['severity'],
|
'severity' => $alert_data['severity'],
|
||||||
'instance' => $obj['hostname'],
|
'instance' => $alert_data['hostname'],
|
||||||
],
|
],
|
||||||
]];
|
]];
|
||||||
|
|
||||||
$url = $api['url'];
|
$alertmanager_opts = $this->parseUserOptions($this->config['alertmanager-options']);
|
||||||
unset($api['url']);
|
foreach ($alertmanager_opts as $label => $value) {
|
||||||
foreach ($api as $label => $value) {
|
|
||||||
// To allow dynamic values
|
// To allow dynamic values
|
||||||
if (preg_match('/^extra_[A-Za-z0-9_]+$/', $label) && (! empty($obj['faults'][1][$value]))) {
|
if (preg_match('/^extra_[A-Za-z0-9_]+$/', $label) && ! empty($alert_data['faults'][1][$value])) {
|
||||||
$data[0]['labels'][$label] = $obj['faults'][1][$value];
|
$data[0]['labels'][$label] = $alert_data['faults'][1][$value];
|
||||||
} else {
|
} else {
|
||||||
$data[0]['labels'][$label] = $value;
|
$data[0]['labels'][$label] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->postAlerts($url, $data, $username, $password);
|
$client = Http::client()->timeout(5);
|
||||||
}
|
|
||||||
|
|
||||||
public static function postAlerts($url, $data, string $username, string $password)
|
|
||||||
{
|
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
|
|
||||||
curl_setopt($curl, CURLOPT_TIMEOUT_MS, 5000);
|
|
||||||
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
|
|
||||||
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT_MS, 5000);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
|
|
||||||
if ($username != '' && $password != '') {
|
if ($username != '' && $password != '') {
|
||||||
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
$client->withBasicAuth($username, $password);
|
||||||
curl_setopt($curl, CURLOPT_USERNAME, $username);
|
|
||||||
curl_setopt($curl, CURLOPT_PASSWORD, $password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$alert_message = json_encode($data);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
|
|
||||||
foreach (explode(',', $url) as $am) {
|
foreach (explode(',', $url) as $am) {
|
||||||
$post_url = ($am . '/api/v2/alerts');
|
$post_url = ($am . '/api/v2/alerts');
|
||||||
curl_setopt($curl, CURLOPT_URL, $post_url);
|
$res = $client->post($post_url, $data);
|
||||||
$ret = curl_exec($curl);
|
|
||||||
if ($ret === false || curl_errno($curl)) {
|
|
||||||
logfile("Failed to contact $post_url: " . curl_error($curl));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code == 200) {
|
|
||||||
curl_close($curl);
|
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$err = "Unable to POST to Alertmanager at $post_url .";
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alertmanager_msg, $data);
|
||||||
|
|
||||||
if ($ret === false || curl_errno($curl)) {
|
|
||||||
$err .= ' cURL error: ' . curl_error($curl);
|
|
||||||
} else {
|
|
||||||
$err .= ' HTTP status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_close($curl);
|
|
||||||
|
|
||||||
logfile($err);
|
|
||||||
|
|
||||||
return $err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -26,73 +26,57 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use App\View\SimpleTemplate;
|
use App\View\SimpleTemplate;
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Api extends Transport
|
class Api extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'API';
|
protected string $name = 'API';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$url = $this->config['api-url'];
|
$request_body = $this->config['api-body'];
|
||||||
$options = $this->config['api-options'];
|
$username = $this->config['api-auth-username'];
|
||||||
$headers = $this->config['api-headers'];
|
$password = $this->config['api-auth-password'];
|
||||||
$body = $this->config['api-body'];
|
|
||||||
$method = $this->config['api-method'];
|
|
||||||
$auth = [$this->config['api-auth-username'], $this->config['api-auth-password']];
|
|
||||||
|
|
||||||
return $this->contactAPI($obj, $url, $options, $method, $auth, $headers, $body);
|
$method = strtolower($this->config['api-method']);
|
||||||
|
$host = explode('?', $this->config['api-url'], 2)[0]; //we don't use the parameter part, cause we build it out of options.
|
||||||
|
|
||||||
|
//get each line of key-values and process the variables for Options
|
||||||
|
$query = $this->parseUserOptions($this->config['api-options'], $alert_data);
|
||||||
|
$request_headers = $this->parseUserOptions($this->config['api-headers'], $alert_data);
|
||||||
|
$client = Http::client()
|
||||||
|
->withHeaders($request_headers); //get each line of key-values and process the variables for Headers
|
||||||
|
|
||||||
|
if ($method !== 'get') {
|
||||||
|
$request_body = SimpleTemplate::parse($this->config['api-body'], $alert_data);
|
||||||
|
$client->withBody($request_body, 'text/plain'); // Content-Type can be overriden by user headers
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($username) {
|
||||||
|
$client->withBasicAuth($username, $password);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client->withOptions([
|
||||||
|
'query' => $query,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$res = match ($method) {
|
||||||
|
'get' => $client->get($host),
|
||||||
|
'put' => $client->put($host),
|
||||||
|
default => $client->post($host),
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $request_body ?? $query, [
|
||||||
|
'query' => $query,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function contactAPI($obj, $api, $options, $method, $auth, $headers, $body)
|
public static function configTemplate(): array
|
||||||
{
|
|
||||||
$request_opts = [];
|
|
||||||
|
|
||||||
$method = strtolower($method);
|
|
||||||
$host = explode('?', $api, 2)[0]; //we don't use the parameter part, cause we build it out of options.
|
|
||||||
|
|
||||||
//get each line of key-values and process the variables for Headers;
|
|
||||||
$request_heads = $this->parseUserOptions($headers, $obj);
|
|
||||||
//get each line of key-values and process the variables for Options;
|
|
||||||
$query = $this->parseUserOptions($options, $obj);
|
|
||||||
|
|
||||||
$client = app(\GuzzleHttp\Client::class);
|
|
||||||
$request_opts['proxy'] = Proxy::forGuzzle();
|
|
||||||
if (isset($auth) && ! empty($auth[0])) {
|
|
||||||
$request_opts['auth'] = $auth;
|
|
||||||
}
|
|
||||||
if (count($request_heads) > 0) {
|
|
||||||
$request_opts['headers'] = $request_heads;
|
|
||||||
}
|
|
||||||
if ($method == 'get') {
|
|
||||||
$request_opts['query'] = $query;
|
|
||||||
$res = $client->request('GET', $host, $request_opts);
|
|
||||||
} elseif ($method == 'put') {
|
|
||||||
$request_opts['query'] = $query;
|
|
||||||
$request_opts['body'] = $body;
|
|
||||||
$res = $client->request('PUT', $host, $request_opts);
|
|
||||||
} else { //Method POST
|
|
||||||
$request_opts['query'] = $query;
|
|
||||||
$request_opts['body'] = SimpleTemplate::parse($body, $obj);
|
|
||||||
$res = $client->request('POST', $host, $request_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
$code = $res->getStatusCode();
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump("API '$host' returned Error");
|
|
||||||
var_dump('Params:');
|
|
||||||
var_dump($query);
|
|
||||||
var_dump('Response headers:');
|
|
||||||
var_dump($res->getHeaders());
|
|
||||||
var_dump('Return: ' . $res->getReasonPhrase());
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function configTemplate()
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* Copyright (C) 2015 James Campbell <neokjames@gmail.com>
|
|
||||||
* 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 <https://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
/* Copyright (C) 2015 Daniel Preussker <f0o@devilcode.org>
|
|
||||||
* 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 <https://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boxcar API Transport
|
|
||||||
*
|
|
||||||
* @author trick77 <jan@trick77.com>
|
|
||||||
* @copyright 2015 trick77, neokjames, f0o, LibreNMS
|
|
||||||
* @license GPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace LibreNMS\Alert\Transport;
|
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
|
||||||
use LibreNMS\Config;
|
|
||||||
use LibreNMS\Enum\AlertState;
|
|
||||||
use LibreNMS\Util\Proxy;
|
|
||||||
|
|
||||||
class Boxcar extends Transport
|
|
||||||
{
|
|
||||||
public function deliverAlert($obj, $opts)
|
|
||||||
{
|
|
||||||
$boxcar_opts = $this->parseUserOptions($this->config['options']);
|
|
||||||
$boxcar_opts['access_token'] = $this->config['boxcar-token'];
|
|
||||||
|
|
||||||
return $this->contactBoxcar($obj, $boxcar_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactBoxcar($obj, $api)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
$data['user_credentials'] = $api['access_token'];
|
|
||||||
$data['notification[source_name]'] = Config::get('project_id', 'librenms');
|
|
||||||
switch ($obj['severity']) {
|
|
||||||
case 'critical':
|
|
||||||
$severity = 'Critical';
|
|
||||||
if (! empty($api['sound_critical'])) {
|
|
||||||
$data['notification[sound]'] = $api['sound_critical'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'warning':
|
|
||||||
$severity = 'Warning';
|
|
||||||
if (! empty($api['sound_warning'])) {
|
|
||||||
$data['notification[sound]'] = $api['sound_warning'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$severity = 'Unknown';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch ($obj['state']) {
|
|
||||||
case AlertState::RECOVERED:
|
|
||||||
$title_text = 'OK';
|
|
||||||
if (! empty($api['sound_ok'])) {
|
|
||||||
$data['notification[sound]'] = $api['sound_ok'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AlertState::ACTIVE:
|
|
||||||
$title_text = $severity;
|
|
||||||
break;
|
|
||||||
case AlertState::ACKNOWLEDGED:
|
|
||||||
$title_text = 'Acknowledged';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$title_text = $severity;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$data['notification[title]'] = $title_text . ' - ' . $obj['hostname'] . ' - ' . $obj['name'];
|
|
||||||
$message_text = 'Timestamp: ' . $obj['timestamp'];
|
|
||||||
if (! empty($obj['faults'])) {
|
|
||||||
$message_text .= "\n\nFaults:\n";
|
|
||||||
foreach ($obj['faults'] as $k => $faults) {
|
|
||||||
$message_text .= '#' . $k . ' ' . $faults['string'] . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$data['notification[long_message]'] = $message_text;
|
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, 'https://new.boxcar.io/api/notifications');
|
|
||||||
curl_setopt($curl, CURLOPT_SAFE_UPLOAD, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
|
||||||
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',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,9 +32,9 @@ use Notification;
|
|||||||
|
|
||||||
class Browserpush extends Transport
|
class Browserpush extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'Browser Push';
|
protected string $name = 'Browser Push';
|
||||||
|
|
||||||
public function deliverAlert($alert_data, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$users = User::when($this->config['user'] ?? 0, function ($query, $user_id) {
|
$users = User::when($this->config['user'] ?? 0, function ($query, $user_id) {
|
||||||
return $query->where('user_id', $user_id);
|
return $query->where('user_id', $user_id);
|
||||||
@ -49,7 +49,7 @@ class Browserpush extends Transport
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
$users = [__('All Users') => 0];
|
$users = [__('All Users') => 0];
|
||||||
foreach (User::get(['user_id', 'username', 'realname']) as $user) {
|
foreach (User::get(['user_id', 'username', 'realname']) as $user) {
|
||||||
|
@ -9,27 +9,14 @@ use PhpAmqpLib\Message\AMQPMessage;
|
|||||||
|
|
||||||
class Canopsis extends Transport
|
class Canopsis extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
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
|
// Configurations
|
||||||
$host = $opts['host'];
|
$host = $this->config['canopsis-host'];
|
||||||
$port = $opts['port'];
|
$port = $this->config['canopsis-port'];
|
||||||
$user = $opts['user'];
|
$user = $this->config['canopsis-user'];
|
||||||
$pass = $opts['pass'];
|
$pass = $this->config['canopsis-pass'];
|
||||||
$vhost = $opts['vhost'];
|
$vhost = $this->config['canopsis-vhost'];
|
||||||
$exchange = 'canopsis.events';
|
$exchange = 'canopsis.events';
|
||||||
|
|
||||||
// Connection
|
// Connection
|
||||||
@ -41,7 +28,7 @@ class Canopsis extends Transport
|
|||||||
$ch->exchange_declare($exchange, AMQPExchangeType::TOPIC, false, true, false);
|
$ch->exchange_declare($exchange, AMQPExchangeType::TOPIC, false, true, false);
|
||||||
|
|
||||||
// Create Canopsis event, see: https://github.com/capensis/canopsis/wiki/Event-specification
|
// Create Canopsis event, see: https://github.com/capensis/canopsis/wiki/Event-specification
|
||||||
switch ($obj['severity']) {
|
switch ($alert_data['severity']) {
|
||||||
case 'ok':
|
case 'ok':
|
||||||
$state = 0;
|
$state = 0;
|
||||||
break;
|
break;
|
||||||
@ -60,10 +47,10 @@ class Canopsis extends Transport
|
|||||||
'connector_name' => 'LibreNMS1',
|
'connector_name' => 'LibreNMS1',
|
||||||
'event_type' => 'check',
|
'event_type' => 'check',
|
||||||
'source_type' => 'resource',
|
'source_type' => 'resource',
|
||||||
'component' => $obj['hostname'],
|
'component' => $alert_data['hostname'],
|
||||||
'resource' => $obj['name'],
|
'resource' => $alert_data['name'],
|
||||||
'state' => $state,
|
'state' => $state,
|
||||||
'output' => $obj['msg'],
|
'output' => $alert_data['msg'],
|
||||||
'display_name' => 'librenms',
|
'display_name' => 'librenms',
|
||||||
];
|
];
|
||||||
$msg_raw = json_encode($msg_body);
|
$msg_raw = json_encode($msg_body);
|
||||||
@ -84,7 +71,7 @@ class Canopsis extends Transport
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -13,75 +13,41 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Ciscospark extends Transport
|
class Ciscospark extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'Cisco Spark';
|
protected string $name = 'Cisco Webex Teams';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (empty($this->config)) {
|
$room_id = $this->config['room-id'];
|
||||||
$room_id = $opts['roomid'];
|
$token = $this->config['api-token'];
|
||||||
$token = $opts['token'];
|
$url = 'https://api.ciscospark.com/v1/messages';
|
||||||
} 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 = null;
|
|
||||||
$data = [
|
$data = [
|
||||||
'roomId' => $room_id,
|
'roomId' => $room_id,
|
||||||
];
|
];
|
||||||
|
|
||||||
$akey = 'text';
|
|
||||||
if ($this->config['use-markdown'] === 'on') {
|
if ($this->config['use-markdown'] === 'on') {
|
||||||
$lines = explode("\n", $obj['msg']);
|
// Remove blank lines as they create weird markdown behaviors.
|
||||||
$mlines = [];
|
$data['markdown'] = preg_replace('/^\s+/m', '', $alert_data['msg']);
|
||||||
/* Remove blank lines as they create weird markdown
|
|
||||||
* behaviors.
|
|
||||||
*/
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
$line = trim($line);
|
|
||||||
if ($line != '') {
|
|
||||||
array_push($mlines, $line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$text = implode("\n", $mlines);
|
|
||||||
$akey = 'markdown';
|
|
||||||
} else {
|
} else {
|
||||||
$text = strip_tags($obj['msg']);
|
$data['text'] = strip_tags($alert_data['msg']);
|
||||||
}
|
|
||||||
$data[$akey] = $text;
|
|
||||||
|
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, 'https://api.ciscospark.com/v1/messages');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-type' => 'application/json',
|
|
||||||
'Expect:',
|
|
||||||
'Authorization: Bearer ' . $token,
|
|
||||||
]);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
if ($code != 200) {
|
|
||||||
echo "Cisco Spark returned Error, retry later\r\n";
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$res = Http::client()
|
||||||
|
->withToken($token)
|
||||||
|
->post($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['text'] ?? $data['markdown'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,39 +24,30 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Clickatell extends Transport
|
class Clickatell extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$clickatell_opts['token'] = $this->config['clickatell-token'];
|
$url = 'https://platform.clickatell.com/messages/http/send';
|
||||||
$clickatell_opts['to'] = preg_split('/([,\r\n]+)/', $this->config['clickatell-numbers']);
|
$params = [
|
||||||
|
'apiKey' => $this->config['clickatell-token'],
|
||||||
|
'to' => implode(',', preg_split('/([,\r\n]+)/', $this->config['clickatell-numbers'])),
|
||||||
|
'content' => $alert_data['title'],
|
||||||
|
];
|
||||||
|
|
||||||
return $this->contactClickatell($obj, $clickatell_opts);
|
$res = Http::client()->get($url, $params);
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactClickatell($obj, $opts)
|
if ($res->successful()) {
|
||||||
{
|
return true;
|
||||||
$url = 'https://platform.clickatell.com/messages/http/send?apiKey=' . $opts['token'] . '&to=' . implode(',', $opts['to']) . '&content=' . urlencode($obj['title']);
|
|
||||||
|
|
||||||
$curl = curl_init($url);
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code > 200) {
|
|
||||||
var_dump($ret);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['title'], $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -31,7 +31,7 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Discord extends Transport
|
class Discord extends Transport
|
||||||
{
|
{
|
||||||
@ -43,26 +43,16 @@ class Discord extends Transport
|
|||||||
'rule' => 'Rule',
|
'rule' => 'Rule',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$discord_opts = [
|
$added_fields = $this->parseUserOptions($this->config['options']);
|
||||||
'url' => $this->config['url'],
|
|
||||||
'options' => $this->parseUserOptions($this->config['options']),
|
|
||||||
];
|
|
||||||
|
|
||||||
return $this->contactDiscord($obj, $discord_opts);
|
$discord_title = '#' . $alert_data['uid'] . ' ' . $alert_data['title'];
|
||||||
}
|
$discord_msg = $alert_data['msg'];
|
||||||
|
$color = hexdec(preg_replace('/[^\dA-Fa-f]/', '', self::getColorForState($alert_data['state'])));
|
||||||
public function contactDiscord($obj, $discord_opts)
|
|
||||||
{
|
|
||||||
$host = $discord_opts['url'];
|
|
||||||
$curl = curl_init();
|
|
||||||
$discord_title = '#' . $obj['uid'] . ' ' . $obj['title'];
|
|
||||||
$discord_msg = $obj['msg'];
|
|
||||||
$color = hexdec(preg_replace('/[^\dA-Fa-f]/', '', self::getColorForState($obj['state'])));
|
|
||||||
|
|
||||||
// Special handling for the elapsed text in the footer if the elapsed is not set.
|
// Special handling for the elapsed text in the footer if the elapsed is not set.
|
||||||
$footer_text = $obj['elapsed'] ? 'alert took ' . $obj['elapsed'] : '';
|
$footer_text = $alert_data['elapsed'] ? 'alert took ' . $alert_data['elapsed'] : '';
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'embeds' => [
|
'embeds' => [
|
||||||
@ -70,36 +60,29 @@ class Discord extends Transport
|
|||||||
'title' => $discord_title,
|
'title' => $discord_title,
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'description' => $discord_msg,
|
'description' => $discord_msg,
|
||||||
'fields' => $this->createDiscordFields($obj, $discord_opts),
|
'fields' => $this->createDiscordFields($alert_data),
|
||||||
'footer' => [
|
'footer' => [
|
||||||
'text' => $footer_text,
|
'text' => $footer_text,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
if (! empty($discord_opts['options'])) {
|
if (! empty($added_fields)) {
|
||||||
$data = array_merge($data, $discord_opts['options']);
|
$data = array_merge($data, $added_fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->embedGraphs($data);
|
$data = $this->embedGraphs($data);
|
||||||
|
|
||||||
// remove all remaining HTML tags
|
// remove all remaining HTML tags
|
||||||
$data['embeds'][0]['description'] = strip_tags($data['embeds'][0]['description']);
|
$data['embeds'][0]['description'] = strip_tags($data['embeds'][0]['description']);
|
||||||
$alert_message = json_encode($data);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
$res = Http::client()->post($this->config['url'], $data);
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 204) {
|
if ($res->successful()) {
|
||||||
throw new AlertTransportDeliveryException($obj, $code, $ret, $alert_message, $data);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $discord_msg, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function embedGraphs(array $data): array
|
private function embedGraphs(array $data): array
|
||||||
@ -118,26 +101,26 @@ class Discord extends Transport
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createDiscordFields($obj, $discord_opts)
|
public function createDiscordFields(array $alert_data): array
|
||||||
{
|
{
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
foreach (self::ALERT_FIELDS_TO_DISCORD_FIELDS as $objKey => $discordKey) {
|
foreach (self::ALERT_FIELDS_TO_DISCORD_FIELDS as $objKey => $discordKey) {
|
||||||
// Skip over keys that do not exist so Discord does not give us a 400.
|
// Skip over keys that do not exist so Discord does not give us a 400.
|
||||||
if (! $obj[$objKey]) {
|
if (! $alert_data[$objKey]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
array_push($result, [
|
$result[] = [
|
||||||
'name' => $discordKey,
|
'name' => $discordKey,
|
||||||
'value' => $obj[$objKey],
|
'value' => $alert_data[$objKey],
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,22 +24,16 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
|
||||||
class Dummy extends Transport
|
class Dummy extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
var_dump($obj);
|
throw new AlertTransportDeliveryException($alert_data, 0, 'Dummy transport always fails', $alert_data['msg']);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function contactDummy()
|
public static function configTemplate(): array
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function configTemplate()
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'validation' => [],
|
'validation' => [],
|
||||||
|
@ -19,191 +19,130 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Elasticsearch extends Transport
|
class Elasticsearch extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
$es_host = $this->config['es-host'];
|
||||||
$opts['es_host'] = $this->config['es-host'];
|
$es_port = $this->config['es-port'] ?: 9200;
|
||||||
$opts['es_port'] = $this->config['es-port'];
|
$index = date($this->config['es-pattern'] ?: "\l\i\b\\r\\e\\n\m\s\-Y.m.d");
|
||||||
$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;
|
|
||||||
$index = date("\l\i\b\\r\\e\\n\m\s\-Y.m.d");
|
|
||||||
$type = 'alert';
|
$type = 'alert';
|
||||||
$severity = $obj['severity'];
|
$severity = $alert_data['severity'];
|
||||||
|
|
||||||
if (! empty($opts['es_host'])) {
|
|
||||||
if (preg_match('/[a-zA-Z]/', $opts['es_host'])) {
|
|
||||||
$es_host = gethostbyname($opts['es_host']);
|
|
||||||
if ($es_host === $opts['es_host']) {
|
|
||||||
return 'Alphanumeric hostname found but does not resolve to an IP.';
|
|
||||||
}
|
|
||||||
} elseif (filter_var($opts['es_host'], FILTER_VALIDATE_IP)) {
|
|
||||||
$es_host = $opts['es_host'];
|
|
||||||
} else {
|
|
||||||
return 'Elasticsearch host is not a valid IP: ' . $opts['es_host'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($opts['es_port']) && preg_match("/^\d+$/", $opts['es_port'])) {
|
|
||||||
$es_port = $opts['es_port'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($opts['es_index'])) {
|
|
||||||
$index = date($opts['es_index']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$host = $es_host . ':' . $es_port . '/' . $index . '/' . $type;
|
$host = $es_host . ':' . $es_port . '/' . $index . '/' . $type;
|
||||||
|
|
||||||
switch ($obj['state']) {
|
$state = match ($alert_data['state']) {
|
||||||
case AlertState::RECOVERED:
|
AlertState::RECOVERED => 'ok',
|
||||||
$state = 'ok';
|
AlertState::ACTIVE => $severity,
|
||||||
break;
|
AlertState::ACKNOWLEDGED => 'acknowledged',
|
||||||
case AlertState::ACTIVE:
|
AlertState::WORSE => 'worse',
|
||||||
$state = $severity;
|
AlertState::BETTER => 'better',
|
||||||
break;
|
default => 'unknown',
|
||||||
case AlertState::ACKNOWLEDGED:
|
};
|
||||||
$state = 'acknowledged';
|
|
||||||
break;
|
|
||||||
case AlertState::WORSE:
|
|
||||||
$state = 'worse';
|
|
||||||
break;
|
|
||||||
case AlertState::BETTER:
|
|
||||||
$state = 'better';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$state = 'unknown';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'@timestamp' => date('c'),
|
'@timestamp' => date('c'),
|
||||||
'host' => gethostname(),
|
'host' => gethostname(),
|
||||||
'location' => $obj['location'],
|
'location' => $alert_data['location'],
|
||||||
'title' => $obj['name'],
|
'title' => $alert_data['name'],
|
||||||
'message' => $obj['string'],
|
'message' => $alert_data['string'],
|
||||||
'device_id' => $obj['device_id'],
|
'device_id' => $alert_data['device_id'],
|
||||||
'device_name' => $obj['hostname'],
|
'device_name' => $alert_data['hostname'],
|
||||||
'device_hardware' => $obj['hardware'],
|
'device_hardware' => $alert_data['hardware'],
|
||||||
'device_version' => $obj['version'],
|
'device_version' => $alert_data['version'],
|
||||||
'state' => $state,
|
'state' => $state,
|
||||||
'severity' => $severity,
|
'severity' => $severity,
|
||||||
'first_occurrence' => $obj['timestamp'],
|
'first_occurrence' => $alert_data['timestamp'],
|
||||||
'entity_type' => 'device',
|
'entity_type' => 'device',
|
||||||
'entity_tab' => 'overview',
|
'entity_tab' => 'overview',
|
||||||
'entity_id' => $obj['device_id'],
|
'entity_id' => $alert_data['device_id'],
|
||||||
'entity_name' => $obj['hostname'],
|
'entity_name' => $alert_data['hostname'],
|
||||||
'entity_descr' => $obj['sysDescr'],
|
'entity_descr' => $alert_data['sysDescr'],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (! empty($obj['faults'])) {
|
foreach ($alert_data['faults'] as $k => $v) {
|
||||||
foreach ($obj['faults'] as $k => $v) {
|
$data['message'] = $v['string'];
|
||||||
$curl = curl_init();
|
switch (true) {
|
||||||
$data['message'] = $v['string'];
|
case array_key_exists('port_id', $v):
|
||||||
switch (true) {
|
$data['entity_type'] = 'port';
|
||||||
case array_key_exists('port_id', $v):
|
$data['entity_tab'] = 'port';
|
||||||
$data['entity_type'] = 'port';
|
$data['entity_id'] = $v['port_id'];
|
||||||
$data['entity_tab'] = 'port';
|
$data['entity_name'] = $v['ifName'];
|
||||||
$data['entity_id'] = $v['port_id'];
|
$data['entity_descr'] = $v['ifAlias'];
|
||||||
$data['entity_name'] = $v['ifName'];
|
break;
|
||||||
$data['entity_descr'] = $v['ifAlias'];
|
case array_key_exists('sensor_id', $v):
|
||||||
break;
|
$data['entity_type'] = $v['sensor_class'];
|
||||||
case array_key_exists('sensor_id', $v):
|
$data['entity_tab'] = 'health';
|
||||||
$data['entity_type'] = $v['sensor_class'];
|
$data['entity_id'] = $v['sensor_id'];
|
||||||
$data['entity_tab'] = 'health';
|
$data['entity_name'] = $v['sensor_descr'];
|
||||||
$data['entity_id'] = $v['sensor_id'];
|
$data['entity_descr'] = $v['sensor_type'];
|
||||||
$data['entity_name'] = $v['sensor_descr'];
|
break;
|
||||||
$data['entity_descr'] = $v['sensor_type'];
|
case array_key_exists('mempool_id', $v):
|
||||||
break;
|
$data['entity_type'] = 'mempool';
|
||||||
case array_key_exists('mempool_id', $v):
|
$data['entity_tab'] = 'health';
|
||||||
$data['entity_type'] = 'mempool';
|
$data['entity_id'] = $v['mempool_id'];
|
||||||
$data['entity_tab'] = 'health';
|
$data['entity_name'] = $v['mempool_index'];
|
||||||
$data['entity_id'] = $v['mempool_id'];
|
$data['entity_descr'] = $v['mempool_descr'];
|
||||||
$data['entity_name'] = $v['mempool_index'];
|
break;
|
||||||
$data['entity_descr'] = $v['mempool_descr'];
|
case array_key_exists('storage_id', $v):
|
||||||
break;
|
$data['entity_type'] = 'storage';
|
||||||
case array_key_exists('storage_id', $v):
|
$data['entity_tab'] = 'health';
|
||||||
$data['entity_type'] = 'storage';
|
$data['entity_id'] = $v['storage_id'];
|
||||||
$data['entity_tab'] = 'health';
|
$data['entity_name'] = $v['storage_index'];
|
||||||
$data['entity_id'] = $v['storage_id'];
|
$data['entity_descr'] = $v['storage_descr'];
|
||||||
$data['entity_name'] = $v['storage_index'];
|
break;
|
||||||
$data['entity_descr'] = $v['storage_descr'];
|
case array_key_exists('processor_id', $v):
|
||||||
break;
|
$data['entity_type'] = 'processor';
|
||||||
case array_key_exists('processor_id', $v):
|
$data['entity_tab'] = 'health';
|
||||||
$data['entity_type'] = 'processor';
|
$data['entity_id'] = $v['processor_id'];
|
||||||
$data['entity_tab'] = 'health';
|
$data['entity_name'] = $v['processor_type'];
|
||||||
$data['entity_id'] = $v['processor_id'];
|
$data['entity_descr'] = $v['processor_descr'];
|
||||||
$data['entity_name'] = $v['processor_type'];
|
break;
|
||||||
$data['entity_descr'] = $v['processor_descr'];
|
case array_key_exists('bgpPeer_id', $v):
|
||||||
break;
|
$data['entity_type'] = 'bgp';
|
||||||
case array_key_exists('bgpPeer_id', $v):
|
$data['entity_tab'] = 'routing';
|
||||||
$data['entity_type'] = 'bgp';
|
$data['entity_id'] = $v['bgpPeer_id'];
|
||||||
$data['entity_tab'] = 'routing';
|
$data['entity_name'] = 'local: ' . $v['bgpPeerLocalAddr'] . ' - AS' . $alert_data['bgpLocalAs'];
|
||||||
$data['entity_id'] = $v['bgpPeer_id'];
|
$data['entity_descr'] = 'remote: ' . $v['bgpPeerIdentifier'] . ' - AS' . $v['bgpPeerRemoteAs'];
|
||||||
$data['entity_name'] = 'local: ' . $v['bgpPeerLocalAddr'] . ' - AS' . $obj['bgpLocalAs'];
|
break;
|
||||||
$data['entity_descr'] = 'remote: ' . $v['bgpPeerIdentifier'] . ' - AS' . $v['bgpPeerRemoteAs'];
|
case array_key_exists('tunnel_id', $v):
|
||||||
break;
|
$data['entity_type'] = 'ipsec_tunnel';
|
||||||
case array_key_exists('tunnel_id', $v):
|
$data['entity_tab'] = 'routing';
|
||||||
$data['entity_type'] = 'ipsec_tunnel';
|
$data['entity_id'] = $v['tunnel_id'];
|
||||||
$data['entity_tab'] = 'routing';
|
$data['entity_name'] = $v['tunnel_name'];
|
||||||
$data['entity_id'] = $v['tunnel_id'];
|
$data['entity_descr'] = 'local: ' . $v['local_addr'] . ':' . $v['local_port'] . ', remote: ' . $v['peer_addr'] . ':' . $v['peer_port'];
|
||||||
$data['entity_name'] = $v['tunnel_name'];
|
break;
|
||||||
$data['entity_descr'] = 'local: ' . $v['local_addr'] . ':' . $v['local_port'] . ', remote: ' . $v['peer_addr'] . ':' . $v['peer_port'];
|
default:
|
||||||
break;
|
$data['entity_type'] = 'generic';
|
||||||
default:
|
break;
|
||||||
$data['entity_type'] = 'generic';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$alert_message = json_encode($data);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
if ($opts['es_proxy'] === true) {
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
}
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200 && $code != 201) {
|
|
||||||
return $host . ' returned HTTP Status code ' . $code . ' for ' . $alert_message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$curl = curl_init();
|
|
||||||
$alert_message = json_encode($data);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
if ($opts['es_proxy'] === true) {
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
}
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200 && $code != 201) {
|
|
||||||
return $host . ' returned HTTP Status code ' . $code . ' for ' . $alert_message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$client = Http::client();
|
||||||
|
|
||||||
|
// silly, just use no_proxy
|
||||||
|
if ($this->config['es-proxy'] !== 'on') {
|
||||||
|
$client->withOptions([
|
||||||
|
'proxy' => [
|
||||||
|
'http' => '',
|
||||||
|
'https' => '',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $client->post($host, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['message'] ?? '', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -212,31 +151,34 @@ class Elasticsearch extends Transport
|
|||||||
'name' => 'es-host',
|
'name' => 'es-host',
|
||||||
'descr' => 'Elasticsearch Host',
|
'descr' => 'Elasticsearch Host',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'default' => '127.0.0.1',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => 'Port',
|
'title' => 'Port',
|
||||||
'name' => 'es-port',
|
'name' => 'es-port',
|
||||||
'descr' => 'Elasticsearch Port',
|
'descr' => 'Elasticsearch Port',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'default' => 9200,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => 'Index Pattern',
|
'title' => 'Index Pattern',
|
||||||
'name' => 'es-pattern',
|
'name' => 'es-pattern',
|
||||||
'descr' => 'Elasticsearch Index Pattern | Default: \l\i\b\\r\\e\\n\m\s\-Y.m.d | Format: https://www.php.net/manual/en/function.date.php',
|
'descr' => 'Elasticsearch Index Pattern | Default: \l\i\b\\r\\e\\n\m\s\-Y.m.d | Format: https://www.php.net/manual/en/function.date.php',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'default' => "\l\i\b\\r\\e\\n\m\s\-Y.m.d",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => 'Use proxy if configured?',
|
'title' => 'Use proxy if configured?',
|
||||||
'name' => 'es-proxy',
|
'name' => 'es-proxy',
|
||||||
'descr' => 'Elasticsearch Proxy',
|
'descr' => 'Elasticsearch Proxy (Deprecated: just use no_proxy setting to exclude ES server)',
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'default' => false,
|
'default' => true,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'es-host' => 'required|string',
|
'es-host' => 'required|ip_or_hostname',
|
||||||
'es-port' => 'required|string',
|
'es-port' => 'integer|between:1,65535',
|
||||||
'es-pattern' => 'required|string',
|
'es-pattern' => 'string',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -25,67 +25,40 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Gitlab extends Transport
|
class Gitlab extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
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
|
// Don't create tickets for resolutions
|
||||||
if ($obj['state'] != AlertState::CLEAR) {
|
if ($alert_data['state'] == AlertState::RECOVERED) {
|
||||||
$project_id = $opts['project-id'];
|
return true;
|
||||||
$project_key = $opts['key'];
|
|
||||||
$details = 'Librenms alert for: ' . $obj['hostname'];
|
|
||||||
$description = $obj['msg'];
|
|
||||||
$title = urlencode($details);
|
|
||||||
$desc = urlencode($description);
|
|
||||||
$url = $opts['host'] . "/api/v4/projects/$project_id/issues?title=$title&description=$desc";
|
|
||||||
$curl = curl_init();
|
|
||||||
|
|
||||||
$data = ['title' => $details,
|
|
||||||
'description' => $description,
|
|
||||||
];
|
|
||||||
$postdata = ['fields' => $data];
|
|
||||||
$datastring = json_encode($postdata);
|
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
|
|
||||||
$headers = ['Accept: application/json', 'Content-Type: application/json', 'PRIVATE-TOKEN: ' . $project_key];
|
|
||||||
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_VERBOSE, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $datastring);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code == 200) {
|
|
||||||
$gitlabout = json_decode($ret, true);
|
|
||||||
d_echo('Created GitLab issue ' . $gitlabout['key'] . ' for ' . $obj['hostname']);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
d_echo('GitLab connection error: ' . serialize($ret));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$project_id = $this->config['gitlab-id'];
|
||||||
|
$url = $this->config['gitlab-host'] . "/api/v4/projects/$project_id/issues";
|
||||||
|
$data = [
|
||||||
|
'title' => 'Librenms alert for: ' . $alert_data['hostname'],
|
||||||
|
'description' => $alert_data['msg'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$res = Http::client()
|
||||||
|
->withHeaders([
|
||||||
|
'PRIVATE-TOKEN' => $this->config['gitlab-key'],
|
||||||
|
])
|
||||||
|
->acceptJson()
|
||||||
|
->post($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['description'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,60 +24,26 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
use Log;
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Googlechat extends Transport
|
class Googlechat extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'Google Chat';
|
protected string $name = 'Google Chat';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$googlechat_conf['webhookurl'] = $this->config['googlechat-webhook'];
|
$data = ['text' => $alert_data['msg']];
|
||||||
|
$res = Http::client()->post($this->config['googlechat-webhook'], $data);
|
||||||
|
|
||||||
return $this->contactGooglechat($obj, $googlechat_conf);
|
if ($res->successful()) {
|
||||||
}
|
return true;
|
||||||
|
|
||||||
public static function contactGooglechat($obj, $data)
|
|
||||||
{
|
|
||||||
$payload = '{"text": "' . $obj['msg'] . '"}';
|
|
||||||
|
|
||||||
Log::debug($payload);
|
|
||||||
|
|
||||||
// Create a new cURL resource
|
|
||||||
$ch = curl_init($data['webhookurl']);
|
|
||||||
Proxy::applyToCurl($ch);
|
|
||||||
|
|
||||||
// Attach encoded JSON string to the POST fields
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
|
||||||
|
|
||||||
// Set the content type to application/json
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type:application/json']);
|
|
||||||
|
|
||||||
// Return response instead of outputting
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
|
|
||||||
// Execute the POST request
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
|
|
||||||
// Close cURL resource
|
|
||||||
|
|
||||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
Log::debug("$code");
|
|
||||||
|
|
||||||
if ($code != 200) {
|
|
||||||
Log::error('Google Chat Transport Error');
|
|
||||||
Log::error($result);
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['text'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -25,92 +25,73 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Enum\AlertState;
|
||||||
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Hipchat extends Transport
|
class Hipchat extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'HipChat';
|
protected string $name = 'HipChat';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$hipchat_opts = $this->parseUserOptions($this->config['hipchat-options']);
|
$options = $this->parseUserOptions($this->config['hipchat-options']);
|
||||||
$hipchat_opts['url'] = $this->config['hipchat-url'];
|
|
||||||
$hipchat_opts['room_id'] = $this->config['hipchat-room-id'];
|
|
||||||
$hipchat_opts['from'] = $this->config['hipchat-from-name'];
|
|
||||||
|
|
||||||
return $this->contactHipchat($obj, $hipchat_opts);
|
// override legacy options
|
||||||
}
|
if (array_key_exists('hipchat-notify', $this->config)) {
|
||||||
|
$options['notify'] = ($this->config['hipchat-notify'] == 'on');
|
||||||
public function contactHipchat($obj, $option)
|
|
||||||
{
|
|
||||||
$version = 1;
|
|
||||||
if (stripos($option['url'], 'v2')) {
|
|
||||||
$version = 2;
|
|
||||||
}
|
}
|
||||||
|
if (isset($this->config['hipchat-message_format'])) {
|
||||||
|
$options['message_format'] = $this->config['hipchat-message_format'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->config['hipchat-url'];
|
||||||
|
$version = str_contains($url, 'v2') ? 2 : 1;
|
||||||
|
|
||||||
// Generate our URL from the base URL + room_id and the auth token if the version is 2.
|
// Generate our URL from the base URL + room_id and the auth token if the version is 2.
|
||||||
$url = $option['url'];
|
|
||||||
if ($version == 2) {
|
if ($version == 2) {
|
||||||
$url .= '/' . urlencode($option['room_id']) . '/notification?auth_token=' . urlencode($option['auth_token']);
|
$url .= '/' . urlencode($this->config['hipchat-room-id']) . '/notification';
|
||||||
}
|
|
||||||
|
|
||||||
$curl = curl_init();
|
|
||||||
|
|
||||||
if (empty($obj['msg'])) {
|
|
||||||
return 'Empty Message';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($option['message_format'])) {
|
|
||||||
$option['message_format'] = 'text';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sane default of making the message color green if the message indicates
|
// Sane default of making the message color green if the message indicates
|
||||||
// that the alert recovered. If it rebooted, make it yellow.
|
// that the alert recovered. If it rebooted, make it yellow.
|
||||||
if (stripos($obj['msg'], 'recovered')) {
|
if ($alert_data['state'] == AlertState::RECOVERED) {
|
||||||
$color = 'green';
|
$color = 'green';
|
||||||
} elseif (stripos($obj['msg'], 'rebooted')) {
|
} elseif (str_contains($alert_data['msg'], 'rebooted')) {
|
||||||
$color = 'yellow';
|
$color = 'yellow';
|
||||||
|
} elseif (empty($options['color']) || $options['color'] == 'u') {
|
||||||
|
$color = 'red';
|
||||||
} else {
|
} else {
|
||||||
if (empty($option['color']) || $option['color'] == 'u') {
|
$color = $options['color'];
|
||||||
$color = 'red';
|
|
||||||
} else {
|
|
||||||
$color = $option['color'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$data[] = 'message=' . urlencode($obj['msg']);
|
$data = [
|
||||||
|
'message' => $alert_data['msg'],
|
||||||
|
'from' => $this->config['hipchat-from-name'] ?: 'LibreNMS',
|
||||||
|
'color' => $color,
|
||||||
|
'notify' => $options['notify'] ?? '1',
|
||||||
|
'message_format' => $options['message_format'] ?: 'text',
|
||||||
|
];
|
||||||
if ($version == 1) {
|
if ($version == 1) {
|
||||||
$data[] = 'room_id=' . urlencode($option['room_id']);
|
$data['room_id'] = $this->config['hipchat-room-id'];
|
||||||
}
|
|
||||||
$data[] = 'from=' . urlencode($option['from']);
|
|
||||||
$data[] = 'color=' . urlencode($color);
|
|
||||||
$data[] = 'notify=' . urlencode($option['notify']);
|
|
||||||
$data[] = 'message_format=' . urlencode($option['message_format']);
|
|
||||||
|
|
||||||
$data = implode('&', $data);
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/x-www-form-urlencoded',
|
|
||||||
]);
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200 && $code != 204) {
|
|
||||||
var_dump("API '$url' returned Error");
|
|
||||||
//var_dump('Params: ' . $message);
|
|
||||||
var_dump('Return: ' . $ret);
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$client = Http::client();
|
||||||
|
|
||||||
|
if ($version == 2) {
|
||||||
|
$client->withToken($options['auth_token']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $client->post($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['message'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -138,6 +119,24 @@ class Hipchat extends Transport
|
|||||||
'descr' => 'Hipchat Options',
|
'descr' => 'Hipchat Options',
|
||||||
'type' => 'textarea',
|
'type' => 'textarea',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'title' => 'Notify?',
|
||||||
|
'name' => 'hipchat-notify',
|
||||||
|
'descr' => 'Send notification',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'default' => 'on',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'title' => 'Message Format',
|
||||||
|
'name' => 'hipchat-message_format',
|
||||||
|
'descr' => 'Format of message',
|
||||||
|
'type' => 'select',
|
||||||
|
'options' => [
|
||||||
|
'Text' => 'text',
|
||||||
|
'HTML' => 'html',
|
||||||
|
],
|
||||||
|
'default' => 'text',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'hipchat-url' => 'required|url',
|
'hipchat-url' => 'required|url',
|
||||||
|
@ -26,7 +26,8 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Hue API currently is fairly limited for alerts.
|
* The Hue API currently is fairly limited for alerts.
|
||||||
@ -35,56 +36,30 @@ use LibreNMS\Util\Proxy;
|
|||||||
*/
|
*/
|
||||||
class Hue extends Transport
|
class Hue extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
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
|
// Don't alert on resolve at this time
|
||||||
if ($obj['state'] == AlertState::RECOVERED) {
|
if ($alert_data['state'] == AlertState::RECOVERED) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
$hue_user = $opts['user'];
|
|
||||||
$url = $opts['bridge'] . "/api/$hue_user/groups/0/action";
|
|
||||||
$curl = curl_init();
|
|
||||||
$duration = $opts['duration'];
|
|
||||||
$data = ['alert' => $duration];
|
|
||||||
$datastring = json_encode($data);
|
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
|
|
||||||
$headers = ['Accept: application/json', 'Content-Type: application/json'];
|
|
||||||
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_VERBOSE, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $datastring);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code == 200) {
|
|
||||||
d_echo('Sent alert to Phillips Hue Bridge ' . $opts['host'] . ' for ' . $obj['hostname']);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
d_echo('Hue bridge connection error: ' . serialize($ret));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hue_user = $this->config['hue-user'];
|
||||||
|
$url = $this->config['hue-host'] . "/api/$hue_user/groups/0/action";
|
||||||
|
$duration = $this->config['hue-duration'];
|
||||||
|
$data = ['alert' => $duration];
|
||||||
|
|
||||||
|
$res = Http::client()
|
||||||
|
->acceptJson()
|
||||||
|
->put($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $duration, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config'=>[
|
'config'=>[
|
||||||
|
@ -25,17 +25,13 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
|
||||||
class Irc extends Transport
|
class Irc extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'IRC';
|
protected string $name = 'IRC';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
return $this->contactIrc($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactIrc($obj, $opts)
|
|
||||||
{
|
{
|
||||||
$container_dir = '/data';
|
$container_dir = '/data';
|
||||||
if (file_exists($container_dir) and posix_getpwuid(fileowner($container_dir))['name'] == 'librenms') {
|
if (file_exists($container_dir) and posix_getpwuid(fileowner($container_dir))['name'] == 'librenms') {
|
||||||
@ -45,19 +41,20 @@ class Irc extends Transport
|
|||||||
}
|
}
|
||||||
if (file_exists($f) && filetype($f) == 'fifo') {
|
if (file_exists($f) && filetype($f) == 'fifo') {
|
||||||
$f = fopen($f, 'w+');
|
$f = fopen($f, 'w+');
|
||||||
$r = fwrite($f, json_encode($obj) . "\n");
|
$r = fwrite($f, json_encode($alert_data) . "\n");
|
||||||
$f = fclose($f);
|
fclose($f);
|
||||||
|
|
||||||
if ($r === false) {
|
if ($r === false) {
|
||||||
return false;
|
throw new AlertTransportDeliveryException($alert_data, 0, 'Could not write to fifo', $alert_data['msg'], $alert_data);
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
throw new AlertTransportDeliveryException($alert_data, 0, 'fifo does not exist', $alert_data['msg'], $alert_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,73 +24,50 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Jira extends Transport
|
class Jira extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
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
|
// Don't create tickets for resolutions
|
||||||
if ($obj['severity'] == 'recovery' && $obj['msg'] != 'This is a test alert') {
|
if ($alert_data['severity'] == 'recovery') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$username = $opts['username'];
|
$prjkey = $this->config['jira-key'];
|
||||||
$password = $opts['password'];
|
$issuetype = $this->config['jira-type'];
|
||||||
$prjkey = $opts['prjkey'];
|
$details = empty($alert_data['title']) ? 'Librenms alert for: ' . $alert_data['hostname'] : $alert_data['title'];
|
||||||
$issuetype = $opts['issuetype'];
|
$description = $alert_data['msg'];
|
||||||
$details = empty($obj['title']) ? 'Librenms alert for: ' . $obj['hostname'] : $obj['title'];
|
$url = $this->config['jira-url'] . '/rest/api/latest/issue';
|
||||||
$description = $obj['msg'];
|
|
||||||
$url = $opts['url'] . '/rest/api/latest/issue';
|
|
||||||
$curl = curl_init();
|
|
||||||
|
|
||||||
$data = ['project' => ['key' => $prjkey],
|
$data = [
|
||||||
'summary' => $details,
|
'fields' => [
|
||||||
'description' => $description,
|
'project' => [
|
||||||
'issuetype' => ['name' => $issuetype], ];
|
'key' => $prjkey,
|
||||||
$postdata = ['fields' => $data];
|
],
|
||||||
$datastring = json_encode($postdata);
|
'summary' => $details,
|
||||||
|
'description' => $description,
|
||||||
|
'issuetype' => [
|
||||||
|
'name' => $issuetype,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
$res = Http::client()
|
||||||
|
->withBasicAuth($this->config['jira-username'], $this->config['jira-password'])
|
||||||
$headers = ['Accept: application/json', 'Content-Type: application/json'];
|
->acceptJson()
|
||||||
|
->post($url, $data);
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
|
||||||
curl_setopt($curl, CURLOPT_USERPWD, "$username:$password");
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_VERBOSE, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $datastring);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code == 200 || $code == 201) {
|
|
||||||
$jiraout = json_decode($ret, true);
|
|
||||||
d_echo('Created jira issue ' . $jiraout['key'] . ' for ' . $obj['hostname']);
|
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
d_echo('Jira connection error: ' . serialize($ret));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $description, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -14,29 +14,18 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Kayako extends Transport
|
class Kayako extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($host, $kayako)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
$url = $this->config['kayako-url'] . '/Tickets/Ticket';
|
||||||
$kayako['url'] = $this->config['kayako-url'];
|
$key = $this->config['kayako-key'];
|
||||||
$kayako['key'] = $this->config['kayako-key'];
|
$secret = $this->config['kayako-secret'];
|
||||||
$kayako['secret'] = $this->config['kayako-secret'];
|
|
||||||
$kayako['department'] = $this->config['kayako-department'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->contactKayako($host, $kayako);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactKayako($host, $kayako)
|
|
||||||
{
|
|
||||||
$url = $kayako['url'] . '/Tickets/Ticket';
|
|
||||||
$key = $kayako['key'];
|
|
||||||
$secret = $kayako['secret'];
|
|
||||||
$user = Config::get('email_from');
|
$user = Config::get('email_from');
|
||||||
$department = $kayako['department'];
|
$department = $this->config['kayako-department'];
|
||||||
$ticket_type = 1;
|
$ticket_type = 1;
|
||||||
$ticket_status = 1;
|
$ticket_status = 1;
|
||||||
$ticket_prio = 1;
|
$ticket_prio = 1;
|
||||||
@ -44,10 +33,10 @@ class Kayako extends Transport
|
|||||||
$signature = base64_encode(hash_hmac('sha256', $salt, $secret, true));
|
$signature = base64_encode(hash_hmac('sha256', $salt, $secret, true));
|
||||||
|
|
||||||
$protocol = [
|
$protocol = [
|
||||||
'subject' => ($host['name'] ? $host['name'] . ' on ' . $host['hostname'] : $host['title']),
|
'subject' => ($alert_data['name'] ? $alert_data['name'] . ' on ' . $alert_data['hostname'] : $alert_data['title']),
|
||||||
'fullname' => 'LibreNMS Alert',
|
'fullname' => 'LibreNMS Alert',
|
||||||
'email' => $user,
|
'email' => $user,
|
||||||
'contents' => strip_tags($host['msg']),
|
'contents' => strip_tags($alert_data['msg']),
|
||||||
'departmentid' => $department,
|
'departmentid' => $department,
|
||||||
'ticketstatusid' => $ticket_status,
|
'ticketstatusid' => $ticket_status,
|
||||||
'ticketpriorityid' => $ticket_prio,
|
'ticketpriorityid' => $ticket_prio,
|
||||||
@ -58,27 +47,19 @@ class Kayako extends Transport
|
|||||||
'salt' => $salt,
|
'salt' => $salt,
|
||||||
'signature' => $signature,
|
'signature' => $signature,
|
||||||
];
|
];
|
||||||
$post_data = http_build_query($protocol, '', '&');
|
|
||||||
|
|
||||||
$curl = curl_init();
|
$res = Http::client()
|
||||||
Proxy::applyToCurl($curl);
|
->asForm() // unsure if this is needed, can't access docs
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
->post($url, $protocol);
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
|
|
||||||
curl_exec($curl);
|
|
||||||
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
if ($res->successful()) {
|
||||||
if ($code != 200) {
|
return true;
|
||||||
var_dump('Kayako returned Error, retry later');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $protocol['contents'], $protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -6,44 +6,32 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Linenotify extends Transport
|
class Linenotify extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'LINE Notify';
|
protected string $name = 'LINE Notify';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
$opts['line-notify-access-token'] = $this->config['line-notify-access-token'];
|
|
||||||
|
|
||||||
return $this->contactLinenotify($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function contactLinenotify($obj, $opts)
|
|
||||||
{
|
{
|
||||||
|
// TODO possible to attach graph images
|
||||||
$lineUrl = 'https://notify-api.line.me/api/notify';
|
$lineUrl = 'https://notify-api.line.me/api/notify';
|
||||||
$lineHead = ['Authorization: Bearer ' . $opts['line-notify-access-token']];
|
$lineFields = ['message' => $alert_data['msg']];
|
||||||
$lineFields = ['message' => $obj['msg']];
|
|
||||||
|
|
||||||
$curl = curl_init();
|
$res = Http::client()
|
||||||
Proxy::applyToCurl($curl);
|
->withToken($this->config['line-notify-access-token'])
|
||||||
curl_setopt($curl, CURLOPT_URL, $lineUrl);
|
->asForm()
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $lineHead);
|
->post($lineUrl, $lineFields);
|
||||||
curl_setopt($curl, CURLOPT_NOBODY, false);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
if ($res->successful()) {
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
return true;
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $lineFields);
|
|
||||||
curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
curl_close($curl);
|
|
||||||
if ($code != 200) {
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $lineFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -28,28 +28,23 @@ use LibreNMS\Config;
|
|||||||
|
|
||||||
class Mail extends Transport
|
class Mail extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
return $this->contactMail($obj);
|
$email = $this->config['email'] ?? $alert_data['contacts'];
|
||||||
}
|
|
||||||
|
|
||||||
public function contactMail($obj)
|
|
||||||
{
|
|
||||||
$email = $this->config['email'] ?? $obj['contacts'];
|
|
||||||
$html = Config::get('email_html');
|
$html = Config::get('email_html');
|
||||||
|
|
||||||
if ($html && ! $this->isHtmlContent($obj['msg'])) {
|
if ($html && ! $this->isHtmlContent($alert_data['msg'])) {
|
||||||
// if there are no html tags in the content, but we are sending an html email, use br for line returns instead
|
// if there are no html tags in the content, but we are sending an html email, use br for line returns instead
|
||||||
$msg = preg_replace("/\r?\n/", "<br />\n", $obj['msg']);
|
$msg = preg_replace("/\r?\n/", "<br />\n", $alert_data['msg']);
|
||||||
} else {
|
} else {
|
||||||
// fix line returns for windows mail clients
|
// fix line returns for windows mail clients
|
||||||
$msg = preg_replace("/(?<!\r)\n/", "\r\n", $obj['msg']);
|
$msg = preg_replace("/(?<!\r)\n/", "\r\n", $alert_data['msg']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return \LibreNMS\Util\Mail::send($email, $obj['title'], $msg, $html, $this->config['attach-graph'] ?? null);
|
return \LibreNMS\Util\Mail::send($email, $alert_data['title'], $msg, $html, $this->config['attach-graph'] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -25,58 +25,40 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use App\View\SimpleTemplate;
|
use App\View\SimpleTemplate;
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Matrix extends Transport
|
class Matrix extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$server = $this->config['matrix-server'];
|
$server = $this->config['matrix-server'];
|
||||||
$room = $this->config['matrix-room'];
|
$room = $this->config['matrix-room'];
|
||||||
$authtoken = $this->config['matrix-authtoken'];
|
$authtoken = $this->config['matrix-authtoken'];
|
||||||
$message = $this->config['matrix-message'];
|
$message = $this->config['matrix-message'];
|
||||||
|
|
||||||
return $this->contactMatrix($obj, $server, $room, $authtoken, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function contactMatrix($obj, $server, $room, $authtoken, $message)
|
|
||||||
{
|
|
||||||
$request_opts = [];
|
|
||||||
$request_heads = [];
|
|
||||||
$txnid = rand(1111, 9999) . time();
|
$txnid = rand(1111, 9999) . time();
|
||||||
|
|
||||||
$server = preg_replace('/\/$/', '', $server);
|
$server = preg_replace('/\/$/', '', $server);
|
||||||
$host = $server . '/_matrix/client/r0/rooms/' . urlencode($room) . '/send/m.room.message/' . $txnid;
|
$host = $server . '/_matrix/client/r0/rooms/' . urlencode($room) . '/send/m.room.message/' . $txnid;
|
||||||
|
|
||||||
$request_heads['Authorization'] = "Bearer $authtoken";
|
$message = SimpleTemplate::parse($message, $alert_data);
|
||||||
$request_heads['Content-Type'] = 'application/json';
|
|
||||||
$request_heads['Accept'] = 'application/json';
|
|
||||||
|
|
||||||
$message = SimpleTemplate::parse($message, $obj);
|
$body = ['body' => strip_tags($message), 'formatted_body' => "$message", 'msgtype' => 'm.text', 'format' => 'org.matrix.custom.html'];
|
||||||
|
|
||||||
$body = ['body'=>strip_tags($message), 'formatted_body'=>"$message", 'msgtype'=>'m.text', 'format'=>'org.matrix.custom.html'];
|
$res = Http::client()
|
||||||
|
->withToken($authtoken)
|
||||||
|
->acceptJson()
|
||||||
|
->put($host, $body);
|
||||||
|
|
||||||
$client = new \GuzzleHttp\Client();
|
if ($res->successful()) {
|
||||||
$request_opts['proxy'] = Proxy::forGuzzle();
|
return true;
|
||||||
$request_opts['headers'] = $request_heads;
|
|
||||||
$request_opts['body'] = json_encode($body);
|
|
||||||
$res = $client->request('PUT', $host, $request_opts);
|
|
||||||
|
|
||||||
$code = $res->getStatusCode();
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump("Matrix '$host' returned Error");
|
|
||||||
var_dump('Params:');
|
|
||||||
var_dump('Response headers:');
|
|
||||||
var_dump($res->getHeaders());
|
|
||||||
var_dump('Return: ' . $res->getReasonPhrase());
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $message, $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,70 +24,42 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Mattermost extends Transport
|
class Mattermost extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$mattermost_opts = [
|
$host = $this->config['mattermost-url'];
|
||||||
'url' => $this->config['mattermost-url'],
|
$mattermost_msg = strip_tags($alert_data['msg']);
|
||||||
'username' => $this->config['mattermost-username'],
|
$color = self::getColorForState($alert_data['state']);
|
||||||
'icon' => $this->config['mattermost-icon'],
|
|
||||||
'channel' => $this->config['mattermost-channel'],
|
|
||||||
'author_name' => $this->config['mattermost-author_name'],
|
|
||||||
];
|
|
||||||
|
|
||||||
return $this->contactMattermost($obj, $mattermost_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactMattermost($obj, $api)
|
|
||||||
{
|
|
||||||
$host = $api['url'];
|
|
||||||
$curl = curl_init();
|
|
||||||
$mattermost_msg = strip_tags($obj['msg']);
|
|
||||||
$color = self::getColorForState($obj['state']);
|
|
||||||
$data = [
|
$data = [
|
||||||
'attachments' => [
|
'attachments' => [
|
||||||
0 => [
|
0 => [
|
||||||
'fallback' => $mattermost_msg,
|
'fallback' => $mattermost_msg,
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'title' => $obj['title'],
|
'title' => $alert_data['title'],
|
||||||
'text' => $obj['msg'],
|
'text' => $alert_data['msg'],
|
||||||
'mrkdwn_in' => ['text', 'fallback'],
|
'mrkdwn_in' => ['text', 'fallback'],
|
||||||
'author_name' => $api['author_name'],
|
'author_name' => $this->config['mattermost-author_name'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'channel' => $api['channel'],
|
'channel' => $this->config['mattermost-channel'],
|
||||||
'username' => $api['username'],
|
'username' => $this->config['mattermost-username'],
|
||||||
'icon_url' => $api['icon'],
|
'icon_url' => $this->config['mattermost-icon'],
|
||||||
];
|
];
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
$res = Http::client()->acceptJson()->post($host, $data);
|
||||||
|
|
||||||
$httpheaders = ['Accept: application/json', 'Content-Type: application/json'];
|
|
||||||
$alert_payload = json_encode($data);
|
|
||||||
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $httpheaders);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_payload);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
d_echo('Mattermost Connection Error: ' . $ret);
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
} else {
|
|
||||||
d_echo('Mattermost message sent for ' . $obj['hostname']);
|
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -13,51 +13,43 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Msteams extends Transport
|
class Msteams extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'Microsoft Teams';
|
protected string $name = 'Microsoft Teams';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
|
||||||
$opts['url'] = $this->config['msteam-url'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->contactMsteams($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactMsteams($obj, $opts)
|
|
||||||
{
|
|
||||||
$url = $opts['url'];
|
|
||||||
$data = [
|
$data = [
|
||||||
'title' => $obj['title'],
|
'title' => $alert_data['title'],
|
||||||
'themeColor' => self::getColorForState($obj['state']),
|
'themeColor' => self::getColorForState($alert_data['state']),
|
||||||
'text' => strip_tags($obj['msg'], '<strong><em><h1><h2><h3><strike><ul><ol><li><pre><blockquote><a><img><p>'),
|
'text' => strip_tags($alert_data['msg'], '<strong><em><h1><h2><h3><strike><ul><ol><li><pre><blockquote><a><img><p>'),
|
||||||
'summary' => $obj['title'],
|
'summary' => $alert_data['title'],
|
||||||
];
|
];
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type:application/json', 'Expect:']);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
|
|
||||||
if ($this->config['use-json'] === 'on' && $obj['uid'] !== '000') {
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $obj['msg']);
|
|
||||||
}
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump('Microsoft Teams returned Error, retry later');
|
|
||||||
|
|
||||||
return false;
|
$client = Http::client();
|
||||||
|
|
||||||
|
// template will contain raw json
|
||||||
|
if ($this->config['use-json'] === 'on') {
|
||||||
|
$msg = $alert_data['uid'] === '000'
|
||||||
|
? $this->messageCard() // use pre-made MessageCard for tests
|
||||||
|
: $alert_data['msg'];
|
||||||
|
|
||||||
|
$client->withBody($msg, 'application/json');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$res = $client->post($this->config['msteam-url'], $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['text'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -70,7 +62,7 @@ class Msteams extends Transport
|
|||||||
[
|
[
|
||||||
'title' => 'Use JSON?',
|
'title' => 'Use JSON?',
|
||||||
'name' => 'use-json',
|
'name' => 'use-json',
|
||||||
'descr' => 'Compose MessageCard with JSON rather than Markdown',
|
'descr' => 'Compose MessageCard with JSON rather than Markdown. Your template must be valid MessageCard JSON',
|
||||||
'type' => 'checkbox',
|
'type' => 'checkbox',
|
||||||
'default' => false,
|
'default' => false,
|
||||||
],
|
],
|
||||||
@ -80,4 +72,48 @@ class Msteams extends Transport
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function messageCard(): string
|
||||||
|
{
|
||||||
|
return '{
|
||||||
|
"@context": "https://schema.org/extensions",
|
||||||
|
"@type": "MessageCard",
|
||||||
|
"potentialAction": [
|
||||||
|
{
|
||||||
|
"@type": "OpenUri",
|
||||||
|
"name": "View MessageCard Reference",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"os": "default",
|
||||||
|
"uri": "https://learn.microsoft.com/en-us/outlook/actionable-messages/message-card-reference"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "OpenUri",
|
||||||
|
"name": "View LibreNMS Website",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"os": "default",
|
||||||
|
"uri": "https://www.librenms.org/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"facts": [
|
||||||
|
{
|
||||||
|
"name": "Next Action:",
|
||||||
|
"value": "Make your alert template emit valid MessageCard Json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "You have successfully sent a pre-formatted MessageCard message to teams."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": "Test Successful",
|
||||||
|
"themeColor": "0072C6",
|
||||||
|
"title": "Test MessageCard"
|
||||||
|
}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,11 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
|
||||||
class Nagios extends Transport
|
class Nagios extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
$opts = $this->config['nagios-fifo'];
|
|
||||||
|
|
||||||
return $this->contactNagios($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactNagios($obj, $opts)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
host_perfdata_file_template=
|
host_perfdata_file_template=
|
||||||
@ -51,20 +45,25 @@ class Nagios extends Transport
|
|||||||
|
|
||||||
$format = '';
|
$format = '';
|
||||||
$format .= "[HOSTPERFDATA]\t";
|
$format .= "[HOSTPERFDATA]\t";
|
||||||
$format .= strtotime($obj['timestamp']) . "\t";
|
$format .= strtotime($alert_data['timestamp']) . "\t";
|
||||||
$format .= $obj['hostname'] . "\t";
|
$format .= $alert_data['hostname'] . "\t";
|
||||||
$format .= md5($obj['rule']) . "\t"; //FIXME: Better entity
|
$format .= md5($alert_data['rule']) . "\t"; //FIXME: Better entity
|
||||||
$format .= ($obj['state'] ? $obj['severity'] : 'ok') . "\t";
|
$format .= ($alert_data['state'] ? $alert_data['severity'] : 'ok') . "\t";
|
||||||
$format .= "0\t";
|
$format .= "0\t";
|
||||||
$format .= "0\t";
|
$format .= "0\t";
|
||||||
$format .= str_replace("\n", '', nl2br($obj['msg'])) . "\t";
|
$format .= str_replace("\n", '', nl2br($alert_data['msg'])) . "\t";
|
||||||
$format .= 'NULL'; //FIXME: What's the HOSTPERFDATA equivalent for LibreNMS? Oo
|
$format .= 'NULL'; //FIXME: What's the HOSTPERFDATA equivalent for LibreNMS? Oo
|
||||||
$format .= "\n";
|
$format .= "\n";
|
||||||
|
|
||||||
return file_put_contents($opts, $format);
|
$fifo = $this->config['nagios-fifo'];
|
||||||
|
if (filetype($fifo) !== 'fifo') {
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, 0, 'File is not a fifo file! Refused to write to it.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_put_contents($fifo, $format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,45 +24,25 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Opsgenie extends Transport
|
class Opsgenie extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
$url = $this->config['genie-url'];
|
||||||
$opts['url'] = $this->config['genie-url'];
|
|
||||||
|
$res = Http::client()->post($url, $alert_data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->contactOpsgenie($obj, $opts);
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), '', $alert_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function contactOpsgenie($obj, $opts)
|
public static function configTemplate(): array
|
||||||
{
|
|
||||||
$url = $opts['url'];
|
|
||||||
|
|
||||||
$curl = curl_init();
|
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($obj));
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump('Error when sending post request to OpsGenie. Response code: ' . $code . ' Response body: ' . $ret); //FIXME: proper debugging
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function configTemplate()
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -14,29 +14,20 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Osticket extends Transport
|
class Osticket extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'osTicket';
|
protected string $name = 'osTicket';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
$url = $this->config['os-url'];
|
||||||
$opts['url'] = $this->config['os-url'];
|
$token = $this->config['os-token'];
|
||||||
$opts['token'] = $this->config['os-token'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->contactOsticket($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactOsticket($obj, $opts)
|
|
||||||
{
|
|
||||||
$url = $opts['url'];
|
|
||||||
$token = $opts['token'];
|
|
||||||
$email = '';
|
$email = '';
|
||||||
|
|
||||||
foreach (parse_email(Config::get('email_from')) as $from => $from_name) {
|
foreach (\LibreNMS\Util\Mail::parseEmails(Config::get('email_from')) as $from => $from_name) {
|
||||||
$email = $from_name . ' <' . $from . '>';
|
$email = $from_name . ' <' . $from . '>';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -44,34 +35,24 @@ class Osticket extends Transport
|
|||||||
$protocol = [
|
$protocol = [
|
||||||
'name' => 'LibreNMS',
|
'name' => 'LibreNMS',
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'subject' => ($obj['name'] ? $obj['name'] . ' on ' . $obj['hostname'] : $obj['title']),
|
'subject' => ($alert_data['name'] ? $alert_data['name'] . ' on ' . $alert_data['hostname'] : $alert_data['title']),
|
||||||
'message' => strip_tags($obj['msg']),
|
'message' => strip_tags($alert_data['msg']),
|
||||||
'ip' => $_SERVER['REMOTE_ADDR'],
|
'ip' => $_SERVER['REMOTE_ADDR'],
|
||||||
'attachments' => [],
|
'attachments' => [],
|
||||||
];
|
];
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-type' => 'application/json',
|
|
||||||
'Expect:',
|
|
||||||
'X-API-Key: ' . $token,
|
|
||||||
]);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($protocol));
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
if ($code != 201) {
|
$res = Http::client()->withHeaders([
|
||||||
var_dump('osTicket returned Error, retry later');
|
'X-API-Key' => $token,
|
||||||
|
])->post($url, $protocol);
|
||||||
|
|
||||||
return false;
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -23,80 +23,55 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Pagerduty extends Transport
|
class Pagerduty extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'PagerDuty';
|
protected string $name = 'PagerDuty';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if ($obj['state'] == AlertState::RECOVERED) {
|
$event_action = match ($alert_data['state']) {
|
||||||
$obj['event_type'] = 'resolve';
|
AlertState::RECOVERED => 'resolve',
|
||||||
} elseif ($obj['state'] == AlertState::ACKNOWLEDGED) {
|
AlertState::ACKNOWLEDGED => 'acknowledge',
|
||||||
$obj['event_type'] = 'acknowledge';
|
default => 'trigger'
|
||||||
} else {
|
};
|
||||||
$obj['event_type'] = 'trigger';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->contactPagerduty($obj, $this->config);
|
$safe_message = strip_tags($alert_data['msg']) ?: 'Test';
|
||||||
}
|
$message = array_filter(explode("\n", $safe_message), 'strlen');
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $obj
|
|
||||||
* @param array $config
|
|
||||||
* @return bool|string
|
|
||||||
*/
|
|
||||||
public function contactPagerduty($obj, $config)
|
|
||||||
{
|
|
||||||
$safe_message = strip_tags($obj['msg']) ?: 'Test';
|
|
||||||
$custom_details = ['message' => array_filter(explode("\n", $safe_message), 'strlen')];
|
|
||||||
$data = [
|
$data = [
|
||||||
'routing_key' => $config['service_key'],
|
'routing_key' => $this->config['service_key'],
|
||||||
'event_action' => $obj['event_type'],
|
'event_action' => $event_action,
|
||||||
'dedup_key' => (string) $obj['alert_id'],
|
'dedup_key' => (string) $alert_data['alert_id'],
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'custom_details' => $custom_details,
|
'custom_details' => ['message' => $message],
|
||||||
'group' => (string) \DeviceCache::get($obj['device_id'])->groups->pluck('name'),
|
'group' => (string) \DeviceCache::get($alert_data['device_id'])->groups->pluck('name'),
|
||||||
'source' => $obj['hostname'],
|
'source' => $alert_data['hostname'],
|
||||||
'severity' => $obj['severity'],
|
'severity' => $alert_data['severity'],
|
||||||
'summary' => ($obj['name'] ? $obj['name'] . ' on ' . $obj['hostname'] : $obj['title']),
|
'summary' => ($alert_data['name'] ? $alert_data['name'] . ' on ' . $alert_data['hostname'] : $alert_data['title']),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// EU service region
|
// EU service region
|
||||||
if ($config['region'] == 'EU') {
|
$url = match ($this->config['region']) {
|
||||||
$url = 'https://events.eu.pagerduty.com/v2/enqueue';
|
'EU' => 'https://events.eu.pagerduty.com/v2/enqueue',
|
||||||
} elseif ($config['region'] == 'US') {
|
'US' => 'https://events.pagerduty.com/v2/enqueue',
|
||||||
// US service region
|
default => $this->config['custom-url'],
|
||||||
$url = 'https://events.pagerduty.com/v2/enqueue';
|
};
|
||||||
} else {
|
|
||||||
$url = $config['custom-url'];
|
$res = Http::client()->post($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$client = new Client();
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), implode(PHP_EOL, $message), $data);
|
||||||
|
|
||||||
$request_opts = ['json' => $data];
|
|
||||||
$request_opts['proxy'] = Proxy::forGuzzle();
|
|
||||||
|
|
||||||
try {
|
|
||||||
$result = $client->request('POST', $url, $request_opts);
|
|
||||||
|
|
||||||
if ($result->getStatusCode() == 202) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result->getReasonPhrase();
|
|
||||||
} catch (GuzzleException $e) {
|
|
||||||
return 'Request to PagerDuty API failed. ' . $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,48 +24,40 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Playsms extends Transport
|
class Playsms extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'playSMS';
|
protected string $name = 'playSMS';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$playsms_opts['url'] = $this->config['playsms-url'];
|
$to = preg_split('/([,\r\n]+)/', $this->config['playsms-mobiles']);
|
||||||
$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->contactPlaysms($obj, $playsms_opts);
|
$url = str_replace('?app=ws', '', $this->config['playsms-url']); // remove old format
|
||||||
}
|
$data = [
|
||||||
|
'app' => 'ws',
|
||||||
public static function contactPlaysms($obj, $opts)
|
'op' => 'pv',
|
||||||
{
|
'u' => $this->config['playsms-user'],
|
||||||
$data = ['u' => $opts['user'], 'h' => $opts['token'], 'to' => implode(',', $opts['to']), 'msg' => $obj['title']];
|
'h' => $this->config['playsms-token'],
|
||||||
if (! empty($opts['from'])) {
|
'to' => implode(',', $to),
|
||||||
$data['from'] = $opts['from'];
|
'msg' => $alert_data['title'],
|
||||||
}
|
];
|
||||||
$url = $opts['url'] . '&op=pv&' . http_build_query($data);
|
if (! empty($this->config['playsms-from'])) {
|
||||||
$curl = curl_init($url);
|
$data['from'] = $this->config['playsms-from'];
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code > 202) {
|
|
||||||
var_dump($ret);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$res = Http::client()->get($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['msg'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,49 +24,29 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Pushbullet extends Transport
|
class Pushbullet extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
if (! empty($this->config)) {
|
|
||||||
$opts = $this->config['pushbullet-token'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->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 ?
|
// Note: At this point it might be useful to iterate through $obj['contacts'] and send each of them a note ?
|
||||||
|
$url = 'https://api.pushbullet.com/v2/pushes';
|
||||||
|
$data = ['type' => 'note', 'title' => $alert_data['title'], 'body' => $alert_data['msg']];
|
||||||
|
|
||||||
$data = ['type' => 'note', 'title' => $obj['title'], 'body' => $obj['msg']];
|
$res = Http::client()
|
||||||
$data = json_encode($data);
|
->withToken($this->config['pushbullet-token'])
|
||||||
|
->post($url, $data);
|
||||||
|
|
||||||
$curl = curl_init('https://api.pushbullet.com/v2/pushes');
|
if ($res->successful()) {
|
||||||
Proxy::applyToCurl($curl);
|
return true;
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Content-Length: ' . strlen($data),
|
|
||||||
'Authorization: Bearer ' . $opts,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code > 201) {
|
|
||||||
var_dump($ret);
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -39,68 +39,57 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Pushover extends Transport
|
class Pushover extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$pushover_opts = $this->config;
|
$options = $this->parseUserOptions($this->config['options']);
|
||||||
$pushover_opts['options'] = $this->parseUserOptions($this->config['options']);
|
|
||||||
|
|
||||||
return $this->contactPushover($obj, $pushover_opts);
|
$url = 'https://api.pushover.net/1/messages.json';
|
||||||
}
|
|
||||||
|
|
||||||
public function contactPushover($obj, $api)
|
|
||||||
{
|
|
||||||
$data = [];
|
$data = [];
|
||||||
$data['token'] = $api['appkey'];
|
$data['token'] = $this->config['appkey'];
|
||||||
$data['user'] = $api['userkey'];
|
$data['user'] = $this->config['userkey'];
|
||||||
switch ($obj['severity']) {
|
switch ($alert_data['severity']) {
|
||||||
case 'critical':
|
case 'critical':
|
||||||
$data['priority'] = 1;
|
$data['priority'] = 1;
|
||||||
if (! empty($api['options']['sound_critical'])) {
|
if (! empty($options['sound_critical'])) {
|
||||||
$data['sound'] = $api['options']['sound_critical'];
|
$data['sound'] = $options['sound_critical'];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
$data['priority'] = 1;
|
$data['priority'] = 1;
|
||||||
if (! empty($api['options']['sound_warning'])) {
|
if (! empty($options['sound_warning'])) {
|
||||||
$data['sound'] = $api['options']['sound_warning'];
|
$data['sound'] = $options['sound_warning'];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch ($obj['state']) {
|
switch ($alert_data['state']) {
|
||||||
case AlertState::RECOVERED:
|
case AlertState::RECOVERED:
|
||||||
$data['priority'] = 0;
|
$data['priority'] = 0;
|
||||||
if (! empty($api['options']['sound_ok'])) {
|
if (! empty($options['sound_ok'])) {
|
||||||
$data['sound'] = $api['options']['sound_ok'];
|
$data['sound'] = $options['sound_ok'];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$data['title'] = $obj['title'];
|
$data['title'] = $alert_data['title'];
|
||||||
$data['message'] = $obj['msg'];
|
$data['message'] = $alert_data['msg'];
|
||||||
if ($api['options']) {
|
if ($options) {
|
||||||
$data = array_merge($data, $api['options']);
|
$data = array_merge($data, $options);
|
||||||
}
|
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($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);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump('Pushover returned error'); //FIXME: proper debugging
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
$res = Http::client()->asForm()->post($url, $data);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $data['message'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,62 +24,43 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Rocket extends Transport
|
class Rocket extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'Rocket Chat';
|
protected string $name = 'Rocket Chat';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$rocket_opts = $this->parseUserOptions($this->config['rocket-options']);
|
$rocket_opts = $this->parseUserOptions($this->config['rocket-options']);
|
||||||
$rocket_opts['url'] = $this->config['rocket-url'];
|
|
||||||
|
|
||||||
return $this->contactRocket($obj, $rocket_opts);
|
$rocket_msg = strip_tags($alert_data['msg']);
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactRocket($obj, $api)
|
|
||||||
{
|
|
||||||
$host = $api['url'];
|
|
||||||
$curl = curl_init();
|
|
||||||
$rocket_msg = strip_tags($obj['msg']);
|
|
||||||
$color = self::getColorForState($obj['state']);
|
|
||||||
$data = [
|
$data = [
|
||||||
'attachments' => [
|
'attachments' => [
|
||||||
0 => [
|
0 => [
|
||||||
'fallback' => $rocket_msg,
|
'fallback' => $rocket_msg,
|
||||||
'color' => $color,
|
'color' => self::getColorForState($alert_data['state']),
|
||||||
'title' => $obj['title'],
|
'title' => $alert_data['title'],
|
||||||
'text' => $rocket_msg,
|
'text' => $rocket_msg,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'channel' => $api['channel'] ?? null,
|
'channel' => $rocket_opts['channel'] ?? null,
|
||||||
'username' => $api['username'] ?? null,
|
'username' => $rocket_opts['username'] ?? null,
|
||||||
'icon_url' => $api['icon_url'] ?? null,
|
'icon_url' => $rocket_opts['icon_url'] ?? null,
|
||||||
'icon_emoji' => $api['icon_emoji'] ?? null,
|
'icon_emoji' => $rocket_opts['icon_emoji'] ?? null,
|
||||||
];
|
];
|
||||||
$alert_message = json_encode($data);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
$res = Http::client()->post($this->config['rocket-url'], $data);
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump("API '$host' returned Error"); //FIXME: propper debuging
|
|
||||||
var_dump('Params: ' . $alert_message); //FIXME: propper debuging
|
|
||||||
var_dump('Return: ' . $ret); //FIXME: propper debuging
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $rocket_msg, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -22,13 +22,12 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Sensu extends Transport
|
class Sensu extends Transport
|
||||||
{
|
{
|
||||||
@ -38,152 +37,120 @@ class Sensu extends Transport
|
|||||||
public const CRITICAL = 2;
|
public const CRITICAL = 2;
|
||||||
public const UNKNOWN = 3;
|
public const UNKNOWN = 3;
|
||||||
|
|
||||||
private static $status = [
|
private static array $status = [
|
||||||
'ok' => Sensu::OK,
|
'ok' => self::OK,
|
||||||
'warning' => Sensu::WARNING,
|
'warning' => self::WARNING,
|
||||||
'critical' => Sensu::CRITICAL,
|
'critical' => self::CRITICAL,
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $severity = [
|
public function deliverAlert(array $alert_data): bool
|
||||||
'recovered' => AlertState::RECOVERED,
|
|
||||||
'alert' => AlertState::ACTIVE,
|
|
||||||
'acknowledged' => AlertState::ACKNOWLEDGED,
|
|
||||||
'worse' => AlertState::WORSE,
|
|
||||||
'better' => AlertState::BETTER,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Client
|
|
||||||
*/
|
|
||||||
private static $client = null;
|
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
|
||||||
{
|
{
|
||||||
$sensu_opts = [];
|
|
||||||
$sensu_opts['url'] = $this->config['sensu-url'] ? $this->config['sensu-url'] : 'http://127.0.0.1:3031';
|
|
||||||
$sensu_opts['namespace'] = $this->config['sensu-namespace'] ? $this->config['sensu-namespace'] : 'default';
|
|
||||||
$sensu_opts['prefix'] = $this->config['sensu-prefix'];
|
|
||||||
$sensu_opts['source-key'] = $this->config['sensu-source-key'];
|
$sensu_opts['source-key'] = $this->config['sensu-source-key'];
|
||||||
|
|
||||||
Sensu::$client = new Client();
|
$url = $this->config['sensu-url'] ?: 'http://127.0.0.1:3031';
|
||||||
|
$client = Http::client();
|
||||||
|
|
||||||
try {
|
|
||||||
return $this->contactSensu($obj, $sensu_opts);
|
|
||||||
} catch (GuzzleException $e) {
|
|
||||||
return 'Sending event to Sensu failed: ' . $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactSensu($obj, $opts)
|
|
||||||
{
|
|
||||||
// The Sensu agent should be running on the poller - events can be sent directly to the backend but this has not been tested, and likely needs mTLS.
|
// The Sensu agent should be running on the poller - events can be sent directly to the backend but this has not been tested, and likely needs mTLS.
|
||||||
// The agent API is documented at https://docs.sensu.io/sensu-go/latest/reference/agent/#create-monitoring-events-using-the-agent-api
|
// The agent API is documented at https://docs.sensu.io/sensu-go/latest/reference/agent/#create-monitoring-events-using-the-agent-api
|
||||||
$request_options = ['proxy' => Proxy::forGuzzle()];
|
|
||||||
if (Sensu::$client->request('GET', $opts['url'] . '/healthz', $request_options)->getStatusCode() !== 200) {
|
$health_check = $client->get($url . '/healthz')->status();
|
||||||
return 'Sensu API is not responding';
|
if ($health_check !== 200) {
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $health_check, 'Sensu API is not responding');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($obj['state'] !== AlertState::RECOVERED && $obj['state'] !== AlertState::ACKNOWLEDGED && $obj['alerted'] === 0) {
|
if ($alert_data['state'] !== AlertState::RECOVERED && $alert_data['state'] !== AlertState::ACKNOWLEDGED && $alert_data['alerted'] === 0) {
|
||||||
// If this is the first event, send a forced "ok" dated (rrd.step / 2) seconds ago to tell Sensu the last time the check was healthy
|
// If this is the first event, send a forced "ok" dated (rrd.step / 2) seconds ago to tell Sensu the last time the check was healthy
|
||||||
$data = Sensu::generateData($obj, $opts, Sensu::OK, round(Config::get('rrd.step', 300) / 2));
|
$data = $this->generateData($alert_data, self::OK, (int) round(Config::get('rrd.step', 300) / 2));
|
||||||
Log::debug('Sensu transport sent last good event to socket: ', $data);
|
Log::debug('Sensu transport sent last good event to socket: ', $data);
|
||||||
|
|
||||||
$request_options['json'] = $data;
|
$result = $client->post($url . '/events', $data);
|
||||||
$result = Sensu::$client->request('POST', $opts['url'] . '/events', $request_options);
|
if ($result->status() !== 202) {
|
||||||
if ($result->getStatusCode() !== 202) {
|
throw new AlertTransportDeliveryException($alert_data, $result->status(), $result->body(), json_encode($data), $this->config);
|
||||||
return $result->getReasonPhrase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(5);
|
sleep(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = Sensu::generateData($obj, $opts, Sensu::calculateStatus($obj['state'], $obj['severity']));
|
$data = $this->generateData($alert_data, $this->calculateStatus($alert_data['state'], $alert_data['severity']));
|
||||||
Log::debug('Sensu transport sent event to socket: ', $data);
|
|
||||||
|
|
||||||
$request_options['json'] = $data;
|
$result = $client->post($url . '/events', $data);
|
||||||
$result = Sensu::$client->request('POST', $opts['url'] . '/events', $request_options);
|
|
||||||
if ($result->getStatusCode() === 202) {
|
if ($result->successful()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result->getReasonPhrase();
|
throw new AlertTransportDeliveryException($alert_data, $result->status(), $result->body(), json_encode($data), $sensu_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateData($obj, $opts, $status, $offset = 0)
|
private function generateData(array $alert_data, int $status, int $offset = 0): array
|
||||||
{
|
{
|
||||||
|
$namespace = $this->config['sensu-namespace'] ?: 'default';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'check' => [
|
'check' => [
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'name' => Sensu::checkName($opts['prefix'], $obj['name']),
|
'name' => $this->checkName($this->config['sensu-prefix'], $alert_data['name']),
|
||||||
'namespace' => $opts['namespace'],
|
'namespace' => $namespace,
|
||||||
'annotations' => Sensu::generateAnnotations($obj),
|
'annotations' => $this->generateAnnotations($alert_data),
|
||||||
],
|
],
|
||||||
'command' => sprintf('LibreNMS: %s', $obj['builder']),
|
'command' => sprintf('LibreNMS: %s', $alert_data['builder']),
|
||||||
'executed' => time() - $offset,
|
'executed' => time() - $offset,
|
||||||
'interval' => Config::get('rrd.step', 300),
|
'interval' => Config::get('rrd.step', 300),
|
||||||
'issued' => time() - $offset,
|
'issued' => time() - $offset,
|
||||||
'output' => $obj['msg'],
|
'output' => $alert_data['msg'],
|
||||||
'status' => $status,
|
'status' => $status,
|
||||||
],
|
],
|
||||||
'entity' => [
|
'entity' => [
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'name' => Sensu::getEntityName($obj, $opts['source-key']),
|
'name' => $this->getEntityName($alert_data),
|
||||||
'namespace' => $opts['namespace'],
|
'namespace' => $namespace,
|
||||||
],
|
],
|
||||||
'system' => [
|
'system' => [
|
||||||
'hostname' => $obj['hostname'],
|
'hostname' => $alert_data['hostname'],
|
||||||
'os' => $obj['os'],
|
'os' => $alert_data['os'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateAnnotations($obj)
|
private function generateAnnotations(array $alert_data): array
|
||||||
{
|
{
|
||||||
return array_filter([
|
return array_filter([
|
||||||
'generated-by' => 'LibreNMS',
|
'generated-by' => 'LibreNMS',
|
||||||
'acknowledged' => $obj['state'] === AlertState::ACKNOWLEDGED ? 'true' : 'false',
|
'acknowledged' => $alert_data['state'] === AlertState::ACKNOWLEDGED ? 'true' : 'false',
|
||||||
'contact' => $obj['sysContact'],
|
'contact' => $alert_data['sysContact'],
|
||||||
'description' => $obj['sysDescr'],
|
'description' => $alert_data['sysDescr'],
|
||||||
'location' => $obj['location'],
|
'location' => $alert_data['location'],
|
||||||
'documentation' => $obj['proc'],
|
'documentation' => $alert_data['proc'],
|
||||||
'librenms-notes' => $obj['notes'],
|
'librenms-notes' => $alert_data['notes'],
|
||||||
'librenms-device-id' => strval($obj['device_id']),
|
'librenms-device-id' => strval($alert_data['device_id']),
|
||||||
'librenms-rule-id' => strval($obj['rule_id']),
|
'librenms-rule-id' => strval($alert_data['rule_id']),
|
||||||
'librenms-status-reason' => $obj['status_reason'],
|
'librenms-status-reason' => $alert_data['status_reason'],
|
||||||
], function (?string $s): bool {
|
], function (?string $s): bool {
|
||||||
return (bool) strlen($s); // strlen returns 0 for null, false or '', but 1 for integer 0 - unlike empty()
|
return (bool) strlen($s); // strlen returns 0 for null, false or '', but 1 for integer 0 - unlike empty()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function calculateStatus($state, $severity)
|
private function calculateStatus(int $state, string $severity): int
|
||||||
{
|
{
|
||||||
// Sensu only has a single short (status) to indicate both severity and status, so we need to map LibreNMS' state and severity onto it
|
// Sensu only has a single short (status) to indicate both severity and status, so we need to map LibreNMS' state and severity onto it
|
||||||
|
|
||||||
if ($state === AlertState::RECOVERED) {
|
if ($state === AlertState::RECOVERED) {
|
||||||
// LibreNMS alert is resolved, send ok
|
// LibreNMS alert is resolved, send ok
|
||||||
return Sensu::OK;
|
return self::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists($severity, Sensu::$status)) {
|
return self::$status[$severity] ?? self::UNKNOWN;
|
||||||
// Severity is known, map the LibreNMS severity to the Sensu status
|
|
||||||
return Sensu::$status[$severity];
|
|
||||||
}
|
|
||||||
|
|
||||||
// LibreNMS severity does not map to Sensu, send unknown
|
|
||||||
return Sensu::UNKNOWN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getEntityName($obj, $key)
|
private function getEntityName(array $obj): string
|
||||||
{
|
{
|
||||||
if ($key === 'shortname') {
|
$key = $this->config['sensu-source-key'] ?: 'display';
|
||||||
return Sensu::shortenName($obj['display']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $obj[$key];
|
return $key === 'shortname' ? $this->shortenName($obj['display']) : $obj[$key];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function shortenName($name)
|
private function shortenName(string $name): string
|
||||||
{
|
{
|
||||||
// Shrink the last domain components - e.g. librenms.corp.example.net becomes librenms.cen
|
// Shrink the last domain components - e.g. librenms.corp.example.net becomes librenms.cen
|
||||||
$components = explode('.', $name);
|
$components = explode('.', $name);
|
||||||
@ -204,7 +171,7 @@ class Sensu extends Transport
|
|||||||
return sprintf('%s.%s', implode('.', $components), $result);
|
return sprintf('%s.%s', implode('.', $components), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function checkName($prefix, $name)
|
private function checkName(string $prefix, string $name): string
|
||||||
{
|
{
|
||||||
$check = strtolower(str_replace(' ', '-', $name));
|
$check = strtolower(str_replace(' ', '-', $name));
|
||||||
|
|
||||||
@ -215,7 +182,7 @@ class Sensu extends Transport
|
|||||||
return $check;
|
return $check;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -26,29 +26,18 @@ use LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
class Signal extends Transport
|
class Signal extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$signalOpts = [
|
exec(escapeshellarg($this->config['path'])
|
||||||
'path' => escapeshellarg($this->config['path']),
|
|
||||||
'recipient-type' => ($this->config['recipient-type'] == 'group') ? ' -g ' : ' ',
|
|
||||||
'recipient' => escapeshellarg($this->config['recipient']),
|
|
||||||
];
|
|
||||||
|
|
||||||
return $this->contactSignal($obj, $signalOpts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactSignal($obj, $opts)
|
|
||||||
{
|
|
||||||
exec($opts['path']
|
|
||||||
. ' --dbus-system send'
|
. ' --dbus-system send'
|
||||||
. $opts['recipient-type']
|
. (($this->config['recipient-type'] == 'group') ? ' -g ' : ' ')
|
||||||
. $opts['recipient']
|
. escapeshellarg($this->config['recipient'])
|
||||||
. ' -m ' . escapeshellarg($obj['title']));
|
. ' -m ' . escapeshellarg($alert_data['title']));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'validation' => [],
|
'validation' => [],
|
||||||
|
@ -18,62 +18,35 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Signalwire extends Transport
|
class Signalwire extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'SignalWire';
|
protected string $name = 'SignalWire';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$signalwire_opts['spaceUrl'] = $this->config['signalwire-spaceUrl'];
|
$url = 'https://' . $this->config['signalwire-spaceUrl'] . '.signalwire.com/api/laml/2010-04-01/Accounts/' . $this->config['signalwire-project-id'] . '/Messages.json';
|
||||||
$signalwire_opts['sid'] = $this->config['signalwire-project-id'];
|
|
||||||
$signalwire_opts['token'] = $this->config['signalwire-token'];
|
|
||||||
$signalwire_opts['sender'] = $this->config['signalwire-sender'];
|
|
||||||
$signalwire_opts['to'] = $this->config['signalwire-to'];
|
|
||||||
|
|
||||||
return $this->contactSignalwire($obj, $signalwire_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactSignalwire($obj, $opts)
|
|
||||||
{
|
|
||||||
$params = [
|
|
||||||
'spaceUrl' => $opts['spaceUrl'],
|
|
||||||
'sid' => $opts['sid'],
|
|
||||||
'token' => $opts['token'],
|
|
||||||
'phone' => $opts['to'],
|
|
||||||
'text' => $obj['title'],
|
|
||||||
'sender' => $opts['sender'],
|
|
||||||
];
|
|
||||||
|
|
||||||
$url = 'https://' . $params['spaceUrl'] . '.signalwire.com/api/laml/2010-04-01/Accounts/' . $params['sid'] . '/Messages.json';
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'From' => $params['sender'],
|
'From' => $this->config['signalwire-sender'],
|
||||||
'Body' => $params['text'],
|
'To' => $this->config['signalwire-to'],
|
||||||
'To' => $params['phone'],
|
'Body' => $alert_data['title'],
|
||||||
];
|
];
|
||||||
$post = http_build_query($data);
|
|
||||||
|
|
||||||
$curl = curl_init($url);
|
$res = Http::client()->asForm()
|
||||||
|
->withBasicAuth($this->config['signalwire-project-id'], $this->config['signalwire-token'])
|
||||||
|
->post($url, $data);
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
if ($res->successful()) {
|
||||||
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
||||||
curl_setopt($curl, CURLOPT_USERPWD, $params['sid'] . ':' . $params['token']);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
|
|
||||||
|
|
||||||
curl_exec($curl);
|
|
||||||
|
|
||||||
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['title'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -23,62 +23,44 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Slack extends Transport
|
class Slack extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$slack_opts = $this->parseUserOptions($this->config['slack-options']);
|
$slack_opts = $this->parseUserOptions($this->config['slack-options'] ?? '');
|
||||||
$slack_opts['url'] = $this->config['slack-url'];
|
$icon = $this->config['slack-icon_emoji'] ?? $slack_opts['icon_emoji'] ?? null;
|
||||||
|
$slack_msg = html_entity_decode(strip_tags($alert_data['msg'] ?? ''), ENT_QUOTES);
|
||||||
|
|
||||||
return $this->contactSlack($obj, $slack_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactSlack($obj, $api)
|
|
||||||
{
|
|
||||||
$host = $api['url'];
|
|
||||||
$curl = curl_init();
|
|
||||||
$slack_msg = html_entity_decode(strip_tags($obj['msg'] ?? ''), ENT_QUOTES);
|
|
||||||
$color = self::getColorForState($obj['state']);
|
|
||||||
$data = [
|
$data = [
|
||||||
'attachments' => [
|
'attachments' => [
|
||||||
0 => [
|
0 => [
|
||||||
'fallback' => $slack_msg,
|
'fallback' => $slack_msg,
|
||||||
'color' => $color,
|
'color' => self::getColorForState($alert_data['state']),
|
||||||
'title' => $obj['title'] ?? null,
|
'title' => $alert_data['title'] ?? null,
|
||||||
'text' => $slack_msg,
|
'text' => $slack_msg,
|
||||||
'mrkdwn_in' => ['text', 'fallback'],
|
'mrkdwn_in' => ['text', 'fallback'],
|
||||||
'author_name' => $api['author_name'] ?? null,
|
'author_name' => $this->config['slack-author'] ?? $slack_opts['author'] ?? null,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'channel' => $api['channel'] ?? null,
|
'channel' => $this->config['slack-channel'] ?? $slack_opts['channel'] ?? null,
|
||||||
'username' => $api['username'] ?? null,
|
'icon_emoji' => $icon ? Str::finish(Str::start($icon, ':'), ':') : null,
|
||||||
'icon_emoji' => isset($api['icon_emoji']) ? ':' . $api['icon_emoji'] . ':' : null,
|
|
||||||
];
|
];
|
||||||
$alert_message = json_encode($data);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $host);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
$res = Http::client()->post($this->config['slack-url'] ?? '', $data);
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump("API '$host' returned Error"); //FIXME: propper debuging
|
|
||||||
var_dump('Params: ' . $alert_message); //FIXME: propper debuging
|
|
||||||
var_dump('Return: ' . $ret); //FIXME: propper debuging
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code;
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $slack_msg, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -89,14 +71,30 @@ class Slack extends Transport
|
|||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => 'Slack Options',
|
'title' => 'Channel',
|
||||||
'name' => 'slack-options',
|
'name' => 'slack-channel',
|
||||||
'descr' => 'Slack Options',
|
'descr' => 'Channel to post to',
|
||||||
'type' => 'textarea',
|
'type' => 'text',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'title' => 'Author Name',
|
||||||
|
'name' => 'slack-author',
|
||||||
|
'descr' => 'Name of author',
|
||||||
|
'type' => 'text',
|
||||||
|
'default' => 'LibreNMS',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'title' => 'Icon',
|
||||||
|
'name' => 'slack-author',
|
||||||
|
'descr' => 'Name of emoji for icon',
|
||||||
|
'type' => 'text',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'slack-url' => 'required|url',
|
'slack-url' => 'required|url',
|
||||||
|
'slack-channel' => 'string',
|
||||||
|
'slack-username' => 'string',
|
||||||
|
'slack-icon_emoji' => 'string',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -23,49 +23,44 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Smseagle extends Transport
|
class Smseagle extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'SMSEagle';
|
protected string $name = 'SMSEagle';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$smseagle_opts['url'] = $this->config['smseagle-url'];
|
$url = $this->config['smseagle-url'] . '/http_api/send_sms';
|
||||||
$smseagle_opts['user'] = $this->config['smseagle-user'];
|
if (! str_starts_with($url, 'http')) {
|
||||||
$smseagle_opts['token'] = $this->config['smseagle-pass'];
|
$url = 'http://' . $url;
|
||||||
$smseagle_opts['to'] = preg_split('/([,\r\n]+)/', $this->config['smseagle-mobiles']);
|
|
||||||
|
|
||||||
return $this->contactSmseagle($obj, $smseagle_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactSmseagle($obj, $opts)
|
|
||||||
{
|
|
||||||
$params = [
|
|
||||||
'login' => $opts['user'],
|
|
||||||
'pass' => $opts['token'],
|
|
||||||
'to' => implode(',', $opts['to']),
|
|
||||||
'message' => $obj['title'],
|
|
||||||
];
|
|
||||||
$url = Str::startsWith($opts['url'], 'http') ? '' : 'http://';
|
|
||||||
$url .= $opts['url'] . '/index.php/http_api/send_sms?' . http_build_query($params);
|
|
||||||
$curl = curl_init($url);
|
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
if (substr($ret, 0, 2) == 'OK') {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
// use token if available
|
||||||
|
if (empty($this->config['smseagle-token'])) {
|
||||||
|
$params['login'] = $this->config['smseagle-user'];
|
||||||
|
$params['pass'] = $this->config['smseagle-pass'];
|
||||||
|
} else {
|
||||||
|
$params['access_token'] = $this->config['smseagle-token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$params['to'] = implode(',', preg_split('/([,\r\n]+)/', $this->config['smseagle-mobiles']));
|
||||||
|
$params['message'] = $alert_data['title'];
|
||||||
|
|
||||||
|
$res = Http::client()->get($url, $params);
|
||||||
|
|
||||||
|
if ($res->successful() && str_starts_with($res->body(), 'OK')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $params['message'], $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -75,6 +70,12 @@ class Smseagle extends Transport
|
|||||||
'descr' => 'SMSEagle Host',
|
'descr' => 'SMSEagle Host',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'title' => 'Access Token',
|
||||||
|
'name' => 'smseagle-token',
|
||||||
|
'descr' => 'SMSEagle Access Token',
|
||||||
|
'type' => 'text',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'title' => 'User',
|
'title' => 'User',
|
||||||
'name' => 'smseagle-user',
|
'name' => 'smseagle-user',
|
||||||
@ -96,8 +97,9 @@ class Smseagle extends Transport
|
|||||||
],
|
],
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'smseagle-url' => 'required|url',
|
'smseagle-url' => 'required|url',
|
||||||
'smseagle-user' => 'required|string',
|
'smseagle-token' => 'required_without:smseagle-user,smseagle-pass|string',
|
||||||
'smseagle-pass' => 'required|string',
|
'smseagle-user' => 'required_without:smseagle-token|string',
|
||||||
|
'smseagle-pass' => 'required_without:smseagle-token|string',
|
||||||
'smseagle-mobiles' => 'required',
|
'smseagle-mobiles' => 'required',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -24,45 +24,34 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Smsfeedback extends Transport
|
class Smsfeedback extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'SMSfeedback';
|
protected string $name = 'SMSfeedback';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
$smsfeedback_opts['user'] = $this->config['smsfeedback-user'];
|
|
||||||
$smsfeedback_opts['token'] = $this->config['smsfeedback-pass'];
|
|
||||||
$smsfeedback_opts['sender'] = $this->config['smsfeedback-sender'];
|
|
||||||
$smsfeedback_opts['to'] = $this->config['smsfeedback-mobiles'];
|
|
||||||
|
|
||||||
return $this->contactsmsfeedback($obj, $smsfeedback_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactsmsfeedback($obj, $opts)
|
|
||||||
{
|
{
|
||||||
|
$url = 'http://api.smsfeedback.ru/messages/v2/send/';
|
||||||
$params = [
|
$params = [
|
||||||
'login' => $opts['user'],
|
'phone' => $this->config['smsfeedback-mobiles'],
|
||||||
'pass' => md5($opts['token']),
|
'text' => $alert_data['title'],
|
||||||
'phone' => $opts['to'],
|
'sender' => $this->config['smsfeedback-sender'],
|
||||||
'text' => $obj['title'],
|
|
||||||
'sender' => $opts['sender'],
|
|
||||||
];
|
];
|
||||||
$url = 'http://' . $opts['user'] . ':' . $opts['token'] . '@' . 'api.smsfeedback.ru/messages/v2/send/?' . http_build_query($params);
|
|
||||||
$curl = curl_init($url);
|
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
$res = Http::client()
|
||||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
|
->withBasicAuth($this->config['smsfeedback-user'], $this->config['smsfeedback-pass'])
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
->get($url, $params);
|
||||||
|
|
||||||
$ret = curl_exec($curl);
|
if ($res->successful() && str_starts_with($res->body(), 'accepted')) {
|
||||||
if (substr($ret, 0, 8) == 'accepted') {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['title'], $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -95,7 +84,7 @@ class Smsfeedback extends Transport
|
|||||||
'smsfeedback-user' => 'required|string',
|
'smsfeedback-user' => 'required|string',
|
||||||
'smsfeedback-pass' => 'required|string',
|
'smsfeedback-pass' => 'required|string',
|
||||||
'smsfeedback-mobiles' => 'required',
|
'smsfeedback-mobiles' => 'required',
|
||||||
'smsfeedback-sender' => 'required|string',
|
'smsfeedback-sender' => 'required|string',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -19,51 +19,18 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
|
||||||
class Splunk extends Transport
|
class Splunk extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
$splunk_host = empty($this->config['Splunk-host']) ? '127.0.0.1' : $this->config['Splunk-host'];
|
||||||
$opts['splunk_host'] = $this->config['Splunk-host'];
|
$splunk_port = empty($this->config['Splunk-port']) ? 514 : $this->config['Splunk-port'];
|
||||||
$opts['splunk_port'] = $this->config['Splunk-port'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->contactSplunk($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactSplunk($obj, $opts)
|
|
||||||
{
|
|
||||||
$splunk_host = '127.0.0.1';
|
|
||||||
$splunk_port = 514;
|
|
||||||
$severity = 6; // Default severity is 6 (Informational)
|
$severity = 6; // Default severity is 6 (Informational)
|
||||||
$device = device_by_id_cache($obj['device_id']); // for event logging
|
$device = device_by_id_cache($alert_data['device_id']); // for event logging
|
||||||
|
|
||||||
if (! empty($opts['splunk_host'])) {
|
switch ($alert_data['severity']) {
|
||||||
if (preg_match('/[a-zA-Z]/', $opts['splunk_host'])) {
|
|
||||||
$splunk_host = gethostbyname($opts['splunk_host']);
|
|
||||||
if ($splunk_host === $opts['splunk_host']) {
|
|
||||||
log_event('Alphanumeric hostname found but does not resolve to an IP.', $device, 'poller', 5);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} elseif (filter_var($opts['splunk_host'], FILTER_VALIDATE_IP)) {
|
|
||||||
$splunk_host = $opts['splunk_host'];
|
|
||||||
} else {
|
|
||||||
log_event('Splunk host is not a valid IP: ' . $opts['splunk_host'], $device, 'poller', 5);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_event('Splunk host is empty.', $device, 'poller');
|
|
||||||
}
|
|
||||||
if (! empty($opts['splunk_port']) && preg_match("/^\d+$/", $opts['splunk_port'])) {
|
|
||||||
$splunk_port = $opts['splunk_port'];
|
|
||||||
} else {
|
|
||||||
log_event('Splunk port is not an integer.', $device, 'poller', 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($obj['severity']) {
|
|
||||||
case 'critical':
|
case 'critical':
|
||||||
$severity = 2;
|
$severity = 2;
|
||||||
break;
|
break;
|
||||||
@ -72,7 +39,7 @@ class Splunk extends Transport
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($obj['state']) {
|
switch ($alert_data['state']) {
|
||||||
case AlertState::RECOVERED:
|
case AlertState::RECOVERED:
|
||||||
$severity = 6;
|
$severity = 6;
|
||||||
break;
|
break;
|
||||||
@ -83,7 +50,7 @@ class Splunk extends Transport
|
|||||||
|
|
||||||
$ignore = ['template', 'contacts', 'rule', 'string', 'debug', 'faults', 'builder', 'transport', 'alert', 'msg', 'transport_name'];
|
$ignore = ['template', 'contacts', 'rule', 'string', 'debug', 'faults', 'builder', 'transport', 'alert', 'msg', 'transport_name'];
|
||||||
$splunk_prefix = '<' . $severity . '> ';
|
$splunk_prefix = '<' . $severity . '> ';
|
||||||
foreach ($obj as $key => $val) {
|
foreach ($alert_data as $key => $val) {
|
||||||
if (in_array($key, $ignore)) {
|
if (in_array($key, $ignore)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -102,26 +69,24 @@ class Splunk extends Transport
|
|||||||
$splunk_prefix = substr($splunk_prefix, 0, -1);
|
$splunk_prefix = substr($splunk_prefix, 0, -1);
|
||||||
|
|
||||||
if (($socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) === false) {
|
if (($socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) === false) {
|
||||||
log_event('socket_create() failed: reason: ' . socket_strerror(socket_last_error()), $device, 'poller', 5);
|
throw new AlertTransportDeliveryException($alert_data, 0, 'socket_create() failed: reason: ' . socket_strerror(socket_last_error()));
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
if (! empty($alert_data['faults'])) {
|
||||||
} else {
|
foreach ($alert_data['faults'] as $fault) {
|
||||||
if (! empty($obj['faults'])) {
|
$splunk_msg = $splunk_prefix . ' - ' . $fault['string'];
|
||||||
foreach ($obj['faults'] as $k => $v) {
|
|
||||||
$splunk_msg = $splunk_prefix . ' - ' . $v['string'];
|
|
||||||
socket_sendto($socket, $splunk_msg, strlen($splunk_msg), 0, $splunk_host, $splunk_port);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$splunk_msg = $splunk_prefix;
|
|
||||||
socket_sendto($socket, $splunk_msg, strlen($splunk_msg), 0, $splunk_host, $splunk_port);
|
socket_sendto($socket, $splunk_msg, strlen($splunk_msg), 0, $splunk_host, $splunk_port);
|
||||||
}
|
}
|
||||||
socket_close($socket);
|
} else {
|
||||||
|
$splunk_msg = $splunk_prefix;
|
||||||
|
socket_sendto($socket, $splunk_msg, strlen($splunk_msg), 0, $splunk_host, $splunk_port);
|
||||||
}
|
}
|
||||||
|
socket_close($socket);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -130,17 +95,19 @@ class Splunk extends Transport
|
|||||||
'name' => 'Splunk-host',
|
'name' => 'Splunk-host',
|
||||||
'descr' => 'Splunk Host',
|
'descr' => 'Splunk Host',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'default' => '127.0.0.1',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'title' => 'UDP Port',
|
'title' => 'UDP Port',
|
||||||
'name' => 'Splunk-port',
|
'name' => 'Splunk-port',
|
||||||
'descr' => 'Splunk Port',
|
'descr' => 'Splunk Port',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'default' => 514,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'Splunk-host' => 'required|string',
|
'Splunk-host' => 'required|ip_or_hostname',
|
||||||
'Splunk-port' => 'required|numeric',
|
'Splunk-port' => 'integer|between:1,65536',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -19,21 +19,16 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
|
||||||
class Syslog extends Transport
|
class Syslog extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
return $this->contactSyslog($alert_data);
|
||||||
$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)
|
public function contactSyslog(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$syslog_host = '127.0.0.1';
|
$syslog_host = '127.0.0.1';
|
||||||
$syslog_port = 514;
|
$syslog_port = 514;
|
||||||
@ -41,38 +36,26 @@ class Syslog extends Transport
|
|||||||
$facility = 24; // Default facility is 3 * 8 (daemon)
|
$facility = 24; // Default facility is 3 * 8 (daemon)
|
||||||
$severity = 6; // Default severity is 6 (Informational)
|
$severity = 6; // Default severity is 6 (Informational)
|
||||||
$sev_txt = 'OK';
|
$sev_txt = 'OK';
|
||||||
$device = device_by_id_cache($obj['device_id']); // for event logging
|
|
||||||
|
|
||||||
if (! empty($opts['syslog_facility']) && preg_match("/^\d+$/", $opts['syslog_facility'])) {
|
if (! empty($this->config['syslog-facility'])) {
|
||||||
$facility = (int) $opts['syslog_facility'] * 8;
|
$facility = (int) $this->config['syslog-facility'] * 8;
|
||||||
} else {
|
|
||||||
log_event('Syslog facility is not an integer: ' . $opts['syslog_facility'], $device, 'poller', 5);
|
|
||||||
}
|
}
|
||||||
if (! empty($opts['syslog_host'])) {
|
|
||||||
if (preg_match('/[a-zA-Z]/', $opts['syslog_host'])) {
|
|
||||||
$syslog_host = gethostbyname($opts['syslog_host']);
|
|
||||||
if ($syslog_host === $opts['syslog_host']) {
|
|
||||||
log_event('Alphanumeric hostname found but does not resolve to an IP.', $device, 'poller', 5);
|
|
||||||
|
|
||||||
return false;
|
if (! empty($this->config['syslog-host'])) {
|
||||||
|
if (preg_match('/[a-zA-Z]/', $this->config['syslog-host'])) {
|
||||||
|
$syslog_host = gethostbyname($this->config['syslog-host']);
|
||||||
|
if ($syslog_host === $this->config['syslog-host']) {
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, 0, 'Hostname found but does not resolve to an IP.');
|
||||||
}
|
}
|
||||||
} elseif (filter_var($opts['syslog_host'], FILTER_VALIDATE_IP)) {
|
|
||||||
$syslog_host = $opts['syslog_host'];
|
|
||||||
} else {
|
|
||||||
log_event('Syslog host is not a valid IP: ' . $opts['syslog_host'], $device, 'poller', 5);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
$syslog_host = $this->config['syslog-host'];
|
||||||
log_event('Syslog host is empty.', $device, 'poller');
|
|
||||||
}
|
|
||||||
if (! empty($opts['syslog_port']) && preg_match("/^\d+$/", $opts['syslog_port'])) {
|
|
||||||
$syslog_port = $opts['syslog_port'];
|
|
||||||
} else {
|
|
||||||
log_event('Syslog port is not an integer.', $device, 'poller', 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($obj['severity']) {
|
if (! empty($this->config['syslog-port'])) {
|
||||||
|
$syslog_port = $this->config['syslog-port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($alert_data['severity']) {
|
||||||
case 'critical':
|
case 'critical':
|
||||||
$severity = 2;
|
$severity = 2;
|
||||||
$sev_txt = 'Critical';
|
$sev_txt = 'Critical';
|
||||||
@ -83,7 +66,7 @@ class Syslog extends Transport
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($obj['state']) {
|
switch ($alert_data['state']) {
|
||||||
case AlertState::RECOVERED:
|
case AlertState::RECOVERED:
|
||||||
$state = 'OK';
|
$state = 'OK';
|
||||||
$severity = 6;
|
$severity = 6;
|
||||||
@ -106,35 +89,33 @@ class Syslog extends Transport
|
|||||||
. gethostname()
|
. gethostname()
|
||||||
. ' librenms'
|
. ' librenms'
|
||||||
. '['
|
. '['
|
||||||
. $obj['device_id']
|
. $alert_data['device_id']
|
||||||
. ']: '
|
. ']: '
|
||||||
. $obj['hostname']
|
. $alert_data['hostname']
|
||||||
. ': ['
|
. ': ['
|
||||||
. $state
|
. $state
|
||||||
. '] '
|
. '] '
|
||||||
. $obj['name'];
|
. $alert_data['name'];
|
||||||
|
|
||||||
if (($socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) === false) {
|
if (($socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) === false) {
|
||||||
log_event('socket_create() failed: reason: ' . socket_strerror(socket_last_error()), $device, 'poller', 5);
|
throw new AlertTransportDeliveryException($alert_data, 0, 'socket_create() failed: reason: ' . socket_strerror(socket_last_error()));
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
if (! empty($alert_data['faults'])) {
|
||||||
} else {
|
foreach ($alert_data['faults'] as $fault) {
|
||||||
if (! empty($obj['faults'])) {
|
$syslog_msg = $syslog_prefix . ' - ' . $fault['string'];
|
||||||
foreach ($obj['faults'] as $k => $v) {
|
|
||||||
$syslog_msg = $syslog_prefix . ' - ' . $v['string'];
|
|
||||||
socket_sendto($socket, $syslog_msg, strlen($syslog_msg), 0, $syslog_host, $syslog_port);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$syslog_msg = $syslog_prefix;
|
|
||||||
socket_sendto($socket, $syslog_msg, strlen($syslog_msg), 0, $syslog_host, $syslog_port);
|
socket_sendto($socket, $syslog_msg, strlen($syslog_msg), 0, $syslog_host, $syslog_port);
|
||||||
}
|
}
|
||||||
socket_close($socket);
|
} else {
|
||||||
|
$syslog_msg = $syslog_prefix;
|
||||||
|
socket_sendto($socket, $syslog_msg, strlen($syslog_msg), 0, $syslog_host, $syslog_port);
|
||||||
}
|
}
|
||||||
|
socket_close($socket);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
@ -158,9 +139,9 @@ class Syslog extends Transport
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'syslog-host' => 'required|string',
|
'syslog-host' => 'required|ip_or_hostname',
|
||||||
'syslog-port' => 'required|numeric',
|
'syslog-port' => 'required|integer|between:1,65536',
|
||||||
'syslog-facility' => 'required|string',
|
'syslog-facility' => 'required|integer|between:0,23',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -26,51 +26,42 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Telegram extends Transport
|
class Telegram extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$telegram_opts['chat_id'] = $this->config['telegram-chat-id'];
|
$url = "https://api.telegram.org/bot{$this->config['telegram-token']}/sendMessage";
|
||||||
$telegram_opts['message_thread_id'] = $this->config['message-thread-id'] ?? null;
|
$format = $this->config['telegram-format'];
|
||||||
$telegram_opts['token'] = $this->config['telegram-token'];
|
$text = $format == 'Markdown'
|
||||||
$telegram_opts['format'] = $this->config['telegram-format'];
|
? preg_replace('/([a-z0-9]+)_([a-z0-9]+)/', "$1\_$2", $alert_data['msg'])
|
||||||
|
: $alert_data['msg'];
|
||||||
|
|
||||||
return $this->contactTelegram($obj, $telegram_opts);
|
$params = [
|
||||||
|
'chat_id' => $this->config['telegram-chat-id'],
|
||||||
|
'text' => $text,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($format) {
|
||||||
|
$params['format'] = $this->config['telegram-format'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($this->config['message-thread-id'])) {
|
||||||
|
$params['message_thread_id'] = $this->config['message-thread-id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = Http::client()->get($url, $params);
|
||||||
|
|
||||||
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $text, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function contactTelegram($obj, $data)
|
public static function configTemplate(): array
|
||||||
{
|
|
||||||
$curl = curl_init();
|
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
$text = urlencode($obj['msg']);
|
|
||||||
$format = '';
|
|
||||||
if ($data['format']) {
|
|
||||||
$format = '&parse_mode=' . $data['format'];
|
|
||||||
if ($data['format'] == 'Markdown') {
|
|
||||||
$text = urlencode(preg_replace('/([a-z0-9]+)_([a-z0-9]+)/', "$1\_$2", $obj['msg']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$messageThreadId = '';
|
|
||||||
if (! empty($data['message_thread_id'])) {
|
|
||||||
$messageThreadId = '&message_thread_id=' . $data['message_thread_id'];
|
|
||||||
}
|
|
||||||
curl_setopt($curl, CURLOPT_URL, "https://api.telegram.org/bot{$data['token']}/sendMessage?chat_id={$data['chat_id']}$messageThreadId&text=$text{$format}");
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump('Telegram returned Error'); //FIXME: propper debuging
|
|
||||||
var_dump('Return: ' . $ret); //FIXME: propper debuging
|
|
||||||
|
|
||||||
return 'HTTP Status code ' . $code . ', Body ' . $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function configTemplate()
|
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -16,58 +16,33 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Twilio extends Transport
|
class Twilio extends Transport
|
||||||
{
|
{
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
$twilio_opts['sid'] = $this->config['twilio-sid'];
|
$url = 'https://api.twilio.com/2010-04-01/Accounts/' . $this->config['twilio-sid'] . '/Messages.json';
|
||||||
$twilio_opts['token'] = $this->config['twilio-token'];
|
|
||||||
$twilio_opts['sender'] = $this->config['twilio-sender'];
|
|
||||||
$twilio_opts['to'] = $this->config['twilio-to'];
|
|
||||||
|
|
||||||
return $this->contactTwilio($obj, $twilio_opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function contactTwilio($obj, $opts)
|
|
||||||
{
|
|
||||||
$params = [
|
|
||||||
'sid' => $opts['sid'],
|
|
||||||
'token' => $opts['token'],
|
|
||||||
'phone' => $opts['to'],
|
|
||||||
'text' => $obj['msg'],
|
|
||||||
'sender' => $opts['sender'],
|
|
||||||
];
|
|
||||||
|
|
||||||
$url = 'https://api.twilio.com/2010-04-01/Accounts/' . $params['sid'] . '/Messages.json';
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'From' => $params['sender'],
|
'From' => $this->config['twilio-sender'],
|
||||||
'Body' => $params['text'],
|
'Body' => $alert_data['msg'],
|
||||||
'To' => $params['phone'],
|
'To' => $this->config['twilio-to'],
|
||||||
];
|
];
|
||||||
$post = http_build_query($data);
|
|
||||||
|
|
||||||
$curl = curl_init($url);
|
$res = Http::client()->asForm()
|
||||||
|
->withBasicAuth($this->config['twilio-sid'], $this->config['twilio-token'])
|
||||||
|
->post($url, $data);
|
||||||
|
|
||||||
Proxy::applyToCurl($curl);
|
if ($res->successful()) {
|
||||||
|
|
||||||
curl_setopt($curl, CURLOPT_POST, true);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
|
||||||
curl_setopt($curl, CURLOPT_USERPWD, $params['sid'] . ':' . $params['token']);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
|
|
||||||
|
|
||||||
curl_exec($curl);
|
|
||||||
|
|
||||||
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -24,18 +24,14 @@
|
|||||||
namespace LibreNMS\Alert\Transport;
|
namespace LibreNMS\Alert\Transport;
|
||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Ukfastpss extends Transport
|
class Ukfastpss extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'UKFast PSS';
|
protected string $name = 'UKFast PSS';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
|
||||||
return $this->contactUkfastpss($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactUkfastpss($obj, $opts)
|
|
||||||
{
|
{
|
||||||
$apiKey = $this->config['api-key'];
|
$apiKey = $this->config['api-key'];
|
||||||
$author = $this->config['author'];
|
$author = $this->config['author'];
|
||||||
@ -47,34 +43,27 @@ class Ukfastpss extends Transport
|
|||||||
'id' => $author,
|
'id' => $author,
|
||||||
],
|
],
|
||||||
'secure' => ($secure == 'on'),
|
'secure' => ($secure == 'on'),
|
||||||
'subject' => $obj['title'],
|
'subject' => $alert_data['title'],
|
||||||
'details' => $obj['msg'],
|
'details' => $alert_data['msg'],
|
||||||
'priority' => $priority,
|
'priority' => $priority,
|
||||||
];
|
];
|
||||||
|
|
||||||
$request_opts = [];
|
$res = Http::client()
|
||||||
$request_headers = [];
|
->withHeaders([
|
||||||
|
'Authorization' => $apiKey,
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
])
|
||||||
|
->post('https://api.ukfast.io/pss/v1/requests', $body);
|
||||||
|
|
||||||
$request_headers['Authorization'] = $apiKey;
|
if ($res->successful()) {
|
||||||
$request_headers['Content-Type'] = 'application/json';
|
return true;
|
||||||
$request_headers['Accept'] = 'application/json';
|
|
||||||
|
|
||||||
$client = new \GuzzleHttp\Client();
|
|
||||||
$request_opts['proxy'] = Proxy::forGuzzle();
|
|
||||||
$request_opts['headers'] = $request_headers;
|
|
||||||
$request_opts['body'] = json_encode($body);
|
|
||||||
|
|
||||||
$res = $client->request('POST', 'https://api.ukfast.io/pss/v1/requests', $request_opts);
|
|
||||||
|
|
||||||
$code = $res->getStatusCode();
|
|
||||||
if ($code != 200) {
|
|
||||||
return 'HTTP Status code ' . $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -26,62 +26,47 @@ namespace LibreNMS\Alert\Transport;
|
|||||||
|
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
use LibreNMS\Enum\AlertState;
|
use LibreNMS\Enum\AlertState;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class Victorops extends Transport
|
class Victorops extends Transport
|
||||||
{
|
{
|
||||||
protected $name = 'VictorOps';
|
protected string $name = 'Splunk On-Call';
|
||||||
|
|
||||||
public function deliverAlert($obj, $opts)
|
public function deliverAlert(array $alert_data): bool
|
||||||
{
|
{
|
||||||
if (! empty($this->config)) {
|
$url = $this->config['victorops-url'];
|
||||||
$opts['url'] = $this->config['victorops-url'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->contactVictorops($obj, $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function contactVictorops($obj, $opts)
|
|
||||||
{
|
|
||||||
$url = $opts['url'];
|
|
||||||
|
|
||||||
$protocol = [
|
$protocol = [
|
||||||
'entity_id' => strval($obj['id'] ? $obj['id'] : $obj['uid']),
|
'entity_id' => strval($alert_data['id'] ?: $alert_data['uid']),
|
||||||
'state_start_time' => strtotime($obj['timestamp']),
|
'state_start_time' => strtotime($alert_data['timestamp']),
|
||||||
'entity_display_name' => $obj['title'],
|
'entity_display_name' => $alert_data['title'],
|
||||||
'state_message' => $obj['msg'],
|
'state_message' => $alert_data['msg'],
|
||||||
'monitoring_tool' => 'librenms',
|
'monitoring_tool' => 'librenms',
|
||||||
];
|
];
|
||||||
if ($obj['state'] == AlertState::RECOVERED) {
|
$protocol['message_type'] = match ($alert_data['state']) {
|
||||||
$protocol['message_type'] = 'recovery';
|
AlertState::RECOVERED => 'RECOVERY',
|
||||||
} elseif ($obj['state'] == AlertState::ACKNOWLEDGED) {
|
AlertState::ACKNOWLEDGED => 'ACKNOWLEDGEMENT',
|
||||||
$protocol['message_type'] = 'acknowledgement';
|
default => match ($alert_data['severity']) {
|
||||||
} elseif ($obj['state'] == AlertState::ACTIVE) {
|
'ok' => 'INFO',
|
||||||
$protocol['message_type'] = 'critical';
|
'warning' => 'WARNING',
|
||||||
}
|
default => 'CRITICAL',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
foreach ($obj['faults'] as $fault => $data) {
|
foreach ($alert_data['faults'] as $fault => $data) {
|
||||||
$protocol['state_message'] .= $data['string'];
|
$protocol['state_message'] .= $data['string'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$curl = curl_init();
|
$res = Http::client()->post($url, $protocol);
|
||||||
Proxy::applyToCurl($curl);
|
|
||||||
curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-type' => 'application/json']);
|
|
||||||
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($protocol));
|
|
||||||
$ret = curl_exec($curl);
|
|
||||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
||||||
if ($code != 200) {
|
|
||||||
var_dump('VictorOps returned Error, retry later'); //FIXME: propper debuging
|
|
||||||
|
|
||||||
return false;
|
if ($res->successful()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alert_data['msg'], $protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function configTemplate()
|
public static function configTemplate(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'config' => [
|
'config' => [
|
||||||
|
@ -27,40 +27,34 @@
|
|||||||
namespace LibreNMS\Data\Store;
|
namespace LibreNMS\Data\Store;
|
||||||
|
|
||||||
use App\Polling\Measure\Measurement;
|
use App\Polling\Measure\Measurement;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Util\Http;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
class Prometheus extends BaseDatastore
|
class Prometheus extends BaseDatastore
|
||||||
{
|
{
|
||||||
private $client;
|
private $client;
|
||||||
private $base_uri;
|
private $base_uri;
|
||||||
private $default_opts;
|
|
||||||
private $enabled;
|
private $enabled;
|
||||||
private $prefix;
|
private $prefix;
|
||||||
|
|
||||||
public function __construct(\GuzzleHttp\Client $client)
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->client = $client;
|
|
||||||
|
|
||||||
$url = Config::get('prometheus.url');
|
$url = Config::get('prometheus.url');
|
||||||
$job = Config::get('prometheus.job', 'librenms');
|
$job = Config::get('prometheus.job', 'librenms');
|
||||||
$this->base_uri = "$url/metrics/job/$job/instance/";
|
$this->base_uri = "$url/metrics/job/$job/instance/";
|
||||||
|
|
||||||
|
$this->client = Http::client()->baseUrl($this->base_uri);
|
||||||
|
|
||||||
$this->prefix = Config::get('prometheus.prefix', '');
|
$this->prefix = Config::get('prometheus.prefix', '');
|
||||||
if ($this->prefix) {
|
if ($this->prefix) {
|
||||||
$this->prefix = "$this->prefix" . '_';
|
$this->prefix = "$this->prefix" . '_';
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->default_opts = [
|
|
||||||
'headers' => ['Content-Type' => 'text/plain'],
|
|
||||||
];
|
|
||||||
if ($proxy = Proxy::get($url)) {
|
|
||||||
$this->default_opts['proxy'] = $proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->enabled = self::isEnabled();
|
$this->enabled = self::isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,52 +76,41 @@ class Prometheus extends BaseDatastore
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$vals = '';
|
||||||
$vals = '';
|
$promtags = '/measurement/' . $measurement;
|
||||||
$promtags = '/measurement/' . $measurement;
|
|
||||||
|
|
||||||
foreach ($fields as $k => $v) {
|
foreach ($fields as $k => $v) {
|
||||||
if ($v !== null) {
|
if ($v !== null) {
|
||||||
$vals .= $this->prefix . "$k $v\n";
|
$vals .= $this->prefix . "$k $v\n";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($tags as $t => $v) {
|
|
||||||
if ($v !== null) {
|
|
||||||
$promtags .= (Str::contains($v, '/') ? "/$t@base64/" . base64_encode($v) : "/$t/$v");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$options = $this->getDefaultOptions();
|
|
||||||
$options['body'] = $vals;
|
|
||||||
|
|
||||||
$promurl = $this->base_uri . $device['hostname'] . $promtags;
|
|
||||||
if (Config::get('prometheus.attach_sysname', false)) {
|
|
||||||
$promurl .= '/sysName/' . $device['sysName'];
|
|
||||||
}
|
|
||||||
$promurl = str_replace(' ', '-', $promurl); // Prometheus doesn't handle tags with spaces in url
|
|
||||||
|
|
||||||
Log::debug("Prometheus put $promurl: ", [
|
|
||||||
'measurement' => $measurement,
|
|
||||||
'tags' => $tags,
|
|
||||||
'fields' => $fields,
|
|
||||||
'vals' => $vals,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$result = $this->client->request('POST', $promurl, $options);
|
|
||||||
|
|
||||||
$this->recordStatistic($stat->end());
|
|
||||||
|
|
||||||
if ($result->getStatusCode() !== 200) {
|
|
||||||
Log::error('Prometheus Error: ' . $result->getReasonPhrase());
|
|
||||||
}
|
|
||||||
} catch (GuzzleException $e) {
|
|
||||||
Log::error('Prometheus Exception: ' . $e->getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function getDefaultOptions()
|
foreach ($tags as $t => $v) {
|
||||||
{
|
if ($v !== null) {
|
||||||
return $this->default_opts;
|
$promtags .= (Str::contains($v, '/') ? "/$t@base64/" . base64_encode($v) : "/$t/$v");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$promurl = $device['hostname'] . $promtags;
|
||||||
|
if (Config::get('prometheus.attach_sysname', false)) {
|
||||||
|
$promurl .= '/sysName/' . $device['sysName'];
|
||||||
|
}
|
||||||
|
$promurl = str_replace(' ', '-', $promurl); // Prometheus doesn't handle tags with spaces in url
|
||||||
|
|
||||||
|
Log::debug("Prometheus put $promurl: ", [
|
||||||
|
'measurement' => $measurement,
|
||||||
|
'tags' => $tags,
|
||||||
|
'fields' => $fields,
|
||||||
|
'vals' => $vals,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = $this->client->withBody($vals, 'text/plain')->post($promurl);
|
||||||
|
|
||||||
|
$this->recordStatistic($stat->end());
|
||||||
|
|
||||||
|
if (! $result->successful()) {
|
||||||
|
Log::error('Prometheus Error: ' . $result->body());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,26 +27,13 @@ namespace LibreNMS\Exceptions;
|
|||||||
|
|
||||||
class AlertTransportDeliveryException extends \Exception
|
class AlertTransportDeliveryException extends \Exception
|
||||||
{
|
{
|
||||||
/** @var array */
|
public function __construct(
|
||||||
protected $params = [];
|
array $data,
|
||||||
/** @var string */
|
int $code = 0,
|
||||||
protected $template = '';
|
protected string $response = '',
|
||||||
/** @var string */
|
protected string $template = '',
|
||||||
protected $response = '';
|
protected array $params = []
|
||||||
|
) {
|
||||||
/**
|
|
||||||
* @param array $data
|
|
||||||
* @param int $code
|
|
||||||
* @param string $response
|
|
||||||
* @param string $message
|
|
||||||
* @param array $params
|
|
||||||
*/
|
|
||||||
public function __construct($data, $code = 0, $response = '', $message = '', $params = [])
|
|
||||||
{
|
|
||||||
$this->params = $params;
|
|
||||||
$this->template = $message;
|
|
||||||
$this->response = $response;
|
|
||||||
|
|
||||||
$name = $data['transport_name'] ?? '';
|
$name = $data['transport_name'] ?? '';
|
||||||
|
|
||||||
$message = "Transport delivery failed with $code for $name: $response";
|
$message = "Transport delivery failed with $code for $name: $response";
|
||||||
|
@ -36,17 +36,16 @@ interface Transport
|
|||||||
* Gets called when an alert is sent
|
* Gets called when an alert is sent
|
||||||
*
|
*
|
||||||
* @param array $alert_data An array created by DescribeAlert
|
* @param array $alert_data An array created by DescribeAlert
|
||||||
* @param array|true $opts The options from the alert_transports transport_config column
|
* @return bool Returns true if the call was successful.
|
||||||
* @return mixed Returns if the call was successful
|
|
||||||
*
|
*
|
||||||
* @throws \LibreNMS\Exceptions\AlertTransportDeliveryException
|
* @throws \LibreNMS\Exceptions\AlertTransportDeliveryException
|
||||||
*/
|
*/
|
||||||
public function deliverAlert($alert_data, $opts);
|
public function deliverAlert(array $alert_data): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function configTemplate();
|
public static function configTemplate(): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the configuration details of this alert transport
|
* Display the configuration details of this alert transport
|
||||||
|
@ -196,7 +196,7 @@ class Git
|
|||||||
return $this->cacheGet('remoteCommit', function () {
|
return $this->cacheGet('remoteCommit', function () {
|
||||||
if ($this->isAvailable()) {
|
if ($this->isAvailable()) {
|
||||||
try {
|
try {
|
||||||
return (array) \Http::withOptions(['proxy' => Proxy::forGuzzle()])->get(Config::get('github_api') . 'commits/master')->json();
|
return (array) Http::client()->get(Config::get('github_api') . 'commits/master')->json();
|
||||||
} catch (ConnectionException $e) {
|
} catch (ConnectionException $e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
namespace LibreNMS\Util;
|
namespace LibreNMS\Util;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
|
|
||||||
class GitHub
|
class GitHub
|
||||||
{
|
{
|
||||||
@ -112,7 +111,7 @@ class GitHub
|
|||||||
*/
|
*/
|
||||||
public function getRelease($tag)
|
public function getRelease($tag)
|
||||||
{
|
{
|
||||||
$release = Http::withHeaders($this->getHeaders())->get($this->github . "/releases/tags/$tag");
|
$release = Http::client()->withHeaders($this->getHeaders())->get($this->github . "/releases/tags/$tag");
|
||||||
|
|
||||||
return $release->json();
|
return $release->json();
|
||||||
}
|
}
|
||||||
@ -122,7 +121,7 @@ class GitHub
|
|||||||
*/
|
*/
|
||||||
public function getPullRequest()
|
public function getPullRequest()
|
||||||
{
|
{
|
||||||
$pull_request = Http::withHeaders($this->getHeaders())->get($this->github . "/pulls/{$this->pr}");
|
$pull_request = Http::client()->withHeaders($this->getHeaders())->get($this->github . "/pulls/{$this->pr}");
|
||||||
$this->pr = $pull_request->json();
|
$this->pr = $pull_request->json();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +179,7 @@ class GitHub
|
|||||||
}
|
}
|
||||||
GRAPHQL;
|
GRAPHQL;
|
||||||
|
|
||||||
$prs = Http::withHeaders($this->getHeaders())->post($this->graphql, ['query' => $query]);
|
$prs = Http::client()->withHeaders($this->getHeaders())->post($this->graphql, ['query' => $query]);
|
||||||
$prs = $prs->json();
|
$prs = $prs->json();
|
||||||
if (! isset($prs['data'])) {
|
if (! isset($prs['data'])) {
|
||||||
var_dump($prs);
|
var_dump($prs);
|
||||||
@ -366,7 +365,7 @@ GRAPHQL;
|
|||||||
$this->createChangelog(false);
|
$this->createChangelog(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
$release = Http::withHeaders($this->getHeaders())->post($this->github . '/releases', [
|
$release = Http::client()->withHeaders($this->getHeaders())->post($this->github . '/releases', [
|
||||||
'tag_name' => $this->tag,
|
'tag_name' => $this->tag,
|
||||||
'target_commitish' => $updated_sha,
|
'target_commitish' => $updated_sha,
|
||||||
'body' => $this->markdown,
|
'body' => $this->markdown,
|
||||||
@ -422,10 +421,10 @@ GRAPHQL;
|
|||||||
*/
|
*/
|
||||||
private function pushFileContents($file, $contents, $message): string
|
private function pushFileContents($file, $contents, $message): string
|
||||||
{
|
{
|
||||||
$existing = Http::withHeaders($this->getHeaders())->get($this->github . '/contents/' . $file);
|
$existing = Http::client()->withHeaders($this->getHeaders())->get($this->github . '/contents/' . $file);
|
||||||
$existing_sha = $existing->json()['sha'];
|
$existing_sha = $existing->json()['sha'];
|
||||||
|
|
||||||
$updated = Http::withHeaders($this->getHeaders())->put($this->github . '/contents/' . $file, [
|
$updated = Http::client()->withHeaders($this->getHeaders())->put($this->github . '/contents/' . $file, [
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
'content' => base64_encode($contents),
|
'content' => base64_encode($contents),
|
||||||
'sha' => $existing_sha,
|
'sha' => $existing_sha,
|
||||||
|
49
LibreNMS/Util/Http.php
Normal file
49
LibreNMS/Util/Http.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Http.php
|
||||||
|
*
|
||||||
|
* -Description-
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @link https://www.librenms.org
|
||||||
|
*
|
||||||
|
* @copyright 2022 Tony Murray
|
||||||
|
* @author Tony Murray <murraytony@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace LibreNMS\Util;
|
||||||
|
|
||||||
|
use Illuminate\Http\Client\PendingRequest;
|
||||||
|
use Illuminate\Support\Facades\Http as LaravelHttp;
|
||||||
|
use LibreNMS\Config;
|
||||||
|
|
||||||
|
class Http
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new client with proxy set if appropriate and a distinct User-Agent header
|
||||||
|
*/
|
||||||
|
public static function client(): PendingRequest
|
||||||
|
{
|
||||||
|
return LaravelHttp::withOptions([
|
||||||
|
'proxy' => [
|
||||||
|
'http' => Proxy::http(),
|
||||||
|
'https' => Proxy::https(),
|
||||||
|
'no' => Proxy::ignore(),
|
||||||
|
],
|
||||||
|
])->withHeaders([
|
||||||
|
'User-Agent' => Config::get('project_name') . '/' . Version::VERSION, // we don't need fine version here, just rough
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -29,67 +29,29 @@ use LibreNMS\Config;
|
|||||||
|
|
||||||
class Proxy
|
class Proxy
|
||||||
{
|
{
|
||||||
/**
|
public static function http(): string
|
||||||
* Check if if the proxy should be used.
|
|
||||||
* (it should not be used for connections to localhost)
|
|
||||||
*/
|
|
||||||
public static function shouldBeUsed(string $target_url): bool
|
|
||||||
{
|
{
|
||||||
return preg_match('#(^|://)(localhost|127\.|::1)#', $target_url) == 0;
|
// use local_only to avoid CVE-2016-5385
|
||||||
|
$http_proxy = getenv('http_proxy', local_only: true) ?: getenv('HTTP_PROXY', local_only: true) ?: Config::get('http_proxy', '');
|
||||||
|
|
||||||
|
return $http_proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function https(): string
|
||||||
* Return the proxy url
|
|
||||||
*
|
|
||||||
* @return array|bool|false|string
|
|
||||||
*/
|
|
||||||
public static function get(?string $target_url = null)
|
|
||||||
{
|
{
|
||||||
if ($target_url && ! self::shouldBeUsed($target_url)) {
|
// use local_only to avoid CVE-2016-5385
|
||||||
return false;
|
return getenv('https_proxy', local_only: true) ?: getenv('HTTPS_PROXY', local_only: true) ?: Config::get('https_proxy', '');
|
||||||
} elseif (getenv('http_proxy')) {
|
}
|
||||||
return getenv('http_proxy');
|
|
||||||
} elseif (getenv('https_proxy')) {
|
public static function ignore(): array
|
||||||
return getenv('https_proxy');
|
{
|
||||||
} elseif ($callback_proxy = Config::get('callback_proxy')) {
|
// use local_only to avoid CVE-2016-5385
|
||||||
return $callback_proxy;
|
$no_proxy = getenv('no_proxy', local_only: true) ?: getenv('NO_PROXY', local_only: true) ?: Config::get('no_proxy', '');
|
||||||
} elseif ($http_proxy = Config::get('http_proxy')) {
|
|
||||||
return $http_proxy;
|
if ($no_proxy == '') {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return explode(',', str_replace(' ', '', $no_proxy));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the proxy url in guzzle format "http://127.0.0.1:8888"
|
|
||||||
*/
|
|
||||||
public static function forGuzzle(?string $target_url = null): string
|
|
||||||
{
|
|
||||||
$proxy = self::forCurl($target_url);
|
|
||||||
|
|
||||||
return empty($proxy) ? '' : ('http://' . $proxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the ip and port of the proxy
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function forCurl(?string $target_url = null): string
|
|
||||||
{
|
|
||||||
return str_replace(['http://', 'https://'], '', rtrim(self::get($target_url), '/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the proxy on a curl handle
|
|
||||||
*
|
|
||||||
* @param \CurlHandle $curl
|
|
||||||
*/
|
|
||||||
public static function applyToCurl($curl): void
|
|
||||||
{
|
|
||||||
$proxy = self::forCurl();
|
|
||||||
if (! empty($proxy)) {
|
|
||||||
curl_setopt($curl, CURLOPT_PROXY, $proxy);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class Stats
|
|||||||
$stats = new static;
|
$stats = new static;
|
||||||
|
|
||||||
if ($stats->isEnabled()) {
|
if ($stats->isEnabled()) {
|
||||||
$response = \Http::withOptions(['proxy' => Proxy::forGuzzle()])
|
Http::client()
|
||||||
->asForm()
|
->asForm()
|
||||||
->post(\LibreNMS\Config::get('callback_post'), [
|
->post(\LibreNMS\Config::get('callback_post'), [
|
||||||
'data' => json_encode($stats->collectData()),
|
'data' => json_encode($stats->collectData()),
|
||||||
@ -63,7 +63,7 @@ class Stats
|
|||||||
{
|
{
|
||||||
$uuid = Callback::get('uuid');
|
$uuid = Callback::get('uuid');
|
||||||
|
|
||||||
$response = \Http::withOptions(['proxy' => Proxy::forGuzzle()])
|
$response = Http::client()
|
||||||
->asForm()
|
->asForm()
|
||||||
->post(\LibreNMS\Config::get('callback_clear'), ['uuid' => $uuid]);
|
->post(\LibreNMS\Config::get('callback_clear'), ['uuid' => $uuid]);
|
||||||
|
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
|
|
||||||
namespace App\ApiClients;
|
namespace App\ApiClients;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Http;
|
use LibreNMS\Util\Http;
|
||||||
use LibreNMS\Util\Proxy;
|
|
||||||
|
|
||||||
class BaseApi
|
class BaseApi
|
||||||
{
|
{
|
||||||
@ -37,9 +36,7 @@ class BaseApi
|
|||||||
protected function getClient(): \Illuminate\Http\Client\PendingRequest
|
protected function getClient(): \Illuminate\Http\Client\PendingRequest
|
||||||
{
|
{
|
||||||
if (is_null($this->client)) {
|
if (is_null($this->client)) {
|
||||||
$this->client = Http::withOptions([
|
$this->client = Http::client()->baseUrl($this->base_uri)
|
||||||
'proxy' => Proxy::forGuzzle($this->base_uri),
|
|
||||||
])->baseUrl($this->base_uri)
|
|
||||||
->timeout($this->timeout);
|
->timeout($this->timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class BingApi extends BaseApi implements Geocoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Guzzle request option array
|
* Build request option array
|
||||||
*
|
*
|
||||||
* @throws \Exception you may throw an Exception if validation fails
|
* @throws \Exception you may throw an Exception if validation fails
|
||||||
*/
|
*/
|
||||||
|
@ -83,7 +83,7 @@ trait GeocodingHelper
|
|||||||
abstract protected function parseLatLng(array $data): array;
|
abstract protected function parseLatLng(array $data): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Guzzle request option array
|
* Build request option array
|
||||||
*
|
*
|
||||||
* @param string $address
|
* @param string $address
|
||||||
* @return array
|
* @return array
|
||||||
|
@ -60,7 +60,7 @@ class GoogleMapsApi extends BaseApi implements Geocoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Guzzle request option array
|
* Build request option array
|
||||||
*
|
*
|
||||||
* @throws \Exception you may throw an Exception if validation fails
|
* @throws \Exception you may throw an Exception if validation fails
|
||||||
*/
|
*/
|
||||||
|
@ -26,12 +26,12 @@
|
|||||||
namespace App\ApiClients;
|
namespace App\ApiClients;
|
||||||
|
|
||||||
use App\Models\Device;
|
use App\Models\Device;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
|
||||||
class GraylogApi
|
class GraylogApi
|
||||||
{
|
{
|
||||||
private Client $client;
|
private \Illuminate\Http\Client\PendingRequest $client;
|
||||||
private string $api_prefix = '';
|
private string $api_prefix = '';
|
||||||
|
|
||||||
public function __construct(array $config = [])
|
public function __construct(array $config = [])
|
||||||
@ -53,7 +53,7 @@ class GraylogApi
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->client = new Client($config);
|
$this->client = Http::client()->withOptions($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStreams(): array
|
public function getStreams(): array
|
||||||
@ -65,9 +65,8 @@ class GraylogApi
|
|||||||
$uri = $this->api_prefix . '/streams';
|
$uri = $this->api_prefix . '/streams';
|
||||||
|
|
||||||
$response = $this->client->get($uri);
|
$response = $this->client->get($uri);
|
||||||
$data = json_decode($response->getBody(), true);
|
|
||||||
|
|
||||||
return $data ?: [];
|
return $response->json() ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,10 +92,9 @@ class GraylogApi
|
|||||||
'filter' => $filter,
|
'filter' => $filter,
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = $this->client->get($uri, ['query' => $data]);
|
$response = $this->client->get($uri, $data);
|
||||||
$data = json_decode($response->getBody(), true);
|
|
||||||
|
|
||||||
return $data ?: [];
|
return $response->json() ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,7 +49,7 @@ class MapquestApi extends BaseApi implements Geocoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Guzzle request option array
|
* Build request option array
|
||||||
*
|
*
|
||||||
* @throws \Exception you may throw an Exception if validation fails
|
* @throws \Exception you may throw an Exception if validation fails
|
||||||
*/
|
*/
|
||||||
|
@ -46,7 +46,7 @@ class NominatimApi extends BaseApi implements Geocoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Guzzle request option array
|
* Build request option array
|
||||||
*
|
*
|
||||||
* @throws \Exception you may throw an Exception if validation fails
|
* @throws \Exception you may throw an Exception if validation fails
|
||||||
*/
|
*/
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
namespace App\ApiClients;
|
namespace App\ApiClients;
|
||||||
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
|
||||||
use LibreNMS\Exceptions\ApiClientException;
|
use LibreNMS\Exceptions\ApiClientException;
|
||||||
|
|
||||||
class RipeApi extends BaseApi
|
class RipeApi extends BaseApi
|
||||||
@ -68,21 +67,12 @@ class RipeApi extends BaseApi
|
|||||||
*/
|
*/
|
||||||
private function makeApiCall(string $uri, array $options): mixed
|
private function makeApiCall(string $uri, array $options): mixed
|
||||||
{
|
{
|
||||||
try {
|
$response_data = $this->getClient()->get($uri, $options['query'])->json();
|
||||||
$response_data = $this->getClient()->get($uri, $options['query'])->json();
|
|
||||||
if (isset($response_data['status']) && $response_data['status'] == 'ok') {
|
|
||||||
return $response_data;
|
|
||||||
} else {
|
|
||||||
throw new ApiClientException('RIPE API call failed', $response_data);
|
|
||||||
}
|
|
||||||
} catch (RequestException $e) {
|
|
||||||
$message = 'RIPE API call to ' . $e->getRequest()->getUri() . ' failed: ';
|
|
||||||
$message .= $e->getResponse()->getReasonPhrase() . ' ' . $e->getResponse()->getStatusCode();
|
|
||||||
|
|
||||||
throw new ApiClientException(
|
if (isset($response_data['status']) && $response_data['status'] == 'ok') {
|
||||||
$message,
|
return $response_data;
|
||||||
json_decode($e->getResponse()->getBody(), true)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new ApiClientException("RIPE API call to $this->base_uri/$uri failed: " . $this->getClient()->get($uri, $options['query'])->status(), $response_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ use Illuminate\Http\JsonResponse;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Data\Store\Rrd;
|
use LibreNMS\Data\Store\Rrd;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
use LibreNMS\Util\Version;
|
use LibreNMS\Util\Version;
|
||||||
|
|
||||||
class AboutController extends Controller
|
class AboutController extends Controller
|
||||||
@ -113,7 +114,7 @@ class AboutController extends Controller
|
|||||||
|
|
||||||
// try to clear usage data if we have a uuid
|
// try to clear usage data if we have a uuid
|
||||||
if ($usage_uuid) {
|
if ($usage_uuid) {
|
||||||
if (! \Http::post(Config::get('callback_clear'), ['uuid' => $usage_uuid])->successful()) {
|
if (! Http::client()->post(Config::get('callback_clear'), ['uuid' => $usage_uuid])->successful()) {
|
||||||
return response()->json([], 500); // don't clear if this fails to delete upstream data
|
return response()->json([], 500); // don't clear if this fails to delete upstream data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,41 +5,19 @@ namespace App\Http\Controllers;
|
|||||||
use App\Models\AlertTransport;
|
use App\Models\AlertTransport;
|
||||||
use App\Models\Device;
|
use App\Models\Device;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use LibreNMS\Alert\AlertUtil;
|
use LibreNMS\Alert\AlertData;
|
||||||
use LibreNMS\Config;
|
|
||||||
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
use LibreNMS\Exceptions\AlertTransportDeliveryException;
|
||||||
|
|
||||||
class AlertTransportController extends Controller
|
class AlertTransportController extends Controller
|
||||||
{
|
{
|
||||||
public function test(Request $request, AlertTransport $transport): \Illuminate\Http\JsonResponse
|
public function test(Request $request, AlertTransport $transport): \Illuminate\Http\JsonResponse
|
||||||
{
|
{
|
||||||
|
/** @var Device $device */
|
||||||
$device = Device::with('location')->first();
|
$device = Device::with('location')->first();
|
||||||
$obj = [
|
$alert_data = AlertData::testData($device);
|
||||||
'hostname' => $device->hostname,
|
|
||||||
'device_id' => $device->device_id,
|
|
||||||
'sysDescr' => $device->sysDescr,
|
|
||||||
'version' => $device->version,
|
|
||||||
'hardware' => $device->hardware,
|
|
||||||
'location' => $device->location,
|
|
||||||
'title' => 'Testing transport from ' . Config::get('project_name'),
|
|
||||||
'elapsed' => '11s',
|
|
||||||
'alert_id' => '000',
|
|
||||||
'id' => '000',
|
|
||||||
'faults' => [],
|
|
||||||
'uid' => '000',
|
|
||||||
'severity' => 'critical',
|
|
||||||
'rule' => 'macros.device = 1',
|
|
||||||
'name' => 'Test-Rule',
|
|
||||||
'string' => '#1: test => string;',
|
|
||||||
'timestamp' => date('Y-m-d H:i:s'),
|
|
||||||
'contacts' => AlertUtil::getContacts($device->toArray()),
|
|
||||||
'state' => '1',
|
|
||||||
'msg' => 'This is a test alert',
|
|
||||||
];
|
|
||||||
|
|
||||||
$opts = Config::get('alert.transports.' . $transport->transport_type);
|
|
||||||
try {
|
try {
|
||||||
$result = $transport->instance()->deliverAlert($obj, $opts);
|
$result = $transport->instance()->deliverAlert($alert_data);
|
||||||
|
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
return response()->json(['status' => 'ok']);
|
return response()->json(['status' => 'ok']);
|
||||||
@ -47,7 +25,7 @@ class AlertTransportController extends Controller
|
|||||||
} catch (AlertTransportDeliveryException $e) {
|
} catch (AlertTransportDeliveryException $e) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $e->getMessage(),
|
'message' => strip_tags($e->getMessage()),
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
\Log::error($e);
|
\Log::error($e);
|
||||||
@ -56,7 +34,7 @@ class AlertTransportController extends Controller
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $result,
|
'message' => strip_tags($result),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,27 +5,8 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use LibreNMS\Alert\Transport;
|
use LibreNMS\Alert\Transport;
|
||||||
|
use LibreNMS\Alert\Transport\Dummy;
|
||||||
|
|
||||||
/**
|
|
||||||
* \App\Models\AlertTransport
|
|
||||||
*
|
|
||||||
* @property int $transport_id
|
|
||||||
* @property string $transport_name
|
|
||||||
* @property string $transport_type
|
|
||||||
* @property bool $is_default
|
|
||||||
* @property array|null $transport_config
|
|
||||||
*
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport newModelQuery()
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport newQuery()
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport query()
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport whereIsDefault($value)
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport whereTransportConfig($value)
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport whereTransportId($value)
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport whereTransportName($value)
|
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|AlertTransport whereTransportType($value)
|
|
||||||
*
|
|
||||||
* @mixin \Eloquent
|
|
||||||
*/
|
|
||||||
class AlertTransport extends Model
|
class AlertTransport extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
@ -36,11 +17,16 @@ class AlertTransport extends Model
|
|||||||
'is_default' => 'boolean',
|
'is_default' => 'boolean',
|
||||||
'transport_config' => 'array',
|
'transport_config' => 'array',
|
||||||
];
|
];
|
||||||
|
protected $fillable = ['transport_config'];
|
||||||
|
|
||||||
public function instance(): Transport
|
public function instance(): Transport
|
||||||
{
|
{
|
||||||
$class = Transport::getClass($this->transport_type);
|
$class = Transport::getClass($this->transport_type);
|
||||||
|
|
||||||
return new $class($this);
|
if (class_exists($class)) {
|
||||||
|
return new $class($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dummy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
"easybook/geshi": "^1.0.8",
|
"easybook/geshi": "^1.0.8",
|
||||||
"ezyang/htmlpurifier": "^4.8",
|
"ezyang/htmlpurifier": "^4.8",
|
||||||
"fico7489/laravel-pivot": "^3.0",
|
"fico7489/laravel-pivot": "^3.0",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
|
||||||
"influxdb/influxdb-php": "^1.15",
|
"influxdb/influxdb-php": "^1.15",
|
||||||
"justinrainbow/json-schema": "^5.2",
|
"justinrainbow/json-schema": "^5.2",
|
||||||
"laravel-notification-channels/webpush": "^7.0",
|
"laravel-notification-channels/webpush": "^7.0",
|
||||||
|
2
composer.lock
generated
2
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "9921ea55b62f77f05208738b5b783b31",
|
"content-hash": "4a8a1c7d52ce1cb29b37826a53d89804",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "amenadiel/jpgraph",
|
"name": "amenadiel/jpgraph",
|
||||||
|
@ -233,6 +233,8 @@ return [
|
|||||||
'PluginManager' => \App\Facades\PluginManager::class,
|
'PluginManager' => \App\Facades\PluginManager::class,
|
||||||
'Rrd' => \App\Facades\Rrd::class,
|
'Rrd' => \App\Facades\Rrd::class,
|
||||||
'SnmpQuery' => \App\Facades\FacadeAccessorSnmp::class,
|
'SnmpQuery' => \App\Facades\FacadeAccessorSnmp::class,
|
||||||
|
])->forget([
|
||||||
|
'Http', // don't use Laravel Http facade, LibreNMS has its own wrapper
|
||||||
])->toArray(),
|
])->toArray(),
|
||||||
|
|
||||||
'charset' => env('CHARSET', ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'),
|
'charset' => env('CHARSET', ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'),
|
||||||
|
@ -183,19 +183,6 @@ You need a token you can find on your personnal space.
|
|||||||
| API URL | https://soap.aspsms.com/aspsmsx.asmx/SimpleTextSMS |
|
| API URL | https://soap.aspsms.com/aspsmsx.asmx/SimpleTextSMS |
|
||||||
| Options | UserKey=USERKEY<br />Password=APIPASSWORD<br />Recipient=RECIPIENT<br/> Originator=ORIGINATOR<br />MessageText={{ $msg }} |
|
| Options | UserKey=USERKEY<br />Password=APIPASSWORD<br />Recipient=RECIPIENT<br/> Originator=ORIGINATOR<br />MessageText={{ $msg }} |
|
||||||
|
|
||||||
## Boxcar
|
|
||||||
|
|
||||||
Copy your access token from the Boxcar app or from the Boxcar.io
|
|
||||||
website and setup the transport.
|
|
||||||
|
|
||||||
[Boxcar Docs](http://developer.boxcar.io/api/publisher/)
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
| Config | Example |
|
|
||||||
| ------ | ------- |
|
|
||||||
| Access Token | i23f23mr23rwerw |
|
|
||||||
|
|
||||||
## Browser Push
|
## Browser Push
|
||||||
|
|
||||||
Browser push notifications can send a notification to the user's
|
Browser push notifications can send a notification to the user's
|
||||||
@ -322,9 +309,9 @@ for details on acceptable values.
|
|||||||
| API URL | <https://api.hipchat.com/v1/rooms/message?auth_token=109jawregoaihj> |
|
| API URL | <https://api.hipchat.com/v1/rooms/message?auth_token=109jawregoaihj> |
|
||||||
| Room ID | 7654321 |
|
| Room ID | 7654321 |
|
||||||
| From Name | LibreNMS |
|
| From Name | LibreNMS |
|
||||||
| Options | color = red <br/> notify = 1 <br/> message_format = text |
|
| Options | color=red |
|
||||||
|
|
||||||
At present the following options are supported: `color`, `notify` and `message_format`.
|
At present the following options are supported: `color`.
|
||||||
|
|
||||||
> Note: The default message format for HipChat messages is HTML. It is
|
> Note: The default message format for HipChat messages is HTML. It is
|
||||||
> recommended that you specify the `text` message format to prevent unexpected
|
> recommended that you specify the `text` message format to prevent unexpected
|
||||||
@ -536,7 +523,7 @@ Here an example using 3 numbers, any amount of numbers is supported:
|
|||||||
|
|
||||||
| Config | Example |
|
| Config | Example |
|
||||||
| ------ | ------- |
|
| ------ | ------- |
|
||||||
| PlaySMS | <https://localhost/index.php?app=ws> |
|
| PlaySMS | <https://localhost/index.php> |
|
||||||
| User | user1 |
|
| User | user1 |
|
||||||
| Token | MYFANCYACCESSTOKEN |
|
| Token | MYFANCYACCESSTOKEN |
|
||||||
| From | My Name |
|
| From | My Name |
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use App\Models\Device;
|
use App\Models\Device;
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Enum\PortAssociationMode;
|
use LibreNMS\Enum\PortAssociationMode;
|
||||||
@ -23,7 +22,6 @@ use LibreNMS\Exceptions\HostUnreachableSnmpException;
|
|||||||
use LibreNMS\Exceptions\InvalidPortAssocModeException;
|
use LibreNMS\Exceptions\InvalidPortAssocModeException;
|
||||||
use LibreNMS\Exceptions\SnmpVersionUnsupportedException;
|
use LibreNMS\Exceptions\SnmpVersionUnsupportedException;
|
||||||
use LibreNMS\Modules\Core;
|
use LibreNMS\Modules\Core;
|
||||||
use LibreNMS\Util\Proxy;
|
|
||||||
|
|
||||||
function array_sort_by_column($array, $on, $order = SORT_ASC)
|
function array_sort_by_column($array, $on, $order = SORT_ASC)
|
||||||
{
|
{
|
||||||
@ -759,14 +757,6 @@ function guidv4($data)
|
|||||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $curl
|
|
||||||
*/
|
|
||||||
function set_curl_proxy($curl)
|
|
||||||
{
|
|
||||||
\LibreNMS\Util\Proxy::applyToCurl($curl);
|
|
||||||
}
|
|
||||||
|
|
||||||
function target_to_id($target)
|
function target_to_id($target)
|
||||||
{
|
{
|
||||||
if ($target[0] . $target[1] == 'g:') {
|
if ($target[0] . $target[1] == 'g:') {
|
||||||
@ -1197,7 +1187,7 @@ function cache_mac_oui()
|
|||||||
//$mac_oui_url_mirror = 'https://raw.githubusercontent.com/wireshark/wireshark/master/manuf';
|
//$mac_oui_url_mirror = 'https://raw.githubusercontent.com/wireshark/wireshark/master/manuf';
|
||||||
|
|
||||||
echo ' -> Downloading ...' . PHP_EOL;
|
echo ' -> Downloading ...' . PHP_EOL;
|
||||||
$get = Http::withOptions(['proxy' => Proxy::forGuzzle()])->get($mac_oui_url);
|
$get = \LibreNMS\Util\Http::client()->get($mac_oui_url);
|
||||||
echo ' -> Processing CSV ...' . PHP_EOL;
|
echo ' -> Processing CSV ...' . PHP_EOL;
|
||||||
$csv_data = $get->body();
|
$csv_data = $get->body();
|
||||||
foreach (explode("\n", $csv_data) as $csv_line) {
|
foreach (explode("\n", $csv_data) as $csv_line) {
|
||||||
@ -1255,7 +1245,7 @@ function cache_peeringdb()
|
|||||||
$ix_keep = [];
|
$ix_keep = [];
|
||||||
foreach (dbFetchRows('SELECT `bgpLocalAs` FROM `devices` WHERE `disabled` = 0 AND `ignore` = 0 AND `bgpLocalAs` > 0 AND (`bgpLocalAs` < 64512 OR `bgpLocalAs` > 65535) AND `bgpLocalAs` < 4200000000 GROUP BY `bgpLocalAs`') as $as) {
|
foreach (dbFetchRows('SELECT `bgpLocalAs` FROM `devices` WHERE `disabled` = 0 AND `ignore` = 0 AND `bgpLocalAs` > 0 AND (`bgpLocalAs` < 64512 OR `bgpLocalAs` > 65535) AND `bgpLocalAs` < 4200000000 GROUP BY `bgpLocalAs`') as $as) {
|
||||||
$asn = $as['bgpLocalAs'];
|
$asn = $as['bgpLocalAs'];
|
||||||
$get = Http::withOptions(['proxy' => Proxy::forGuzzle()])->get($peeringdb_url . '/net?depth=2&asn=' . $asn);
|
$get = \LibreNMS\Util\Http::client()->get($peeringdb_url . '/net?depth=2&asn=' . $asn);
|
||||||
$json_data = $get->body();
|
$json_data = $get->body();
|
||||||
$data = json_decode($json_data);
|
$data = json_decode($json_data);
|
||||||
$ixs = $data->{'data'}[0]->{'netixlan_set'};
|
$ixs = $data->{'data'}[0]->{'netixlan_set'};
|
||||||
@ -1276,7 +1266,7 @@ function cache_peeringdb()
|
|||||||
$pdb_ix_id = dbInsert($insert, 'pdb_ix');
|
$pdb_ix_id = dbInsert($insert, 'pdb_ix');
|
||||||
}
|
}
|
||||||
$ix_keep[] = $pdb_ix_id;
|
$ix_keep[] = $pdb_ix_id;
|
||||||
$get_ix = Http::withOptions(['proxy' => Proxy::forGuzzle()])->get("$peeringdb_url/netixlan?ix_id=$ixid");
|
$get_ix = \LibreNMS\Util\Http::client()->get("$peeringdb_url/netixlan?ix_id=$ixid");
|
||||||
$ix_json = $get_ix->body();
|
$ix_json = $get_ix->body();
|
||||||
$ix_data = json_decode($ix_json);
|
$ix_data = json_decode($ix_json);
|
||||||
$peers = $ix_data->{'data'};
|
$peers = $ix_data->{'data'};
|
||||||
|
@ -823,8 +823,12 @@ return [
|
|||||||
'help' => 'Can be a ENV or HTTP-header field like REMOTE_USER, PHP_AUTH_USER or a custom variant',
|
'help' => 'Can be a ENV or HTTP-header field like REMOTE_USER, PHP_AUTH_USER or a custom variant',
|
||||||
],
|
],
|
||||||
'http_proxy' => [
|
'http_proxy' => [
|
||||||
'description' => 'HTTP(S) Proxy',
|
'description' => 'HTTP Proxy',
|
||||||
'help' => 'Set this as a fallback if http_proxy or https_proxy environment variable is not available.',
|
'help' => 'Set this as a fallback if http_proxy environment variable is not available.',
|
||||||
|
],
|
||||||
|
'https_proxy' => [
|
||||||
|
'description' => 'HTTPS Proxy',
|
||||||
|
'help' => 'Set this as a fallback if https_proxy environment variable is not available.',
|
||||||
],
|
],
|
||||||
'ignore_mount' => [
|
'ignore_mount' => [
|
||||||
'description' => 'Mountpoints to be ignored',
|
'description' => 'Mountpoints to be ignored',
|
||||||
@ -968,6 +972,10 @@ return [
|
|||||||
'nmap' => [
|
'nmap' => [
|
||||||
'description' => 'Path to nmap',
|
'description' => 'Path to nmap',
|
||||||
],
|
],
|
||||||
|
'no_proxy' => [
|
||||||
|
'description' => 'Proxy Exceptions',
|
||||||
|
'help' => 'Set this as a fallback if no_proxy environment variable is not available. Comma seperated list of IPs, hosts or domains to ignore.',
|
||||||
|
],
|
||||||
'opentsdb' => [
|
'opentsdb' => [
|
||||||
'enable' => [
|
'enable' => [
|
||||||
'description' => 'Enable',
|
'description' => 'Enable',
|
||||||
|
@ -3757,7 +3757,25 @@
|
|||||||
"group": "system",
|
"group": "system",
|
||||||
"section": "proxy",
|
"section": "proxy",
|
||||||
"order": 0,
|
"order": 0,
|
||||||
"type": "text"
|
"type": "text",
|
||||||
|
"validate": {
|
||||||
|
"value": [
|
||||||
|
"nullable",
|
||||||
|
"starts_with:http://,https://,tcp://"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https_proxy": {
|
||||||
|
"group": "system",
|
||||||
|
"section": "proxy",
|
||||||
|
"order": 1,
|
||||||
|
"type": "text",
|
||||||
|
"validate": {
|
||||||
|
"value": [
|
||||||
|
"nullable",
|
||||||
|
"starts_with:http://,https://,tcp://"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"icmp_check": {
|
"icmp_check": {
|
||||||
"default": true,
|
"default": true,
|
||||||
@ -4352,6 +4370,13 @@
|
|||||||
"order": 5,
|
"order": 5,
|
||||||
"type": "executable"
|
"type": "executable"
|
||||||
},
|
},
|
||||||
|
"no_proxy": {
|
||||||
|
"group": "system",
|
||||||
|
"default": "localhost,127.0.0.1,::1",
|
||||||
|
"section": "proxy",
|
||||||
|
"order": 2,
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
"notifications.LibreNMS": {
|
"notifications.LibreNMS": {
|
||||||
"default": "https://www.librenms.org/notifications.rss",
|
"default": "https://www.librenms.org/notifications.rss",
|
||||||
"type": "text"
|
"type": "text"
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
parameters:
|
parameters:
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
-
|
|
||||||
message: "#^Static property LibreNMS\\\\Alert\\\\Transport\\\\Sensu\\:\\:\\$severity is never read, only written\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: LibreNMS/Alert/Transport/Sensu.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^If condition is always false\\.$#"
|
message: "#^If condition is always false\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
@ -27,12 +27,11 @@ namespace LibreNMS\Tests;
|
|||||||
|
|
||||||
use RecursiveDirectoryIterator;
|
use RecursiveDirectoryIterator;
|
||||||
use RecursiveIteratorIterator;
|
use RecursiveIteratorIterator;
|
||||||
use RecursiveRegexIterator;
|
|
||||||
use RegexIterator;
|
use RegexIterator;
|
||||||
|
|
||||||
class AlertingTest extends TestCase
|
class AlertingTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testJsonAlertCollection()
|
public function testJsonAlertCollection(): void
|
||||||
{
|
{
|
||||||
$rules = get_rules_from_json();
|
$rules = get_rules_from_json();
|
||||||
$this->assertIsArray($rules);
|
$this->assertIsArray($rules);
|
||||||
@ -41,7 +40,7 @@ class AlertingTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransports()
|
public function testTransports(): void
|
||||||
{
|
{
|
||||||
foreach ($this->getTransportFiles() as $file => $_unused) {
|
foreach ($this->getTransportFiles() as $file => $_unused) {
|
||||||
$parts = explode('/', $file);
|
$parts = explode('/', $file);
|
||||||
@ -52,10 +51,10 @@ class AlertingTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getTransportFiles()
|
private function getTransportFiles(): RegexIterator
|
||||||
{
|
{
|
||||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('LibreNMS/Alert/Transport'));
|
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('LibreNMS/Alert/Transport'));
|
||||||
|
|
||||||
return new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
|
return new RegexIterator($iterator, '/^.+\.php$/i', RegexIterator::GET_MATCH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,21 +22,70 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Tests;
|
namespace LibreNMS\Tests;
|
||||||
|
|
||||||
use LibreNMS\Util\Proxy;
|
use LibreNMS\Config;
|
||||||
|
use LibreNMS\Util\Http;
|
||||||
|
use LibreNMS\Util\Version;
|
||||||
|
|
||||||
class ProxyTest extends TestCase
|
class ProxyTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testShouldBeUsed(): void
|
public function testClientAgentIsCorrect(): void
|
||||||
{
|
{
|
||||||
$this->assertTrue(Proxy::shouldBeUsed('http://example.com/foobar'));
|
$this->assertEquals('LibreNMS/' . Version::VERSION, Http::client()->getOptions()['headers']['User-Agent']);
|
||||||
$this->assertTrue(Proxy::shouldBeUsed('foo/bar'));
|
}
|
||||||
$this->assertTrue(Proxy::shouldBeUsed('192.168.0.1'));
|
|
||||||
$this->assertTrue(Proxy::shouldBeUsed('2001:db8::8a2e:370:7334'));
|
|
||||||
|
|
||||||
$this->assertFalse(Proxy::shouldBeUsed('http://localhost/foobar'));
|
public function testProxyIsNotSet(): void
|
||||||
$this->assertFalse(Proxy::shouldBeUsed('localhost/foobar'));
|
{
|
||||||
$this->assertFalse(Proxy::shouldBeUsed('127.0.0.1'));
|
Config::set('http_proxy', '');
|
||||||
$this->assertFalse(Proxy::shouldBeUsed('127.0.0.1:1337'));
|
Config::set('https_proxy', '');
|
||||||
$this->assertFalse(Proxy::shouldBeUsed('::1'));
|
Config::set('no_proxy', '');
|
||||||
|
$client_options = Http::client()->getOptions();
|
||||||
|
$this->assertEmpty($client_options['proxy']['http']);
|
||||||
|
$this->assertEmpty($client_options['proxy']['https']);
|
||||||
|
$this->assertEmpty($client_options['proxy']['no']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProxyIsSet(): void
|
||||||
|
{
|
||||||
|
Config::set('http_proxy', 'http://proxy:5000');
|
||||||
|
Config::set('https_proxy', 'tcp://proxy:5183');
|
||||||
|
Config::set('no_proxy', 'localhost,127.0.0.1,::1,.domain.com');
|
||||||
|
$client_options = Http::client()->getOptions();
|
||||||
|
$this->assertEquals('http://proxy:5000', $client_options['proxy']['http']);
|
||||||
|
$this->assertEquals('tcp://proxy:5183', $client_options['proxy']['https']);
|
||||||
|
$this->assertEquals([
|
||||||
|
'localhost',
|
||||||
|
'127.0.0.1',
|
||||||
|
'::1',
|
||||||
|
'.domain.com',
|
||||||
|
], $client_options['proxy']['no']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProxyIsSetFromEnv(): void
|
||||||
|
{
|
||||||
|
Config::set('http_proxy', '');
|
||||||
|
Config::set('https_proxy', '');
|
||||||
|
Config::set('no_proxy', '');
|
||||||
|
|
||||||
|
putenv('HTTP_PROXY=someproxy:3182');
|
||||||
|
putenv('HTTPS_PROXY=https://someproxy:3182');
|
||||||
|
putenv('NO_PROXY=.there.com');
|
||||||
|
|
||||||
|
$client_options = Http::client()->getOptions();
|
||||||
|
$this->assertEquals('someproxy:3182', $client_options['proxy']['http']);
|
||||||
|
$this->assertEquals('https://someproxy:3182', $client_options['proxy']['https']);
|
||||||
|
$this->assertEquals([
|
||||||
|
'.there.com',
|
||||||
|
], $client_options['proxy']['no']);
|
||||||
|
|
||||||
|
putenv('http_proxy=otherproxy:3182');
|
||||||
|
putenv('https_proxy=otherproxy:3183');
|
||||||
|
putenv('no_proxy=dontproxymebro');
|
||||||
|
|
||||||
|
$client_options = Http::client()->getOptions();
|
||||||
|
$this->assertEquals('otherproxy:3182', $client_options['proxy']['http']);
|
||||||
|
$this->assertEquals('otherproxy:3183', $client_options['proxy']['https']);
|
||||||
|
$this->assertEquals([
|
||||||
|
'dontproxymebro',
|
||||||
|
], $client_options['proxy']['no']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace LibreNMS\Tests\Traits;
|
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use GuzzleHttp\Handler\MockHandler;
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Middleware;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mixin \LibreNMS\Tests\TestCase
|
|
||||||
*/
|
|
||||||
trait MockGuzzleClient
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var MockHandler
|
|
||||||
*/
|
|
||||||
private $guzzleMockHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $guzzleConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $guzzleHistory = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Guzzle MockHandler and bind Client with the handler to the Laravel container
|
|
||||||
*
|
|
||||||
* @param array $queue Sequential Responses to give to the client.
|
|
||||||
* @param array $config Guzzle config settings.
|
|
||||||
*/
|
|
||||||
public function mockGuzzleClient(array $queue, array $config = []): MockHandler
|
|
||||||
{
|
|
||||||
$this->guzzleConfig = $config;
|
|
||||||
$this->guzzleMockHandler = new MockHandler($queue);
|
|
||||||
|
|
||||||
$this->app->bind(Client::class, function () {
|
|
||||||
$handlerStack = HandlerStack::create($this->guzzleMockHandler);
|
|
||||||
$handlerStack->push(Middleware::history($this->guzzleHistory));
|
|
||||||
|
|
||||||
return new Client(array_merge($this->guzzleConfig, ['handler' => $handlerStack]));
|
|
||||||
});
|
|
||||||
|
|
||||||
return $this->guzzleMockHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the request and response history to inspect
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function guzzleHistory(): array
|
|
||||||
{
|
|
||||||
return $this->guzzleHistory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the request history to inspect
|
|
||||||
*
|
|
||||||
* @return \GuzzleHttp\Psr7\Request[]
|
|
||||||
*/
|
|
||||||
public function guzzleRequestHistory(): array
|
|
||||||
{
|
|
||||||
return array_column($this->guzzleHistory, 'request');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the response history to inspect
|
|
||||||
*
|
|
||||||
* @return \GuzzleHttp\Psr7\Response[]
|
|
||||||
*/
|
|
||||||
public function guzzleResponseHistory(): array
|
|
||||||
{
|
|
||||||
return array_column($this->guzzleHistory, 'response');
|
|
||||||
}
|
|
||||||
}
|
|
155
tests/Unit/Alert/Transports/SlackTest.php
Normal file
155
tests/Unit/Alert/Transports/SlackTest.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SlackTest.php
|
||||||
|
*
|
||||||
|
* -Description-
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @link https://www.librenms.org
|
||||||
|
*
|
||||||
|
* @copyright 2022 Tony Murray
|
||||||
|
* @author Tony Murray <murraytony@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace LibreNMS\Tests\Unit\Alert\Transports;
|
||||||
|
|
||||||
|
use App\Models\AlertTransport;
|
||||||
|
use App\Models\Device;
|
||||||
|
use Illuminate\Http\Client\Request;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use LibreNMS\Alert\AlertData;
|
||||||
|
use LibreNMS\Alert\Transport;
|
||||||
|
use LibreNMS\Tests\TestCase;
|
||||||
|
|
||||||
|
class SlackTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testSlackNoConfigDelivery(): void
|
||||||
|
{
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
$slack = new Transport\Slack(new AlertTransport);
|
||||||
|
|
||||||
|
/** @var Device $mock_device */
|
||||||
|
$mock_device = Device::factory()->make();
|
||||||
|
$slack->deliverAlert(AlertData::testData($mock_device));
|
||||||
|
|
||||||
|
Http::assertSent(function (Request $request) {
|
||||||
|
return
|
||||||
|
$request->url() == '' &&
|
||||||
|
$request->method() == 'POST' &&
|
||||||
|
$request->hasHeader('Content-Type', 'application/json') &&
|
||||||
|
$request->data() == [
|
||||||
|
'attachments' => [
|
||||||
|
[
|
||||||
|
'fallback' => 'This is a test alert',
|
||||||
|
'color' => '#ff0000',
|
||||||
|
'title' => 'Testing transport from LibreNMS',
|
||||||
|
'text' => 'This is a test alert',
|
||||||
|
'mrkdwn_in' => [
|
||||||
|
'text',
|
||||||
|
'fallback',
|
||||||
|
],
|
||||||
|
'author_name' => null,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'channel' => null,
|
||||||
|
'icon_emoji' => null,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSlackLegacyDelivery(): void
|
||||||
|
{
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
$slack = new Transport\Slack(new AlertTransport([
|
||||||
|
'transport_config' => [
|
||||||
|
'slack-url' => 'https://slack.com/some/webhook',
|
||||||
|
'slack-options' => "icon_emoji=smile\nauthor=Me\nchannel=Alerts",
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
|
/** @var Device $mock_device */
|
||||||
|
$mock_device = Device::factory()->make();
|
||||||
|
$slack->deliverAlert(AlertData::testData($mock_device));
|
||||||
|
|
||||||
|
Http::assertSent(function (Request $request) {
|
||||||
|
return
|
||||||
|
$request->url() == 'https://slack.com/some/webhook' &&
|
||||||
|
$request->method() == 'POST' &&
|
||||||
|
$request->hasHeader('Content-Type', 'application/json') &&
|
||||||
|
$request->data() == [
|
||||||
|
'attachments' => [
|
||||||
|
[
|
||||||
|
'fallback' => 'This is a test alert',
|
||||||
|
'color' => '#ff0000',
|
||||||
|
'title' => 'Testing transport from LibreNMS',
|
||||||
|
'text' => 'This is a test alert',
|
||||||
|
'mrkdwn_in' => [
|
||||||
|
'text',
|
||||||
|
'fallback',
|
||||||
|
],
|
||||||
|
'author_name' => 'Me',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'channel' => 'Alerts',
|
||||||
|
'icon_emoji' => ':smile:',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSlackDelivery(): void
|
||||||
|
{
|
||||||
|
Http::fake();
|
||||||
|
|
||||||
|
$slack = new Transport\Slack(new AlertTransport([
|
||||||
|
'transport_config' => [
|
||||||
|
'slack-url' => 'https://slack.com/some/webhook',
|
||||||
|
'slack-options' => "icon_emoji=smile\nauthor=Me\nchannel=Alerts",
|
||||||
|
'slack-icon_emoji' => ':slight_smile:',
|
||||||
|
'slack-author' => 'Other',
|
||||||
|
'slack-channel' => 'Critical',
|
||||||
|
],
|
||||||
|
]));
|
||||||
|
|
||||||
|
/** @var Device $mock_device */
|
||||||
|
$mock_device = Device::factory()->make();
|
||||||
|
$slack->deliverAlert(AlertData::testData($mock_device));
|
||||||
|
|
||||||
|
Http::assertSent(function (Request $request) {
|
||||||
|
return
|
||||||
|
$request->url() == 'https://slack.com/some/webhook' &&
|
||||||
|
$request->method() == 'POST' &&
|
||||||
|
$request->hasHeader('Content-Type', 'application/json') &&
|
||||||
|
$request->data() == [
|
||||||
|
'attachments' => [
|
||||||
|
[
|
||||||
|
'fallback' => 'This is a test alert',
|
||||||
|
'color' => '#ff0000',
|
||||||
|
'title' => 'Testing transport from LibreNMS',
|
||||||
|
'text' => 'This is a test alert',
|
||||||
|
'mrkdwn_in' => [
|
||||||
|
'text',
|
||||||
|
'fallback',
|
||||||
|
],
|
||||||
|
'author_name' => 'Other',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'channel' => 'Critical',
|
||||||
|
'icon_emoji' => ':slight_smile:',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -3,34 +3,31 @@
|
|||||||
namespace LibreNMS\Tests\Unit;
|
namespace LibreNMS\Tests\Unit;
|
||||||
|
|
||||||
use App\Models\AlertTransport;
|
use App\Models\AlertTransport;
|
||||||
use GuzzleHttp\Psr7\Response;
|
use Illuminate\Http\Client\Request;
|
||||||
use LibreNMS\Config;
|
use Illuminate\Support\Facades\Http as LaravelHttp;
|
||||||
use LibreNMS\Tests\TestCase;
|
use LibreNMS\Tests\TestCase;
|
||||||
use LibreNMS\Tests\Traits\MockGuzzleClient;
|
|
||||||
|
|
||||||
class ApiTransportTest extends TestCase
|
class ApiTransportTest extends TestCase
|
||||||
{
|
{
|
||||||
use MockGuzzleClient;
|
|
||||||
|
|
||||||
public function testGetMultilineVariables(): void
|
public function testGetMultilineVariables(): void
|
||||||
{
|
{
|
||||||
/** @var AlertTransport $transport */
|
/** @var AlertTransport $transport */
|
||||||
$transport = AlertTransport::factory()->api('text={{ $msg }}')->make();
|
$transport = AlertTransport::factory()->api('text={{ $msg }}')->make();
|
||||||
|
|
||||||
$this->mockGuzzleClient([
|
LaravelHttp::fake([
|
||||||
new Response(200),
|
'*' => LaravelHttp::response(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$obj = ['msg' => "This is a multi-line\nalert."];
|
$obj = ['msg' => "This is a multi-line\nalert."];
|
||||||
$opts = Config::get('alert.transports.' . $transport->transport_type);
|
$result = $transport->instance()->deliverAlert($obj);
|
||||||
$result = $transport->instance()->deliverAlert($obj, $opts);
|
|
||||||
|
|
||||||
$this->assertTrue($result);
|
$this->assertTrue($result);
|
||||||
|
|
||||||
$history = $this->guzzleRequestHistory();
|
LaravelHttp::assertSentCount(1);
|
||||||
$this->assertCount(1, $history);
|
LaravelHttp::assertSent(function (Request $request) {
|
||||||
$this->assertEquals('GET', $history[0]->getMethod());
|
return $request->method() == 'GET' &&
|
||||||
$this->assertEquals('text=This%20is%20a%20multi-line%0Aalert.', $history[0]->getUri()->getQuery());
|
$request->url() == 'https://librenms.org?text=This%20is%20a%20multi-line%0Aalert.';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPostMultilineVariables(): void
|
public function testPostMultilineVariables(): void
|
||||||
@ -42,21 +39,20 @@ class ApiTransportTest extends TestCase
|
|||||||
'bodytext={{ $msg }}',
|
'bodytext={{ $msg }}',
|
||||||
)->make();
|
)->make();
|
||||||
|
|
||||||
$this->mockGuzzleClient([
|
LaravelHttp::fake([
|
||||||
new Response(200),
|
'*' => LaravelHttp::response(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$obj = ['msg' => "This is a post multi-line\nalert."];
|
$obj = ['msg' => "This is a post multi-line\nalert."];
|
||||||
$opts = Config::get('alert.transports.' . $transport->transport_type);
|
$result = $transport->instance()->deliverAlert($obj);
|
||||||
$result = $transport->instance()->deliverAlert($obj, $opts);
|
|
||||||
|
|
||||||
$this->assertTrue($result);
|
$this->assertTrue($result);
|
||||||
|
|
||||||
$history = $this->guzzleRequestHistory();
|
LaravelHttp::assertSentCount(1);
|
||||||
$this->assertCount(1, $history);
|
LaravelHttp::assertSent(function (Request $request) {
|
||||||
$this->assertEquals('POST', $history[0]->getMethod());
|
return $request->method() == 'POST' &&
|
||||||
// FUBAR
|
$request->url() == 'https://librenms.org?text=This%20is%20a%20post%20multi-line%0Aalert.' &&
|
||||||
$this->assertEquals('text=This%20is%20a%20post%20multi-line%0Aalert.', $history[0]->getUri()->getQuery());
|
$request->body() == "bodytext=This is a post multi-line\nalert.";
|
||||||
$this->assertEquals("bodytext=This is a post multi-line\nalert.", (string) $history[0]->getBody());
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,21 +25,16 @@
|
|||||||
|
|
||||||
namespace LibreNMS\Tests\Unit\Data;
|
namespace LibreNMS\Tests\Unit\Data;
|
||||||
|
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use Illuminate\Support\Facades\Http as LaravelHttp;
|
||||||
use GuzzleHttp\Psr7\Request;
|
|
||||||
use GuzzleHttp\Psr7\Response;
|
|
||||||
use LibreNMS\Config;
|
use LibreNMS\Config;
|
||||||
use LibreNMS\Data\Store\Prometheus;
|
use LibreNMS\Data\Store\Prometheus;
|
||||||
use LibreNMS\Tests\TestCase;
|
use LibreNMS\Tests\TestCase;
|
||||||
use LibreNMS\Tests\Traits\MockGuzzleClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group datastores
|
* @group datastores
|
||||||
*/
|
*/
|
||||||
class PrometheusStoreTest extends TestCase
|
class PrometheusStoreTest extends TestCase
|
||||||
{
|
{
|
||||||
use MockGuzzleClient;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
@ -48,26 +43,20 @@ class PrometheusStoreTest extends TestCase
|
|||||||
Config::set('prometheus.url', 'http://fake:9999');
|
Config::set('prometheus.url', 'http://fake:9999');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFailWrite()
|
public function testFailWrite(): void
|
||||||
{
|
{
|
||||||
$this->mockGuzzleClient([
|
LaravelHttp::fakeSequence()->push('Bad response', 422);
|
||||||
new Response(422, [], 'Bad response'),
|
|
||||||
new RequestException('Exception thrown', new Request('POST', 'test')),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$prometheus = app(Prometheus::class);
|
$prometheus = app(Prometheus::class);
|
||||||
|
|
||||||
\Log::shouldReceive('debug');
|
\Log::shouldReceive('debug');
|
||||||
\Log::shouldReceive('error')->once()->with("Prometheus Exception: Client error: `POST http://fake:9999/metrics/job/librenms/instance/test/measurement/none` resulted in a `422 Unprocessable Entity` response:\nBad response\n");
|
\Log::shouldReceive('error')->once()->with('Prometheus Error: Bad response');
|
||||||
\Log::shouldReceive('error')->once()->with('Prometheus Exception: Exception thrown');
|
|
||||||
$prometheus->put(['hostname' => 'test'], 'none', [], ['one' => 1]);
|
|
||||||
$prometheus->put(['hostname' => 'test'], 'none', [], ['one' => 1]);
|
$prometheus->put(['hostname' => 'test'], 'none', [], ['one' => 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSimpleWrite()
|
public function testSimpleWrite(): void
|
||||||
{
|
{
|
||||||
$this->mockGuzzleClient([
|
LaravelHttp::fake([
|
||||||
new Response(200),
|
'*' => LaravelHttp::response(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$prometheus = app(Prometheus::class);
|
$prometheus = app(Prometheus::class);
|
||||||
@ -82,12 +71,11 @@ class PrometheusStoreTest extends TestCase
|
|||||||
|
|
||||||
$prometheus->put($device, $measurement, $tags, $fields);
|
$prometheus->put($device, $measurement, $tags, $fields);
|
||||||
|
|
||||||
$history = $this->guzzleRequestHistory();
|
LaravelHttp::assertSentCount(1);
|
||||||
$this->assertCount(1, $history, 'Did not receive the expected number of requests');
|
LaravelHttp::assertSent(function (\Illuminate\Http\Client\Request $request) {
|
||||||
$this->assertEquals('POST', $history[0]->getMethod());
|
return $request->method() == 'POST' &&
|
||||||
$this->assertEquals('/metrics/job/librenms/instance/testhost/measurement/testmeasure/ifName/testifname/type/testtype', $history[0]->getUri()->getPath());
|
$request->url() == 'http://fake:9999/metrics/job/librenms/instance/testhost/measurement/testmeasure/ifName/testifname/type/testtype' &&
|
||||||
$this->assertEquals('fake', $history[0]->getUri()->getHost());
|
$request->body() == "ifIn 234234\nifOut 53453\n";
|
||||||
$this->assertEquals(9999, $history[0]->getUri()->getPort());
|
});
|
||||||
$this->assertEquals("ifIn 234234\nifOut 53453\n", (string) $history[0]->getBody());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user