2014-11-30 17:49:52 +00:00
< ? php
/*
* LibreNMS
*
2018-03-14 20:25:19 +00:00
* Copyright ( c ) 2018 Neil Lathwood < https :// github . com / laf / http :// www . lathwood . co . uk / fa >
* Copyright ( c ) 2018 Tony Murray < murraytony @ gmail . com >
2014-11-30 17:49:52 +00:00
*
* This program is free software : you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation , either version 3 of the License , or ( at your
* option ) any later version . Please see LICENSE . txt at the top level of
* the source code distribution for details .
*/
2018-03-14 20:25:19 +00:00
use LibreNMS\Alerting\QueryBuilderFilter ;
2014-11-30 17:49:52 +00:00
2019-08-05 14:16:05 -05:00
if ( Auth :: user () -> hasGlobalAdmin ()) {
2018-03-14 20:25:19 +00:00
$filters = json_encode ( new QueryBuilderFilter ( 'alert' ));
2014-11-30 17:49:52 +00:00
2018-03-14 20:25:19 +00:00
?>
< div class = " modal fade " id = " create-alert " tabindex = " -1 " role = " dialog "
aria - labelledby = " Create " aria - hidden = " true " >
< div class = " modal-dialog modal-lg " >
< div class = " modal-content " >
< div class = " modal-header " >
< button type = " button " class = " close " data - dismiss = " modal " aria - hidden = " true " >& times ; </ button >
2018-07-14 22:15:43 +01:00
< h5 class = " modal-title " id = " Create " > Alert Rule :: < a href = " https://docs.librenms.org/Alerting/ " >< i class = " fa fa-book fa-1x " ></ i > Docs </ a > </ h5 >
2014-11-30 17:49:52 +00:00
</ div >
2018-03-14 20:25:19 +00:00
< div class = " modal-body " >
2018-09-19 13:47:45 +01:00
< ul class = " nav nav-tabs " role = " tablist " >
< li role = " presentation " class = " active " >< a href = " #main " aria - controls = " main " role = " tab " data - toggle = " tab " > Main </ a ></ li >
< li role = " presentation " >< a href = " #advanced " aria - controls = " advanced " role = " tab " data - toggle = " tab " > Advanced </ a ></ li >
</ ul >
< br />
2018-03-14 20:25:19 +00:00
< form method = " post " role = " form " id = " rules " class = " form-horizontal alerts-form " >
2019-07-17 07:20:26 -05:00
< ? php echo csrf_field () ?>
2018-03-14 20:25:19 +00:00
< input type = " hidden " name = " device_id " id = " device_id " value = " <?php echo isset( $device['device_id'] ) ? $device['device_id'] : -1; ?> " >
< input type = " hidden " name = " device_name " id = " device_name " value = " <?php echo format_hostname( $device ); ?> " >
< input type = " hidden " name = " rule_id " id = " rule_id " value = " " >
< input type = " hidden " name = " type " id = " type " value = " alert-rules " >
< input type = " hidden " name = " template_id " id = " template_id " value = " " >
< input type = " hidden " name = " builder_json " id = " builder_json " value = " " >
2018-09-19 13:47:45 +01:00
< div class = " tab-content " >
< div role = " tabpanel " class = " tab-pane active " id = " main " >
< div class = 'form-group' title = " The description of this alert rule. " >
< label for = 'name' class = 'col-sm-3 col-md-2 control-label' > Rule name : </ label >
< div class = 'col-sm-9 col-md-10' >
< input type = 'text' id = 'name' name = 'name' class = 'form-control validation' maxlength = '200' required >
</ div >
</ div >
< div class = " form-group " >
< div class = " col-sm-3 col-md-2 " >
< div class = " dropdown " >
< button class = " btn btn-default dropdown-toggle " type = " button "
id = " import-from " data - toggle = " dropdown " aria - haspopup = " true "
aria - expanded = " true " >
Import from
< span class = " caret " ></ span >
</ button >
< ul class = " dropdown-menu " aria - labelledby = " import-from " id = " import-dropdown " >
< li >< a href = " # " name = " import-query " id = " import-query " > SQL Query </ a ></ li >
< li >< a href = " # " name = " import-old-format " id = " import-old-format " > Old Format </ a ></ li >
< li >< a href = " # " name = " import-collection " id = " import-collection " > Collection </ a ></ li >
</ ul >
</ div >
</ div >
< div class = " col-sm-9 col-md-10 " >
< div id = " builder " ></ div >
</ div >
</ div >
< div class = " form-group " title = " How to display the alert. OK: green, Warning: yellow, Critical: red " >
< label for = 'severity' class = 'col-sm-3 col-md-2 control-label' > Severity : </ label >
< div class = " col-sm-2 " >
< select name = 'severity' id = 'severity' class = 'form-control' >
< option value = 'ok' > OK </ option >
< option value = 'warning' > Warning </ option >
< option value = 'critical' selected > Critical </ option >
</ select >
</ div >
</ div >
< div class = " form-group form-inline " >
< label for = 'count' class = 'col-sm-3 col-md-2 control-label' title = " How many notifications to issue while active before stopping. -1 means no limit. If interval is 0, this has no effect. " > Max alerts : </ label >
< div class = " col-sm-2 " title = " How many notifications to issue while active before stopping. -1 means no limit. If interval is 0, this has no effect. " >
< input type = 'text' id = 'count' name = 'count' class = 'form-control' size = " 4 " value = " 123 " >
</ div >
< div class = " col-sm-3 " title = " How log to wait before issuing a notification. If the alert clears before the delay, no notification will be issued. (s,m,h,d) " >
< label for = 'delay' class = 'control-label' style = " vertical-align: top; " > Delay : </ label >
< input type = 'text' id = 'delay' name = 'delay' class = 'form-control' size = " 4 " >
</ div >
< div class = " col-sm-4 col-md-3 " title = " How often to re-issue notifications while this alert is active. 0 means notify once. This is affected by the poller interval. (s,m,h,d) " >
< label for = 'interval' class = 'control-label' style = " vertical-align: top; " > Interval : </ label >
< input type = 'text' id = 'interval' name = 'interval' class = 'form-control' size = " 4 " >
</ div >
</ div >
< div class = 'form-group form-inline' >
< label for = 'mute' class = 'col-sm-3 col-md-2 control-label' title = " Show alert status in the webui, but do not issue notifications. " > Mute alerts : </ label >
< div class = 'col-sm-2' title = " Show alert status in the webui, but do not issue notifications. " >
< input type = " checkbox " name = " mute " id = " mute " >
</ div >
2020-01-19 14:55:51 +01:00
< label for = 'invert' class = 'col-sm-3 col-md-3 control-label' title = " Alert when this rule doesn't match. " style = " vertical-align: top; " > Invert rule match : </ label >
2018-09-19 13:47:45 +01:00
< div class = 'col-sm-2' title = " Alert when this rule doesn't match. " >
< input type = 'checkbox' name = 'invert' id = 'invert' >
</ div >
</ div >
< div class = " form-group " title = " Issue recovery notifications. " >
< label for = 'recovery' class = 'col-sm-3 col-md-2 control-label' > Recovery alerts : </ label >
< div class = 'col-sm-2' >
< input type = 'checkbox' name = 'recovery' id = 'recovery' >
</ div >
</ div >
2020-01-19 14:55:51 +01:00
< div class = " form-group form-inline " >
< label for = 'maps' class = 'col-sm-3 col-md-2 control-label' title = " Restricts this alert rule to the selected devices and groups. " > Match devices and groups list : </ label >
< div class = " col-sm-7 " style = " width: 56%; " >
2018-09-19 13:47:45 +01:00
< select id = " maps " name = " maps[] " class = " form-control " multiple = " multiple " ></ select >
</ div >
2020-01-19 14:55:51 +01:00
< div >
< label for = 'invert_map' class = 'col-md-1' style = " width: 14.1333%; " text - align = " left " title = " If ON, alert rule check will run on all devices except the selected devices and groups. " > All devices except in list : </ label >
< input type = 'checkbox' name = 'invert_map' id = 'invert_map' >
</ div >
2018-09-19 13:47:45 +01:00
</ div >
2018-09-20 12:35:54 +01:00
< div class = " form-group " title = " Restricts this alert rule to specified transports. " >
< label for = " transports " class = " col-sm-3 col-md-2 control-label " > Transports : </ label >
< div class = " col-sm-9 col-md-10 " >
< select id = " transports " name = " transports[] " class = " form-control " multiple = " multiple " ></ select >
</ div >
</ div >
2018-09-19 13:47:45 +01:00
< div class = 'form-group' title = " A link to some documentation on how to handle this alert. This will be included in notifications. " >
< label for = 'proc' class = 'col-sm-3 col-md-2 control-label' > Procedure URL : </ label >
< div class = 'col-sm-9 col-md-10' >
< input type = 'text' id = 'proc' name = 'proc' class = 'form-control validation' pattern = '(http|https)://.*' maxlength = '80' >
</ div >
2018-03-14 20:25:19 +00:00
</ div >
</ div >
2018-09-19 13:47:45 +01:00
< div role = " tabpanel " class = " tab-pane " id = " advanced " >
< div class = " form-group " >
< label for = " override_query " class = " col-sm-3 col-md-2 control-label " > Override SQL </ label >
< div class = " col-sm-9 col-md-10 " >
< input type = 'checkbox' name = 'override_query' id = 'override_query' >
</ div >
</ div >
< div class = " form-group " >
< label for = " adv_query " class = " col-sm-3 col-md-2 control-label " > Query </ label >
< div class = " col-sm-9 col-md-10 " >
< input type = " text " id = " adv_query " name = " adv_query " class = " form-control " >
</ div >
</ div >
2018-03-14 20:25:19 +00:00
</ div >
</ div >
< div class = " form-group " >
2018-03-29 14:57:41 -05:00
< div class = " col-sm-12 text-center " >
2018-03-14 20:25:19 +00:00
< button type = " button " class = " btn btn-success " id = " btn-save " name = " save-alert " >
Save Rule
</ button >
</ div >
</ div >
</ form >
2014-11-30 17:49:52 +00:00
</ div >
2018-03-14 20:25:19 +00:00
</ div >
2014-11-30 17:49:52 +00:00
</ div >
2018-03-14 20:25:19 +00:00
</ div >
2014-11-30 17:49:52 +00:00
2018-03-14 20:25:19 +00:00
< script src = " js/sql-parser.min.js " ></ script >
< script src = " js/query-builder.standalone.min.js " ></ script >
< script >
$ ( '#builder' ) . on ( 'afterApplyRuleFlags.queryBuilder afterCreateRuleFilters.queryBuilder' , function () {
$ ( " [name $ ='_filter'] " ) . each ( function () {
$ ( this ) . select2 ({
dropdownParent : $ ( " #create-alert " ),
dropdownAutoWidth : true ,
width : 'auto'
});
});
}) . on ( 'ruleToSQL.queryBuilder.filter' , function ( e , rule ) {
if ( rule . operator === 'regexp' ) {
e . value += ' \'' + rule . value + '\'' ;
}
}) . queryBuilder ({
plugins : [
'bt-tooltip-errors'
// 'not-group'
],
2015-04-03 18:22:29 +00:00
2018-03-14 20:25:19 +00:00
filters : < ? php echo $filters ; ?> ,
operators : [
2018-03-14 20:03:58 -05:00
'equal' , 'not_equal' , 'between' , 'not_between' , 'begins_with' , 'not_begins_with' , 'contains' , 'not_contains' , 'ends_with' , 'not_ends_with' , 'is_empty' , 'is_not_empty' , 'is_null' , 'is_not_null' ,
2018-03-14 20:25:19 +00:00
{ type : 'less' , nb_inputs : 1 , multiple : false , apply_to : [ 'string' , 'number' , 'datetime' ]},
{ type : 'less_or_equal' , nb_inputs : 1 , multiple : false , apply_to : [ 'string' , 'number' , 'datetime' ]},
{ type : 'greater' , nb_inputs : 1 , multiple : false , apply_to : [ 'string' , 'number' , 'datetime' ]},
{ type : 'greater_or_equal' , nb_inputs : 1 , multiple : false , apply_to : [ 'string' , 'number' , 'datetime' ]},
2018-03-28 22:10:54 -05:00
{ type : 'regex' , nb_inputs : 1 , multiple : false , apply_to : [ 'string' , 'number' ]},
{ type : 'not_regex' , nb_inputs : 1 , multiple : false , apply_to : [ 'string' , 'number' ]}
2018-03-14 20:25:19 +00:00
],
lang : {
operators : {
2018-03-14 21:53:47 +00:00
regexp : 'regex' ,
not_regex : 'not regex'
2018-03-14 20:25:19 +00:00
}
},
sqlOperators : {
2018-03-14 21:53:47 +00:00
regexp : { op : 'REGEXP' },
not_regexp : { op : 'NOT REGEXP' }
2018-03-14 20:25:19 +00:00
},
sqlRuleOperator : {
'REGEXP' : function ( v ) {
return { val : v , op : 'regexp' };
2018-03-14 21:53:47 +00:00
},
'NOT REGEXP' : function ( v ) {
return { val : v , op : 'not_regexp' };
2018-03-14 20:25:19 +00:00
}
}
});
2014-12-19 21:32:05 +00:00
2018-03-14 20:25:19 +00:00
$ ( '#btn-save' ) . on ( 'click' , function ( e ) {
e . preventDefault ();
var result_json = $ ( '#builder' ) . queryBuilder ( 'getRules' );
if ( result_json !== null && result_json . valid ) {
$ ( '#builder_json' ) . val ( JSON . stringify ( result_json ));
$ . ajax ({
type : " POST " ,
url : " ajax_form.php " ,
data : $ ( 'form.alerts-form' ) . serializeArray (),
dataType : " json " ,
success : function ( data ) {
if ( data . status == 'ok' ) {
toastr . success ( data . message );
$ ( '#create-alert' ) . modal ( 'hide' );
window . location . reload (); // FIXME: reload table
} else {
toastr . error ( data . message );
}
},
error : function () {
toastr . error ( 'Failed to process rule' );
}
2016-10-26 06:54:48 +00:00
});
}
});
2018-03-14 20:25:19 +00:00
$ ( '#import-query' ) . on ( 'click' , function ( e ) {
e . preventDefault ();
var sql_import = window . prompt ( " Enter your SQL query: " );
if ( sql_import ) {
try {
$ ( " #builder " ) . queryBuilder ( " setRulesFromSQL " , sql_import );
} catch ( e ) {
alert ( 'Your query could not be parsed' );
}
2014-11-30 17:49:52 +00:00
}
2018-03-14 20:25:19 +00:00
});
$ ( '#import-old-format' ) . on ( 'click' , function ( e ) {
e . preventDefault ();
var old_import = window . prompt ( " Enter your old alert rule: " );
if ( old_import ) {
try {
old_import = old_import . replace ( /&&/ g , 'AND' );
old_import = old_import . replace ( / \ | \ |/ g , 'OR' );
old_import = old_import . replace ( /%/ g , '' );
old_import = old_import . replace ( / " /g, " ' " );
old_import = old_import . replace ( /~/ g , " REGEXP " );
old_import = old_import . replace ( /@/ g , " .* " );
$ ( " #builder " ) . queryBuilder ( " setRulesFromSQL " , old_import );
} catch ( e ) {
alert ( 'Your query could not be parsed' );
}
2015-05-20 21:46:54 +01:00
}
2018-03-14 20:25:19 +00:00
});
2014-11-30 17:49:52 +00:00
2018-03-14 20:25:19 +00:00
$ ( '#import-collection' ) . on ( 'click' , function ( e ) {
e . preventDefault ();
$ ( " #search_rule_modal " ) . modal ( 'show' );
});
2015-04-29 17:48:57 +01:00
2018-03-14 20:25:19 +00:00
$ ( '#create-alert' ) . on ( 'show.bs.modal' , function ( e ) {
//get data-id attribute of the clicked element
var rule_id = $ ( e . relatedTarget ) . data ( 'rule_id' );
$ ( '#rule_id' ) . val ( rule_id );
2014-11-30 17:49:52 +00:00
2018-03-14 20:25:19 +00:00
if ( rule_id >= 0 ) {
$ . ajax ({
type : " POST " ,
url : " ajax_form.php " ,
data : { type : " parse-alert-rule " , alert_id : rule_id },
dataType : " json " ,
success : function ( data ) {
loadRule ( data );
}
});
} else {
// new, reset form
$ ( " #builder " ) . queryBuilder ( " reset " );
var $severity = $ ( '#severity' );
$severity . val ( $severity . find ( " option[selected] " ) . val ());
$ ( " #mute " ) . bootstrapSwitch ( 'state' , false );
$ ( " #invert " ) . bootstrapSwitch ( 'state' , false );
2018-03-22 17:38:16 +00:00
$ ( " #recovery " ) . bootstrapSwitch ( 'state' , true );
2018-09-19 13:47:45 +01:00
$ ( " #override_query " ) . bootstrapSwitch ( 'state' , false );
2020-01-19 14:55:51 +01:00
$ ( " #invert_map " ) . bootstrapSwitch ( 'state' , false );
2018-03-14 20:25:19 +00:00
$ ( this ) . find ( " input[type=text] " ) . val ( " " );
$ ( '#count' ) . val ( '-1' );
2018-03-28 22:10:54 -05:00
$ ( '#delay' ) . val ( '1m' );
$ ( '#interval' ) . val ( '5m' );
2018-09-19 13:47:45 +01:00
$ ( '#adv_query' ) . val ( '' );
2018-03-14 20:25:19 +00:00
var $maps = $ ( '#maps' );
$maps . empty ();
$maps . val ( null ) . trigger ( 'change' );
2018-09-19 13:47:45 +01:00
setRuleDevice (); // pre-populate device in the maps if this is a per-device rule
2018-07-21 13:34:59 -06:00
var $transports = $ ( " #transports " );
$transports . empty ();
$transports . val ( null ) . trigger ( 'change' );
$ ( " #transport-choice " ) . val ( " email " );
2018-03-14 20:25:19 +00:00
}
2014-11-30 17:49:52 +00:00
});
2018-03-14 20:25:19 +00:00
function loadRule ( rule ) {
$ ( '#name' ) . val ( rule . name );
$ ( '#proc' ) . val ( rule . proc );
$ ( '#builder' ) . queryBuilder ( " setRules " , rule . builder );
$ ( '#severity' ) . val ( rule . severity ) . trigger ( 'change' );
2018-09-19 13:47:45 +01:00
$ ( '#adv_query' ) . val ( rule . adv_query );
2018-03-14 20:25:19 +00:00
var $maps = $ ( '#maps' );
$maps . empty ();
$maps . val ( null ) . trigger ( 'change' ); // clear
if ( rule . maps == null ) {
// collection rule
setRuleDevice ()
2015-05-22 08:17:26 +01:00
} else {
2018-03-14 20:25:19 +00:00
$ . each ( rule . maps , function ( index , value ) {
var option = new Option ( value . text , value . id , true , true );
$maps . append ( option ) . trigger ( 'change' )
});
2014-11-30 17:49:52 +00:00
}
2018-07-21 13:34:59 -06:00
var $transports = $ ( " #transports " );
$transports . empty ();
$transports . val ( null ) . trigger ( 'change' );
if ( rule . transports != null ) {
$ . each ( rule . transports , function ( index , value ) {
var option = new Option ( value . text , value . id , true , true );
$transports . append ( option ) . trigger ( " change " );
});
}
2014-11-30 17:49:52 +00:00
2018-03-14 20:25:19 +00:00
if ( rule . extra != null ) {
var extra = rule . extra ;
$ ( '#count' ) . val ( extra . count );
if (( extra . delay / 86400 ) >= 1 ) {
2018-03-28 22:10:54 -05:00
$ ( '#delay' ) . val ( extra . delay / 86400 + 'd' );
2018-03-14 20:25:19 +00:00
} else if (( extra . delay / 3600 ) >= 1 ) {
2018-03-28 22:10:54 -05:00
$ ( '#delay' ) . val ( extra . delay / 3600 + 'h' );
2018-03-14 20:25:19 +00:00
} else if (( extra . delay / 60 ) >= 1 ) {
2018-03-28 22:10:54 -05:00
$ ( '#delay' ) . val ( extra . delay / 60 + 'm' );
2018-03-14 20:25:19 +00:00
} else {
$ ( '#delay' ) . val ( extra . delay );
}
2015-05-22 09:33:52 +01:00
2018-03-14 20:25:19 +00:00
if (( extra . interval / 86400 ) >= 1 ) {
2018-03-28 22:10:54 -05:00
$ ( '#interval' ) . val ( extra . interval / 86400 + 'd' );
2018-03-14 20:25:19 +00:00
} else if (( extra . interval / 3600 ) >= 1 ) {
2018-03-28 22:10:54 -05:00
$ ( '#interval' ) . val ( extra . interval / 3600 + 'h' );
2018-03-14 20:25:19 +00:00
} else if (( extra . interval / 60 ) >= 1 ) {
2018-03-28 22:10:54 -05:00
$ ( '#interval' ) . val ( extra . interval / 60 + 'm' );
2018-03-14 20:25:19 +00:00
} else {
$ ( '#interval' ) . val ( extra . interval );
}
2015-03-05 11:40:02 +00:00
2018-09-19 13:47:45 +01:00
if ( extra . adv_query ) {
$ ( '#adv_query' ) . val ( extra . adv_query );
}
2018-03-14 20:25:19 +00:00
$ ( " [name='mute'] " ) . bootstrapSwitch ( 'state' , extra . mute );
$ ( " [name='invert'] " ) . bootstrapSwitch ( 'state' , extra . invert );
2018-03-22 17:38:16 +00:00
if ( typeof extra . recovery == 'undefined' ) {
extra . recovery = true ;
}
2020-01-19 14:55:51 +01:00
2018-10-06 22:15:07 +01:00
if ( typeof extra . options == 'undefined' ) {
extra . options = new Array ();
}
2018-09-19 13:47:45 +01:00
if ( typeof extra . options . override_query == 'undefined' ) {
extra . options . override_query = false ;
}
$ ( " [name='recovery'] " ) . bootstrapSwitch ( 'state' , extra . recovery );
2020-01-19 14:55:51 +01:00
if ( rule . invert_map == 1 ) {
$ ( " [name='invert_map'] " ) . bootstrapSwitch ( 'state' , true );
} else {
$ ( " [name='invert_map'] " ) . bootstrapSwitch ( 'state' , false );
}
2018-09-19 13:47:45 +01:00
$ ( " [name='override_query'] " ) . bootstrapSwitch ( 'state' , extra . options . override_query );
2018-03-14 20:25:19 +00:00
} else {
$ ( '#count' ) . val ( '-1' );
}
}
function setRuleDevice () {
// pre-populate device in the maps if this is a per-device rule
var device_id = $ ( '#device_id' ) . val ();
if ( device_id > 0 ) {
var device_name = $ ( '#device_name' ) . val ();
var option = new Option ( device_name , device_id , true , true );
$ ( '#maps' ) . append ( option ) . trigger ( 'change' )
}
}
$ ( " #maps " ) . select2 ({
width : '100%' ,
placeholder : " Devices or Groups " ,
ajax : {
url : 'ajax_list.php' ,
delay : 250 ,
data : function ( params ) {
return {
type : 'devices_groups' ,
search : params . term
};
}
}
});
2018-07-21 13:34:59 -06:00
$ ( " #transports " ) . select2 ({
width : " 100% " ,
placeholder : " Transport/Group Name " ,
ajax : {
url : 'ajax_list.php' ,
delay : 250 ,
data : function ( params ) {
return {
type : " transport_groups " ,
search : params . term
}
}
}
});
2018-03-14 20:25:19 +00:00
</ script >
< ? php
2015-03-05 11:40:02 +00:00
}