blob: 8a3456d1ede7002541ed3acc9f553006ea5f950c [file] [log] [blame]
savex4448e132018-04-25 15:51:14 +02001import json
2import os
Alex Savatieievc9055712019-03-01 14:43:56 -06003#import sys
savex4448e132018-04-25 15:51:14 +02004
5from copy import deepcopy
6
Alex41485522019-04-12 17:26:18 -05007from cfg_checker.common.exception import ConfigException
Alex Savatieiev5118de02019-02-20 15:50:42 -06008from cfg_checker.common import utils, const
9from cfg_checker.common import config, logger, logger_cli, pkg_dir
10from cfg_checker.common import salt_utils
Alex41485522019-04-12 17:26:18 -050011from cfg_checker.helpers.console_utils import Progress
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060012from cfg_checker.nodes import SaltNodes, node_tmpl
Alex41485522019-04-12 17:26:18 -050013from cfg_checker.reports import reporter
14
15from versions import PkgVersions, DebianVersion, VersionCmpResult
savex4448e132018-04-25 15:51:14 +020016
17
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060018class CloudPackageChecker(SaltNodes):
Alex41485522019-04-12 17:26:18 -050019 @staticmethod
20 def presort_packages(all_packages, full=None):
21 logger_cli.info("-> Presorting packages")
22 # labels
23 _data = {}
24 _data = {
25 "cs": {
26 "ok": const.VERSION_OK,
27 "up": const.VERSION_UP,
28 "down": const.VERSION_DOWN,
29 "err": const.VERSION_ERR
30 },
31 "ca": {
32 "na": const.ACT_NA,
33 "up": const.ACT_UPGRADE,
34 "need_up": const.ACT_NEED_UP,
35 "need_down": const.ACT_NEED_DOWN,
36 "repo": const.ACT_REPO
37 }
38 }
39 _data['status_err'] = const.VERSION_ERR
40 _data['status_down'] = const.VERSION_DOWN
41
42 # Presort packages
43 _data['critical'] = {}
44 _data['system'] = {}
45 _data['other'] = {}
46 _data['unlisted'] = {}
47
48 _l = len(all_packages)
49 _progress = Progress(_l)
50 _progress_index = 0
51 # counters
52 _ec = _es = _eo = _eu = 0
53 _dc = _ds = _do = _du = 0
54 while _progress_index < _l:
55 # progress bar
56 _progress_index += 1
57 _progress.write_progress(_progress_index)
58 # sort packages
59 _pn, _val = all_packages.popitem()
60 _c = _val['desc']['component']
61 if full:
62 # Check if this packet has errors
63 # if all is ok -> just skip it
64 _max_status = max(_val['results'].keys())
65 if _max_status <= const.VERSION_OK:
66 _max_action = max(_val['results'][_max_status].keys())
67 if _max_action == const.ACT_NA:
68 # this package do not ha any comments
69 # ...just skip it from report
70 continue
71
72 if len(_c) > 0 and _c == 'unlisted':
73 # not listed package in version lib
74 _data['unlisted'].update({
75 _pn: _val
76 })
77 _eu += _val['results'].keys().count(const.VERSION_ERR)
78 _du += _val['results'].keys().count(const.VERSION_DOWN)
79 # mirantis/critical
80 elif len(_c) > 0 and _c != 'System':
81 # not blank and not system
82 _data['critical'].update({
83 _pn: _val
84 })
85 _ec += _val['results'].keys().count(const.VERSION_ERR)
86 _dc += _val['results'].keys().count(const.VERSION_DOWN)
87 # system
88 elif _c == 'System':
89 _data['system'].update({
90 _pn: _val
91 })
92 _es += _val['results'].keys().count(const.VERSION_ERR)
93 _ds += _val['results'].keys().count(const.VERSION_DOWN)
94 # rest
95 else:
96 _data['other'].update({
97 _pn: _val
98 })
99 _eo += _val['results'].keys().count(const.VERSION_ERR)
100 _do += _val['results'].keys().count(const.VERSION_DOWN)
101
102
103 _progress.newline()
104
105 _data['errors'] = {
106 'mirantis': _ec,
107 'system': _es,
108 'other': _eo,
109 'unlisted': _eu
110 }
111 _data['downgrades'] = {
112 'mirantis': _dc,
113 'system': _ds,
114 'other': _do,
115 'unlisted': _du
116 }
117
118 return _data
119
savex4448e132018-04-25 15:51:14 +0200120 def collect_installed_packages(self):
121 """
122 Collect installed packages on each node
123 sets 'installed' dict property in the class
124
125 :return: none
126 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600127 logger_cli.info("# Collecting installed packages")
Alex Savatieiev01f0d7f2019-03-07 17:53:29 -0600128 _result = self.execute_script_on_active_nodes("pkg_versions.py")
savex4448e132018-04-25 15:51:14 +0200129
savex4448e132018-04-25 15:51:14 +0200130 for key in self.nodes.keys():
131 # due to much data to be passed from salt, it is happening in order
132 if key in _result:
133 _text = _result[key]
Alex Savatieievfa5910a2019-03-18 18:12:24 -0500134 try:
135 _dict = json.loads(_text[_text.find('{'):])
136 except ValueError as e:
137 logger_cli.info("... no JSON for '{}'".format(
138 key
139 ))
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500140 logger_cli.debug("ERROR:\n{}\n".format(_text[:_text.find('{')]))
Alex Savatieievfa5910a2019-03-18 18:12:24 -0500141 _dict = {}
142
savex4448e132018-04-25 15:51:14 +0200143 self.nodes[key]['packages'] = _dict
144 else:
145 self.nodes[key]['packages'] = {}
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600146 logger_cli.debug("... {} has {} packages installed".format(
savex4448e132018-04-25 15:51:14 +0200147 key,
148 len(self.nodes[key]['packages'].keys())
149 ))
Alex Savatieiev799bee32019-02-20 17:19:26 -0600150 logger_cli.info("-> Done")
savex4448e132018-04-25 15:51:14 +0200151
152 def collect_packages(self):
153 """
154 Check package versions in repos vs installed
155
156 :return: no return values, all date put to dict in place
157 """
Alex41485522019-04-12 17:26:18 -0500158 # Preload OpenStack release versions
159 _desc = PkgVersions()
160
161 logger_cli.info("# Cross-comparing: Installed vs Candidates vs Release")
162 _progress = Progress(len(self.nodes.keys()))
163 _progress_index = 0
164 _total_processed = 0
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500165 # Collect packages from all of the nodes in flat dict
Alex41485522019-04-12 17:26:18 -0500166 _all_packages = {}
savex4448e132018-04-25 15:51:14 +0200167 for node_name, node_value in self.nodes.iteritems():
Alex41485522019-04-12 17:26:18 -0500168 _uniq_len = len(_all_packages.keys())
169 _progress_index += 1
170 # progress will jump from node to node
171 # it is very costly operation to execute it for each pkg
172 _progress.write_progress(
173 _progress_index,
174 note="/ {} uniq out of {} packages found".format(
175 _uniq_len,
176 _total_processed
177 )
178 )
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500179 for _name, _value in node_value['packages'].iteritems():
Alex41485522019-04-12 17:26:18 -0500180 _total_processed += 1
181 # Parse versions
182 _ver_ins = DebianVersion(_value['installed'])
183 _ver_can = DebianVersion(_value['candidate'])
184
185 # All packages list with version and node list
186 if _name not in _all_packages:
187 # shortcuts for this cloud values
188 _os = self.openstack_release
189 _mcp = self.mcp_release
190 _pkg_desc = {}
191 if _desc[_name]:
192 # shortcut to version library
193 _vers = _desc[_name]['versions']
194 _pkg_desc = _desc[_name]
195 else:
196 # no description - no library :)
197 _vers = {}
198 _pkg_desc = _desc.dummy_desc
199
200 # get specific set for this OS release if present
201 if _os in _vers:
202 _v = _vers[_os]
203 elif 'any' in _vers:
204 _v = _vers['any']
205 else:
206 _v = {}
207 # Finally, get specific version
208 _release = DebianVersion(_v[_mcp] if _mcp in _v else '')
209 # Populate package info
210 _all_packages[_name] = {
211 "desc": _pkg_desc,
212 "results": {},
213 "r": _release,
Alex Savatieiev3db12a72019-03-22 16:32:31 -0500214 }
Alex41485522019-04-12 17:26:18 -0500215
216 _cmp = VersionCmpResult(
217 _ver_ins,
218 _ver_can,
219 _all_packages[_name]['r']
220 )
221
222 # shortcut to results
223 _res = _all_packages[_name]['results']
224 # update status
225 if _cmp.status not in _res:
226 _res[_cmp.status] = {}
227 # update action
228 if _cmp.action not in _res[_cmp.status]:
229 _res[_cmp.status][_cmp.action] = {}
230 # update node
231 if node_name not in _res[_cmp.status][_cmp.action]:
232 _res[_cmp.status][_cmp.action][node_name] = {}
233 # put result
234 _res[_cmp.status][_cmp.action][node_name] = {
235 'i': _ver_ins,
236 'c': _ver_can,
237 'res': _cmp,
238 'raw': _value['raw']
239 }
savex4448e132018-04-25 15:51:14 +0200240
Alex41485522019-04-12 17:26:18 -0500241 self._packages = _all_packages
242 _progress.newline()
243
savex4448e132018-04-25 15:51:14 +0200244
Alex41485522019-04-12 17:26:18 -0500245 def create_report(self, filename, rtype, full=None):
savex4448e132018-04-25 15:51:14 +0200246 """
247 Create static html showing packages diff per node
248
249 :return: buff with html
250 """
Alex Savatieiev42b89fa2019-03-07 18:45:26 -0600251 logger_cli.info("# Generating report to '{}'".format(filename))
Alex41485522019-04-12 17:26:18 -0500252 if rtype == 'html':
253 _type = reporter.HTMLPackageCandidates()
254 elif rtype == 'csv':
255 _type = reporter.CSVAllPackages()
256 else:
257 raise ConfigException("Report type not set")
Alex Savatieievd48994d2018-12-13 12:13:00 +0100258 _report = reporter.ReportToFile(
Alex41485522019-04-12 17:26:18 -0500259 _type,
savex4448e132018-04-25 15:51:14 +0200260 filename
261 )
Alex41485522019-04-12 17:26:18 -0500262 payload = {
Alex Savatieiev36b938d2019-01-21 11:01:18 +0100263 "nodes": self.nodes,
Alex41485522019-04-12 17:26:18 -0500264 "mcp_release": self.mcp_release,
265 "openstack_release": self.openstack_release
266 }
267 payload.update(self.presort_packages(self._packages, full))
268 _report(payload)
Alex Savatieiev799bee32019-02-20 17:19:26 -0600269 logger_cli.info("-> Done")