Exemplo n.º 1
0
 def test_not_one_constraint_something_matched_but_constraint_rejected_it(
         self):
     rule = Rule(
         [self.cause_a], self.effect, [
             {
                 'clues_groups': [[0, 1], [1, 1]],
                 'name': 'identical',
                 'params': {}
             }
         ], Rule.LINKAGE_NOT
     )  # yapf: disable
     effect_clues_dict = {
         'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
     }
     clues = {
         'cause_a': [
             Clue((13,), '13 carrots', 130, self.line_source),
         ],
         'dummy': [
             Clue((98,), '98 foo bar', 980, self.line_source),
             Clue((99,), '99 foo bar', 990, self.line_source)
         ]
     }  # yapf: disable
     results = rule.constraints_check(clues, effect_clues_dict)
     assert results
     assert len(results) == 1
     assert results[0].lines == []
     assert results[0].constraints_linkage == InvestigationResult.NOT
Exemplo n.º 2
0
 def test_not_one_constraint_something_matched_but_constraint_approves(
         self):
     rule = Rule(
         [self.cause_a], self.effect, [
             {
                 'clues_groups': [[0, 1], [1, 1]],
                 'name': 'identical',
                 'params': {}
             }
         ], Rule.LINKAGE_NOT
     )  # yapf: disable
     effect_clues_dict = {
         'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
     }
     clues = {
         'cause_a': [
             Clue((42,), '42 carrots', 420, self.line_source),
         ],
         'dummy': [
             Clue((98,), '98 foo bar', 980, self.line_source),
             Clue((99,), '99 foo bar', 990, self.line_source)
         ]
     }  # yapf: disable
     results = rule.constraints_check(clues, effect_clues_dict)
     assert not results
Exemplo n.º 3
0
 def test_one_matched_line_when_two_occurrences_requested(self):
     rule = Rule(
         [self.cause_a, self.cause_a], self.effect, [
             {
                 'clues_groups': [[0, 1], [1, 1], [2, 1]],
                 'name': 'identical',
                 'params': {}
             }
         ], Rule.LINKAGE_AND
     )  # yapf: disable
     effect_clues_dict = {
         'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
     }
     clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
         'cause_a': [
             Clue((42,), '42 carrots', 420, self.line_source),
         ],
         'dummy': [
             Clue((98,), '98 foo bar', 980, self.line_source),
             Clue((99,), '99 foo bar', 990, self.line_source)
         ]
     }  # yapf: disable
     results = rule.constraints_check(clues, effect_clues_dict)
     assert effect_clues_dict['effect'].regex_parameters[0] == \
         clues['cause_a'][0].regex_parameters[0]
     assert not results
Exemplo n.º 4
0
    def test_constraints_or_two_time_constraints(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Foo occurred', datetime(2000, 6, 14, second=15)),
                      'Foo occurred, 2000 06 14 00:00:15', 500, line_source)
        clues_lists = [
            ([
                 Clue(('Bar occurred', datetime(2000, 6, 14, second=10)), 'Bar occurred, 2000 06 14 00:00:10', 250, line_source),
             ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[1, 2], [0, 2]],
            'name': 'time_delta',
            'params': {
                'max_delta': 8.0
            }
        }, {
            'clues_groups': [[1, 2], [0, 2]],
            'name': 'time_delta',
            'params': {
                'max_delta': 3.0
            }
        }]
        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Bar occurred', datetime(2000, 6, 14, second=10)), 'Bar occurred, 2000 06 14 00:00:10', 250, line_source))
        ]  # yapf: disable
        assert len(causes[0].constraints) == 1
        assert causes[0].constraints[0] == constraints[0]
