stackstorm 24. 源码分析之----stackstorm的rulesengine服务分析---3

本文详细分析了StackStorm的RulesEngine服务如何处理trigger实例,包括查找匹配规则、创建和执行规则。首先,`handle_trigger_instance`方法根据trigger_instance获取匹配的规则,并创建rule enforcers。接着,`enforce_rules`方法执行这些规则。通过`get_matching_rules_for_trigger`确定匹配的规则,使用`RulesMatcher`进行过滤。最后,`RuleEnforcer`的`enforce`方法执行规则,启动ActionExecution并发布状态。
摘要由CSDN通过智能技术生成

目标:
弄清楚st2rulesengine服务原理

1和2的分析参见:
https://blog.csdn.net/qingyuanluofeng/article/details/105374863

3的分析参见:
https://blog.csdn.net/qingyuanluofeng/article/details/105374978

4 分析RulesEngine规则引擎
分析RulesEngine类对象的handle_trigger_instance方法
class RulesEngine(object):
    def handle_trigger_instance(self, trigger_instance):
        # Find matching rules for trigger instance.
        matching_rules = self.get_matching_rules_for_trigger(trigger_instance)

        if matching_rules:
            # Create rule enforcers.
            enforcers = self.create_rule_enforcers(trigger_instance, matching_rules)

            # Enforce the rules.
            self.enforce_rules(enforcers)
        else:
            LOG.info('No matching rules found for trigger instance %s.', trigger_instance['id'])

分析:
handle_trigger_instance(self, trigger_instance):
1 根据给定的trigger_instance,形如:
(Pdb) p trigger_instance
<TriggerInstanceDB: TriggerInstanceDB(id=5e79d4d58dc0dd00ea0f362f, occurrence_time="2020-03-24 09:31:27.463733+00:00", payload={'executed_at': '2020-03-24 09:17:33.887548+00:00', 'schedule': None}, status="processing", trigger="core.8c13d9a3-12a3-4cff-88d3-66c804a47590")>
2 调用get_matching_rules_for_trigger方法,获得匹配的rule列表,具体是:
3 调用create_rule_enforcers(self, trigger_instance, matching_rules):
    遍历所有匹配的rule列表,对每个rule,实例化一个RuleEnforcer(trigger_instance, matching_rule)并加入
    结果列表中,最后返回结果列表
4 调用enforce_rules(self, enforcers):
遍历enforcers中每个enforcer,对每个enforcer,调用enforce方法,具体是

4.1) 分析get_matching_rules_for_trigger方法
class RulesEngine(object):

    def get_matching_rules_for_trigger(self, trigger_instance):
        trigger = trigger_instance.trigger

        trigger_db = get_trigger_db_by_ref(trigger_instance.trigger)

        if not trigger_db:
            LOG.error('No matching trigger found in db for trigger instance %s.', trigger_instance)
            return None

        rules = get_rules_given_trigger(trigger=trigger)

        LOG.info('Found %d rules defined for trigger %s', len(rules),
                 trigger_db.get_reference().ref)

        if len(rules) < 1:
            return rules

        matcher = RulesMatcher(trigger_instance=trigger_instance,
                               trigger=trigger_db, rules=rules)

        matching_rules = matcher.get_matching_rules()
        LOG.info('Matched %s rule(s) for trigger_instance %s (trigger=%s)', len(matching_rules),
                 trigger_instance['id'], trigger_db.ref)
        return matching_rules

分析:
get_matching_rules_for_trigger(self, trigger_instance):
1 根据给定trigger_instance中的trigger信息,形如:
(Pdb) p trigger
u'core.8c13d9a3-12a3-4cff-88d3-66c804a47590'
向trigger_d_b表查询该trigger记录
2 根据trigger记录查询到对应的rules。
原理: 由于特定的触发器会在trigger表生成一条trigger记录,包含ref字段。
而rule中会持有这个trigger的ref。所以通过trigger的ref可以查询到对应的rule。
3 用trigger_instance,trigger,rules实例化一个RulesMatcher。
对rules列表处理,对每个rule根据trigger_instance,trigger等信息实例化RuleFileter对象, 调用其filter方法,
判断其trigger_instance中的payload是否符合rule中criteria字典的校验要求。返回匹配criteria的rules列表。


