blob: e1d6b6fd9f3bf8ca0ad2aa012497ebcdf3bab7c3 [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__)
Alex Savatieiev6d010be2019-03-11 10:36:59 -05009pkg_dir = os.path.join(pkg_dir, os.pardir, os.pardir)
savex4448e132018-04-25 15:51:14 +020010pkg_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 Savatieiev3db12a72019-03-22 16:32:31 -050066 "rc_diffs": payload['rc_diffs'],
67 "pkg_diffs": payload['pkg_diffs'],
Alex Savatieiev4f149d02019-02-28 17:15:29 -060068 "tabs": {}
savex4448e132018-04-25 15:51:14 +020069 })
70
71 # add template specific data
72 self._extend_data(data)
73
74 # do counts global
75 self._count_totals(data)
76
77 # specific filters
78 self.jinja2_env.filters['shortname'] = shortname
79 self.jinja2_env.filters['is_equal'] = is_equal
80 self.jinja2_env.filters['is_active'] = is_active
81 self.jinja2_env.filters['linebreaks'] = line_breaks
82
83 # render!
84 tmpl = self.jinja2_env.get_template(self.tmpl)
85 return tmpl.render(data)
86
87 def common_data(self):
88 return {
89 'counters': {},
90 'salt_info': {}
91 }
92
93 def _extend_data(self, data):
94 pass
95
96
97# Package versions report
savexce010ba2018-04-27 09:49:23 +020098class HTMLPackageCandidates(_TMPLBase):
savex4448e132018-04-25 15:51:14 +020099 tmpl = "pkg_versions_tmpl.j2"
100
101 @staticmethod
102 def is_fail_uniq(p_dict, p_name, nodes, node_name):
103 # look up package fail for nodes with similar role
104 _tgroup = nodes[node_name]['node_group']
105 # filter all nodes with the same role
106 _nodes_list = filter(
107 lambda nd: nodes[nd]['node_group'] == _tgroup and nd != node_name,
108 nodes
109 )
110 # lookup same package
111 _fail_uniq = False
112 for _node_name in _nodes_list:
113 # check if there is a package present on node
114 _nd = nodes[_node_name]['packages']
115 if p_name not in _nd:
116 continue
117 # if both backages has same version and differ from candidate
118 if p_dict['candidate'] == _nd[p_name]['candidate'] \
119 and _nd[p_name]['candidate'] == _nd[p_name]['installed']:
120 # it is not uniq, mark and break
121 _fail_uniq = True
122 return _fail_uniq
123
124 def _extend_data(self, data):
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500125 # Count values on per-node basis
savex4448e132018-04-25 15:51:14 +0200126 for key, value in data['nodes'].iteritems():
savex4448e132018-04-25 15:51:14 +0200127 # count differences
128 data['counters'][key] = {}
129 data['counters'][key]['packages'] = len(value['packages'].keys())
130 data['counters'][key]['package_diff'] = 0
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500131 data['counters'][key]['package_eq'] = 0
132
133 # Lookup if this fail is uniq for this node
savex4448e132018-04-25 15:51:14 +0200134 for pkg_name, pkg_value in value['packages'].iteritems():
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500135 if pkg_value['is_equal']:
136 pkg_value['fail_uniq'] = False
137 data['counters'][key]['package_eq'] += 1
138 else:
savex4448e132018-04-25 15:51:14 +0200139 pkg_value['fail_uniq'] = self.is_fail_uniq(
140 pkg_value,
141 pkg_name,
142 data['nodes'],
143 key
144 )
145 data['counters'][key]['package_diff'] += 1
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500146
147 # Count values on all-diffs basis
148 for key, value in data['pkg_diffs'].iteritems():
149 data['counters'][key] = {}
150 data['counters'][key]['df_nodes'] = len(value['df_nodes'].keys())
151 data['counters'][key]['eq_nodes'] = len(value['eq_nodes'])
savex4448e132018-04-25 15:51:14 +0200152
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500153 # Save all packages counter
154 data['counters']['total_packages'] = data['pkg_diffs'].keys()
savex4448e132018-04-25 15:51:14 +0200155
156
Alex Savatieievd48994d2018-12-13 12:13:00 +0100157# Package versions report
158class HTMLModelCompare(_TMPLBase):
159 tmpl = "model_tree_cmp_tmpl.j2"
160
161 def _extend_data(self, data):
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100162 # move names into separate place
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500163 data["names"] = data["rc_diffs"].pop("diff_names")
164 data["tabs"] = data.pop("rc_diffs")
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600165
Alex Savatieievd48994d2018-12-13 12:13:00 +0100166 # counters - mdl_diff
Alex Savatieiev4f149d02019-02-28 17:15:29 -0600167 for _tab in data["tabs"].keys():
168 data['counters'][_tab] = len(data["tabs"][_tab]["diffs"].keys())
Alex Savatieievd48994d2018-12-13 12:13:00 +0100169
170
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600171class HTMLNetworkReport(_TMPLBase):
172 tmpl = "network_check_tmpl.j2"
173
174 def _extend_data(self, data):
175
176 return
177
178
savex4448e132018-04-25 15:51:14 +0200179class ReportToFile(object):
180 def __init__(self, report, target):
181 self.report = report
182 self.target = target
183
184 def __call__(self, payload):
185 payload = self.report(payload)
186
187 if isinstance(self.target, six.string_types):
188 self._wrapped_dump(payload)
189 else:
190 self._dump(payload, self.target)
191
192 def _wrapped_dump(self, payload):
193 with open(self.target, 'wt') as target:
194 self._dump(payload, target)
195
196 @staticmethod
197 def _dump(payload, target):
198 target.write(payload)