Test Failed
Push — master ( e380d0...f5671d )
by W
02:58
created

tools/st2-analyze-links.py (12 issues)

1
#!/usr/bin/env python
2
3
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
4
# contributor license agreements.  See the NOTICE file distributed with
5
# this work for additional information regarding copyright ownership.
6
# The ASF licenses this file to You under the Apache License, Version 2.0
7
# (the "License"); you may not use this file except in compliance with
8
# the License.  You may obtain a copy of the License at
9
#
10
#     http://www.apache.org/licenses/LICENSE-2.0
11
#
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS,
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
# See the License for the specific language governing permissions and
16
# limitations under the License.
17
18
19
"""
20
Visualize the links created by rules.
21
22
1. requires graphviz
23
        pip install graphviz
24
        apt-get install graphviz
25
26
To run :
27
    ./st2-analyze-links.py --action_ref <action-ref>
28
29
The command must run on a StackStorm box.
30
"""
31
32
from __future__ import print_function
33
34
from __future__ import absolute_import
35
import os
36
37
from oslo_config import cfg
38
39
from st2common import config
40
from st2common.util.monkey_patch import monkey_patch
41
from st2common.persistence.rule import Rule
42
from st2common.service_setup import db_setup
43
44
try:
45
    from graphviz import Digraph
46
except ImportError:
47
    msg = ('Missing "graphviz" dependency. You can install it using pip: \n'
48
           'pip install graphviz')
49
    raise ImportError(msg)
50
51
52
def do_register_cli_opts(opts, ignore_errors=False):
53
    for opt in opts:
54
        try:
55
            cfg.CONF.register_cli_opt(opt)
56
        except:
57
            if not ignore_errors:
58
                raise
59
60
61
class RuleLink(object):
62
63
    def __init__(self, source_action_ref, rule_ref, dest_action_ref):
64
        self._source_action_ref = source_action_ref
65
        self._rule_ref = rule_ref
66
        self._dest_action_ref = dest_action_ref
67
68
    def __str__(self):
69
        return '(%s -> %s -> %s)' % (self._source_action_ref, self._rule_ref, self._dest_action_ref)
70
71
72
class LinksAnalyzer(object):
73
74
    def __init__(self):
75
        self._rule_link_by_action_ref = {}
76
        self._rules = {}
77
78
    def analyze(self, root_action_ref, link_tigger_ref):
79
        rules = Rule.query(trigger=link_tigger_ref, enabled=True)
80
        # pprint.pprint([rule.ref for rule in rules])
81
        for rule in rules:
82
            source_action_ref = self._get_source_action_ref(rule)
83
            if not source_action_ref:
84
                print('No source_action_ref for rule %s' % rule.ref)
85
                continue
86
            rule_links = self._rules.get(source_action_ref, None)
87
            if rule_links is None:
88
                rule_links = []
89
                self._rules[source_action_ref] = rule_links
90
            rule_links.append(RuleLink(source_action_ref=source_action_ref, rule_ref=rule.ref,
91
                                       dest_action_ref=rule.action.ref))
92
        analyzed = self._do_analyze(action_ref=root_action_ref)
93
        for (depth, rule_link) in analyzed:
94
            print('%s%s' % ('  ' * depth, rule_link))
95
        return analyzed
96
97
    def _get_source_action_ref(self, rule):
98
        criteria = rule.criteria
99
        source_action_ref = criteria.get('trigger.action_name', None)
100
        if not source_action_ref:
101
            source_action_ref = criteria.get('trigger.action_ref', None)
102
        return source_action_ref['pattern'] if source_action_ref else None
103
104
    def _do_analyze(self, action_ref, rule_links=None, processed=None, depth=0):
105
        if processed is None:
106
            processed = set()
107
        if rule_links is None:
108
            rule_links = []
109
        processed.add(action_ref)
110
        for rule_link in self._rules.get(action_ref, []):
111
            rule_links.append((depth, rule_link))
112
            if rule_link._dest_action_ref in processed:
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _dest_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
113
                continue
114
            self._do_analyze(rule_link._dest_action_ref, rule_links=rule_links,
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _dest_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
115
                             processed=processed, depth=depth + 1)
116
        return rule_links
117
118
119
class Grapher(object):
120
    def generate_graph(self, rule_links, out_file):
121
        graph_label = 'Rule based visualizer'
122
123
        graph_attr = {
124
            'rankdir': 'TD',
125
            'labelloc': 't',
126
            'fontsize': '15',
127
            'label': graph_label
128
        }
129
        node_attr = {}
130
        dot = Digraph(comment='Rule based links visualization',
131
                      node_attr=node_attr, graph_attr=graph_attr, format='png')
132
133
        nodes = set()
134
        for _, rule_link in rule_links:
135
            print(rule_link._source_action_ref)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _source_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
136
            if rule_link._source_action_ref not in nodes:
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _source_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
137
                nodes.add(rule_link._source_action_ref)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _source_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
138
                dot.node(rule_link._source_action_ref, rule_link._source_action_ref)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _source_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
139
            if rule_link._dest_action_ref not in nodes:
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _dest_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
140
                nodes.add(rule_link._dest_action_ref)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _dest_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
141
                dot.node(rule_link._dest_action_ref, rule_link._dest_action_ref)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _dest_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
142
            dot.edge(rule_link._source_action_ref, rule_link._dest_action_ref, constraint='true',
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _source_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
Coding Style Best Practice introduced by
It seems like _dest_action_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
143
                     label=rule_link._rule_ref)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _rule_ref was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
144
        output_path = os.path.join(os.getcwd(), out_file)
145
        dot.format = 'png'
146
        dot.render(output_path)
147
148
149
def main():
150
    monkey_patch()
151
152
    cli_opts = [
153
        cfg.StrOpt('action_ref', default=None,
154
                   help='Root action to begin analysis.'),
155
        cfg.StrOpt('link_trigger_ref', default='core.st2.generic.actiontrigger',
156
                   help='Root action to begin analysis.'),
157
        cfg.StrOpt('out_file', default='pipeline')
158
    ]
159
    do_register_cli_opts(cli_opts)
160
    config.parse_args()
161
    db_setup()
162
    rule_links = LinksAnalyzer().analyze(cfg.CONF.action_ref, cfg.CONF.link_trigger_ref)
163
    Grapher().generate_graph(rule_links, cfg.CONF.out_file)
164
165
166
if __name__ == '__main__':
167
    main()
168