4.2) 分析enforce_rules(self, enforcers)方法
class RulesEngine(object):

    def enforce_rules(self, enforcers):
        for enforcer in enforcers:
            try:
                enforcer.enforce()  # Should this happen in an eventlet pool?
            except:
                LOG.exception('Exception enforcing rule %s.', enforcer.rule)

分析:
遍历enforcers中每个enforcer,对每个enforcer,调用enforce方法,

4.2.1) 调用RuleEnforcer的
class RuleEnforcer(object):
    def __init__(self, trigger_instance, rule):
        self.trigger_instance = trigger_instance
        self.rule = rule

        try:
            self.data_transformer = get_transformer(trigger_instance.payload)
        except Exception as e:
            message = ('Failed to template-ize trigger payload: %s. If the payload contains '
                       'special characters such as "{ {" which dont\'t reference value in '
                       'a datastore, those characters need to be escaped' % (str(e)))
            raise ValueError(message)

    def enforce(self):
        rule_spec = {'ref': self.rule.ref, 'id': str(self.rule.id), 'uid': self.rule.uid}
        enforcement_db = RuleEnforcementDB(trigger_instance_id=str(self.trigger_instance.id),
                                           rule=rule_spec)
        extra = {
            'trigger_instance_db': self.trigger_instance,
            'rule_db': self.rule
        }
        execution_db = None
        try:
            execution_db = self._do_enforce()
            # pylint: disable=no-member
            enforcement_db.execution_id = str(execution_db.id)
            extra['execution_db'] = execution_db
        except Exception as e:
            # Record the failure reason in the RuleEnforcement.
            enforcement_db.failure_reason = e.message
            LOG.exception('Failed kicking off execution for rule %s.', self.rule, extra=extra)
        finally:
            self._update_enforcement(enforcement_db)

        # pylint: disable=no-member
        if not execution_db or execution_db.status not in EXEC_KICKED_OFF_STATES:
            LOG.audit('Rule enforcement failed. Execution of Action %s failed. '
                      'TriggerInstance: %s and Rule: %s',
                      self.rule.action.ref, self.trigger_instance, self.rule,
                      extra=extra)
        else:                 
            LOG.audit('Rule enforced. Execution %s, TriggerInstance %s and Rule %s.',
                      execution_db, self.trigger_instance, self.rule, extra=extra)

        return execution_db

分析:
enforce(self):
1 根据trigger_instance_id和 rule_spec具体是一个字典,实例化一个rule_enforcement_d_b
rule_spec 样例:{'ref': self.rule.ref, 'id': str(self.rule.id), 'uid': self.rule.uid}
2 调用_do_enforce方法,具体是:
3 指定enforcement_db的execution_id为execution对象的id,相当于做了关联,然后更新rule_enforcement,
4 返回execution_db,样例如下:
<ActionExecutionDB: ActionExecutionDB(action={'name': u'mistral-network-check', 'runner_type': u'mistral-v2', 'tags': [], 'description': u'Run network check script and output the result then send email.', 'enabled': True, 'entry_point': u'workflows/mistral-network-check.yaml', 'notify': {}, 'uid': u'action:email:mistral-network-check', 'parameters': {u'email_account': {u'default': u'esdozer', u'type': u'string'}, u'cmd': {u'required': True, u'type': u'string'}, u'email_to': {u'required': True, u'type': u'array'}, u'email_from': {u'type': u'string'}, u'timeout': {u'default': 1800, u'type': u'integer'}}, 'ref': u'email.mistral-network-check', 'id': '5e8067ae146db400130967e8', 'pack': u'email'}, children=[], context={'trigger_instance': {'id': '5e81d44bb99f2a002b34aeed', 'name': None}, 'trace_context': {'id_': '5e81d44bb99f2a002b34aeee', 'trace_tag': u'st2.IntervalTimer-ac7abf3a-9966-480a-8376-1b156bdbef56'}, 'rule': {'id': '5e8067b3146db4001309686c', 'name': u'network-check'}, 'user': 'admin@example.org'}, end_timestamp=None, id=5e81d44cb99f2a002b34aef0, liveaction={'callback': {}, 'runner_info': {}, 'parameters': {u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}, 'action': u'email.mistral-network-check', 'action_is_workflow': True, 'id': '5e81d44cb99f2a002b34aeef'}, log=[{'status': 'requested', 'timestamp': datetime.datetime(2020, 3, 30, 11, 13, 16, 797752, tzinfo=tzutc())}], parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}, parent=None, result={}, rule={'description': u'Check L3 vRouter & LB & DHCP', 'tags': [], 'ref': u'email.network-check', 'enabled': True, 'trigger': {'type': u'core.st2.IntervalTimer', 'ref': u'core.ac7abf3a-9966-480a-8376-1b156bdbef56', 'parameters': {u'unit': u'minutes', u'delta': 10}}, 'context': {}, 'criteria': {}, 'action': {'ref': u'email.mistral-network-check', 'parameters': {u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}}, 'uid': u'rule:email:network-check', 'pack': u'email', 'type': {'ref': u'standard', 'parameters': {}}, 'id': '5e8067b3146db4001309686c', 'name': u'network-check'}, runner={'runner_module': u'mistral_v2', 'uid': u'runner_type:mistral-v2', 'name': u'mistral-v2', 'enabled': True, 'query_module': u'mistral_v2', 'runner_parameters': {u'skip_notify': {u'default': [], u'type': u'array', u'description': u'List of tasks to skip notifications for.'}, u'task': {u'type': u'string', u'description': u'The name of the task to run for reverse workflow.'}, u'context': {u'default': {}, u'type': u'object', u'description': u'Additional workflow inputs.'}, u'workflow': {u'type': u'string', u'description': u'The name of the workflow to run if the entry_point is a workbook of many workflows. The name should be in the format "<pack_name>.<action_name>.<workflow_name>". If entry point is a workflow or a workbook with a single workflow, the runner will identify the workflow automatically.'}}, 'id': '5e806772146db40013096339', 'description': u'A runner for executing mistral v2 workflow.'}, start_timestamp="2020-03-30 11:13:16.718857+00:00", status="requested", trigger={'uid': u'trigger:core:ac7abf3a-9966-480a-8376-1b156bdbef56:d561c05e470c37028940525af03d7eb5', 'parameters': {u'unit': u'minutes', u'delta': 10}, 'type': u'core.st2.IntervalTimer', 'pack': u'core', 'ref': u'core.ac7abf3a-9966-480a-8376-1b156bdbef56', 'id': '5e8160e028d08b002d5ecd0c', 'name': u'ac7abf3a-9966-480a-8376-1b156bdbef56'}, trigger_instance={'status': u'processing', 'occurrence_time': '2020-03-30T11:13:15.815000Z', 'trigger': u'core.ac7abf3a-9966-480a-8376-1b156bdbef56', 'id': '5e81d44bb99f2a002b34aeed', 'payload': {u'executed_at': u'2020-03-30 11:13:15.770559+00:00', u'schedule': None}}, trigger_type={'uid': u'trigger_type:core:st2.IntervalTimer', 'tags': [], 'parameters_schema': {u'additionalProperties': False, u'type': u'object', u'properties': {u'timezone': {u'type': u'string'}, u'unit': {u'enum': [u'weeks', u'days', u'hours', u'minutes', u'seconds'], u'required': True}, u'delta': {u'required': True, u'type': u'integer'}}}, 'name': u'st2.IntervalTimer', 'payload_schema': {u'type': u'object', u'properties': {u'executed_at': {u'default': u'2014-07-30 05:04:24.578325', u'type': u'string', u'format': u'date-time'}, u'schedule': {u'default': {u'units': u'seconds', u'delta': 30}, u'type': u'object'}}}, 'pack': u'core', 'ref': u'core.st2.IntervalTimer', 'id': '5e80674e4a20a500018b1713', 'description': u'Triggers on specified intervals. e.g. every 30s, 1week etc.'}, web_url="https://dozer-st2rulesengine-5b6cc5466-ttchq/#/history/5e81d44cb99f2a002b34aef0/general")>