Exemplo n.º 5
0
    def test_constraints_and_basic(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Pear', 2), 'Pear, 2 times', 3000, line_source)
        clues_lists = [
            ([
                Clue(('Milk', 3), 'Milk, 3 times', 50, line_source),
                Clue(('Chocolate', 2), 'Chocolate, 2 times', 100, line_source),
                Clue(('Pear', 2), 'Pear, 2 times', 150, line_source)
            ], 1), ([
                Clue(('Pear', 2), 'Pear, 2 times', 1050, line_source),
                Clue(('Milk', 1), 'Milk, 1 times', 1100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 2], [1, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]
        causes = Verifier.constraints_and(clues_lists, effect, constraints,
                                          ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.AND
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Pear', 2), 'Pear, 2 times', 150, line_source)),
            FrontInput.from_clue(Clue(
                ('Pear', 2), 'Pear, 2 times', 1050, line_source))
        ]  # yapf: disable
Exemplo n.º 6
0
    def test_constraints_or_without_constraints(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Pineapple', 44), 'Pineapple, 44 times', 3000,
                      line_source)
        clues_lists = [
            ([
                Clue(('Banana', 2), 'Banana, 2 times', 1050, line_source),
                Clue(('Milk', 1), 'Milk, 2 times', 1100, line_source)
            ], 1),
            ([
                Clue(('Chocolate', 2), 'Chocolate, 2 times', 100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = []

        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 2

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Banana', 2), 'Banana, 2 times', 1050, line_source)),
            FrontInput.from_clue(Clue(
                ('Chocolate', 2), 'Chocolate, 2 times', 100, line_source))
        ]  # yapf: disable
        assert causes[1].lines == [
            FrontInput.from_clue(Clue(
                ('Milk', 1), 'Milk, 2 times', 1100, line_source)),
            FrontInput.from_clue(Clue(
                ('Chocolate', 2), 'Chocolate, 2 times', 100, line_source))
        ]  # yapf: disable
        assert all(not cause.constraints for cause in causes)
Exemplo n.º 7
0
def mocked_investigation_plan():
    super_parser = RegexSuperParser('^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d).*', [1], {1: 'date'})
    matcher = WildCardFilenameMatcher('localhost', 'node_1.log', 'default', super_parser)
    default_log_type = LogType('default', [matcher])
    cause = RegexParser(
        'cause', '2015-12-03 12:08:08 root cause',
        '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) root cause$', [1], 'default', {1: 'date'}
    )
    effect = RegexParser(
        'effect', '2015-12-03 12:08:09 visible effect',
        '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) visible effect$', [1], 'default', {1: 'date'}
    )
    concatenated = ConcatenatedRegexParser([cause])
    effect_time = datetime(2015, 12, 3, 12, 8, 9)
    search_range = {
        'default': {
            'date': {
                'left_bound': datetime(2015, 12, 3, 12, 8, 8),
                'right_bound': effect_time
            }
        }
    }
    default_investigation_step = InvestigationStep(concatenated, search_range)
    rule = Rule(
        [cause], effect, [
            {
                'clues_groups': [[1, 1], [0, 1]],
                'name': 'time',
                'params': {'max_delta': 1}
            }
        ], Rule.LINKAGE_AND
    )  # yapf: disable
    line_source = LineSource('localhost', 'node_1.log')
    effect_clues = {'effect': Clue((effect_time,), 'visible effect', 40, line_source)}
    return InvestigationPlan([rule], [(default_investigation_step, default_log_type)], effect_clues)
Exemplo n.º 8
0
    def setUpClass(cls):
        SettingsFactorySelector.WHYLOG_DIR = TestPaths.WHYLOG_DIR
        cls.config = SettingsFactorySelector.get_settings()['config']
        cls.whylog_dir = SettingsFactorySelector._attach_whylog_dir(os.getcwd())

        cause1_regex = '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) cause1 transaction number: (\d+) Host: (\w)$'
        cause1_line = '2016-04-12 23:39:43 cause1 transaction number: 10101 Host: db_host'
        convertions = {1: 'date'}
        cls.cause1 = RegexParser("cause1", cause1_line, cause1_regex, [1], 'database', convertions)

        cause2_regex = '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) cause2 moved resource id: (\d+) Host: (\w)$'
        cause2_line = '2016-04-12 23:40:43 cause2 moved resource id: 1234 Host: apache_host'
        convertions = {1: 'date'}
        cls.cause2 = RegexParser("cause2", cause2_line, cause2_regex, [1], 'apache', convertions)

        effect_regex = '^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d) effect internal server error Host: (\w)$'
        effect_line = '2016-04-12 23:54:43 effect internal server error Host: apache_host'
        convertions = {1: 'date'}
        cls.effect = RegexParser("effect", effect_line, effect_regex, [1], 'apache', convertions)

        line_source = LineSource('localhost', 'node_1.log')
        cls.effect_time = datetime(2016, 4, 12, 23, 54, 43)
        effect_line = '2016-04-12 23:54:43 effect internal server error Host: apache_host'
        cls.effect_clues = {
            'effect': Clue((cls.effect_time, 'apache_host'), effect_line, 40, line_source)
        }

        cls.earliest_date = datetime(1, 1, 1, 1, 1, 1)
        cls.ten_second_earlier = datetime(2016, 4, 12, 23, 54, 33)
        cls.one_hundred_second_earlier = datetime(2016, 4, 12, 23, 53, 3)
        cls.ten_second_later = datetime(2016, 4, 12, 23, 54, 53)
Exemplo n.º 9
0
    def test_constraints_check_same_cause_parser_as_effect(self):
        rule = Rule(
            [self.cause_a], self.cause_a, [
                {
                    'clues_groups': [[0, 1], [1, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'cause_a': Clue((42, ), '42 carrots', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [
                Clue((40,), '40 carrots', 400, self.line_source),
                Clue((42,), '42 carrots', 420, self.line_source),
                Clue((44,), '44 carrots', 440, self.line_source)
            ],
            'dummy': [
                Clue((98,), '98 foo bar', 980, self.line_source),
                Clue((99,), '99 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)

        assert len(results) == 1

        assert results[0].lines == [
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 420, self.line_source))
        ]  # yapf: disable
Exemplo n.º 10
0
    def test_single_constraint_not_basic(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Dinner', 44), 'Dinner, 44 times', 440, line_source)
        clues_lists = [
            ([
                Clue(('Car', 40), 'Car, 40 times', 400, line_source),
                Clue(('Car', 1), 'Car, 1 times', 100, line_source),
                Clue(('Car', 15), 'Car, 15 times', 150, line_source)
            ], 1),
            ([
                Clue(('Forest', 0), 'Forest, 0 times', 0, line_source),
                Clue(('Forest', 3), 'Forest, 3 times', 30, line_source),
                Clue(('Forest', 42), 'Forest, 42 times', 420, line_source)
            ], 1)
        ]  # yapf: disable
        constraint = {
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }
        causes = Verifier.single_constraint_not(clues_lists,
                                                effect, constraint,
                                                ConstraintManager())

        assert len(causes) == 1
        assert causes[0].constraints_linkage == InvestigationResult.NOT
        assert causes[0].constraints[0] == constraint
Exemplo n.º 11
0
 def _create_effect_clues(self, effect_params, front_input):
     effect_clues = {}
     for parser_name, params in six.iteritems(effect_params):
         parser = self._parsers[parser_name]
         clue = Clue(
             parser.convert_params(
                 params
             ), front_input.line_content, front_input.offset, front_input.line_source
         )  # yapf: disable
         effect_clues[parser_name] = clue
     return effect_clues
Exemplo n.º 12
0
    def test_constraints_when_one_unmatched(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Banana', 2), 'Banana, 2 times', 3000, line_source)
        clues_lists = [
            ([
            ], 1), ([
                Clue(('Banana', 2), 'Banana, 2 times', 1050, line_source),
                Clue(('Milk', 1), 'Milk, 1 times', 1100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[0, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]

        # testing 'or'
        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Banana', 2), 'Banana, 2 times', 1050, line_source))
        ]  # yapf: disable
        assert causes[0].constraints == [{
            'clues_groups': [[0, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]

        # testing 'and'
        causes = Verifier.constraints_and(clues_lists, effect, constraints,
                                          ConstraintManager())
        assert not causes
Exemplo n.º 13
0
 def test_constraints_or_verification_failed(self):
     line_source = LineSource('localhost', 'node_0.log')
     effect = Clue(('Pineapple', 44), 'Pineapple, 44 times', 3000,
                   line_source)
     clues_lists = [
         ([
             Clue(('Milk', 3), 'Milk, 3 times', 50, line_source),
             Clue(('Chocolate', 2), 'Chocolate, 2 times', 100, line_source),
             Clue(('Banana', 1), 'Banana, 1 times', 150, line_source)
         ], 1), ([
             Clue(('Banana', 2), 'Banana, 2 times', 1050, line_source),
             Clue(('Milk', 1), 'Milk, 2 times', 1100, line_source)
         ], 1)
     ]  # yapf: disable
     constraints = [{
         'clues_groups': [[0, 1], [1, 1], [2, 1]],
         'name': 'identical',
         'params': {}
     }, {
         'clues_groups': [[0, 2], [1, 2], [2, 2]],
         'name': 'identical',
         'params': {}
     }]
     causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                      ConstraintManager())
     assert not causes
Exemplo n.º 14
0
 def test_empty_clues_going_to_verify(self):
     rule = Rule(
         [self.cause_a, self.cause_a], self.effect, [
             {
                 'clues_groups': [[0, 1], [1, 1], [2, 1]],
                 'name': 'identical',
                 'params': {}
             }
         ], Rule.LINKAGE_AND
     )  # yapf: disable
     effect_clues_dict = {
         'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
     }
     clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
         'cause_a': [],
         'dummy': [
             Clue((98,), '98 foo bar', 980, self.line_source),
             Clue((99,), '99 foo bar', 990, self.line_source)
         ]
     }  # yapf: disable
     results = rule.constraints_check(clues, effect_clues_dict)
     assert not results
Exemplo n.º 15
0
    def test_constraints_and_verification_failed_when_or_succeeded(self):
        line_source = LineSource('localhost', 'node_0.log')
        effect = Clue(('Banana', 44), 'Banana, 44 times', 3000, line_source)
        clues_lists = [
            ([
                Clue(('Milk', 3), 'Milk, 3 times', 50, line_source),
                Clue(('Chocolate', 4), 'Chocolate, 4 times', 100, line_source),
                Clue(('Pear', 2), 'Pear, 2 times', 150, line_source)                # <- should be found (parser 1)
            ], 1), ([
                Clue(('Pineapple', 2), 'Pineapple, 2 times', 1050, line_source),    # <- should be found (parser 2)
                Clue(('Milk', 1), 'Milk, 1 times', 1100, line_source)
            ], 1)
        ]  # yapf: disable
        constraints = [{
            'clues_groups': [[0, 1], [1, 1], [2, 1]],
            'name': 'identical',
            'params': {}
        }, {
            'clues_groups': [[1, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]

        # testing 'and'
        causes = Verifier.constraints_and(clues_lists, effect, constraints,
                                          ConstraintManager())
        assert not causes

        # testing 'or'
        causes = Verifier.constraints_or(clues_lists, effect, constraints,
                                         ConstraintManager())
        assert len(causes) == 1

        assert all(cause.constraints_linkage == InvestigationResult.OR
                   for cause in causes)
        assert causes[0].lines == [
            FrontInput.from_clue(Clue(
                ('Pear', 2), 'Pear, 2 times', 150, line_source)),
            FrontInput.from_clue(Clue(
                ('Pineapple', 2), 'Pineapple, 2 times', 1050, line_source))
        ]  # yapf: disable
        assert causes[0].constraints == [{
            'clues_groups': [[1, 2], [2, 2]],
            'name': 'identical',
            'params': {}
        }]
Exemplo n.º 16
0
 def test_unmatched_clues_comparison(self):
     unmatched_clue = Clue(None, None, None, None)
     assert unmatched_clue == Verifier.UNMATCHED
Exemplo n.º 17
0
    def test_constraints_check_basic(self):
        rule = Rule(
            [self.cause_a, self.cause_b], self.effect, [
                {
                    'clues_groups': [[0, 1], [1, 1], [2, 1]],
                    'name': 'identical',
                    'params': {}
                }
            ], Rule.LINKAGE_AND
        )  # yapf: disable
        effect_clues_dict = {
            'effect': Clue((42, ), '42 dinners', 1420, self.line_source)
        }
        clues = {  # it's dictionary of the same type as clues dict collected in SearchManager
            'cause_a': [
                Clue((40,), '40 carrots', 400, self.line_source),
                Clue((42,), '42 carrots', 420, self.line_source),
                Clue((44,), '44 carrots', 440, self.line_source)
            ],
            'cause_b': [
                Clue((32,), '32 broccoli', 100, self.line_source),
                Clue((42,), '42 broccoli', 120, self.line_source),
                Clue((52,), '52 broccoli', 140, self.line_source)
            ],
            'dummy': [
                Clue((42,), '42 foo bar', 980, self.line_source),
                Clue((84,), '84 foo bar', 990, self.line_source)
            ]
        }  # yapf: disable
        results = rule.constraints_check(clues, effect_clues_dict)

        assert len(results) == 1

        assert results[0].lines == [
            FrontInput.from_clue(
                Clue((42,), '42 carrots', 420, self.line_source)),
            FrontInput.from_clue(
                Clue((42,), '42 broccoli', 120, self.line_source))
        ]  # yapf: disable
Exemplo n.º 18
0
class Verifier(object):
    UNMATCHED = Clue(None, None, None, None)

    @classmethod
    def _create_investigation_result(cls, clues_combination, constraints,
                                     linkage):
        """
        basing on clues combination and constraints,
        returns appropriate InvestigationResult object
        which collects information about lines
        (FrontInput objects) instead of Clues
        """
        return InvestigationResult(
            [FrontInput.from_clue(clue) for clue in clues_combination],
            constraints, linkage)

    @classmethod
    def _verify_constraint(cls, combination, effect, constraint,
                           constraint_manager):
        """
        checks if specified clues (which represents parsers: 1,2,.. for some rule) and
        effect (which represents parser 0 from this rule) satisfy one given constraint.
        returns True if so, or False otherwise
        """
        constraint_verifier = constraint_manager[constraint]
        groups = []
        for group_info in constraint['clues_groups']:
            parser_num, group_num = group_info
            if parser_num == 0:
                groups.append(effect.regex_parameters[group_num - 1])
            else:
                if combination[parser_num - 1] == Verifier.UNMATCHED:
                    return False
                groups.append(combination[parser_num -
                                          1].regex_parameters[group_num - 1])
        return constraint_verifier.verify(groups, constraint['params'])

    @classmethod
    def _clues_combinations(cls, clues_tuples, collected_subset=[]):
        """
        recursive generator that returns all permutations according to schema:
        from first pair (list, number) of clues_tuples,
        produces permutations with size 'number' from 'list's elements
        and concatenates it with _clues_combinations invoked on the rest of clues_tuples.
        example:
        >>> xs = [([1, 2, 3], 2), ([4, 5], 1)]
        >>> for l in Verifier._clues_combinations(xs):
        >>>     print l
        [1, 2, 4]
        [1, 2, 5]
        [1, 3, 4]
        [1, 3, 5]
        [2, 1, 4]
        [2, 1, 5]
        [2, 3, 4]
        [2, 3, 5]
        [3, 1, 4]
        [3, 1, 5]
        [3, 2, 4]
        [3, 2, 5]
        it always should be called with empty accumulator,
        that is collected_subset=[]
        """
        if len(clues_tuples) != 0:
            first_list, repetitions_number = clues_tuples[0]
            for clues in itertools.permutations(first_list,
                                                repetitions_number):
                for subset in cls._clues_combinations(
                        clues_tuples[1:], collected_subset + list(clues)):
                    yield subset
        else:
            yield collected_subset

    @classmethod
    def _construct_proper_clues_lists(cls, original_clues_lists):
        clues_lists = []
        for clues, occurrences in original_clues_lists:
            if clues:
                clues_lists.append((clues, occurrences))
            else:
                clues_lists.append(([Verifier.UNMATCHED], occurrences))
        return clues_lists

    @classmethod
    def _pack_results_for_constraint_or(cls, combination, constraints):
        return cls._create_investigation_result(
            (clue for clue in combination if not clue == Verifier.UNMATCHED),
            constraints, InvestigationResult.OR)

    @classmethod
    def constraints_and(cls, clues_lists, effect, constraints,
                        constraint_manager):
        """
        for each combination of clues (they are generated by _clues_combinations)
        checks if for all given constraints their requirements are satisfied
        and for each such combination produces InvestigationResult object.
        returns list of all produced InvestigationResults
        """
        clues_lists = cls._construct_proper_clues_lists(clues_lists)
        causes = []
        for combination in cls._clues_combinations(clues_lists):
            if all(
                    cls._verify_constraint(combination, effect, constraint,
                                           constraint_manager)
                    for constraint in constraints):
                causes.append(
                    cls._create_investigation_result(combination, constraints,
                                                     InvestigationResult.AND))
        return causes

    @classmethod
    def constraints_or(cls, clues_lists, effect, constraints,
                       constraint_manager):
        """
        for each combination of clues (they are generated by _clues_combinations)
        checks if for any of given constraints their requirements are satisfied
        and for each such combination produces InvestigationResult object.
        returns list of all produced InvestigationResults
        """
        if not constraints:
            # when there is lack of constraints, but there are existing clues combinations,
            # each of them should be returned
            return [
                cls._pack_results_for_constraint_or(combination, constraints)
                for combination in cls._clues_combinations(clues_lists)
            ]
        causes = []
        clues_lists = cls._construct_proper_clues_lists(clues_lists)
        for combination in cls._clues_combinations(clues_lists):
            verified_constraints = [
                constraint
                for constraint in constraints
                if cls._verify_constraint(combination, effect, constraint, constraint_manager)
            ]  # yapf: disable
            if verified_constraints:
                causes.append(
                    cls._pack_results_for_constraint_or(
                        combination, verified_constraints))
        return causes

    @classmethod
    def constraints_not(cls, clues_lists, effect, constraints,
                        constraint_manager):
        """
        provide investigation if there is zero or one constraint,
        because only in such cases NOT linkage has sense
        """
        if len(constraints) > 1:
            raise TooManyConstraintsToNegate()
        if constraints:
            if clues_lists:
                return cls.single_constraint_not(clues_lists, effect,
                                                 constraints[0],
                                                 constraint_manager)
        else:
            if clues_lists:
                # if all parsers found their matched logs, the NOT requirement isn't satisfied
                return []
        return [
            cls._create_investigation_result([], [], InvestigationResult.NOT)
        ]

    @classmethod
    def single_constraint_not(cls, clues_lists, effect, constraint,
                              constraint_manager):
        """
        for each combination of clues (they are generated by _clues_combinations)
        checks for given constraint if its requirements are not satisfied
        and if they are not, it produces InvestigationResult object.
        returns list of all produced InvestigationResults
        """
        clues_lists = cls._construct_proper_clues_lists(clues_lists)
        for combination in cls._clues_combinations(clues_lists):
            if cls._verify_constraint(combination, effect, constraint,
                                      constraint_manager):
                return []
        return [
            cls._create_investigation_result([], [constraint],
                                             InvestigationResult.NOT)
        ]