Issues (2963)

includes/html/modal/new_alert_rule.inc.php (1 issue)

1
<?php
2
/*
3
 * LibreNMS
4
 *
5
 * Copyright (c) 2018 Neil Lathwood <https://github.com/laf/ http://www.lathwood.co.uk/fa>
6
 * Copyright (c) 2018 Tony Murray <[email protected]>
7
 *
8
 * This program is free software: you can redistribute it and/or modify it
9
 * under the terms of the GNU General Public License as published by the
10
 * Free Software Foundation, either version 3 of the License, or (at your
11
 * option) any later version.  Please see LICENSE.txt at the top level of
12
 * the source code distribution for details.
13
 */
14
15
use LibreNMS\Alerting\QueryBuilderFilter;
16
use LibreNMS\Config;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Config. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
17
18
$default_severity = Config::get('alert_rule.severity');
19
$default_max_alerts = Config::get('alert_rule.max_alerts');
20
$default_delay = Config::get('alert_rule.delay') . 'm';
21
$default_interval = Config::get('alert_rule.interval') . 'm';
22
$default_mute_alerts = Config::get('alert_rule.mute_alerts');
23
$default_invert_rule_match = Config::get('alert_rule.invert_rule_match');
24
$default_recovery_alerts = Config::get('alert_rule.recovery_alerts');
25
$default_invert_map = Config::get('alert_rule.invert_map');
26
27
if (Auth::user()->hasGlobalAdmin()) {
28
    $filters = json_encode(new QueryBuilderFilter('alert')); ?>
29
30
    <div class="modal fade" id="create-alert" tabindex="-1" role="dialog"
31
         aria-labelledby="Create" aria-hidden="true">
32
        <div class="modal-dialog modal-lg">
33
            <div class="modal-content">
34
                <div class="modal-header">
35
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
36
                    <h5 class="modal-title" id="Create">Alert Rule :: <a target="_blank" href="https://docs.librenms.org/Alerting/"><i class="fa fa-book fa-1x"></i> Docs</a> </h5>
37
                </div>
38
                <div class="modal-body">
39
                    <ul class="nav nav-tabs" role="tablist">
40
                        <li role="presentation" class="active"><a href="#main" aria-controls="main" role="tab" data-toggle="tab">Main </a></li>
41
                        <li role="presentation"><a href="#advanced" aria-controls="advanced" role="tab" data-toggle="tab">Advanced</a></li>
42
                    </ul>
43
                    <br />
44
                    <form method="post" role="form" id="rules" class="form-horizontal alerts-form">
45
                        <?php echo csrf_field() ?>
46
                        <input type="hidden" name="device_id" id="device_id" value="<?php echo isset($device['device_id']) ? $device['device_id'] : -1; ?>">
47
                        <input type="hidden" name="device_name" id="device_name" value="<?php echo format_hostname($device); ?>">
48
                        <input type="hidden" name="rule_id" id="rule_id" value="">
49
                        <input type="hidden" name="type" id="type" value="alert-rules">
50
                        <input type="hidden" name="template_id" id="template_id" value="">
51
                        <input type="hidden" name="builder_json" id="builder_json" value="">
52
                        <div class="tab-content">
53
                            <div role="tabpanel" class="tab-pane active" id="main">
54
                                <div class='form-group' title="The description of this alert rule.">
55
                                    <label for='rule_name' class='col-sm-3 col-md-2 control-label'>Rule name: </label>
56
                                    <div class='col-sm-9 col-md-10'>
57
                                        <input type='text' id='rule_name' name='name' class='form-control validation' maxlength='200' required>
58
                                    </div>
59
                                </div>
60
                                <div class="form-group">
61
                                    <div class="col-sm-3 col-md-2">
62
                                        <div class="dropdown">
63
                                            <button class="btn btn-default dropdown-toggle" type="button"
64
                                                    id="import-from" data-toggle="dropdown" aria-haspopup="true"
65
                                                    aria-expanded="true">
66
                                                Import from
67
                                                <span class="caret"></span>
68
                                            </button>
69
                                            <ul class="dropdown-menu" aria-labelledby="import-from" id="import-dropdown">
70
                                                <li><a href="#" name="import-query" id="import-query">SQL Query</a></li>
71
                                                <li><a href="#" name="import-old-format" id="import-old-format">Old Format</a></li>
72
                                                <li><a href="#" name="import-collection" id="import-collection">Collection</a></li>
73
                                                <li><a href="#" name="import-alert_rule" id="import-alert_rule">Alert Rule</a></li>
74
                                            </ul>
75
                                        </div>
76
                                    </div>
77
                                    <div class="col-sm-9 col-md-10">
78
                                        <div id="builder"></div>
79
                                    </div>
80
                                </div>
81
                                <div class="form-group" title="How to display the alert.  OK: green, Warning: yellow, Critical: red">
82
                                    <label for='severity' class='col-sm-3 col-md-2 control-label'>Severity: </label>
83
                                    <div class="col-sm-2">
84
                                        <select name='severity' id='severity' class='form-control'>
85
                                            <option value='ok'>OK</option>
86
                                            <option value='warning'>Warning</option>
87
                                            <option value='critical' selected>Critical</option>
88
                                        </select>
89
                                    </div>
90
                                </div>
91
                                <div class="form-group form-inline">
92
                                    <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>
93
                                    <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.">
94
                                        <input type='text' id='count' name='count' class='form-control' size="4" value="123">
95
                                    </div>
96
                                    <div class="col-sm-3" title="How long to wait before issuing a notification. If the alert clears before the delay, no notification will be issued. (s,m,h,d)">
97
                                        <label for='delay' class='control-label' style="vertical-align: top;">Delay: </label>
98
                                        <input type='text' id='delay' name='delay' class='form-control' size="4">
99
                                    </div>
100
                                    <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)">