4.2.2) 分析_do_enforce方法
class RuleEnforcer(object):

    def _do_enforce(self):
        params = self.get_resolved_parameters()
        LOG.info('Invoking action %s for trigger_instance %s with params %s.',
                 self.rule.action.ref, self.trigger_instance.id,
                 json.dumps(params))

        # update trace before invoking the action.
        trace_context = self._update_trace()
        LOG.debug('Updated trace %s with rule %s.', trace_context, self.rule.id)

        context = {
            'trigger_instance': reference.get_ref_from_model(self.trigger_instance),
            'rule': reference.get_ref_from_model(self.rule),
            'user': get_system_username(),
            TRACE_CONTEXT: trace_context
        }

        return RuleEnforcer._invoke_action(self.rule.action, params, context)

分析:
_do_enforce(self):
1 获取参数字典,形如:
{u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'receiver@example.org'], u'email_from': u'sender@example.org'}
2 调用 update_trace方法,具体是:
s1  调用get_trace_db_by_trigger_instance(trigger_instance=None, trigger_instance_id=None):
        根据trigger_instance的id,调用_get_single_trace_by_component(**component_filter):
        根据形如如下的参数:
        {'trigger_instances__object_id': '5e79d4d58dc0dd00ea0f362f'}
        向trace_d_b表查询属于该trigger_instance id的trace记录,结果样例如下:
        <TraceDB: TraceDB(action_executions=[], id=5e79ec278dc0dd00ea0f3630, rules=[], start_timestamp="2020-03-24 11:09:19.309179+00:00", trace_tag="st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590", trigger_instances=[<TraceComponentDB: TraceComponentDB@(object_id:5e79d4d58dc0dd00ea0f362f, updated_at:2020-03-24 11:14:41.289695+00:00)>], uid="trace:aaa8de00c846febf51ac71bd86a776c9")>
s2   更新trace记录,设置该trace记录的rules为形如如下的结果
    [{'caused_by': {'type': 'trigger_instance', 'id': '5e79d4d58dc0dd00ea0f362f'}, 'ref': u'email.network-check', 'id': '5e660433c75b50001c06b822'}]
s3 返回TraceContext对象的字典
3 调用RuleEnforcer._invoke_action(self.rule.action, params, context)方法

4.2.3) 分析RuleEnforcer._invoke_action方法
class RuleEnforcer(object):

    @staticmethod
    def _invoke_action(action_exec_spec, params, context=None):
        """
        Schedule an action execution.

        :type action_exec_spec: :class:`ActionExecutionSpecDB`

        :param params: Parameters to execute the action with.
        :type params: ``dict``

        :rtype: :class:`LiveActionDB` on successful schedueling, None otherwise.
        """
        action_ref = action_exec_spec['ref']

        # prior to shipping off the params cast them to the right type.
        params = action_param_utils.cast_params(action_ref, params)
        liveaction = LiveActionDB(action=action_ref, context=context, parameters=params)
        liveaction, execution = action_service.request(liveaction)

        return execution

