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
|
|||
113 | continue |
||
114 | self._do_analyze(rule_link._dest_action_ref, rule_links=rule_links, |
||
0 ignored issues
–
show
It seems like
_dest_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_source_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_source_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_source_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_source_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_dest_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_dest_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_dest_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_source_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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...
It seems like
_dest_action_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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
It seems like
_rule_ref was declared protected and should not be accessed from this context.
Prefixing a member variable 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 |
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: