blob: 8b14a8a35799e5a880d314c58650b700b14a5b0d [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import jinja2
2import six
3import abc
4import os
5
6from check_versions.common import const
7
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
59 def __call__(self, nodes):
60 # init data structures
61 data = self.common_data()
62 data.update({
63 "nodes": nodes
64 })
65
66 # add template specific data
67 self._extend_data(data)
68
69 # do counts global
70 self._count_totals(data)
71
72 # specific filters
73 self.jinja2_env.filters['shortname'] = shortname
74 self.jinja2_env.filters['is_equal'] = is_equal
75 self.jinja2_env.filters['is_active'] = is_active
76 self.jinja2_env.filters['linebreaks'] = line_breaks
77
78 # render!
79 tmpl = self.jinja2_env.get_template(self.tmpl)
80 return tmpl.render(data)
81
82 def common_data(self):
83 return {
84 'counters': {},
85 'salt_info': {}
86 }
87
88 def _extend_data(self, data):
89 pass
90
91
92# Package versions report
savexce010ba2018-04-27 09:49:23 +020093class HTMLPackageCandidates(_TMPLBase):
savex4448e132018-04-25 15:51:14 +020094 tmpl = "pkg_versions_tmpl.j2"
95
96 @staticmethod
97 def is_fail_uniq(p_dict, p_name, nodes, node_name):
98 # look up package fail for nodes with similar role
99 _tgroup = nodes[node_name]['node_group']
100 # filter all nodes with the same role
101 _nodes_list = filter(
102 lambda nd: nodes[nd]['node_group'] == _tgroup and nd != node_name,
103 nodes
104 )
105 # lookup same package
106 _fail_uniq = False
107 for _node_name in _nodes_list:
108 # check if there is a package present on node
109 _nd = nodes[_node_name]['packages']
110 if p_name not in _nd:
111 continue
112 # if both backages has same version and differ from candidate
113 if p_dict['candidate'] == _nd[p_name]['candidate'] \
114 and _nd[p_name]['candidate'] == _nd[p_name]['installed']:
115 # it is not uniq, mark and break
116 _fail_uniq = True
117 return _fail_uniq
118
119 def _extend_data(self, data):
120 _all_pkg = 0
121 for key, value in data['nodes'].iteritems():
122 # add count of packages for this node to total
123 _all_pkg += len(value.keys())
124
125 # count differences
126 data['counters'][key] = {}
127 data['counters'][key]['packages'] = len(value['packages'].keys())
128 data['counters'][key]['package_diff'] = 0
129 for pkg_name, pkg_value in value['packages'].iteritems():
130 if pkg_value['installed'] != pkg_value['candidate']:
131 pkg_value['is_equal'] = False
132 pkg_value['fail_uniq'] = self.is_fail_uniq(
133 pkg_value,
134 pkg_name,
135 data['nodes'],
136 key
137 )
138 data['counters'][key]['package_diff'] += 1
139 else:
140 pkg_value['is_equal'] = True
141 pkg_value['fail_uniq'] = False
142
143 data['counters']['total_packages'] = _all_pkg
144
145
146class ReportToFile(object):
147 def __init__(self, report, target):
148 self.report = report
149 self.target = target
150
151 def __call__(self, payload):
152 payload = self.report(payload)
153
154 if isinstance(self.target, six.string_types):
155 self._wrapped_dump(payload)
156 else:
157 self._dump(payload, self.target)
158
159 def _wrapped_dump(self, payload):
160 with open(self.target, 'wt') as target:
161 self._dump(payload, target)
162
163 @staticmethod
164 def _dump(payload, target):
165 target.write(payload)