分析:
_invoke_action(action_exec_spec, params, context=None):
1 根据输入参数,样例如下:
(Pdb) p action_exec_spec
<ActionExecutionSpecDB: ActionExecutionSpecDB@71113936(ref="email.mistral-network-check", parameters="{u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'receiver@example.org'], u'email_from': u'sender@example.org'}")>
(Pdb) p params
{u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'receiver@example.org'], u'email_from': u'sender@example.org'}
(Pdb) p context
{'trigger_instance': {'id': '5e79d4d58dc0dd00ea0f362f', 'name': None}, 'trace_context': {'id_': '5e79ec278dc0dd00ea0f3630', 'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, 'user': 'admin@example.org', 'rule': {'id': '5e660433c75b50001c06b822', 'name': u'network-check'}}
2 解析获得参数字典,形如:
{u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'receiver@example.org'], u'email_from': u'sender@example.org'}
3 实例化一个LiveActionDB(action=action_ref, context=context, parameters=params)对象,形如:
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=False, callback={}, context={'trigger_instance': {'id': '5e79d4d58dc0dd00ea0f362f', 'name': None}, 'trace_context': {'id_': '5e79ec278dc0dd00ea0f3630', 'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, 'rule': {'id': '5e660433c75b50001c06b822', 'name': u'network-check'}, 'user': 'admin@example.org'}, end_timestamp=None, id=None, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'receiver@example.org'], u'email_from': u'sender@example.org'}, result={}, runner_info={}, start_timestamp="2020-03-24 13:24:21.009098+00:00", status=None)>
4 调用/opt/stackstorm/st2/lib/python2.7/site-packages/st2common/services/action.py(155)request(liveaction)方法


4.2.4) 分析/opt/stackstorm/st2/lib/python2.7/site-packages/st2common/services/action.py的request(liveaction)方法
def request(liveaction):
    liveaction, execution = create_request(liveaction)
    liveaction, execution = publish_request(liveaction, execution)

    return liveaction, execution

分析:
request(liveaction):
1 根据输入参数liveaction,样例如下:
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=False, callback={}, context={'trigger_instance': {'id': '5e79d4d58dc0dd00ea0f362f', 'name': None}, 'trace_context': {'id_': '5e79ec278dc0dd00ea0f3630', 'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, 'rule': {'id': '5e660433c75b50001c06b822', 'name': u'network-check'}, 'user': 'admin@example.org'}, end_timestamp=None, id=None, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'receiver@example.org'], u'email_from': u'sender@example.org'}, result={}, runner_info={}, start_timestamp="2020-03-24 13:24:21.009098+00:00", status=None)>
2 调用 create_request(liveaction): 创建一个action的执行,返回(liveaction, execution)
3 调用 publish_request(liveaction, execution),具体是
4 返回: liveaction, execution
样例如下:
(Pdb) p liveaction
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={'trigger_instance': {'id': '5e81d44bb99f2a002b34aeed', 'name': None}, 'trace_context': {'id_': '5e81d44bb99f2a002b34aeee', 'trace_tag': u'st2.IntervalTimer-ac7abf3a-9966-480a-8376-1b156bdbef56'}, 'rule': {'id': '5e8067b3146db4001309686c', 'name': u'network-check'}, 'user': 'admin@example.org'}, end_timestamp=None, id=5e81d44cb99f2a002b34aeef, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}, result={}, runner_info={}, start_timestamp="2020-03-30 11:13:16.718857+00:00", status="requested")>
(Pdb) p execution
<ActionExecutionDB: ActionExecutionDB(action={'name': u'mistral-network-check', 'runner_type': u'mistral-v2', 'tags': [], 'description': u'Run network check script and output the result then send email.', 'enabled': True, 'entry_point': u'workflows/mistral-network-check.yaml', 'notify': {}, 'uid': u'action:email:mistral-network-check', 'parameters': {u'email_account': {u'default': u'esdozer', u'type': u'string'}, u'cmd': {u'required': True, u'type': u'string'}, u'email_to': {u'required': True, u'type': u'array'}, u'email_from': {u'type': u'string'}, u'timeout': {u'default': 1800, u'type': u'integer'}}, 'ref': u'email.mistral-network-check', 'id': '5e8067ae146db400130967e8', 'pack': u'email'}, children=[], context={'trigger_instance': {'id': '5e81d44bb99f2a002b34aeed', 'name': None}, 'trace_context': {'id_': '5e81d44bb99f2a002b34aeee', 'trace_tag': u'st2.IntervalTimer-ac7abf3a-9966-480a-8376-1b156bdbef56'}, 'rule': {'id': '5e8067b3146db4001309686c', 'name': u'network-check'}, 'user': 'admin@example.org'}, end_timestamp=None, id=5e81d44cb99f2a002b34aef0, liveaction={'callback': {}, 'runner_info': {}, 'parameters': {u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}, 'action': u'email.mistral-network-check', 'action_is_workflow': True, 'id': '5e81d44cb99f2a002b34aeef'}, log=[{'status': 'requested', 'timestamp': datetime.datetime(2020, 3, 30, 11, 13, 16, 797752, tzinfo=tzutc())}], parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}, parent=None, result={}, rule={'description': u'Check L3 vRouter & LB & DHCP', 'tags': [], 'ref': u'email.network-check', 'enabled': True, 'trigger': {'type': u'core.st2.IntervalTimer', 'ref': u'core.ac7abf3a-9966-480a-8376-1b156bdbef56', 'parameters': {u'unit': u'minutes', u'delta': 10}}, 'context': {}, 'criteria': {}, 'action': {'ref': u'email.mistral-network-check', 'parameters': {u'cmd': u'cu

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值