101
                                        <label for='interval' class='control-label' style="vertical-align: top;">Interval: </label>
102
                                        <input type='text' id='interval' name='interval' class='form-control' size="4">
103
                                    </div>
104
                                </div>
105
                                <div class='form-group form-inline'>
106
                                    <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>
107
                                    <div class='col-sm-2' title="Show alert status in the webui, but do not issue notifications.">
108
                                        <input type="checkbox" name="mute" id="mute">
109
                                    </div>
110
                                    <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>
111
                                    <div class='col-sm-2' title="Alert when this rule doesn't match.">
112
                                        <input type='checkbox' name='invert' id='invert'>
113
                                    </div>
114
                                </div>
115
                                <div class="form-group" title="Issue recovery notifications.">
116
                                    <label for='recovery' class='col-sm-3 col-md-2 control-label'>Recovery alerts: </label>
117
                                    <div class='col-sm-2'>
118
                                        <input type='checkbox' name='recovery' id='recovery'>
119
                                    </div>
120
                                </div>
121
                                <div class="form-group form-inline">
122
                                    <label for='maps' class='col-sm-3 col-md-2 control-label' title="Restricts this alert rule to the selected devices, groups and locations.">Match devices, groups and locations list: </label>
123
                                    <div class="col-sm-7" style="width: 56%;">
124
                                        <select id="maps" name="maps[]" class="form-control" multiple="multiple"></select>
125
                                    </div>
126
                                    <div>
127
                                        <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>
128
                                        <input type='checkbox' name='invert_map' id='invert_map'>
129
                                    </div>
130
                                </div>
131
                                <div class="form-group" title="Restricts this alert rule to specified transports.">
132
                                    <label for="transports" class="col-sm-3 col-md-2 control-label">Transports: </label>
133
                                    <div class="col-sm-9 col-md-10">
134
                                        <select id="transports" name="transports[]" class="form-control" multiple="multiple"></select>
135
                                    </div>
136
                                </div>
137
                                <div class='form-group' title="A link to some documentation on how to handle this alert. This will be included in notifications.">
138
                                    <label for='proc' class='col-sm-3 col-md-2 control-label'>Procedure URL: </label>
