diff --git a/LibreNMS/Alert/AlertData.php b/LibreNMS/Alert/AlertData.php index 25246aaf35..cee04d835f 100644 --- a/LibreNMS/Alert/AlertData.php +++ b/LibreNMS/Alert/AlertData.php @@ -25,6 +25,11 @@ namespace LibreNMS\Alert; +use App\Models\Device; +use LibreNMS\Config; +use LibreNMS\Enum\AlertState; +use LibreNMS\Util\Time; + class AlertData extends \Illuminate\Support\Collection { public function __get($name) @@ -35,4 +40,50 @@ class AlertData extends \Illuminate\Support\Collection 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' => '{}', + ]; + } } diff --git a/LibreNMS/Alert/Transport.php b/LibreNMS/Alert/Transport.php index 37f44d8810..9f242fcaf1 100644 --- a/LibreNMS/Alert/Transport.php +++ b/LibreNMS/Alert/Transport.php @@ -4,7 +4,6 @@ namespace LibreNMS\Alert; use App\Models\AlertTransport; use App\View\SimpleTemplate; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Str; use LibreNMS\Config; use LibreNMS\Enum\AlertState; @@ -12,11 +11,8 @@ use LibreNMS\Interfaces\Alert\Transport as TransportInterface; abstract class Transport implements TransportInterface { - protected $config; - /** - * @var string - */ - protected $name; + protected ?array $config; + protected string $name = ''; public static function make(string $type): TransportInterface { @@ -43,25 +39,9 @@ abstract class Transport implements TransportInterface return $list; } - /** - * Transport constructor. - * - * @param null $transport - */ - public function __construct($transport = null) + public function __construct(?AlertTransport $transport = null) { - if (! empty($transport)) { - 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 = []; - } - } - } + $this->config = $transport ? $transport->transport_config : []; } /** @@ -69,7 +49,7 @@ abstract class Transport implements TransportInterface */ public function name(): string { - if ($this->name !== null) { + if ($this->name !== '') { return $this->name; } @@ -158,7 +138,7 @@ abstract class Transport implements TransportInterface return 'LibreNMS\\Alert\\Transport\\' . ucfirst($type); } - protected function isHtmlContent($content): bool + protected function isHtmlContent(string $content): bool { return $content !== strip_tags($content); } diff --git a/LibreNMS/Alert/Transport/Alerta.php b/LibreNMS/Alert/Transport/Alerta.php index 191a6b3f99..039cee3c3a 100644 --- a/LibreNMS/Alert/Transport/Alerta.php +++ b/LibreNMS/Alert/Transport/Alerta.php @@ -5,6 +5,7 @@ * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. Please see LICENSE.txt at the top level of * the source code distribution for details. */ + /** * API Transport * @@ -16,76 +17,53 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Config; use LibreNMS\Enum\AlertState; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; +use LibreNMS\Util\Url; class Alerta extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - $opts['url'] = $this->config['alerta-url']; - $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']; + $severity = ($alert_data['state'] == AlertState::RECOVERED ? $this->config['recoverstate'] : $this->config['alertstate']); $data = [ - 'resource' => $devicehostname, - 'event' => $obj['name'], - 'environment' => $opts['environment'], + 'resource' => $alert_data['display'], + 'event' => $alert_data['name'], + 'environment' => $this->config['environment'], 'severity' => $severity, - 'service' => [$obj['title']], - 'group' => $obj['name'], - 'value' => $obj['state'], - 'text' => $text, - 'tags' => [$obj['title']], + 'service' => [$alert_data['title']], + 'group' => $alert_data['name'], + 'value' => $alert_data['state'], + 'text' => strip_tags($alert_data['msg']), + 'tags' => [$alert_data['title']], 'attributes' => [ - 'sysName' => $obj['sysName'], - 'sysDescr' => $obj['sysDescr'], - 'os' => $obj['os'], - 'type' => $obj['type'], - 'ip' => $obj['ip'], - 'uptime' => $obj['uptime_long'], - 'moreInfo' => '' . $devicehostname . '', + 'sysName' => $alert_data['sysName'], + 'sysDescr' => $alert_data['sysDescr'], + 'os' => $alert_data['os'], + 'type' => $alert_data['type'], + 'ip' => $alert_data['ip'], + 'uptime' => $alert_data['uptime_long'], + 'moreInfo' => '' . $alert_data['display'] . '', ], - 'origin' => $obj['rule'], - 'type' => $obj['title'], + 'origin' => $alert_data['rule'], + '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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Alertmanager.php b/LibreNMS/Alert/Transport/Alertmanager.php index 929666c630..b479452621 100644 --- a/LibreNMS/Alert/Transport/Alertmanager.php +++ b/LibreNMS/Alert/Transport/Alertmanager.php @@ -23,116 +23,67 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Config; use LibreNMS\Enum\AlertState; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; +use LibreNMS\Util\Url; 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']); - $alertmanager_opts['url'] = $this->config['alertmanager-url']; + $url = $this->config['alertmanager-url']; + $username = $this->config['alertmanager-username']; + $password = $this->config['alertmanager-password']; - $alertmanager_username = $this->config['alertmanager-username']; - $alertmanager_password = $this->config['alertmanager-password']; - - 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']); + $alertmanager_status = $alert_data['state'] == AlertState::RECOVERED ? 'endsAt' : 'startsAt'; + $alertmanager_msg = strip_tags($alert_data['msg']); $data = [[ $alertmanager_status => date('c'), - 'generatorURL' => $gen_url, + 'generatorURL' => Url::deviceUrl($alert_data['device_id']), 'annotations' => [ - 'summary' => $obj['name'], - 'title' => $obj['title'], + 'summary' => $alert_data['name'], + 'title' => $alert_data['title'], 'description' => $alertmanager_msg, ], 'labels' => [ - 'alertname' => $obj['name'], - 'severity' => $obj['severity'], - 'instance' => $obj['hostname'], + 'alertname' => $alert_data['name'], + 'severity' => $alert_data['severity'], + 'instance' => $alert_data['hostname'], ], ]]; - $url = $api['url']; - unset($api['url']); - foreach ($api as $label => $value) { + $alertmanager_opts = $this->parseUserOptions($this->config['alertmanager-options']); + foreach ($alertmanager_opts as $label => $value) { // To allow dynamic values - if (preg_match('/^extra_[A-Za-z0-9_]+$/', $label) && (! empty($obj['faults'][1][$value]))) { - $data[0]['labels'][$label] = $obj['faults'][1][$value]; + if (preg_match('/^extra_[A-Za-z0-9_]+$/', $label) && ! empty($alert_data['faults'][1][$value])) { + $data[0]['labels'][$label] = $alert_data['faults'][1][$value]; } else { $data[0]['labels'][$label] = $value; } } - return $this->postAlerts($url, $data, $username, $password); - } - - 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); + $client = Http::client()->timeout(5); if ($username != '' && $password != '') { - curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - curl_setopt($curl, CURLOPT_USERNAME, $username); - curl_setopt($curl, CURLOPT_PASSWORD, $password); + $client->withBasicAuth($username, $password); } - $alert_message = json_encode($data); - curl_setopt($curl, CURLOPT_POSTFIELDS, $alert_message); - foreach (explode(',', $url) as $am) { $post_url = ($am . '/api/v2/alerts'); - curl_setopt($curl, CURLOPT_URL, $post_url); - $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); + $res = $client->post($post_url, $data); + if ($res->successful()) { return true; } } - $err = "Unable to POST to Alertmanager at $post_url ."; - - 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; + throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $alertmanager_msg, $data); } - public static function configTemplate() + public static function configTemplate(): array { return [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Api.php b/LibreNMS/Alert/Transport/Api.php index dd82538ee4..44060310b3 100644 --- a/LibreNMS/Alert/Transport/Api.php +++ b/LibreNMS/Alert/Transport/Api.php @@ -26,73 +26,57 @@ namespace LibreNMS\Alert\Transport; use App\View\SimpleTemplate; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; 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']; - $options = $this->config['api-options']; - $headers = $this->config['api-headers']; - $body = $this->config['api-body']; - $method = $this->config['api-method']; - $auth = [$this->config['api-auth-username'], $this->config['api-auth-password']]; + $request_body = $this->config['api-body']; + $username = $this->config['api-auth-username']; + $password = $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) - { - $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() + public static function configTemplate(): array { return [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Boxcar.php b/LibreNMS/Alert/Transport/Boxcar.php deleted file mode 100644 index 34fe5626a0..0000000000 --- a/LibreNMS/Alert/Transport/Boxcar.php +++ /dev/null @@ -1,142 +0,0 @@ - - * 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 . */ - -/* Copyright (C) 2015 Daniel Preussker - * 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 . */ - -/** - * Boxcar API Transport - * - * @author trick77 - * @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', - ], - ]; - } -} diff --git a/LibreNMS/Alert/Transport/Browserpush.php b/LibreNMS/Alert/Transport/Browserpush.php index 8c9808e3f8..94629db81e 100644 --- a/LibreNMS/Alert/Transport/Browserpush.php +++ b/LibreNMS/Alert/Transport/Browserpush.php @@ -32,9 +32,9 @@ use Notification; 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) { return $query->where('user_id', $user_id); @@ -49,7 +49,7 @@ class Browserpush extends Transport return true; } - public static function configTemplate() + public static function configTemplate(): array { $users = [__('All Users') => 0]; foreach (User::get(['user_id', 'username', 'realname']) as $user) { diff --git a/LibreNMS/Alert/Transport/Canopsis.php b/LibreNMS/Alert/Transport/Canopsis.php index 9bee95d182..c1dcbe8b3f 100644 --- a/LibreNMS/Alert/Transport/Canopsis.php +++ b/LibreNMS/Alert/Transport/Canopsis.php @@ -9,27 +9,14 @@ use PhpAmqpLib\Message\AMQPMessage; class Canopsis extends Transport { - public function deliverAlert($obj, $opts) - { - if (! empty($this->config)) { - $opts['host'] = $this->config['canopsis-host']; - $opts['port'] = $this->config['canopsis-port']; - $opts['user'] = $this->config['canopsis-user']; - $opts['pass'] = $this->config['canopsis-pass']; - $opts['vhost'] = $this->config['canopsis-vhost']; - } - - return $this->contactCanopsis($obj, $opts); - } - - public function contactCanopsis($obj, $opts) + public function deliverAlert(array $alert_data): bool { // Configurations - $host = $opts['host']; - $port = $opts['port']; - $user = $opts['user']; - $pass = $opts['pass']; - $vhost = $opts['vhost']; + $host = $this->config['canopsis-host']; + $port = $this->config['canopsis-port']; + $user = $this->config['canopsis-user']; + $pass = $this->config['canopsis-pass']; + $vhost = $this->config['canopsis-vhost']; $exchange = 'canopsis.events'; // Connection @@ -41,7 +28,7 @@ class Canopsis extends Transport $ch->exchange_declare($exchange, AMQPExchangeType::TOPIC, false, true, false); // Create Canopsis event, see: https://github.com/capensis/canopsis/wiki/Event-specification - switch ($obj['severity']) { + switch ($alert_data['severity']) { case 'ok': $state = 0; break; @@ -60,10 +47,10 @@ class Canopsis extends Transport 'connector_name' => 'LibreNMS1', 'event_type' => 'check', 'source_type' => 'resource', - 'component' => $obj['hostname'], - 'resource' => $obj['name'], + 'component' => $alert_data['hostname'], + 'resource' => $alert_data['name'], 'state' => $state, - 'output' => $obj['msg'], + 'output' => $alert_data['msg'], 'display_name' => 'librenms', ]; $msg_raw = json_encode($msg_body); @@ -84,7 +71,7 @@ class Canopsis extends Transport return true; } - public static function configTemplate() + public static function configTemplate(): array { return [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Ciscospark.php b/LibreNMS/Alert/Transport/Ciscospark.php index 983cabc595..c87f5bc6fc 100644 --- a/LibreNMS/Alert/Transport/Ciscospark.php +++ b/LibreNMS/Alert/Transport/Ciscospark.php @@ -13,75 +13,41 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; 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 = $opts['roomid']; - $token = $opts['token']; - } 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; + $room_id = $this->config['room-id']; + $token = $this->config['api-token']; + $url = 'https://api.ciscospark.com/v1/messages'; $data = [ 'roomId' => $room_id, ]; - $akey = 'text'; if ($this->config['use-markdown'] === 'on') { - $lines = explode("\n", $obj['msg']); - $mlines = []; - /* 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'; + // Remove blank lines as they create weird markdown behaviors. + $data['markdown'] = preg_replace('/^\s+/m', '', $alert_data['msg']); } else { - $text = strip_tags($obj['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; + $data['text'] = strip_tags($alert_data['msg']); } - 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Clickatell.php b/LibreNMS/Alert/Transport/Clickatell.php index d4b2237808..5d074f5b4f 100644 --- a/LibreNMS/Alert/Transport/Clickatell.php +++ b/LibreNMS/Alert/Transport/Clickatell.php @@ -24,39 +24,30 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Clickatell extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - $clickatell_opts['token'] = $this->config['clickatell-token']; - $clickatell_opts['to'] = preg_split('/([,\r\n]+)/', $this->config['clickatell-numbers']); + $url = 'https://platform.clickatell.com/messages/http/send'; + $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) - { - $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; + if ($res->successful()) { + 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Discord.php b/LibreNMS/Alert/Transport/Discord.php index 4e43b8906b..7c63333b0a 100644 --- a/LibreNMS/Alert/Transport/Discord.php +++ b/LibreNMS/Alert/Transport/Discord.php @@ -31,7 +31,7 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; use LibreNMS\Exceptions\AlertTransportDeliveryException; -use LibreNMS\Util\Proxy; +use LibreNMS\Util\Http; class Discord extends Transport { @@ -43,26 +43,16 @@ class Discord extends Transport 'rule' => 'Rule', ]; - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - $discord_opts = [ - 'url' => $this->config['url'], - 'options' => $this->parseUserOptions($this->config['options']), - ]; + $added_fields = $this->parseUserOptions($this->config['options']); - return $this->contactDiscord($obj, $discord_opts); - } - - 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']))); + $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']))); // 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 = [ 'embeds' => [ @@ -70,36 +60,29 @@ class Discord extends Transport 'title' => $discord_title, 'color' => $color, 'description' => $discord_msg, - 'fields' => $this->createDiscordFields($obj, $discord_opts), + 'fields' => $this->createDiscordFields($alert_data), 'footer' => [ 'text' => $footer_text, ], ], ], ]; - if (! empty($discord_opts['options'])) { - $data = array_merge($data, $discord_opts['options']); + if (! empty($added_fields)) { + $data = array_merge($data, $added_fields); } $data = $this->embedGraphs($data); // remove all remaining HTML tags $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); - $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - if ($code != 204) { - throw new AlertTransportDeliveryException($obj, $code, $ret, $alert_message, $data); + $res = Http::client()->post($this->config['url'], $data); + + if ($res->successful()) { + return true; } - return true; + throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $discord_msg, $data); } private function embedGraphs(array $data): array @@ -118,26 +101,26 @@ class Discord extends Transport return $data; } - public function createDiscordFields($obj, $discord_opts) + public function createDiscordFields(array $alert_data): array { $result = []; 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. - if (! $obj[$objKey]) { + if (! $alert_data[$objKey]) { continue; } - array_push($result, [ + $result[] = [ 'name' => $discordKey, - 'value' => $obj[$objKey], - ]); + 'value' => $alert_data[$objKey], + ]; } return $result; } - public static function configTemplate() + public static function configTemplate(): array { return [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Dummy.php b/LibreNMS/Alert/Transport/Dummy.php index b855a57da0..b13c588b02 100644 --- a/LibreNMS/Alert/Transport/Dummy.php +++ b/LibreNMS/Alert/Transport/Dummy.php @@ -24,22 +24,16 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; +use LibreNMS\Exceptions\AlertTransportDeliveryException; class Dummy extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - var_dump($obj); - - return true; + throw new AlertTransportDeliveryException($alert_data, 0, 'Dummy transport always fails', $alert_data['msg']); } - public function contactDummy() - { - return true; - } - - public static function configTemplate() + public static function configTemplate(): array { return [ 'validation' => [], diff --git a/LibreNMS/Alert/Transport/Elasticsearch.php b/LibreNMS/Alert/Transport/Elasticsearch.php index 0aab6aaafa..6d5eccc218 100644 --- a/LibreNMS/Alert/Transport/Elasticsearch.php +++ b/LibreNMS/Alert/Transport/Elasticsearch.php @@ -19,191 +19,130 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; use LibreNMS\Enum\AlertState; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Elasticsearch extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - if (! empty($this->config)) { - $opts['es_host'] = $this->config['es-host']; - $opts['es_port'] = $this->config['es-port']; - $opts['es_index'] = $this->config['es-pattern']; - $opts['es_proxy'] = $this->config['es-proxy']; - } - - return $this->contactElasticsearch($obj, $opts); - } - - public function contactElasticsearch($obj, $opts) - { - $es_host = '127.0.0.1'; - $es_port = 9200; - $index = date("\l\i\b\\r\\e\\n\m\s\-Y.m.d"); + $es_host = $this->config['es-host']; + $es_port = $this->config['es-port'] ?: 9200; + $index = date($this->config['es-pattern'] ?: "\l\i\b\\r\\e\\n\m\s\-Y.m.d"); $type = 'alert'; - $severity = $obj['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']); - } + $severity = $alert_data['severity']; $host = $es_host . ':' . $es_port . '/' . $index . '/' . $type; - switch ($obj['state']) { - case AlertState::RECOVERED: - $state = 'ok'; - break; - case AlertState::ACTIVE: - $state = $severity; - break; - case AlertState::ACKNOWLEDGED: - $state = 'acknowledged'; - break; - case AlertState::WORSE: - $state = 'worse'; - break; - case AlertState::BETTER: - $state = 'better'; - break; - default: - $state = 'unknown'; - break; - } + $state = match ($alert_data['state']) { + AlertState::RECOVERED => 'ok', + AlertState::ACTIVE => $severity, + AlertState::ACKNOWLEDGED => 'acknowledged', + AlertState::WORSE => 'worse', + AlertState::BETTER => 'better', + default => 'unknown', + }; $data = [ '@timestamp' => date('c'), 'host' => gethostname(), - 'location' => $obj['location'], - 'title' => $obj['name'], - 'message' => $obj['string'], - 'device_id' => $obj['device_id'], - 'device_name' => $obj['hostname'], - 'device_hardware' => $obj['hardware'], - 'device_version' => $obj['version'], + 'location' => $alert_data['location'], + 'title' => $alert_data['name'], + 'message' => $alert_data['string'], + 'device_id' => $alert_data['device_id'], + 'device_name' => $alert_data['hostname'], + 'device_hardware' => $alert_data['hardware'], + 'device_version' => $alert_data['version'], 'state' => $state, 'severity' => $severity, - 'first_occurrence' => $obj['timestamp'], + 'first_occurrence' => $alert_data['timestamp'], 'entity_type' => 'device', 'entity_tab' => 'overview', - 'entity_id' => $obj['device_id'], - 'entity_name' => $obj['hostname'], - 'entity_descr' => $obj['sysDescr'], + 'entity_id' => $alert_data['device_id'], + 'entity_name' => $alert_data['hostname'], + 'entity_descr' => $alert_data['sysDescr'], ]; - if (! empty($obj['faults'])) { - foreach ($obj['faults'] as $k => $v) { - $curl = curl_init(); - $data['message'] = $v['string']; - switch (true) { - case array_key_exists('port_id', $v): - $data['entity_type'] = 'port'; - $data['entity_tab'] = 'port'; - $data['entity_id'] = $v['port_id']; - $data['entity_name'] = $v['ifName']; - $data['entity_descr'] = $v['ifAlias']; - break; - case array_key_exists('sensor_id', $v): - $data['entity_type'] = $v['sensor_class']; - $data['entity_tab'] = 'health'; - $data['entity_id'] = $v['sensor_id']; - $data['entity_name'] = $v['sensor_descr']; - $data['entity_descr'] = $v['sensor_type']; - break; - case array_key_exists('mempool_id', $v): - $data['entity_type'] = 'mempool'; - $data['entity_tab'] = 'health'; - $data['entity_id'] = $v['mempool_id']; - $data['entity_name'] = $v['mempool_index']; - $data['entity_descr'] = $v['mempool_descr']; - break; - case array_key_exists('storage_id', $v): - $data['entity_type'] = 'storage'; - $data['entity_tab'] = 'health'; - $data['entity_id'] = $v['storage_id']; - $data['entity_name'] = $v['storage_index']; - $data['entity_descr'] = $v['storage_descr']; - break; - case array_key_exists('processor_id', $v): - $data['entity_type'] = 'processor'; - $data['entity_tab'] = 'health'; - $data['entity_id'] = $v['processor_id']; - $data['entity_name'] = $v['processor_type']; - $data['entity_descr'] = $v['processor_descr']; - break; - case array_key_exists('bgpPeer_id', $v): - $data['entity_type'] = 'bgp'; - $data['entity_tab'] = 'routing'; - $data['entity_id'] = $v['bgpPeer_id']; - $data['entity_name'] = 'local: ' . $v['bgpPeerLocalAddr'] . ' - AS' . $obj['bgpLocalAs']; - $data['entity_descr'] = 'remote: ' . $v['bgpPeerIdentifier'] . ' - AS' . $v['bgpPeerRemoteAs']; - break; - case array_key_exists('tunnel_id', $v): - $data['entity_type'] = 'ipsec_tunnel'; - $data['entity_tab'] = 'routing'; - $data['entity_id'] = $v['tunnel_id']; - $data['entity_name'] = $v['tunnel_name']; - $data['entity_descr'] = 'local: ' . $v['local_addr'] . ':' . $v['local_port'] . ', remote: ' . $v['peer_addr'] . ':' . $v['peer_port']; - break; - default: - $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; + foreach ($alert_data['faults'] as $k => $v) { + $data['message'] = $v['string']; + switch (true) { + case array_key_exists('port_id', $v): + $data['entity_type'] = 'port'; + $data['entity_tab'] = 'port'; + $data['entity_id'] = $v['port_id']; + $data['entity_name'] = $v['ifName']; + $data['entity_descr'] = $v['ifAlias']; + break; + case array_key_exists('sensor_id', $v): + $data['entity_type'] = $v['sensor_class']; + $data['entity_tab'] = 'health'; + $data['entity_id'] = $v['sensor_id']; + $data['entity_name'] = $v['sensor_descr']; + $data['entity_descr'] = $v['sensor_type']; + break; + case array_key_exists('mempool_id', $v): + $data['entity_type'] = 'mempool'; + $data['entity_tab'] = 'health'; + $data['entity_id'] = $v['mempool_id']; + $data['entity_name'] = $v['mempool_index']; + $data['entity_descr'] = $v['mempool_descr']; + break; + case array_key_exists('storage_id', $v): + $data['entity_type'] = 'storage'; + $data['entity_tab'] = 'health'; + $data['entity_id'] = $v['storage_id']; + $data['entity_name'] = $v['storage_index']; + $data['entity_descr'] = $v['storage_descr']; + break; + case array_key_exists('processor_id', $v): + $data['entity_type'] = 'processor'; + $data['entity_tab'] = 'health'; + $data['entity_id'] = $v['processor_id']; + $data['entity_name'] = $v['processor_type']; + $data['entity_descr'] = $v['processor_descr']; + break; + case array_key_exists('bgpPeer_id', $v): + $data['entity_type'] = 'bgp'; + $data['entity_tab'] = 'routing'; + $data['entity_id'] = $v['bgpPeer_id']; + $data['entity_name'] = 'local: ' . $v['bgpPeerLocalAddr'] . ' - AS' . $alert_data['bgpLocalAs']; + $data['entity_descr'] = 'remote: ' . $v['bgpPeerIdentifier'] . ' - AS' . $v['bgpPeerRemoteAs']; + break; + case array_key_exists('tunnel_id', $v): + $data['entity_type'] = 'ipsec_tunnel'; + $data['entity_tab'] = 'routing'; + $data['entity_id'] = $v['tunnel_id']; + $data['entity_name'] = $v['tunnel_name']; + $data['entity_descr'] = 'local: ' . $v['local_addr'] . ':' . $v['local_port'] . ', remote: ' . $v['peer_addr'] . ':' . $v['peer_port']; + break; + default: + $data['entity_type'] = 'generic'; + break; } } - 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 [ 'config' => [ @@ -212,31 +151,34 @@ class Elasticsearch extends Transport 'name' => 'es-host', 'descr' => 'Elasticsearch Host', 'type' => 'text', + 'default' => '127.0.0.1', ], [ 'title' => 'Port', 'name' => 'es-port', 'descr' => 'Elasticsearch Port', 'type' => 'text', + 'default' => 9200, ], [ 'title' => 'Index 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', 'type' => 'text', + 'default' => "\l\i\b\\r\\e\\n\m\s\-Y.m.d", ], [ 'title' => 'Use proxy if configured?', 'name' => 'es-proxy', - 'descr' => 'Elasticsearch Proxy', + 'descr' => 'Elasticsearch Proxy (Deprecated: just use no_proxy setting to exclude ES server)', 'type' => 'checkbox', - 'default' => false, + 'default' => true, ], ], 'validation' => [ - 'es-host' => 'required|string', - 'es-port' => 'required|string', - 'es-pattern' => 'required|string', + 'es-host' => 'required|ip_or_hostname', + 'es-port' => 'integer|between:1,65535', + 'es-pattern' => 'string', ], ]; } diff --git a/LibreNMS/Alert/Transport/Gitlab.php b/LibreNMS/Alert/Transport/Gitlab.php index 858432131a..6b9ef37936 100644 --- a/LibreNMS/Alert/Transport/Gitlab.php +++ b/LibreNMS/Alert/Transport/Gitlab.php @@ -25,67 +25,40 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; use LibreNMS\Enum\AlertState; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Gitlab extends Transport { - public function deliverAlert($obj, $opts) - { - if (! empty($this->config)) { - $opts['project-id'] = $this->config['gitlab-id']; - $opts['key'] = $this->config['gitlab-key']; - $opts['host'] = $this->config['gitlab-host']; - } - - return $this->contactGitlab($obj, $opts); - } - - public function contactGitlab($obj, $opts) + public function deliverAlert(array $alert_data): bool { // Don't create tickets for resolutions - if ($obj['state'] != AlertState::CLEAR) { - $project_id = $opts['project-id']; - $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; - } + if ($alert_data['state'] == AlertState::RECOVERED) { + return true; } + + $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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Googlechat.php b/LibreNMS/Alert/Transport/Googlechat.php index 6863dabd86..6e69023e04 100755 --- a/LibreNMS/Alert/Transport/Googlechat.php +++ b/LibreNMS/Alert/Transport/Googlechat.php @@ -24,60 +24,26 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; -use Log; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; 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); - } - - 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; + 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Hipchat.php b/LibreNMS/Alert/Transport/Hipchat.php index 9126d59a85..a4b8be45e0 100644 --- a/LibreNMS/Alert/Transport/Hipchat.php +++ b/LibreNMS/Alert/Transport/Hipchat.php @@ -25,92 +25,73 @@ namespace 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 { - 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']); - $hipchat_opts['url'] = $this->config['hipchat-url']; - $hipchat_opts['room_id'] = $this->config['hipchat-room-id']; - $hipchat_opts['from'] = $this->config['hipchat-from-name']; + $options = $this->parseUserOptions($this->config['hipchat-options']); - return $this->contactHipchat($obj, $hipchat_opts); - } - - public function contactHipchat($obj, $option) - { - $version = 1; - if (stripos($option['url'], 'v2')) { - $version = 2; + // override legacy options + if (array_key_exists('hipchat-notify', $this->config)) { + $options['notify'] = ($this->config['hipchat-notify'] == 'on'); } + 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. - $url = $option['url']; if ($version == 2) { - $url .= '/' . urlencode($option['room_id']) . '/notification?auth_token=' . urlencode($option['auth_token']); - } - - $curl = curl_init(); - - if (empty($obj['msg'])) { - return 'Empty Message'; - } - - if (empty($option['message_format'])) { - $option['message_format'] = 'text'; + $url .= '/' . urlencode($this->config['hipchat-room-id']) . '/notification'; } // Sane default of making the message color green if the message indicates // that the alert recovered. If it rebooted, make it yellow. - if (stripos($obj['msg'], 'recovered')) { + if ($alert_data['state'] == AlertState::RECOVERED) { $color = 'green'; - } elseif (stripos($obj['msg'], 'rebooted')) { + } elseif (str_contains($alert_data['msg'], 'rebooted')) { $color = 'yellow'; + } elseif (empty($options['color']) || $options['color'] == 'u') { + $color = 'red'; } else { - if (empty($option['color']) || $option['color'] == 'u') { - $color = 'red'; - } else { - $color = $option['color']; - } + $color = $options['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) { - $data[] = 'room_id=' . urlencode($option['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; + $data['room_id'] = $this->config['hipchat-room-id']; } - 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 [ 'config' => [ @@ -138,6 +119,24 @@ class Hipchat extends Transport 'descr' => 'Hipchat Options', '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' => [ 'hipchat-url' => 'required|url', diff --git a/LibreNMS/Alert/Transport/Hue.php b/LibreNMS/Alert/Transport/Hue.php index 9b9a5a0ee7..e431d6a74d 100644 --- a/LibreNMS/Alert/Transport/Hue.php +++ b/LibreNMS/Alert/Transport/Hue.php @@ -26,7 +26,8 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; 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. @@ -35,56 +36,30 @@ use LibreNMS\Util\Proxy; */ class Hue extends Transport { - public function deliverAlert($obj, $opts) - { - if (! empty($this->config)) { - $opts['user'] = $this->config['hue-user']; - $opts['bridge'] = $this->config['hue-host']; - $opts['duration'] = $this->config['hue-duration']; - } - - return $this->contactHue($obj, $opts); - } - - public function contactHue($obj, $opts) + public function deliverAlert(array $alert_data): bool { // Don't alert on resolve at this time - if ($obj['state'] == AlertState::RECOVERED) { + if ($alert_data['state'] == AlertState::RECOVERED) { 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 [ 'config'=>[ diff --git a/LibreNMS/Alert/Transport/Irc.php b/LibreNMS/Alert/Transport/Irc.php index 09d1374a85..e06c4256d5 100644 --- a/LibreNMS/Alert/Transport/Irc.php +++ b/LibreNMS/Alert/Transport/Irc.php @@ -25,17 +25,13 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; use LibreNMS\Config; +use LibreNMS\Exceptions\AlertTransportDeliveryException; class Irc extends Transport { - protected $name = 'IRC'; + protected string $name = 'IRC'; - public function deliverAlert($obj, $opts) - { - return $this->contactIrc($obj, $opts); - } - - public function contactIrc($obj, $opts) + public function deliverAlert(array $alert_data): bool { $container_dir = '/data'; 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') { $f = fopen($f, 'w+'); - $r = fwrite($f, json_encode($obj) . "\n"); - $f = fclose($f); + $r = fwrite($f, json_encode($alert_data) . "\n"); + fclose($f); + if ($r === false) { - return false; - } else { - return true; + throw new AlertTransportDeliveryException($alert_data, 0, 'Could not write to fifo', $alert_data['msg'], $alert_data); } + + 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Jira.php b/LibreNMS/Alert/Transport/Jira.php index 9aaca8a4cc..e1124b4cbb 100644 --- a/LibreNMS/Alert/Transport/Jira.php +++ b/LibreNMS/Alert/Transport/Jira.php @@ -24,73 +24,50 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Jira extends Transport { - public function deliverAlert($obj, $opts) - { - if (! empty($this->config)) { - $opts['username'] = $this->config['jira-username']; - $opts['password'] = $this->config['jira-password']; - $opts['prjkey'] = $this->config['jira-key']; - $opts['issuetype'] = $this->config['jira-type']; - $opts['url'] = $this->config['jira-url']; - } - - return $this->contactJira($obj, $opts); - } - - public function contactJira($obj, $opts) + public function deliverAlert(array $alert_data): bool { // Don't create tickets for resolutions - if ($obj['severity'] == 'recovery' && $obj['msg'] != 'This is a test alert') { + if ($alert_data['severity'] == 'recovery') { return true; } - $username = $opts['username']; - $password = $opts['password']; - $prjkey = $opts['prjkey']; - $issuetype = $opts['issuetype']; - $details = empty($obj['title']) ? 'Librenms alert for: ' . $obj['hostname'] : $obj['title']; - $description = $obj['msg']; - $url = $opts['url'] . '/rest/api/latest/issue'; - $curl = curl_init(); + $prjkey = $this->config['jira-key']; + $issuetype = $this->config['jira-type']; + $details = empty($alert_data['title']) ? 'Librenms alert for: ' . $alert_data['hostname'] : $alert_data['title']; + $description = $alert_data['msg']; + $url = $this->config['jira-url'] . '/rest/api/latest/issue'; - $data = ['project' => ['key' => $prjkey], - 'summary' => $details, - 'description' => $description, - 'issuetype' => ['name' => $issuetype], ]; - $postdata = ['fields' => $data]; - $datastring = json_encode($postdata); + $data = [ + 'fields' => [ + 'project' => [ + 'key' => $prjkey, + ], + 'summary' => $details, + 'description' => $description, + 'issuetype' => [ + 'name' => $issuetype, + ], + ], + ]; - 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_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']); + $res = Http::client() + ->withBasicAuth($this->config['jira-username'], $this->config['jira-password']) + ->acceptJson() + ->post($url, $data); + if ($res->successful()) { 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Kayako.php b/LibreNMS/Alert/Transport/Kayako.php index bf2b2732c2..f7155c9f9c 100644 --- a/LibreNMS/Alert/Transport/Kayako.php +++ b/LibreNMS/Alert/Transport/Kayako.php @@ -14,29 +14,18 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; use LibreNMS\Config; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Kayako extends Transport { - public function deliverAlert($host, $kayako) + public function deliverAlert(array $alert_data): bool { - if (! empty($this->config)) { - $kayako['url'] = $this->config['kayako-url']; - $kayako['key'] = $this->config['kayako-key']; - $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']; + $url = $this->config['kayako-url'] . '/Tickets/Ticket'; + $key = $this->config['kayako-key']; + $secret = $this->config['kayako-secret']; $user = Config::get('email_from'); - $department = $kayako['department']; + $department = $this->config['kayako-department']; $ticket_type = 1; $ticket_status = 1; $ticket_prio = 1; @@ -44,10 +33,10 @@ class Kayako extends Transport $signature = base64_encode(hash_hmac('sha256', $salt, $secret, true)); $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', 'email' => $user, - 'contents' => strip_tags($host['msg']), + 'contents' => strip_tags($alert_data['msg']), 'departmentid' => $department, 'ticketstatusid' => $ticket_status, 'ticketpriorityid' => $ticket_prio, @@ -58,27 +47,19 @@ class Kayako extends Transport 'salt' => $salt, 'signature' => $signature, ]; - $post_data = http_build_query($protocol, '', '&'); - $curl = curl_init(); - Proxy::applyToCurl($curl); - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data); - curl_exec($curl); + $res = Http::client() + ->asForm() // unsure if this is needed, can't access docs + ->post($url, $protocol); - $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - if ($code != 200) { - var_dump('Kayako returned Error, retry later'); - - return false; + if ($res->successful()) { + return true; } - return true; + throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $protocol['contents'], $protocol); } - public static function configTemplate() + public static function configTemplate(): array { return [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Linenotify.php b/LibreNMS/Alert/Transport/Linenotify.php index 46b3348e68..5be45466ba 100644 --- a/LibreNMS/Alert/Transport/Linenotify.php +++ b/LibreNMS/Alert/Transport/Linenotify.php @@ -6,44 +6,32 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Linenotify extends Transport { - protected $name = 'LINE Notify'; + protected string $name = 'LINE Notify'; - public function deliverAlert($obj, $opts) - { - $opts['line-notify-access-token'] = $this->config['line-notify-access-token']; - - return $this->contactLinenotify($obj, $opts); - } - - private function contactLinenotify($obj, $opts) + public function deliverAlert(array $alert_data): bool { + // TODO possible to attach graph images $lineUrl = 'https://notify-api.line.me/api/notify'; - $lineHead = ['Authorization: Bearer ' . $opts['line-notify-access-token']]; - $lineFields = ['message' => $obj['msg']]; + $lineFields = ['message' => $alert_data['msg']]; - $curl = curl_init(); - Proxy::applyToCurl($curl); - curl_setopt($curl, CURLOPT_URL, $lineUrl); - curl_setopt($curl, CURLOPT_HTTPHEADER, $lineHead); - curl_setopt($curl, CURLOPT_NOBODY, false); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_POST, 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; + $res = Http::client() + ->withToken($this->config['line-notify-access-token']) + ->asForm() + ->post($lineUrl, $lineFields); + + if ($res->successful()) { + return true; } - 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Mail.php b/LibreNMS/Alert/Transport/Mail.php index 76ff0660a9..d3438fc1ed 100644 --- a/LibreNMS/Alert/Transport/Mail.php +++ b/LibreNMS/Alert/Transport/Mail.php @@ -28,28 +28,23 @@ use LibreNMS\Config; class Mail extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - return $this->contactMail($obj); - } - - public function contactMail($obj) - { - $email = $this->config['email'] ?? $obj['contacts']; + $email = $this->config['email'] ?? $alert_data['contacts']; $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 - $msg = preg_replace("/\r?\n/", "
\n", $obj['msg']); + $msg = preg_replace("/\r?\n/", "
\n", $alert_data['msg']); } else { // fix line returns for windows mail clients - $msg = preg_replace("/(?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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Matrix.php b/LibreNMS/Alert/Transport/Matrix.php index 1aedc06fc5..6971e5cbc8 100644 --- a/LibreNMS/Alert/Transport/Matrix.php +++ b/LibreNMS/Alert/Transport/Matrix.php @@ -25,58 +25,40 @@ namespace LibreNMS\Alert\Transport; use App\View\SimpleTemplate; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Matrix extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { $server = $this->config['matrix-server']; $room = $this->config['matrix-room']; $authtoken = $this->config['matrix-authtoken']; $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(); $server = preg_replace('/\/$/', '', $server); $host = $server . '/_matrix/client/r0/rooms/' . urlencode($room) . '/send/m.room.message/' . $txnid; - $request_heads['Authorization'] = "Bearer $authtoken"; - $request_heads['Content-Type'] = 'application/json'; - $request_heads['Accept'] = 'application/json'; + $message = SimpleTemplate::parse($message, $alert_data); - $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(); - $request_opts['proxy'] = Proxy::forGuzzle(); - $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; + if ($res->successful()) { + return true; } - return true; + throw new AlertTransportDeliveryException($alert_data, $res->status(), $res->body(), $message, $body); } - public static function configTemplate() + public static function configTemplate(): array { return [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Mattermost.php b/LibreNMS/Alert/Transport/Mattermost.php index 3cfbf3bb07..05d3c3b6e5 100755 --- a/LibreNMS/Alert/Transport/Mattermost.php +++ b/LibreNMS/Alert/Transport/Mattermost.php @@ -24,70 +24,42 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; class Mattermost extends Transport { - public function deliverAlert($obj, $opts) + public function deliverAlert(array $alert_data): bool { - $mattermost_opts = [ - 'url' => $this->config['mattermost-url'], - 'username' => $this->config['mattermost-username'], - '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']); + $host = $this->config['mattermost-url']; + $mattermost_msg = strip_tags($alert_data['msg']); + $color = self::getColorForState($alert_data['state']); $data = [ 'attachments' => [ 0 => [ 'fallback' => $mattermost_msg, 'color' => $color, - 'title' => $obj['title'], - 'text' => $obj['msg'], + 'title' => $alert_data['title'], + 'text' => $alert_data['msg'], 'mrkdwn_in' => ['text', 'fallback'], - 'author_name' => $api['author_name'], + 'author_name' => $this->config['mattermost-author_name'], ], ], - 'channel' => $api['channel'], - 'username' => $api['username'], - 'icon_url' => $api['icon'], + 'channel' => $this->config['mattermost-channel'], + 'username' => $this->config['mattermost-username'], + 'icon_url' => $this->config['mattermost-icon'], ]; - Proxy::applyToCurl($curl); - - $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']); + $res = Http::client()->acceptJson()->post($host, $data); + if ($res->successful()) { 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 [ 'config' => [ diff --git a/LibreNMS/Alert/Transport/Msteams.php b/LibreNMS/Alert/Transport/Msteams.php index 408eed7196..e613fac46e 100644 --- a/LibreNMS/Alert/Transport/Msteams.php +++ b/LibreNMS/Alert/Transport/Msteams.php @@ -13,51 +13,43 @@ namespace LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport; -use LibreNMS\Util\Proxy; +use LibreNMS\Exceptions\AlertTransportDeliveryException; +use LibreNMS\Util\Http; 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 = [ - 'title' => $obj['title'], - 'themeColor' => self::getColorForState($obj['state']), - 'text' => strip_tags($obj['msg'], '