blob: c0f2c583d71586a74f1be56418b8d40ebeeb0840 [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import jinja2
2import six
3import abc
4import os
5
Alex Savatieiev5118de02019-02-20 15:50:42 -06006from cfg_checker.common import const
savex4448e132018-04-25 15:51:14 +02007
8pkg_dir = os.path.dirname(__file__)
9pkg_dir = os.path.join(pkg_dir, os.pardir)
10pkg_dir = os.path.normpath(pkg_dir)
11
12
13def shortname(node_fqdn):
14 # form shortname out of node fqdn
15 return node_fqdn.split(".", 1)[0]
16
17
18def is_equal(pkg_dict):
19 # compare versions of given package
20 return pkg_dict['installed'] == pkg_dict['candidate']
21
22
23def is_active(node_dict):
24 # check node status in node dict
25 return node_dict['status'] == const.NODE_UP
26
27
28def line_breaks(text):
29 # replace python linebreaks with html breaks
30 return text.replace("\n", "<br />")
31
32
33@six.add_metaclass(abc.ABCMeta)
34class _Base(object):
35 def __init__(self):
36 self.jinja2_env = self.init_jinja2_env()
37
38 @abc.abstractmethod
39 def __call__(self, payload):
40 pass
41
42 @staticmethod
43 def init_jinja2_env():
44 return jinja2.Environment(
45 loader=jinja2.FileSystemLoader(os.path.join(pkg_dir, 'templates')),
46 trim_blocks=True,
47 lstrip_blocks=True)
48
49
50class _TMPLBase(_Base):
51 @abc.abstractproperty
52 def tmpl(self):
53 pass
54
55 @staticmethod
56 def _count_totals(data):
57 data['counters']['total_nodes'] = len(data['nodes'])
58
Alex Savatieiev36b938d2019-01-21 11:01:18 +010059 def __call__(self, payload):
savex4448e132018-04-25 15:51:14 +020060 # init data structures
61 data = self.common_data()
Alex Savatieiev4f149d02019-02-28 17:15:29 -060062 # payload should have pre-sorted structure
63 # system, nodes, clusters, and the rest in other
savex4448e132018-04-25 15:51:14 +020064 data.update({
Alex Savatieiev36b938d2019-01-21 11:01:18 +010065 "nodes": payload['nodes'],
Alex Savatieiev4f149d02019-02-28 17:15:29 -060066 "all_diffs": payload['diffs'],
67 "tabs": {}
savex4448e132018-04-25 15:51:14 +020068 })
69
70 # add template specific data
71 self._extend_data(data)
72
73 # do counts global
74 self._count_totals(data)
75
76 # specific filters
77 self.jinja2_env.filters['shortname'] = shortname
78 self.jinja2_env.filters['is_equal'] = is_equal
79 self.jinja2_env.filters['is_active'] = is_active
80 self.jinja2_env.filters['linebreaks'] = line_breaks
81
82 # render!
83 tmpl = self.jinja2_env.get_template(self.tmpl)
84 return tmpl.render(data)
85
86 def common_data(self):
87 return {
88 'counters': {},
89 'salt_info': {}
90 }
91
92 def _extend_data(self, data):
93 pass
94
95
96# Package versions report
savexce010ba2018-04-27 09:49:23 +020097class HTMLPackageCandidates(_TMPLBase):
savex4448e132018-04-25 15:51:14 +020098 tmpl = "pkg_versions_tmpl.j2"
99
100 @staticmethod
101 def is_fail_uniq(p_dict, p_name, nodes, node_name):
102 # look up package fail for nodes with similar role
103 _tgroup = nodes[node_name]['node_group']
104 # filter all nodes with the same role
105 _nodes_list = filter(
106 lambda nd: nodes[nd]['node_group'] == _tgroup and nd != node_name,
107 nodes
108 )
109 # lookup same package
110 _fail_uniq = False
111 for _node_name in _nodes_list:
112 # check if there is a package present on node
113 _nd = nodes[_node_name]['packages']
114 if p_name not in _nd:
115 continue
116 # if both backages has same version and differ from candidate
117 if p_dict['candidate'] == _nd[p_name]['candidate'] \
118 and _nd[p_name]['candidate'] == _nd[p_name]['installed']:
119 # it is not uniq, mark and break
120 _fail_uniq = True
121 return _fail_uniq
122
123 def _extend_data(self, data):
124 _all_pkg = 0
125 for key, value in data['nodes'].iteritems():
126 # add count of packages for this node to total
127 _all_pkg += len(value.keys())
128
129 # count differences
130 data['counters'][key] = {}
131 data['counters'][key]['packages'] = len(value['packages'].keys())
132 data['counters'][key]['package_diff'] = 0
133 for pkg_name, pkg_value in value['packages'].iteritems():
134 if pkg_value['installed'] != pkg_value['candidate']:
135 pkg_value['is_equal'] = False
136 pkg_value['fail_uniq'] = self.is_fail_uniq(
137 pkg_value,
138 pkg_name,
139 data['nodes'],
140 key
141 )
142 data['counters'][key]['package_diff'] += 1
143 else:
144 pkg_value['is_equal'] = True
145 pkg_value['fail_uniq'] = False
146
147 data['counters']['total_packages'] = _all_pkg
148
149
Alex Savatieievd48994d2018-12-13 12:13:00 +0100150# Package versions report
151class HTMLModelCompare(_TMPLBase):
152 tmpl = "model_tree_cmp_tmpl.j2"
153
154 def _extend_data(self, data):
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100155 # move names into separate place
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600156 data["names"] = data["all_diffs"].pop("diff_names")
157 data["tabs"] = data.pop("all_diffs")
158
Alex Savatieievd48994d2018-12-13 12:13:00 +0100159 # counters - mdl_diff
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600160 for _tab in data["tabs"].keys():
161 data['counters'][_tab] = len(data["tabs"][_tab]["diffs"].keys())
Alex Savatieievd48994d2018-12-13 12:13:00 +0100162
163
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600164class HTMLNetworkReport(_TMPLBase):
165 tmpl = "network_check_tmpl.j2"
166
167 def _extend_data(self, data):
168
169 return
170
171
savex4448e132018-04-25 15:51:14 +0200172class ReportToFile(object):
173 def __init__(self, report, target):
174 self.report = report
175 self.target = target
176
177 def __call__(self, payload):
178 payload = self.report(payload)
179
180 if isinstance(self.target, six.string_types):
181 self._wrapped_dump(payload)
182 else:
183 self._dump(payload, self.target)
184
185 def _wrapped_dump(self, payload):
186 with open(self.target, 'wt') as target:
187 self._dump(payload, target)
188
189 @staticmethod
190 def _dump(payload, target):
191 target.write(payload)