2018-07-21 13:34:59 -06:00
< ? php
2019-07-18 08:36:02 -05:00
/**
* AlertUtil . php
*
* Extending the built in logging to add an event logger function
*
* 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
2021-02-09 00:29:04 +01:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2019-07-18 08:36:02 -05:00
*
2021-02-09 00:29:04 +01:00
* @ link https :// www . librenms . org
2021-09-10 20:09:53 +02:00
*
2019-07-18 08:36:02 -05:00
* @ copyright 2019 KanREN , Inc .
* @ author Heath Barnhart < hbarnhart @ kanren . net >
*/
2018-07-21 13:34:59 -06:00
namespace LibreNMS\Alert ;
2019-07-18 08:36:02 -05:00
use App\Models\Device ;
2019-08-05 14:16:05 -05:00
use App\Models\User ;
2020-06-14 12:39:10 -05:00
use DeviceCache ;
2023-09-28 09:49:29 -05:00
use Illuminate\Database\Eloquent\Builder ;
2023-09-08 09:27:23 -05:00
use Illuminate\Support\Arr ;
2019-07-18 08:36:02 -05:00
use LibreNMS\Config ;
use PHPMailer\PHPMailer\PHPMailer ;
2018-07-21 13:34:59 -06:00
class AlertUtil
{
2018-08-17 22:38:00 +01:00
/**
* Get the rule_id for a specific alert
*
2021-09-08 23:35:56 +02:00
* @ param int $alert_id
2018-08-17 22:38:00 +01:00
* @ return mixed | null
*/
2018-07-21 13:34:59 -06:00
private static function getRuleId ( $alert_id )
{
$query = 'SELECT `rule_id` FROM `alerts` WHERE `id`=?' ;
2020-09-21 14:54:51 +02:00
2018-07-21 13:34:59 -06:00
return dbFetchCell ( $query , [ $alert_id ]);
}
2018-08-17 22:38:00 +01:00
/**
* Get the transport for a given alert_id
*
2021-09-08 23:35:56 +02:00
* @ param int $alert_id
2018-08-17 22:38:00 +01:00
* @ return array
*/
2018-07-21 13:34:59 -06:00
public static function getAlertTransports ( $alert_id )
{
2018-11-07 17:04:45 +00:00
$query = " SELECT b.transport_id, b.transport_type, b.transport_name FROM alert_transport_map AS a LEFT JOIN alert_transports AS b ON b.transport_id=a.transport_or_group_id WHERE a.target_type='single' AND a.rule_id=? UNION DISTINCT SELECT d.transport_id, d.transport_type, d.transport_name FROM alert_transport_map AS a LEFT JOIN alert_transport_groups AS b ON a.transport_or_group_id=b.transport_group_id LEFT JOIN transport_group_transport AS c ON b.transport_group_id=c.transport_group_id LEFT JOIN alert_transports AS d ON c.transport_id=d.transport_id WHERE a.target_type='group' AND a.rule_id=? " ;
2018-07-21 13:34:59 -06:00
$rule_id = self :: getRuleId ( $alert_id );
2020-09-21 14:54:51 +02:00
2018-07-21 13:34:59 -06:00
return dbFetchRows ( $query , [ $rule_id , $rule_id ]);
}
2018-08-17 22:38:00 +01:00
/**
* Returns the default transports
*
* @ return array
*/
2018-07-21 13:34:59 -06:00
public static function getDefaultAlertTransports ()
{
2018-11-05 07:56:16 +00:00
$query = 'SELECT transport_id, transport_type, transport_name FROM alert_transports WHERE is_default=true' ;
2020-09-21 14:54:51 +02:00
2018-07-21 13:34:59 -06:00
return dbFetchRows ( $query );
}
2019-07-18 08:36:02 -05:00
/**
* Find contacts for alert
2021-09-10 20:09:53 +02:00
*
2021-09-08 23:35:56 +02:00
* @ param array $results Rule - Result
2019-07-18 08:36:02 -05:00
* @ return array
*/
public static function getContacts ( $results )
{
if ( empty ( $results )) {
return [];
}
2023-09-08 09:27:23 -05:00
2019-07-18 08:36:02 -05:00
if ( Config :: get ( 'alert.default_only' ) === true || Config :: get ( 'alerts.email.default_only' ) === true ) {
$email = Config :: get ( 'alert.default_mail' , Config :: get ( 'alerts.email.default' ));
2020-09-21 14:54:51 +02:00
2019-07-18 08:36:02 -05:00
return $email ? [ $email => '' ] : [];
}
2023-09-08 09:27:23 -05:00
2019-07-18 08:36:02 -05:00
$contacts = [];
2023-09-08 09:27:23 -05:00
if ( Config :: get ( 'alert.syscontact' )) {
$contacts = array_merge ( $contacts , self :: findContactsSysContact ( $results ));
2019-07-18 08:36:02 -05:00
}
2023-08-28 00:13:40 -05:00
2023-09-08 09:27:23 -05:00
if ( Config :: get ( 'alert.users' )) {
$contacts = array_merge ( $contacts , self :: findContactsOwners ( $results ));
}
2023-08-28 00:13:40 -05:00
2023-09-08 09:27:23 -05:00
$roles = Config :: get ( 'alert.globals' )
? [ 'admin' , 'global-read' ]
: ( Config :: get ( 'alert.admins' ) ? [ 'admin' ] : []);
if ( $roles ) {
$contacts = array_merge ( $contacts , self :: findContactsRoles ( $roles ));
2019-07-18 08:36:02 -05:00
}
$tmp_contacts = [];
foreach ( $contacts as $email => $name ) {
if ( strstr ( $email , ',' )) {
$split_contacts = preg_split ( '/[,\s]+/' , $email );
foreach ( $split_contacts as $split_email ) {
if ( ! empty ( $split_email )) {
$tmp_contacts [ $split_email ] = $name ;
}
}
} else {
$tmp_contacts [ $email ] = $name ;
}
}
if ( ! empty ( $tmp_contacts )) {
// Validate contacts so we can fall back to default if configured.
$mail = new PHPMailer ();
foreach ( $tmp_contacts as $tmp_email => $tmp_name ) {
if ( $mail -> validateAddress ( $tmp_email ) != true ) {
unset ( $tmp_contacts [ $tmp_email ]);
}
}
}
// Copy all email alerts to default contact if configured.
$default_mail = Config :: get ( 'alert.default_mail' );
if ( ! isset ( $tmp_contacts [ $default_mail ]) && Config :: get ( 'alert.default_copy' )) {
$tmp_contacts [ $default_mail ] = '' ;
}
// Send email to default contact if no other contact found
if ( empty ( $tmp_contacts ) && Config :: get ( 'alert.default_if_none' ) && $default_mail ) {
$tmp_contacts [ $default_mail ] = '' ;
}
return $tmp_contacts ;
}
2023-09-08 09:27:23 -05:00
public static function findContactsRoles ( array $roles ) : array
{
2024-02-05 21:13:27 +08:00
return User :: whereIs ( ... $roles ) -> whereNot ( 'email' , '' ) -> pluck ( 'realname' , 'email' ) -> toArray ();
2023-09-08 09:27:23 -05:00
}
public static function findContactsSysContact ( array $results ) : array
{
$contacts = [];
foreach ( $results as $result ) {
2023-09-15 08:05:55 -05:00
$device = DeviceCache :: get ( $result [ 'device_id' ]);
2023-09-08 09:27:23 -05:00
$email = $device -> getAttrib ( 'override_sysContact_bool' )
? $device -> getAttrib ( 'override_sysContact_string' )
: $device -> sysContact ;
$contacts [ $email ] = '' ;
}
return $contacts ;
}
public static function findContactsOwners ( array $results ) : array
{
2023-09-28 09:49:29 -05:00
return User :: whereNot ( 'email' , '' ) -> where ( function ( Builder $query ) use ( $results ) {
if ( $device_ids = array_filter ( Arr :: pluck ( $results , 'device_id' ))) {
$query -> orWhereHas ( 'devicesOwned' , fn ( $q ) => $q -> whereIn ( 'devices_perms.device_id' , $device_ids ));
}
if ( $port_ids = array_filter ( Arr :: pluck ( $results , 'port_id' ))) {
$query -> orWhereHas ( 'portsOwned' , fn ( $q ) => $q -> whereIn ( 'ports_perms.port_id' , $port_ids ));
}
if ( $bill_ids = array_filter ( Arr :: pluck ( $results , 'bill_id' ))) {
$query -> orWhereHas ( 'bills' , fn ( $q ) => $q -> whereIn ( 'bill_perms.bill_id' , $bill_ids ));
2023-09-08 09:27:23 -05:00
}
}) -> pluck ( 'realname' , 'email' ) -> all ();
}
2019-07-18 08:36:02 -05:00
public static function getRules ( $device_id )
{
$query = ' SELECT DISTINCT a .* FROM alert_rules a
2020-01-19 14:55:51 +01:00
LEFT JOIN alert_device_map d ON a . id = d . rule_id AND ( a . invert_map = 0 OR a . invert_map = 1 AND d . device_id = ? )
LEFT JOIN alert_group_map g ON a . id = g . rule_id AND ( a . invert_map = 0 OR a . invert_map = 1 AND g . group_id IN ( SELECT DISTINCT device_group_id FROM device_group_device WHERE device_id = ? ))
2020-02-12 19:53:26 +01:00
LEFT JOIN alert_location_map l ON a . id = l . rule_id AND ( a . invert_map = 0 OR a . invert_map = 1 AND l . location_id IN ( SELECT DISTINCT location_id FROM devices WHERE device_id = ? ))
2022-09-23 16:15:46 -03:00
LEFT JOIN devices ld ON l . location_id = ld . location_id AND ld . device_id = ?
2020-01-19 14:55:51 +01:00
LEFT JOIN device_group_device dg ON g . group_id = dg . device_group_id AND dg . device_id = ?
WHERE a . disabled = 0 AND (
2022-09-23 16:15:46 -03:00
( d . device_id IS NULL AND g . group_id IS NULL AND l . location_id IS NULL )
OR ( a . invert_map = 0 AND ( d . device_id = ? OR dg . device_id = ? OR ld . device_id = ? ))
OR ( a . invert_map = 1 AND ( d . device_id != ? OR d . device_id IS NULL ) AND ( dg . device_id != ? OR dg . device_id IS NULL ) AND ( ld . device_id != ? OR ld . device_id IS NULL ))
2020-01-19 14:55:51 +01:00
) ' ;
2019-07-18 08:36:02 -05:00
2022-09-23 16:15:46 -03:00
$params = [ $device_id , $device_id , $device_id , $device_id , $device_id , $device_id , $device_id , $device_id , $device_id , $device_id , $device_id ];
2020-09-21 14:54:51 +02:00
2019-07-18 08:36:02 -05:00
return dbFetchRows ( $query , $params );
}
/**
* Check if device is under maintenance
2021-09-10 20:09:53 +02:00
*
2021-09-08 23:35:56 +02:00
* @ param int $device_id Device - ID
2019-07-18 08:36:02 -05:00
* @ return bool
*/
public static function isMaintenance ( $device_id )
{
2020-06-14 12:39:10 -05:00
return DeviceCache :: get ( $device_id ) -> isUnderMaintenance ();
2019-07-18 08:36:02 -05:00
}
2020-01-10 01:46:52 +01:00
/**
* Check if device is set to ignore alerts
2021-09-10 20:09:53 +02:00
*
2021-09-08 23:35:56 +02:00
* @ param int $device_id Device - ID
2020-01-10 01:46:52 +01:00
* @ return bool
*/
public static function hasDisableNotify ( $device_id )
{
$device = Device :: find ( $device_id );
2020-09-21 14:54:51 +02:00
2020-01-10 01:46:52 +01:00
return ! is_null ( $device ) && $device -> disable_notify ;
}
2019-07-18 08:36:02 -05:00
/**
* Process Macros
2021-09-10 20:09:53 +02:00
*
2021-09-08 23:35:56 +02:00
* @ param string $rule Rule to process
* @ param int $x Recursion - Anchor
2019-07-18 08:36:02 -05:00
* @ return string | bool
*/
public static function runMacros ( $rule , $x = 1 )
{
2021-03-23 23:08:42 +01:00
$macros = Config :: get ( 'alert.macros.rule' , []);
2019-07-18 08:36:02 -05:00
krsort ( $macros );
foreach ( $macros as $macro => $value ) {
if ( ! strstr ( $macro , ' ' )) {
$rule = str_replace ( '%macros.' . $macro , '(' . $value . ')' , $rule );
}
}
if ( strstr ( $rule , '%macros.' )) {
if ( ++ $x < 30 ) {
$rule = self :: runMacros ( $rule , $x );
} else {
return false ;
}
}
2020-09-21 14:54:51 +02:00
2019-07-18 08:36:02 -05:00
return $rule ;
}
2018-07-21 13:34:59 -06:00
}