Show More
@@ -29,6 +29,10 b' To run celery queue processing:' | |||||
29 |
|
29 | |||
30 | celery worker -A appenlight.celery -Q "reports,logs,metrics,default" --ini=development.ini |
|
30 | celery worker -A appenlight.celery -Q "reports,logs,metrics,default" --ini=development.ini | |
31 |
|
31 | |||
|
32 | To run celery beats scheduling: | |||
|
33 | ||||
|
34 | celery beat -A appenlight.celery --ini=development.ini | |||
|
35 | ||||
32 | You should also run the channelstream websocket server for real-time notifications |
|
36 | You should also run the channelstream websocket server for real-time notifications | |
33 |
|
37 | |||
34 | channelstream -i filename.ini |
|
38 | channelstream -i filename.ini |
@@ -188,6 +188,10 b' class Rule(RuleBase):' | |||||
188 | rule = OR(self.config['rules'], self.type_matrix, |
|
188 | rule = OR(self.config['rules'], self.type_matrix, | |
189 | config_manipulator=self.config_manipulator) |
|
189 | config_manipulator=self.config_manipulator) | |
190 | return rule.match(struct) |
|
190 | return rule.match(struct) | |
|
191 | elif field_name == '__NOT__': | |||
|
192 | rule = NOT(self.config['rules'], self.type_matrix, | |||
|
193 | config_manipulator=self.config_manipulator) | |||
|
194 | return rule.match(struct) | |||
191 |
|
195 | |||
192 | if test_value is None: |
|
196 | if test_value is None: | |
193 | return False |
|
197 | return False | |
@@ -236,6 +240,16 b' class AND(Rule):' | |||||
236 | in self.rules]) |
|
240 | in self.rules]) | |
237 |
|
241 | |||
238 |
|
242 | |||
|
243 | class NOT(Rule): | |||
|
244 | def __init__(self, rules, *args, **kwargs): | |||
|
245 | super(NOT, self).__init__({}, *args, **kwargs) | |||
|
246 | self.rules = rules | |||
|
247 | ||||
|
248 | def match(self, struct): | |||
|
249 | return all([not self.subrule_check(r_conf, struct) for r_conf | |||
|
250 | in self.rules]) | |||
|
251 | ||||
|
252 | ||||
239 | class OR(Rule): |
|
253 | class OR(Rule): | |
240 | def __init__(self, rules, *args, **kwargs): |
|
254 | def __init__(self, rules, *args, **kwargs): | |
241 | super(OR, self).__init__({}, *args, **kwargs) |
|
255 | super(OR, self).__init__({}, *args, **kwargs) | |
@@ -266,7 +280,8 b' class RuleService(object):' | |||||
266 | if manipulator_func is None: |
|
280 | if manipulator_func is None: | |
267 | def label_rewriter_func(rule): |
|
281 | def label_rewriter_func(rule): | |
268 | field = rule.config.get('field') |
|
282 | field = rule.config.get('field') | |
269 |
if not field or rule.config['field'] in ['__OR__', |
|
283 | if not field or rule.config['field'] in ['__OR__', | |
|
284 | '__AND__', '__NOT__']: | |||
270 | return |
|
285 | return | |
271 |
|
286 | |||
272 | to_map = field_mappings.get(rule.config['field']) |
|
287 | to_map = field_mappings.get(rule.config['field']) |
@@ -5376,11 +5376,11 b' function kickstartAE() {' | |||||
5376 | " {{rule_ctrlr.readOnlyPossibleFields[rule_ctrlr.rule.field]}}\n" + |
|
5376 | " {{rule_ctrlr.readOnlyPossibleFields[rule_ctrlr.rule.field]}}\n" + | |
5377 | " </span>\n" + |
|
5377 | " </span>\n" + | |
5378 | "\n" + |
|
5378 | "\n" + | |
5379 | " <span ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'\">\n" + |
|
5379 | " <span ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'\">\n" + | |
5380 | " is {{rule_ctrlr.ruleDefinitions.allOps[rule_ctrlr.rule.op]}} {{rule_ctrlr.rule.value}}\n" + |
|
5380 | " is {{rule_ctrlr.ruleDefinitions.allOps[rule_ctrlr.rule.op]}} {{rule_ctrlr.rule.value}}\n" + | |
5381 | " </span>\n" + |
|
5381 | " </span>\n" + | |
5382 | "\n" + |
|
5382 | "\n" + | |
5383 | " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'\">\n" + |
|
5383 | " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'\">\n" + | |
5384 | " <p ng-if=\"parent\"><strong>Subrules</strong></p>\n" + |
|
5384 | " <p ng-if=\"parent\"><strong>Subrules</strong></p>\n" + | |
5385 | " <div ng-repeat=\"subrule in rule_ctrlr.rule.rules\" class=\"m-l-2\">\n" + |
|
5385 | " <div ng-repeat=\"subrule in rule_ctrlr.rule.rules\" class=\"m-l-2\">\n" + | |
5386 | "\n" + |
|
5386 | "\n" + | |
@@ -5408,7 +5408,7 b' function kickstartAE() {' | |||||
5408 | " ng-options=\"key as label for (key, label) in rule_ctrlr.ruleDefinitions.possibleFields\"></select>\n" + |
|
5408 | " ng-options=\"key as label for (key, label) in rule_ctrlr.ruleDefinitions.possibleFields\"></select>\n" + | |
5409 | " </div>\n" + |
|
5409 | " </div>\n" + | |
5410 | "\n" + |
|
5410 | "\n" + | |
5411 | " <div ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'\" class=\"form-group\">\n" + |
|
5411 | " <div ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'\" class=\"form-group\">\n" + | |
5412 | "\n" + |
|
5412 | "\n" + | |
5413 | " <select ng-model=\"rule_ctrlr.rule.op\" class=\"form-control\"\n" + |
|
5413 | " <select ng-model=\"rule_ctrlr.rule.op\" class=\"form-control\"\n" + | |
5414 | " ng-change=\"rule_ctrlr.setDirty()\"\n" + |
|
5414 | " ng-change=\"rule_ctrlr.setDirty()\"\n" + | |
@@ -5419,7 +5419,7 b' function kickstartAE() {' | |||||
5419 | "\n" + |
|
5419 | "\n" + | |
5420 | " </div>\n" + |
|
5420 | " </div>\n" + | |
5421 | "\n" + |
|
5421 | "\n" + | |
5422 | " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'\">\n" + |
|
5422 | " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'\">\n" + | |
5423 | " <p ng-if=\"parent\"><strong>Subrules</strong></p>\n" + |
|
5423 | " <p ng-if=\"parent\"><strong>Subrules</strong></p>\n" + | |
5424 | " <div ng-repeat=\"subrule in rule_ctrlr.rule.rules\" class=\"m-l-2\">\n" + |
|
5424 | " <div ng-repeat=\"subrule in rule_ctrlr.rule.rules\" class=\"m-l-2\">\n" + | |
5425 | " <div class=\"panel panel-default\">\n" + |
|
5425 | " <div class=\"panel panel-default\">\n" + | |
@@ -10694,6 +10694,7 b' function AlertChannelsController(userSelfPropertyResource, applicationsNoIdResou' | |||||
10694 | var possibleFields = { |
|
10694 | var possibleFields = { | |
10695 | '__AND__': 'All met (composite rule)', |
|
10695 | '__AND__': 'All met (composite rule)', | |
10696 | '__OR__': 'One met (composite rule)', |
|
10696 | '__OR__': 'One met (composite rule)', | |
|
10697 | '__NOT__': 'Not met (composite rule)', | |||
10697 | 'http_status': 'HTTP Status', |
|
10698 | 'http_status': 'HTTP Status', | |
10698 | 'duration': 'Request duration', |
|
10699 | 'duration': 'Request duration', | |
10699 | 'group:priority': 'Group -> Priority', |
|
10700 | 'group:priority': 'Group -> Priority', | |
@@ -11610,6 +11611,7 b" angular.module('appenlight.directives.postProcessAction', []).directive('postPro" | |||||
11610 | var possibleFields = { |
|
11611 | var possibleFields = { | |
11611 | '__AND__': 'All met (composite rule)', |
|
11612 | '__AND__': 'All met (composite rule)', | |
11612 | '__OR__': 'One met (composite rule)', |
|
11613 | '__OR__': 'One met (composite rule)', | |
|
11614 | '__NOT__': 'Not met (composite rule)', | |||
11613 | 'http_status': 'HTTP Status', |
|
11615 | 'http_status': 'HTTP Status', | |
11614 | 'duration': 'Request duration', |
|
11616 | 'duration': 'Request duration', | |
11615 | 'group:priority': 'Group -> Priority', |
|
11617 | 'group:priority': 'Group -> Priority', | |
@@ -11927,8 +11929,9 b" angular.module('appenlight.directives.rule', []).directive('rule', function () {" | |||||
11927 | }; |
|
11929 | }; | |
11928 |
|
11930 | |||
11929 | vm.fieldChange = function () { |
|
11931 | vm.fieldChange = function () { | |
11930 |
var |
|
11932 | var compound_types = ['__AND__', '__OR__', '__NOT__']; | |
11931 |
var |
|
11933 | var new_is_compound = compound_types.indexOf(vm.rule.field) !== -1; | |
|
11934 | var old_was_compound = compound_types.indexOf(vm.oldField) !== -1; | |||
11932 |
|
11935 | |||
11933 | if (!new_is_compound) { |
|
11936 | if (!new_is_compound) { | |
11934 | vm.rule.op = vm.ruleDefinitions.fieldOps[vm.rule.field][0]; |
|
11937 | vm.rule.op = vm.ruleDefinitions.fieldOps[vm.rule.field][0]; |
@@ -1031,6 +1031,38 b' class TestRulesParsing():' | |||||
1031 |
|
1031 | |||
1032 | @pytest.mark.usefixtures('report_type_matrix') |
|
1032 | @pytest.mark.usefixtures('report_type_matrix') | |
1033 | class TestNestedRuleParsing(): |
|
1033 | class TestNestedRuleParsing(): | |
|
1034 | ||||
|
1035 | @pytest.mark.parametrize("data, result", [ | |||
|
1036 | ({"http_status": 501, "group": {"priority": 7, "occurences": 11}}, | |||
|
1037 | False), | |||
|
1038 | ({"http_status": 101, "group": {"priority": 7, "occurences": 11}}, | |||
|
1039 | False), | |||
|
1040 | ({"http_status": 500, "group": {"priority": 1, "occurences": 11}}, | |||
|
1041 | False), | |||
|
1042 | ({"http_status": 101, "group": {"priority": 3, "occurences": 5}}, | |||
|
1043 | True), | |||
|
1044 | ]) | |||
|
1045 | def test_NOT_rule(self, data, result, report_type_matrix): | |||
|
1046 | from appenlight.lib.rule import Rule | |||
|
1047 | rule_config = { | |||
|
1048 | "field": "__NOT__", | |||
|
1049 | "rules": [ | |||
|
1050 | { | |||
|
1051 | "op": "ge", | |||
|
1052 | "field": "group:occurences", | |||
|
1053 | "value": "10" | |||
|
1054 | }, | |||
|
1055 | { | |||
|
1056 | "op": "ge", | |||
|
1057 | "field": "group:priority", | |||
|
1058 | "value": "4" | |||
|
1059 | } | |||
|
1060 | ] | |||
|
1061 | } | |||
|
1062 | ||||
|
1063 | rule = Rule(rule_config, report_type_matrix) | |||
|
1064 | assert rule.match(data) is result | |||
|
1065 | ||||
1034 | @pytest.mark.parametrize("data, result", [ |
|
1066 | @pytest.mark.parametrize("data, result", [ | |
1035 | ({"http_status": 501, "group": {"priority": 7, "occurences": 11}}, |
|
1067 | ({"http_status": 501, "group": {"priority": 7, "occurences": 11}}, | |
1036 | True), |
|
1068 | True), |
@@ -716,7 +716,7 b' def build_rule_schema(ruleset, check_matrix):' | |||||
716 | schema = colander.SchemaNode(colander.Mapping()) |
|
716 | schema = colander.SchemaNode(colander.Mapping()) | |
717 | schema.add(colander.SchemaNode(colander.String(), name='field')) |
|
717 | schema.add(colander.SchemaNode(colander.String(), name='field')) | |
718 |
|
718 | |||
719 | if ruleset['field'] in ['__AND__', '__OR__']: |
|
719 | if ruleset['field'] in ['__AND__', '__OR__', '__NOT__']: | |
720 | subrules = colander.SchemaNode(colander.Tuple(), name='rules') |
|
720 | subrules = colander.SchemaNode(colander.Tuple(), name='rules') | |
721 | for rule in ruleset['rules']: |
|
721 | for rule in ruleset['rules']: | |
722 | subrules.add(build_rule_schema(rule, check_matrix)) |
|
722 | subrules.add(build_rule_schema(rule, check_matrix)) |
@@ -70,6 +70,7 b' function AlertChannelsController(userSelfPropertyResource, applicationsNoIdResou' | |||||
70 | var possibleFields = { |
|
70 | var possibleFields = { | |
71 | '__AND__': 'All met (composite rule)', |
|
71 | '__AND__': 'All met (composite rule)', | |
72 | '__OR__': 'One met (composite rule)', |
|
72 | '__OR__': 'One met (composite rule)', | |
|
73 | '__NOT__': 'Not met (composite rule)', | |||
73 | 'http_status': 'HTTP Status', |
|
74 | 'http_status': 'HTTP Status', | |
74 | 'duration': 'Request duration', |
|
75 | 'duration': 'Request duration', | |
75 | 'group:priority': 'Group -> Priority', |
|
76 | 'group:priority': 'Group -> Priority', |
@@ -61,6 +61,7 b" angular.module('appenlight.directives.postProcessAction', []).directive('postPro" | |||||
61 | var possibleFields = { |
|
61 | var possibleFields = { | |
62 | '__AND__': 'All met (composite rule)', |
|
62 | '__AND__': 'All met (composite rule)', | |
63 | '__OR__': 'One met (composite rule)', |
|
63 | '__OR__': 'One met (composite rule)', | |
|
64 | '__NOT__': 'Not met (composite rule)', | |||
64 | 'http_status': 'HTTP Status', |
|
65 | 'http_status': 'HTTP Status', | |
65 | 'duration': 'Request duration', |
|
66 | 'duration': 'Request duration', | |
66 | 'group:priority': 'Group -> Priority', |
|
67 | 'group:priority': 'Group -> Priority', |
@@ -56,8 +56,9 b" angular.module('appenlight.directives.rule', []).directive('rule', function () {" | |||||
56 | }; |
|
56 | }; | |
57 |
|
57 | |||
58 | vm.fieldChange = function () { |
|
58 | vm.fieldChange = function () { | |
59 |
var |
|
59 | var compound_types = ['__AND__', '__OR__', '__NOT__']; | |
60 |
var |
|
60 | var new_is_compound = compound_types.indexOf(vm.rule.field) !== -1; | |
|
61 | var old_was_compound = compound_types.indexOf(vm.oldField) !== -1; | |||
61 |
|
62 | |||
62 | if (!new_is_compound) { |
|
63 | if (!new_is_compound) { | |
63 | vm.rule.op = vm.ruleDefinitions.fieldOps[vm.rule.field][0]; |
|
64 | vm.rule.op = vm.ruleDefinitions.fieldOps[vm.rule.field][0]; |
@@ -7,7 +7,7 b'' | |||||
7 | ng-options="key as label for (key, label) in rule_ctrlr.ruleDefinitions.possibleFields"></select> |
|
7 | ng-options="key as label for (key, label) in rule_ctrlr.ruleDefinitions.possibleFields"></select> | |
8 | </div> |
|
8 | </div> | |
9 |
|
9 | |||
10 | <div ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'" class="form-group"> |
|
10 | <div ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'" class="form-group"> | |
11 |
|
11 | |||
12 | <select ng-model="rule_ctrlr.rule.op" class="form-control" |
|
12 | <select ng-model="rule_ctrlr.rule.op" class="form-control" | |
13 | ng-change="rule_ctrlr.setDirty()" |
|
13 | ng-change="rule_ctrlr.setDirty()" | |
@@ -18,7 +18,7 b'' | |||||
18 |
|
18 | |||
19 | </div> |
|
19 | </div> | |
20 |
|
20 | |||
21 | <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'"> |
|
21 | <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'"> | |
22 | <p ng-if="parent"><strong>Subrules</strong></p> |
|
22 | <p ng-if="parent"><strong>Subrules</strong></p> | |
23 | <div ng-repeat="subrule in rule_ctrlr.rule.rules" class="m-l-2"> |
|
23 | <div ng-repeat="subrule in rule_ctrlr.rule.rules" class="m-l-2"> | |
24 | <div class="panel panel-default"> |
|
24 | <div class="panel panel-default"> |
@@ -4,11 +4,11 b'' | |||||
4 | {{rule_ctrlr.readOnlyPossibleFields[rule_ctrlr.rule.field]}} |
|
4 | {{rule_ctrlr.readOnlyPossibleFields[rule_ctrlr.rule.field]}} | |
5 | </span> |
|
5 | </span> | |
6 |
|
6 | |||
7 | <span ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'"> |
|
7 | <span ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'"> | |
8 | is {{rule_ctrlr.ruleDefinitions.allOps[rule_ctrlr.rule.op]}} {{rule_ctrlr.rule.value}} |
|
8 | is {{rule_ctrlr.ruleDefinitions.allOps[rule_ctrlr.rule.op]}} {{rule_ctrlr.rule.value}} | |
9 | </span> |
|
9 | </span> | |
10 |
|
10 | |||
11 | <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'"> |
|
11 | <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'"> | |
12 | <p ng-if="parent"><strong>Subrules</strong></p> |
|
12 | <p ng-if="parent"><strong>Subrules</strong></p> | |
13 | <div ng-repeat="subrule in rule_ctrlr.rule.rules" class="m-l-2"> |
|
13 | <div ng-repeat="subrule in rule_ctrlr.rule.rules" class="m-l-2"> | |
14 |
|
14 |
General Comments 0
You need to be logged in to leave comments.
Login now