blob: 3225f70833f4ed6095c9936727885e2c2b6fc17f [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import json
savex4448e132018-04-25 15:51:14 +02002
Alex3ebc5632019-04-18 16:47:18 -05003from cfg_checker.common import const, logger_cli
Alex41485522019-04-12 17:26:18 -05004from cfg_checker.common.exception import ConfigException
Alex41485522019-04-12 17:26:18 -05005from cfg_checker.helpers.console_utils import Progress
Alex3ebc5632019-04-18 16:47:18 -05006from cfg_checker.nodes import SaltNodes
Alex41485522019-04-12 17:26:18 -05007from cfg_checker.reports import reporter
8
Alex3ebc5632019-04-18 16:47:18 -05009from versions import DebianVersion, PkgVersions, VersionCmpResult
savex4448e132018-04-25 15:51:14 +020010
11
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060012class CloudPackageChecker(SaltNodes):
Alex41485522019-04-12 17:26:18 -050013 @staticmethod
14 def presort_packages(all_packages, full=None):
15 logger_cli.info("-> Presorting packages")
16 # labels
17 _data = {}
18 _data = {
19 "cs": {
20 "ok": const.VERSION_OK,
21 "up": const.VERSION_UP,
22 "down": const.VERSION_DOWN,
23 "err": const.VERSION_ERR
24 },
25 "ca": {
26 "na": const.ACT_NA,
27 "up": const.ACT_UPGRADE,
28 "need_up": const.ACT_NEED_UP,
29 "need_down": const.ACT_NEED_DOWN,
30 "repo": const.ACT_REPO
31 }
32 }
33 _data['status_err'] = const.VERSION_ERR
34 _data['status_down'] = const.VERSION_DOWN
35
36 # Presort packages
37 _data['critical'] = {}
38 _data['system'] = {}
39 _data['other'] = {}
40 _data['unlisted'] = {}
41
42 _l = len(all_packages)
43 _progress = Progress(_l)
44 _progress_index = 0
45 # counters
46 _ec = _es = _eo = _eu = 0
47 _dc = _ds = _do = _du = 0
48 while _progress_index < _l:
49 # progress bar
50 _progress_index += 1
51 _progress.write_progress(_progress_index)
52 # sort packages
53 _pn, _val = all_packages.popitem()
54 _c = _val['desc']['component']
55 if full:
56 # Check if this packet has errors
57 # if all is ok -> just skip it
58 _max_status = max(_val['results'].keys())
59 if _max_status <= const.VERSION_OK:
60 _max_action = max(_val['results'][_max_status].keys())
61 if _max_action == const.ACT_NA:
62 # this package do not ha any comments
63 # ...just skip it from report
64 continue
65
66 if len(_c) > 0 and _c == 'unlisted':
67 # not listed package in version lib
68 _data['unlisted'].update({
69 _pn: _val
70 })
71 _eu += _val['results'].keys().count(const.VERSION_ERR)
72 _du += _val['results'].keys().count(const.VERSION_DOWN)
73 # mirantis/critical
74 elif len(_c) > 0 and _c != 'System':
75 # not blank and not system
76 _data['critical'].update({
77 _pn: _val
78 })
79 _ec += _val['results'].keys().count(const.VERSION_ERR)
80 _dc += _val['results'].keys().count(const.VERSION_DOWN)
81 # system
82 elif _c == 'System':
83 _data['system'].update({
84 _pn: _val
85 })
86 _es += _val['results'].keys().count(const.VERSION_ERR)
87 _ds += _val['results'].keys().count(const.VERSION_DOWN)
88 # rest
89 else:
90 _data['other'].update({
91 _pn: _val
92 })
93 _eo += _val['results'].keys().count(const.VERSION_ERR)
94 _do += _val['results'].keys().count(const.VERSION_DOWN)
95
Alex41485522019-04-12 17:26:18 -050096 _progress.newline()
97
98 _data['errors'] = {
99 'mirantis': _ec,
100 'system': _es,
101 'other': _eo,
102 'unlisted': _eu
103 }
104 _data['downgrades'] = {
105 'mirantis': _dc,
106 'system': _ds,
107 'other': _do,
108 'unlisted': _du
109 }
110
111 return _data
112
savex4448e132018-04-25 15:51:14 +0200113 def collect_installed_packages(self):
114 """
115 Collect installed packages on each node
116 sets 'installed' dict property in the class
117
118 :return: none
119 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600120 logger_cli.info("# Collecting installed packages")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600121 _result = self.execute_script_on_active_nodes("pkg_versions.py")
savex4448e132018-04-25 15:51:14 +0200122
savex4448e132018-04-25 15:51:14 +0200123 for key in self.nodes.keys():
124 # due to much data to be passed from salt, it is happening in order
125 if key in _result:
126 _text = _result[key]
Alex Savatieievfa5910a2019-03-18 18:12:24 -0500127 try:
128 _dict = json.loads(_text[_text.find('{'):])
Alex3ebc5632019-04-18 16:47:18 -0500129 except ValueError:
Alex Savatieievfa5910a2019-03-18 18:12:24 -0500130 logger_cli.info("... no JSON for '{}'".format(
131 key
132 ))
Alex3ebc5632019-04-18 16:47:18 -0500133 logger_cli.debug(
134 "ERROR:\n{}\n".format(_text[:_text.find('{')])
135 )
Alex Savatieievfa5910a2019-03-18 18:12:24 -0500136 _dict = {}
Alex3ebc5632019-04-18 16:47:18 -0500137
savex4448e132018-04-25 15:51:14 +0200138 self.nodes[key]['packages'] = _dict
139 else:
140 self.nodes[key]['packages'] = {}
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600141 logger_cli.debug("... {} has {} packages installed".format(
savex4448e132018-04-25 15:51:14 +0200142 key,
143 len(self.nodes[key]['packages'].keys())
144 ))
Alex Savatieiev799bee32019-02-20 17:19:26 -0600145 logger_cli.info("-> Done")
savex4448e132018-04-25 15:51:14 +0200146
147 def collect_packages(self):
148 """
149 Check package versions in repos vs installed
150
151 :return: no return values, all date put to dict in place
152 """
Alex41485522019-04-12 17:26:18 -0500153 # Preload OpenStack release versions
154 _desc = PkgVersions()
Alex3ebc5632019-04-18 16:47:18 -0500155
156 logger_cli.info(
157 "# Cross-comparing: Installed vs Candidates vs Release"
158 )
Alex41485522019-04-12 17:26:18 -0500159 _progress = Progress(len(self.nodes.keys()))
160 _progress_index = 0
161 _total_processed = 0
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500162 # Collect packages from all of the nodes in flat dict
Alex41485522019-04-12 17:26:18 -0500163 _all_packages = {}
savex4448e132018-04-25 15:51:14 +0200164 for node_name, node_value in self.nodes.iteritems():
Alex41485522019-04-12 17:26:18 -0500165 _uniq_len = len(_all_packages.keys())
166 _progress_index += 1
167 # progress will jump from node to node
168 # it is very costly operation to execute it for each pkg
169 _progress.write_progress(
170 _progress_index,
171 note="/ {} uniq out of {} packages found".format(
172 _uniq_len,
173 _total_processed
174 )
175 )
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500176 for _name, _value in node_value['packages'].iteritems():
Alex41485522019-04-12 17:26:18 -0500177 _total_processed += 1
178 # Parse versions
179 _ver_ins = DebianVersion(_value['installed'])
180 _ver_can = DebianVersion(_value['candidate'])
181
182 # All packages list with version and node list
183 if _name not in _all_packages:
184 # shortcuts for this cloud values
185 _os = self.openstack_release
186 _mcp = self.mcp_release
187 _pkg_desc = {}
188 if _desc[_name]:
189 # shortcut to version library
190 _vers = _desc[_name]['versions']
191 _pkg_desc = _desc[_name]
192 else:
193 # no description - no library :)
194 _vers = {}
195 _pkg_desc = _desc.dummy_desc
Alex3ebc5632019-04-18 16:47:18 -0500196
Alex41485522019-04-12 17:26:18 -0500197 # get specific set for this OS release if present
198 if _os in _vers:
Alex3ebc5632019-04-18 16:47:18 -0500199 _v = _vers[_os]
Alex41485522019-04-12 17:26:18 -0500200 elif 'any' in _vers:
201 _v = _vers['any']
202 else:
203 _v = {}
204 # Finally, get specific version
205 _release = DebianVersion(_v[_mcp] if _mcp in _v else '')
206 # Populate package info
207 _all_packages[_name] = {
208 "desc": _pkg_desc,
209 "results": {},
210 "r": _release,
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500211 }
Alex3ebc5632019-04-18 16:47:18 -0500212
Alex41485522019-04-12 17:26:18 -0500213 _cmp = VersionCmpResult(
214 _ver_ins,
215 _ver_can,
216 _all_packages[_name]['r']
217 )
Alex3ebc5632019-04-18 16:47:18 -0500218
Alex41485522019-04-12 17:26:18 -0500219 # shortcut to results
220 _res = _all_packages[_name]['results']
221 # update status
222 if _cmp.status not in _res:
223 _res[_cmp.status] = {}
224 # update action
225 if _cmp.action not in _res[_cmp.status]:
226 _res[_cmp.status][_cmp.action] = {}
227 # update node
228 if node_name not in _res[_cmp.status][_cmp.action]:
229 _res[_cmp.status][_cmp.action][node_name] = {}
230 # put result
231 _res[_cmp.status][_cmp.action][node_name] = {
232 'i': _ver_ins,
233 'c': _ver_can,
234 'res': _cmp,
235 'raw': _value['raw']
236 }
savex4448e132018-04-25 15:51:14 +0200237
Alex41485522019-04-12 17:26:18 -0500238 self._packages = _all_packages
239 _progress.newline()
savex4448e132018-04-25 15:51:14 +0200240
Alex41485522019-04-12 17:26:18 -0500241 def create_report(self, filename, rtype, full=None):
savex4448e132018-04-25 15:51:14 +0200242 """
243 Create static html showing packages diff per node
244
245 :return: buff with html
246 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600247 logger_cli.info("# Generating report to '{}'".format(filename))
Alex41485522019-04-12 17:26:18 -0500248 if rtype == 'html':
249 _type = reporter.HTMLPackageCandidates()
250 elif rtype == 'csv':
251 _type = reporter.CSVAllPackages()
252 else:
253 raise ConfigException("Report type not set")
Alex Savatieievd48994d2018-12-13 12:13:00 +0100254 _report = reporter.ReportToFile(
Alex41485522019-04-12 17:26:18 -0500255 _type,
savex4448e132018-04-25 15:51:14 +0200256 filename
257 )
Alex41485522019-04-12 17:26:18 -0500258 payload = {
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100259 "nodes": self.nodes,
Alex41485522019-04-12 17:26:18 -0500260 "mcp_release": self.mcp_release,
261 "openstack_release": self.openstack_release
262 }
263 payload.update(self.presort_packages(self._packages, full))
264 _report(payload)
Alex Savatieiev799bee32019-02-20 17:19:26 -0600265 logger_cli.info("-> Done")