139
                                    <div class='col-sm-9 col-md-10'>
140
                                        <input type='text' id='proc' name='proc' class='form-control validation' pattern='(http|https)://.*' maxlength='80'>
141
                                    </div>
142
                                </div>
143
                            </div>
144
                            <div role="tabpanel" class="tab-pane" id="advanced">
145
                                <div class="form-group">
146
                                    <label for="override_query" class="col-sm-3 col-md-2 control-label">Override SQL</label>
147
                                    <div class="col-sm-9 col-md-10">
148
                                        <input type='checkbox' name='override_query' id='override_query'>
149
                                    </div>
150
                                </div>
151
                                <div class="form-group">
152
                                    <label for="adv_query" class="col-sm-3 col-md-2 control-label">Query</label>
153
                                    <div class="col-sm-9 col-md-10">
154
                                        <input type="text" id="adv_query" name="adv_query" class="form-control">
155
                                    </div>
156
                                </div>
157
                            </div>
158
                        </div>
159
                        <div class="form-group">
160
                            <div class="col-sm-12 text-center">
161
                                <button type="button" class="btn btn-success" id="btn-save" name="save-alert">
162
                                    Save Rule
163
                                </button>
164
                            </div>
165
                        </div>
166
                    </form>
167
                </div>
168
            </div>
169
        </div>
170
    </div>
171
172
    <script src="js/sql-parser.min.js"></script>
173
    <script src="js/query-builder.standalone.min.js"></script>
174
    <script>
175
        $('#builder').on('afterApplyRuleFlags.queryBuilder afterCreateRuleFilters.queryBuilder', function () {
176
            $("[name$='_filter']").each(function () {
177
                $(this).select2({
178
                    dropdownParent: $("#create-alert"),
179
                    dropdownAutoWidth : true,
180
                    width: 'auto'
181
                });
182
            });
183
        }).on('ruleToSQL.queryBuilder.filter', function (e, rule) {
184
            if (rule.operator === 'regexp') {
185
                e.value += ' \'' + rule.value + '\'';
186
            }
187
        }).queryBuilder({
188
            plugins: [
189
                'bt-tooltip-errors'
190
                // 'not-group'
191
            ],
192
193
            filters: <?php echo $filters; ?>,
194
            operators: [
195
                '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',
196
                {type: 'less', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime']},
197
                {type: 'less_or_equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime']},
198
                {type: 'greater', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime']},
199
                {type: 'greater_or_equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime']},
200
                {type: 'regex', nb_inputs: 1, multiple: false, apply_to: ['string', 'number']},
201
                {type: 'not_regex', nb_inputs: 1, multiple: false, apply_to: ['string', 'number']}
202
            ],
203
            lang: {
204
                operators: {
205
                    regexp: 'regex',
206
                    not_regex: 'not regex'
207
                }
208
            },
209
            sqlOperators: {
210
                regexp: {op: 'REGEXP'},
211
                not_regexp: {op: 'NOT REGEXP'}
212
            },
213
            sqlRuleOperator: {
214
                'REGEXP': function (v) {
215
                    return {val: v, op: 'regexp'};
216
                },
217
                'NOT REGEXP': function (v) {
218
                    return {val: v, op: 'not_regexp'};
219
                }
220
            }
221
        });
222
223
        $('#btn-save').on('click', function (e) {
224
            e.preventDefault();
225
            var result_json = $('#builder').queryBuilder('getRules');
226
            if (result_json !== null && result_json.valid) {
227
                $('#builder_json').val(JSON.stringify(result_json));
228
                $.ajax({
229
                    type: "POST",
230
                    url: "ajax_form.php",
231
                    data: $('form.alerts-form').serializeArray(),
232
                    dataType: "json",
233
                    success: function (data) {
234
                        if (data.status == 'ok') {
235
                            toastr.success(data.message);
236
                            $('#create-alert').modal('hide');
237
                            window.location.reload(); // FIXME: reload table
238
                        } else {
239
                            toastr.error(data.message);
240
                        }
241
                    },
242
                    error: function () {
243
                        toastr.error('Failed to process rule');
244
                    }
245
                });
246
            }
247
        });
