Completed
Pull Request — master (#2588)
by Manas
05:57
created

st2reactor.rules.RuleTester.evaluate()   B

Complexity

Conditions 6

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 41
rs 7.5384
cc 6
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import os
17
import six
18
19
from jinja2.exceptions import UndefinedError
20
21
from st2common import log as logging
22
from st2common.content.loader import MetaLoader
23
from st2common.models.db.rule import RuleDB
24
from st2common.models.db.trigger import TriggerDB
25
from st2common.models.db.trigger import TriggerInstanceDB
26
from st2common.models.system.common import ResourceReference
27
from st2common.persistence.reactor import Rule, TriggerInstance, Trigger
28
29
from st2reactor.rules.enforcer import RuleEnforcer
30
from st2reactor.rules.matcher import RulesMatcher
31
32
__all__ = [
33
    'RuleTester'
34
]
35
36
LOG = logging.getLogger(__name__)
37
38
39
class RuleTester(object):
40
    def __init__(self, rule_file_path=None, rule_ref=None, trigger_instance_file_path=None,
41
                 trigger_instance_id=None):
42
        """
43
        :param rule_file_path: Path to the file containing rule definition.
44
        :type rule_file_path: ``str``
45
46
        :param trigger_instance_file_path: Path to the file containg trigger instance definition.
47
        :type trigger_instance_file_path: ``str``
48
        """
49
        self._rule_file_path = rule_file_path
50
        self._rule_ref = rule_ref
51
        self._trigger_instance_file_path = trigger_instance_file_path
52
        self._trigger_instance_id = trigger_instance_id
53
        self._meta_loader = MetaLoader()
54
55
    def evaluate(self):
56
        """
57
        Evaluate trigger instance against the rule.
58
59
        :return: ``True`` if the rule matches, ``False`` otherwise.
60
        :rtype: ``boolean``
61
        """
62
63
        rule_db = self._get_rule_db()
64
        trigger_instance_db, trigger_db = self._get_trigger_instance_db()
65
66
        # The trigger check needs to be performed here as that is not performed
67
        # by RulesMatcher.
68
        if rule_db.trigger != trigger_db.ref:
69
            LOG.info('rule.trigger "%s" and trigger.ref "%s" do not match.',
70
                     rule_db.trigger, trigger_db.ref)
71
            return False
72
73
        # Check if rule matches criteria.
74
        matcher = RulesMatcher(trigger_instance=trigger_instance_db, trigger=trigger_db,
75
                               rules=[rule_db], extra_info=True)
76
        matching_rules = matcher.get_matching_rules()
77
78
        # Rule does not match so early exit.
79
        if len(matching_rules) < 1:
80
            return False
81
82
        # Check if rule can be enforced
83
        try:
84
            enforcer = RuleEnforcer(trigger_instance=trigger_instance_db, rule=rule_db)
85
            params = enforcer.get_resolved_parameters()
86
            LOG.info('Action parameters resolved to:')
87
            for param in six.iteritems(params):
88
                LOG.info('\t%s: %s', param[0], param[1])
89
            return True
90
        except (UndefinedError, ValueError) as e:
91
            LOG.error('Failed to resolve parameters\n\tOriginal error : %s', str(e))
92
            return False
93
        except:
94
            LOG.exception('Failed to resolve parameters.')
95
            return False
96
97
    def _get_rule_db(self):
98
        if self._rule_file_path:
99
            return self._get_rule_db_from_file(
100
                file_path=os.path.realpath(self._rule_file_path))
101
        elif self._rule_ref:
102
            return Rule.get_by_ref(self._rule_ref)
103
        raise ValueError('One of _rule_file_path or _rule_ref should be specified.')
104
105
    def _get_trigger_instance_db(self):
106
        if self._trigger_instance_file_path:
107
            return self._get_trigger_instance_db_from_file(
108
                file_path=os.path.realpath(self._trigger_instance_file_path))
109
        elif self._trigger_instance_id:
110
            trigger_instance_db = TriggerInstance.get_by_id(self._trigger_instance_id)
111
            trigger_db = Trigger.get_by_ref(trigger_instance_db.trigger)
112
            return trigger_instance_db, trigger_db
113
        raise ValueError('One of _trigger_instance_file_path or'
114
                         '_trigger_instance_id should be specified.')
115
116
    def _get_rule_db_from_file(self, file_path):
117
        data = self._meta_loader.load(file_path=file_path)
118
        pack = data.get('pack', 'unknown')
119
        name = data.get('name', 'unknown')
120
        trigger = data['trigger']['type']
121
        criteria = data.get('criteria', None)
122
123
        rule_db = RuleDB(pack=pack, name=name, trigger=trigger, criteria=criteria, action={},
124
                         enabled=True)
125
        return rule_db
126
127
    def _get_trigger_instance_db_from_file(self, file_path):
128
        data = self._meta_loader.load(file_path=file_path)
129
        instance = TriggerInstanceDB(**data)
130
131
        trigger_ref = ResourceReference.from_string_reference(instance['trigger'])
132
        trigger_db = TriggerDB(pack=trigger_ref.pack, name=trigger_ref.name, type=trigger_ref.ref)
133
        return instance, trigger_db
134