Converted PagerDuty to new transport format (#9092)

* Converted PagerDuty to new transport format

* Attempt at oauth for transports

* clean

* missing images

* reverted wrong file. phpstorm saved me...

* fix typo

* Updated to help the display

* Fix validation error, start to try to send alerts.
Editing the transport, deletes the service_key...

* Ignore hidden config options

* Fix up api call, proper feedback.

* hide oauth field in print list
This commit is contained in:
Neil Lathwood
2018-08-29 15:44:29 +01:00
committed by Tony Murray
parent b411f6c5d8
commit e603fcfd6f
9 changed files with 203 additions and 46 deletions

View File

@@ -23,25 +23,43 @@
*/ */
namespace LibreNMS\Alert\Transport; namespace LibreNMS\Alert\Transport;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Http\Request;
use LibreNMS\Alert\Transport; use LibreNMS\Alert\Transport;
use Log;
use Validator;
class Pagerduty extends Transport class Pagerduty extends Transport
{ {
public static $integrationKey = '2fc7c9f3c8030e74aae6';
public function deliverAlert($obj, $opts) public function deliverAlert($obj, $opts)
{ {
if ($obj['state'] == 0) {
$obj['event_type'] = 'resolve';
} elseif ($obj['state'] == 2) {
$obj['event_type'] = 'acknowledge';
} else {
$obj['event_type'] = 'trigger';
}
if (empty($this->config)) {
return $this->deliverAlertEvent($obj, $opts);
}
return $this->contactPagerduty($obj, $this->config);
}
public function deliverAlertEvent($obj, $opts)
{
// This code uses events for PD
$protocol = array( $protocol = array(
'service_key' => $opts, 'service_key' => $opts,
'incident_key' => ($obj['id'] ? $obj['id'] : $obj['uid']), 'incident_key' => ($obj['id'] ? $obj['id'] : $obj['uid']),
'description' => ($obj['name'] ? $obj['name'] . ' on ' . $obj['hostname'] : $obj['title']), 'description' => ($obj['name'] ? $obj['name'] . ' on ' . $obj['hostname'] : $obj['title']),
'client' => 'LibreNMS', 'client' => 'LibreNMS',
); );
if ($obj['state'] == 0) {
$protocol['event_type'] = 'resolve';
} elseif ($obj['state'] == 2) {
$protocol['event_type'] = 'acknowledge';
} else {
$protocol['event_type'] = 'trigger';
}
foreach ($obj['faults'] as $fault => $data) { foreach ($obj['faults'] as $fault => $data) {
$protocol['details'][] = $data['string']; $protocol['details'][] = $data['string'];
} }
@@ -60,13 +78,91 @@ class Pagerduty extends Transport
return true; return true;
} }
public function contactPagerduty() /**
* @param $obj
* @param $config
* @return bool|string
*/
public function contactPagerduty($obj, $config)
{ {
return [];// Pagerduty is a custom transport. $data = [
'routing_key' => $config['service_key'],
'event_action' => $obj['event_type'],
'dedup_key' => $obj['uid'],
'payload' => [
'summary' => implode(PHP_EOL, array_column($obj['faults'], 'string')) ?: 'Test',
'source' => $obj['hostname'],
'severity' => $obj['severity'],
],
];
$url = 'https://events.pagerduty.com/v2/enqueue';
$client = new Client();
try {
$result = $client->request('POST', $url, ['json' => $data]);
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()
{ {
return [];// Pagerduty is a custom transport. return [
'config' => [
[
'title' => 'Authorize',
'descr' => 'Alert with PagerDuty',
'type' => 'oauth',
'icon' => 'pagerduty-white.svg',
'class' => 'btn-success',
'url' => 'https://connect.pagerduty.com/connect?vendor=' . self::$integrationKey . '&callback='
],
[
'title' => 'Account',
'type' => 'hidden',
'name' => 'account',
],
[
'title' => 'Service',
'type' => 'hidden',
'name' => 'service_name',
]
],
'validation' => []
];
}
public function handleOauth(Request $request)
{
$validator = Validator::make($request->all(), [
'account' => 'alpha_dash',
'service_key' => 'regex:/^[a-fA-F0-9]+$/',
'service_name' => 'string',
]);
if ($validator->fails()) {
Log::error('Pagerduty oauth failed validation.', ['request' => $request->all()]);
return false;
}
$config = json_encode($request->only('account', 'service_key', 'service_name'));
if ($id = $request->get('id')) {
return (bool)dbUpdate(['transport_config' => $config], 'alert_transports', 'transport_id=?', [$id]);
} else {
return (bool)dbInsert([
'transport_name' => $request->get('service_name', 'PagerDuty'),
'transport_type' => 'pagerduty',
'is_default' => 0,
'transport_config' => $config,
], 'alert_transports');
}
} }
} }

View File

@@ -254,9 +254,18 @@ LibreNMS can send alerts to osTicket API which are then converted to osTicket ti
| API Token | 123456789 | | API Token | 123456789 |
## PagerDuty ## PagerDuty
PagerDuty setup is currently done by a two way integration. Start this process from Settings -> Alerting Settings from within LibreNMS. LibreNMS can make use of PagerDuty, this is done by utilizing an API key and Integraton Key.
[PagerDuty Docs](https://www.pagerduty.com/docs/guides/librenms-integration-guide/) API Keys can be found under 'API Access' in the PagerDuty portal.
Integration Keys can be found under 'Integration' for the particular Service you have created in the PagerDuty portal.
**Example:**
| Config | Example |
| ------ | ------- |
| API Key | randomsample |
| Integration Key | somerandomstring |
## Philips Hue ## Philips Hue
Want to spice up your noc life? LibreNMS will flash all lights connected to your philips hue bridge whenever an alert is triggered. Want to spice up your noc life? LibreNMS will flash all lights connected to your philips hue bridge whenever an alert is triggered.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 229.155 229.155"><g fill="#66ba68"><path d="M148.065 138.327c-.032 1.246.27 4.724 2.071 6.618 1.745 1.834 5.175 2.219 7.744 2.219h37.848c9.427-.129 9.427-7.21 9.427-9.54V82.059l-48.106-.019h-.054c-1.326 0-4.822.387-6.732 2.238-1.435 1.39-2.163 3.899-2.163 7.457zm47.626 32.837H157.88c-13.425 0-21.261-5.459-25.47-10.039-8.398-9.134-8.46-20.936-8.31-24.085V91.735c0-13.089 5.34-20.836 9.817-25.032 9.18-8.598 21.198-8.743 24.115-8.644h47.123V17.268h24v120.356c0 12.32-5.06 19.868-9.306 24.032-9.212 9.035-21.662 9.508-24.158 9.508M72.244 146.99c1.26-.016 4.873-.362 6.824-2.25 1.445-1.4 2.177-3.92 2.177-7.495l.034-46.699c.033-1.252-.27-4.75-2.084-6.656-1.755-1.845-5.2-2.233-7.781-2.233h-37.94C24 81.787 24 88.906 24 91.25v55.723zM24 211.887H0V91.249c0-12.34 5.067-19.9 9.318-24.069 9.412-9.232 22.163-9.545 24.335-9.523h37.76c13.446 0 21.294 5.467 25.51 10.054 8.41 9.149 8.472 20.97 8.322 24.122v45.412c0 13.109-5.347 20.868-9.831 25.07-9.191 8.608-21.219 8.756-24.153 8.657H24v40.915"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 229.155 229.155"><g fill="#fff"><path d="M148.065 138.327c-.032 1.246.27 4.724 2.071 6.618 1.745 1.834 5.175 2.219 7.744 2.219h37.848c9.427-.129 9.427-7.21 9.427-9.54V82.059l-48.106-.019h-.054c-1.326 0-4.822.387-6.732 2.238-1.435 1.39-2.163 3.899-2.163 7.457zm47.626 32.837H157.88c-13.425 0-21.261-5.459-25.47-10.039-8.398-9.134-8.46-20.936-8.31-24.085V91.735c0-13.089 5.34-20.836 9.817-25.032 9.18-8.598 21.198-8.743 24.115-8.644h47.123V17.268h24v120.356c0 12.32-5.06 19.868-9.306 24.032-9.212 9.035-21.662 9.508-24.158 9.508M72.244 146.99c1.26-.016 4.873-.362 6.824-2.25 1.445-1.4 2.177-3.92 2.177-7.495l.034-46.699c.033-1.252-.27-4.75-2.084-6.656-1.755-1.845-5.2-2.233-7.781-2.233h-37.94C24 81.787 24 88.906 24 91.25v55.723zM24 211.887H0V91.249c0-12.34 5.067-19.9 9.318-24.069 9.412-9.232 22.163-9.545 24.335-9.523h37.76c13.446 0 21.294 5.467 25.51 10.054 8.41 9.149 8.472 20.97 8.322 24.122v45.412c0 13.109-5.347 20.868-9.831 25.07-9.191 8.608-21.219 8.756-24.153 8.657H24v40.915"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -93,16 +93,19 @@ if (empty($name)) {
} }
$status = 'error'; $status = 'error';
} else { } else {
$transport_config = json_decode(dbFetchCell('SELECT transport_config FROM alert_transports WHERE transport_id=?', [$transport_id]), true);
foreach ($result['config'] as $tmp_config) { foreach ($result['config'] as $tmp_config) {
$transport_config[$tmp_config['name']] = $vars[$tmp_config['name']]; if (isset($tmp_config['name']) && $tmp_config['type'] !== 'hidden') {
$transport_config[$tmp_config['name']] = $vars[$tmp_config['name']];
}
} }
//Update the json config field //Update the json config field
if ($transport_config) { if ($transport_config) {
$transport_config = json_encode($transport_config); $transport_config = json_encode($transport_config);
$detail = array( $detail = [
'transport_type' => $transport_type, 'transport_type' => $transport_type,
'transport_config' => $transport_config 'transport_config' => $transport_config
); ];
$where = 'transport_id=?'; $where = 'transport_id=?';
dbUpdate($detail, 'alert_transports', $where, [$transport_id]); dbUpdate($detail, 'alert_transports', $where, [$transport_id]);

View File

@@ -56,6 +56,7 @@ if (Auth::user()->hasGlobalAdmin()) {
<option value="nagios-form">Nagios</option> <option value="nagios-form">Nagios</option>
<option value="opsgenie-form">OpsGenie</option> <option value="opsgenie-form">OpsGenie</option>
<option value="osticket-form">osTicket</option> <option value="osticket-form">osTicket</option>
<option value="pagerduty-form">PagerDuty</option>
<option value="hue-form">Phillips Hue</option> <option value="hue-form">Phillips Hue</option>
<option value="playsms-form">PlaySMS</option> <option value="playsms-form">PlaySMS</option>
<option value="pushbullet-form">Pushbullet</option> <option value="pushbullet-form">Pushbullet</option>
@@ -102,37 +103,51 @@ foreach (scandir($transport_dir) as $transport) {
$tmp = call_user_func($class.'::configTemplate'); $tmp = call_user_func($class.'::configTemplate');
foreach ($tmp['config'] as $item) { foreach ($tmp['config'] as $item) {
echo '<div class="form-group" title="'.$item['descr'].'">'; if ($item['type'] !== 'hidden') {
echo '<label for="'.$item['name'].'" class="col-sm-3 col-md-2 control-label">'.$item['title'].': </label>'; echo '<div class="form-group" title="' . $item['descr'] . '">';
if ($item['type'] == 'text') { echo '<label for="' . $item['name'] . '" class="col-sm-3 col-md-2 control-label">' . $item['title'] . ': </label>';
echo '<div class="col-sm-9 col-md-10">'; if ($item['type'] == 'text') {
echo '<input type="'.$item['type'].'" id="'.$item['name'].'" name="'.$item['name'].'" class="form-control" '; echo '<div class="col-sm-9 col-md-10">';
if ($item['required']) { echo '<input type="' . $item['type'] . '" id="' . $item['name'] . '" name="' . $item['name'] . '" class="form-control" ';
echo 'required>'; if ($item['required']) {
} else { echo 'required>';
echo '>'; } else {
echo '>';
}
echo '</div>';
} elseif ($item['type'] == 'checkbox') {
echo '<div class="col-sm-2">';
echo '<input type="checkbox" name="' . $item['name'] . '" id="' . $item['name'] . '">';
echo '</div>';
$switches[$item['name']] = $item['default'];
} elseif ($item['type'] == 'select') {
echo '<div class="col-sm-3">';
echo '<select name="' . $item['name'] . '" id="' . $item['name'] . '" class="form-control">';
foreach ($item['options'] as $descr => $opt) {
echo '<option value="' . $opt . '">' . $descr . '</option>';
}
echo '</select>';
echo '</div>';
} elseif ($item['type'] === 'textarea') {
echo '<div class="col-sm-9 col-md-10">';
echo '<textarea name="' . $item['name'] . '" id="' . $item['name'] . '" class="form-control" placeholder="' . $item['descr'] . '">';
echo '</textarea>';
echo '</div>';
} elseif ($item['type'] === 'oauth') {
$class = isset($item['class']) ? $item['class'] : 'btn-success';
$callback = urlencode(url()->current() . '/?oauthtransport=' . $transport);
$url = $item['url'] . $callback;
echo '<a class="btn btn-oauth ' . $class . '"';
echo '" href="' . $url . '" data-base-url="' . $url . '">';
if (isset($item['icon'])) {
echo '<img src="' . asset('images/transports/' . $item['icon']) . '" width="24" height="24"> ';
}
echo $item['descr'];
echo '</a>';
} }
echo '</div>'; echo '</div>';
} elseif ($item['type'] == 'checkbox') {
echo '<div class="col-sm-2">';
echo '<input type="checkbox" name="'.$item['name'].'" id="'.$item['name'].'">';
echo '</div>';
$switches[$item['name']] = $item['default'];
} elseif ($item['type'] == 'select') {
echo '<div class="col-sm-3">';
echo '<select name="'.$item['name'].'" id="'.$item['name'].'" class="form-control">';
foreach ($item['options'] as $descr => $opt) {
echo '<option value="'.$opt.'">'.$descr.'</option>';
}
echo '</select>';
echo '</div>';
} elseif ($item['type'] === 'textarea') {
echo '<div class="col-sm-9 col-md-10">';
echo '<textarea name="' . $item['name'] . '" id="' . $item['name'] . '" class="form-control" placeholder="'.$item['descr'].'">';
echo '</textarea>';
echo '</div>';
} }
echo '</div>';
} }
echo '<div class="form-group">'; echo '<div class="form-group">';
echo '<div class="col-sm-12 text-center">'; echo '<div class="col-sm-12 text-center">';
@@ -236,6 +251,10 @@ foreach (scandir($transport_dir) as $transport) {
}); });
} }
$(".btn-oauth").click(function (e) {
this.href = $(this).data('base-url') + '%26id=' + $("#transport_id").val();
});
// Save alert transport // Save alert transport
$(".btn-save").on("click", function (e) { $(".btn-save").on("click", function (e) {
e.preventDefault(); e.preventDefault();

View File

@@ -60,6 +60,10 @@ foreach (dbFetchRows($query) as $transport) {
$transport_config = json_decode($transport['config'], true); $transport_config = json_decode($transport['config'], true);
foreach ($tmp['config'] as $item) { foreach ($tmp['config'] as $item) {
if ($item['type'] == 'oauth') {
continue;
}
$val = $transport_config[$item['name']]; $val = $transport_config[$item['name']];
// Match value to key name for select inputs // Match value to key name for select inputs

View File

@@ -1,3 +1,29 @@
<?php <?php
// handle OAuth requests
$request = request(); // grab the Request object
if ($request->has('oauthtransport')) {
// make sure transport is safe
$validator = Validator::make($request->all(), ['oauthtransport' => 'required|alpha']);
if ($validator->passes()) {
$transport_name = $request->get('oauthtransport');
$class = 'LibreNMS\\Alert\\Transport\\'.$transport_name;
if (class_exists($class)) {
$transport = app($class);
if ($transport->handleOauth($request)) {
Toastr::success("$transport_name added successfully.");
} else {
Toastr::error("$transport_name was not added. Check the log for details.");
}
}
}
// remove get variables otherwise things will get double added
echo '<script>window.history.replaceState(null, null, window.location.pathname);</script>';
}
unset($request);
// print alert transports
require_once 'includes/print-alert-transports.php'; require_once 'includes/print-alert-transports.php';

View File

@@ -606,9 +606,7 @@ function IssueAlert($alert)
$obj = DescribeAlert($alert); $obj = DescribeAlert($alert);
if (is_array($obj)) { if (is_array($obj)) {
echo 'Issuing Alert-UID #'.$alert['id'].'/'.$alert['state'].': '; echo 'Issuing Alert-UID #'.$alert['id'].'/'.$alert['state'].': ';
if (!empty($config['alert']['transports'])) { ExtTransports($obj);
ExtTransports($obj);
}
echo "\r\n"; echo "\r\n";
} }