248
        $('#import-query').on('click', function (e) {
249
            e.preventDefault();
250
            var sql_import = window.prompt("Enter your SQL query:");
251
            if (sql_import) {
252
                try {
253
                    $("#builder").queryBuilder("setRulesFromSQL", sql_import);
254
                } catch (e) {
255
                    alert('Your query could not be parsed');
256
                }
257
            }
258
        });
259
260
        $('#import-old-format').on('click', function (e) {
261
            e.preventDefault();
262
            var old_import = window.prompt("Enter your old alert rule:");
263
            if (old_import) {
264
                try {
265
                    old_import = old_import.replace(/&&/g, 'AND');
266
                    old_import = old_import.replace(/\|\|/g, 'OR');
267
                    old_import = old_import.replace(/%/g, '');
268
                    old_import = old_import.replace(/"/g, "'");
269
                    old_import = old_import.replace(/~/g, "REGEXP");
270
                    old_import = old_import.replace(/@/g, ".*");
271
                    $("#builder").queryBuilder("setRulesFromSQL", old_import);
272
                } catch (e) {
273
                    alert('Your query could not be parsed');
274
                }
275
            }
276
        });
277
278
        $('#import-collection').on('click', function (e) {
279
            e.preventDefault();
280
            $("#search_rule_modal").modal('show');
281
        });
282
283
        $('#import-alert_rule').on('click', function (e) {
284
            e.preventDefault();
285
            $("#search_alert_rule_modal").modal('show');
286
        });
287
288
        $('#create-alert').on('show.bs.modal', function(e) {
289
            //get data-id attribute of the clicked element
290
            var rule_id = $(e.relatedTarget).data('rule_id');
291
            $('#rule_id').val(rule_id);
292
293
            if (rule_id >= 0) {
294
                $.ajax({
295
                    type: "POST",
296
                    url: "ajax_form.php",
297
                    data: { type: "parse-alert-rule", alert_id: rule_id },
298
                    dataType: "json",
299
                    success: function (data) {
300
                        loadRule(data);
301
                    }
302
                });
303
            } else {
304
                // new, reset form
305
                $("#builder").queryBuilder("reset");
306
                var $severity = $('#severity');
307
                $severity.val($severity.find("option[selected]").val());
308
                $("#mute").bootstrapSwitch('state', <?=$default_mute_alerts?>);
309
                $("#invert").bootstrapSwitch('state', <?=$default_invert_rule_match?>);
310
                $("#recovery").bootstrapSwitch('state', <?=$default_recovery_alerts?>);
311
                $("#override_query").bootstrapSwitch('state', false);
312
                $("#invert_map").bootstrapSwitch('state', <?=$default_invert_map?>);
313
                $(this).find("input[type=text]").val("");
314
                $('#count').val('<?=$default_max_alerts?>');
315
                $('#delay').val('<?=$default_delay?>');
316
                $('#interval').val('<?=$default_interval?>');
317
                $('#adv_query').val('');
318
                $('#severity').val('<?=$default_severity?>');
319
320
                var $maps = $('#maps');
321
                $maps.empty();
322
                $maps.val(null).trigger('change');
323
                setRuleDevice();// pre-populate device in the maps if this is a per-device rule
324
325
                var $transports = $("#transports");
326
                $transports.empty();
327
                $transports.val(null).trigger('change');
328
                $("#transport-choice").val("email");
329
            }
330
        });
331
332
        function loadRule(rule) {
333
            $('#rule_name').val(rule.name);
334
            $('#proc').val(rule.proc);
335
            $('#builder').queryBuilder("setRules", rule.builder);
336
            $('#severity').val(rule.severity).trigger('change');
337
            $('#adv_query').val(rule.adv_query);
338
339
            var $maps = $('#maps');
340
            $maps.empty();
341
            $maps.val(null).trigger('change'); // clear
342
            if (rule.maps == null) {
343
                // collection rule
344
                setRuleDevice()
345
            } else {
346
                $.each(rule.maps, function(index, value) {
347
                    var option = new Option(value.text, value.id, true, true);
348
                    $maps.append(option).trigger('change')
349
                });
350
            }
351
            var $transports = $("#transports");
352
            $transports.empty();
353
            $transports.val(null).trigger('change');
354
            if(rule.transports != null) {
355
                $.each(rule.transports, function(index, value) {
356
                    var option = new Option(value.text, value.id, true, true);
357
                    $transports.append(option).trigger("change");
358
                });
359
            }
360
361
            if (rule.extra != null) {
362
                var extra = rule.extra;
363
                $('#count').val(extra.count);
364
                if ((extra.delay / 86400) >= 1) {
365
                    $('#delay').val(extra.delay / 86400 + 'd');
366
                } else if ((extra.delay / 3600) >= 1) {
367
                    $('#delay').val( extra.delay / 3600 + 'h');
368
                } else if ((extra.delay / 60) >= 1) {
369
                    $('#delay').val( extra.delay / 60 + 'm');
370
                } else {
371
                    $('#delay').val(extra.delay);
372
                }
373
374
                if ((extra.interval / 86400) >= 1) {
375
                    $('#interval').val(extra.interval / 86400 + 'd');
376
                } else if ((extra.interval / 3600) >= 1) {
377
                    $('#interval').val(extra.interval / 3600 + 'h');
378
                } else if ((extra.interval / 60) >= 1) {
379
                    $('#interval').val(extra.interval / 60 + 'm');
380
                } else {
381
                    $('#interval').val(extra.interval);
382
                }
383
384
                if (extra.adv_query) {
385
                    $('#adv_query').val(extra.adv_query);
386
                }
387
                $("[name='mute']").bootstrapSwitch('state', extra.mute);
388
                $("[name='invert']").bootstrapSwitch('state', extra.invert);
389
                if (typeof extra.recovery == 'undefined') {
390
                    extra.recovery = '<?=$default_recovery_alerts?>';
391
                }
392
393
                if (typeof extra.options == 'undefined') {
394
                    extra.options = new Array();
395
                }
396
                if (typeof extra.options.override_query == 'undefined') {
397
                    extra.options.override_query = false;
398
                }
399
                $("[name='recovery']").bootstrapSwitch('state', extra.recovery);
400
401
                if (rule.invert_map == 1) {
402
                    $("[name='invert_map']").bootstrapSwitch('state', true);
403
                }else{
404
                    $("[name='invert_map']").bootstrapSwitch('state', false);
405
                }
406
407
                $("[name='override_query']").bootstrapSwitch('state', extra.options.override_query);
408
            } else {
409
                $('#count').val('<?=$default_max_alerts?>');
410
            }
411
        }
412
413
        function setRuleDevice() {
414
            // pre-populate device in the maps if this is a per-device rule
415
            var device_id = $('#device_id').val();
416
            if (device_id > 0) {
417
                var device_name = $('#device_name').val();
418
                var option = new Option(device_name, device_id, true, true);
419
                $('#maps').append(option).trigger('change')
420
            }
421
        }
422
423
        $("#maps").select2({
424
            width: '100%',
425
            placeholder: "Devices, Groups or Locations",
426
            ajax: {
427
                url: 'ajax_list.php',
428
                delay: 250,
429
                data: function (params) {
430
                    return {
431
                        type: 'devices_groups_locations',
432
                        search: params.term
433
                    };
434
                }
435
            }
436
        });
437
438
        $("#transports").select2({
439
            width: "100%",
440
            placeholder: "Transport/Group Name",
441
            ajax: {
442
                url: 'ajax_list.php',
443
                delay: 250,
444
                data: function(params) {
445
                    return {
446
                        type: "transport_groups",
447
                        search: params.term
448
                    }
449
                }
450
            }
451
        });
452
    </script>
453
    <?php
454
}
455