savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 1 | import json |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 2 | |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 3 | from cfg_checker.common import const, logger_cli |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 4 | from cfg_checker.common.exception import ConfigException |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 5 | from cfg_checker.helpers.console_utils import Progress |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 6 | from cfg_checker.modules.packages.repos import RepoManager |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 7 | from cfg_checker.nodes import salt_master |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 8 | from cfg_checker.reports import reporter |
| 9 | |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 10 | from versions import DebianVersion, PkgVersions, VersionCmpResult |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 11 | |
| 12 | |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 13 | class CloudPackageChecker(object): |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 14 | rm = RepoManager() |
| 15 | |
| 16 | def __init__(self): |
| 17 | # Init salt master info |
| 18 | if not salt_master.nodes: |
| 19 | salt_master.nodes = salt_master.get_nodes() |
| 20 | |
| 21 | # check that this env tag is present in Manager |
| 22 | _tags = self.rm.get_available_tags(tag=salt_master.mcp_release) |
| 23 | if not _tags: |
| 24 | logger_cli.warning( |
| 25 | "\n# hWARNING: '{0}' is not listed in repo index. " |
| 26 | "Consider running:\n\t{1}\nto add info on this tag's " |
| 27 | "release package versions".format( |
| 28 | salt_master.mcp_release, |
| 29 | "mcp-checker packages versions --tag {0}" |
| 30 | ) |
| 31 | ) |
| 32 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 33 | @staticmethod |
| 34 | def presort_packages(all_packages, full=None): |
| 35 | logger_cli.info("-> Presorting packages") |
| 36 | # labels |
| 37 | _data = {} |
| 38 | _data = { |
| 39 | "cs": { |
| 40 | "ok": const.VERSION_OK, |
| 41 | "up": const.VERSION_UP, |
| 42 | "down": const.VERSION_DOWN, |
| 43 | "err": const.VERSION_ERR |
| 44 | }, |
| 45 | "ca": { |
| 46 | "na": const.ACT_NA, |
| 47 | "up": const.ACT_UPGRADE, |
| 48 | "need_up": const.ACT_NEED_UP, |
| 49 | "need_down": const.ACT_NEED_DOWN, |
| 50 | "repo": const.ACT_REPO |
| 51 | } |
| 52 | } |
| 53 | _data['status_err'] = const.VERSION_ERR |
| 54 | _data['status_down'] = const.VERSION_DOWN |
| 55 | |
| 56 | # Presort packages |
| 57 | _data['critical'] = {} |
| 58 | _data['system'] = {} |
| 59 | _data['other'] = {} |
| 60 | _data['unlisted'] = {} |
| 61 | |
| 62 | _l = len(all_packages) |
| 63 | _progress = Progress(_l) |
| 64 | _progress_index = 0 |
| 65 | # counters |
| 66 | _ec = _es = _eo = _eu = 0 |
| 67 | _dc = _ds = _do = _du = 0 |
| 68 | while _progress_index < _l: |
| 69 | # progress bar |
| 70 | _progress_index += 1 |
| 71 | _progress.write_progress(_progress_index) |
| 72 | # sort packages |
| 73 | _pn, _val = all_packages.popitem() |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 74 | _c = _val['desc']['section'] |
| 75 | |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 76 | if not full: |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 77 | # Check if this packet has errors |
| 78 | # if all is ok -> just skip it |
| 79 | _max_status = max(_val['results'].keys()) |
| 80 | if _max_status <= const.VERSION_OK: |
| 81 | _max_action = max(_val['results'][_max_status].keys()) |
| 82 | if _max_action == const.ACT_NA: |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 83 | # this package does not have any comments |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 84 | # ...just skip it from report |
| 85 | continue |
| 86 | |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 87 | if len(_c) > 0 and _val['is_mirantis'] is None: |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 88 | # not listed package in version lib |
| 89 | _data['unlisted'].update({ |
| 90 | _pn: _val |
| 91 | }) |
| 92 | _eu += _val['results'].keys().count(const.VERSION_ERR) |
| 93 | _du += _val['results'].keys().count(const.VERSION_DOWN) |
| 94 | # mirantis/critical |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 95 | # elif len(_c) > 0 and _c != 'System': |
| 96 | elif _val['is_mirantis']: |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 97 | # not blank and not system |
| 98 | _data['critical'].update({ |
| 99 | _pn: _val |
| 100 | }) |
| 101 | _ec += _val['results'].keys().count(const.VERSION_ERR) |
| 102 | _dc += _val['results'].keys().count(const.VERSION_DOWN) |
| 103 | # system |
| 104 | elif _c == 'System': |
| 105 | _data['system'].update({ |
| 106 | _pn: _val |
| 107 | }) |
| 108 | _es += _val['results'].keys().count(const.VERSION_ERR) |
| 109 | _ds += _val['results'].keys().count(const.VERSION_DOWN) |
| 110 | # rest |
| 111 | else: |
| 112 | _data['other'].update({ |
| 113 | _pn: _val |
| 114 | }) |
| 115 | _eo += _val['results'].keys().count(const.VERSION_ERR) |
| 116 | _do += _val['results'].keys().count(const.VERSION_DOWN) |
| 117 | |
Alex | d9fd85e | 2019-05-16 16:58:24 -0500 | [diff] [blame] | 118 | _progress.end() |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 119 | |
| 120 | _data['errors'] = { |
| 121 | 'mirantis': _ec, |
| 122 | 'system': _es, |
| 123 | 'other': _eo, |
| 124 | 'unlisted': _eu |
| 125 | } |
| 126 | _data['downgrades'] = { |
| 127 | 'mirantis': _dc, |
| 128 | 'system': _ds, |
| 129 | 'other': _do, |
| 130 | 'unlisted': _du |
| 131 | } |
| 132 | |
| 133 | return _data |
| 134 | |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 135 | def collect_installed_packages(self): |
| 136 | """ |
| 137 | Collect installed packages on each node |
| 138 | sets 'installed' dict property in the class |
| 139 | |
| 140 | :return: none |
| 141 | """ |
Alex Savatieiev | 42b89fa | 2019-03-07 18:45:26 -0600 | [diff] [blame] | 142 | logger_cli.info("# Collecting installed packages") |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 143 | salt_master.prepare_script_on_active_nodes("pkg_versions.py") |
| 144 | _result = salt_master.execute_script_on_active_nodes("pkg_versions.py") |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 145 | |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 146 | for key in salt_master.nodes.keys(): |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 147 | # due to much data to be passed from salt, it is happening in order |
| 148 | if key in _result: |
| 149 | _text = _result[key] |
Alex Savatieiev | fa5910a | 2019-03-18 18:12:24 -0500 | [diff] [blame] | 150 | try: |
| 151 | _dict = json.loads(_text[_text.find('{'):]) |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 152 | except ValueError: |
Alex Savatieiev | fa5910a | 2019-03-18 18:12:24 -0500 | [diff] [blame] | 153 | logger_cli.info("... no JSON for '{}'".format( |
| 154 | key |
| 155 | )) |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 156 | logger_cli.debug( |
| 157 | "ERROR:\n{}\n".format(_text[:_text.find('{')]) |
| 158 | ) |
Alex Savatieiev | fa5910a | 2019-03-18 18:12:24 -0500 | [diff] [blame] | 159 | _dict = {} |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 160 | |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 161 | salt_master.nodes[key]['packages'] = _dict |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 162 | else: |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 163 | salt_master.nodes[key]['packages'] = {} |
Alex Savatieiev | 42b89fa | 2019-03-07 18:45:26 -0600 | [diff] [blame] | 164 | logger_cli.debug("... {} has {} packages installed".format( |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 165 | key, |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 166 | len(salt_master.nodes[key]['packages'].keys()) |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 167 | )) |
Alex Savatieiev | 799bee3 | 2019-02-20 17:19:26 -0600 | [diff] [blame] | 168 | logger_cli.info("-> Done") |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 169 | |
| 170 | def collect_packages(self): |
| 171 | """ |
| 172 | Check package versions in repos vs installed |
| 173 | |
| 174 | :return: no return values, all date put to dict in place |
| 175 | """ |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 176 | # Preload OpenStack release versions |
| 177 | _desc = PkgVersions() |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 178 | |
| 179 | logger_cli.info( |
| 180 | "# Cross-comparing: Installed vs Candidates vs Release" |
| 181 | ) |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 182 | # shortcuts for this cloud values |
| 183 | _os = salt_master.openstack_release |
| 184 | _mcp = salt_master.mcp_release |
| 185 | # Progress class |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 186 | _progress = Progress(len(salt_master.nodes.keys())) |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 187 | _progress_index = 0 |
| 188 | _total_processed = 0 |
Alex Savatieiev | 3db12a7 | 2019-03-22 16:32:31 -0500 | [diff] [blame] | 189 | # Collect packages from all of the nodes in flat dict |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 190 | _all_packages = {} |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 191 | _all_tags = set([]) |
| 192 | # get env tag's year and major version |
| 193 | _tag_major = _mcp[:_mcp.find('.', _mcp.find('.')+1)] |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 194 | for node_name, node_value in salt_master.nodes.iteritems(): |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 195 | _uniq_len = len(_all_packages.keys()) |
| 196 | _progress_index += 1 |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 197 | # progress updates shown before next node only |
| 198 | # it is costly operation to do it for each of the 150k packages |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 199 | _progress.write_progress( |
| 200 | _progress_index, |
| 201 | note="/ {} uniq out of {} packages found".format( |
| 202 | _uniq_len, |
| 203 | _total_processed |
| 204 | ) |
| 205 | ) |
Alex Savatieiev | 3db12a7 | 2019-03-22 16:32:31 -0500 | [diff] [blame] | 206 | for _name, _value in node_value['packages'].iteritems(): |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 207 | _total_processed += 1 |
| 208 | # Parse versions |
| 209 | _ver_ins = DebianVersion(_value['installed']) |
| 210 | _ver_can = DebianVersion(_value['candidate']) |
| 211 | |
| 212 | # All packages list with version and node list |
| 213 | if _name not in _all_packages: |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 214 | # get repo versions list, |
| 215 | _linux = salt_master.nodes[node_name]['linux_codename'] |
| 216 | _arch = salt_master.nodes[node_name]['linux_arch'] |
| 217 | if _name == u'qemu-block-extra': |
| 218 | a = 1 |
| 219 | # omit tag as target versions might be of different tag |
| 220 | _r = self.rm.get_filtered_versions( |
| 221 | _name, |
| 222 | tag=_mcp, |
| 223 | include=[_os, _linux, _arch], |
| 224 | exclude=["nightly"] |
| 225 | ) |
| 226 | # save versions for matching major tags |
| 227 | _vs = {} |
| 228 | _sections = {} |
| 229 | _apps = {} |
| 230 | # get all versions for this year |
| 231 | for s, apps in _r.iteritems(): |
| 232 | for a, versions in apps.iteritems(): |
| 233 | for v, repos in versions.iteritems(): |
| 234 | for repo in repos: |
| 235 | t = repo['tag'] |
| 236 | _major = t[:t.find('.', t.find('.')+1)] |
| 237 | if _tag_major == _major: |
| 238 | if v not in _vs: |
| 239 | _vs[v] = [] |
| 240 | _vs[v].append(repo) |
| 241 | if v not in _sections: |
| 242 | _sections[v] = [] |
| 243 | _sections[v].append(s) |
| 244 | if v not in _apps: |
| 245 | _apps[v] = [] |
| 246 | _apps[v].append(a) |
| 247 | |
| 248 | # check if we have candidate version among found |
| 249 | _r_desc = [] |
| 250 | _vs_keys = _vs.keys() |
| 251 | if _vs_keys: |
| 252 | _newest = _newest = DebianVersion(_vs_keys.pop()) |
| 253 | else: |
| 254 | _newest = DebianVersion('') |
| 255 | # if _ver_ins.version in _vs_keys: |
| 256 | # # exact match, save it |
| 257 | # _release = _ver_ins |
| 258 | # else: |
| 259 | # detect newest version among saved |
| 260 | |
| 261 | for v in _vs_keys: |
| 262 | _this = DebianVersion(v) |
| 263 | if _this > _newest: |
| 264 | _newest = _this |
| 265 | # newest version for the YEAR.MAJOR will be the release |
| 266 | _release = _newest |
| 267 | # save repos list for this version |
| 268 | if _release.version != 'n/a': |
| 269 | _r_desc = _vs[_release.version] |
| 270 | # preload special description |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 271 | if _desc[_name]: |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 272 | _pkg_desc = _desc[_name] |
| 273 | else: |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 274 | _pkg_desc = _desc.dummy_desc |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 275 | # Check if we can provide better from the package |
| 276 | if _release.version != 'n/a': |
| 277 | if not _pkg_desc['section']: |
| 278 | _pkg_desc['section'] = \ |
| 279 | "/".join(_sections[_release.version]) |
| 280 | if not _pkg_desc['app']: |
| 281 | _pkg_desc['app'] = \ |
| 282 | "/".join(_apps[_release.version]) |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 283 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 284 | # get specific set for this OS release if present |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 285 | # if not, try search in all repos for this tag |
| 286 | # _r_desc = _r[_newest.version] |
| 287 | # if _r: |
| 288 | # _vs = _r.keys() |
| 289 | # if len(_vs) > 1: |
| 290 | # # search best match |
| 291 | # _release = None |
| 292 | # for _v in _vs: |
| 293 | # _deb = DebianVersion(_v) |
| 294 | # if _ver_can == _deb: |
| 295 | # _release = _deb |
| 296 | # _r_desc = _r[_v] |
| 297 | # break |
| 298 | # if not _release: |
| 299 | # _progress.clearline() |
| 300 | # logger_cli.error( |
| 301 | # "# ERROR: No release version found " |
| 302 | # "for '{}'".format(_name) |
| 303 | # ) |
| 304 | # _release = DebianVersion('') |
| 305 | # else: |
| 306 | # _release = DebianVersion(_vs[0]) |
| 307 | # _r_desc = _r[_vs[0]] |
| 308 | # else: |
| 309 | # # not found... 99% that it will not happen |
| 310 | # _release = DebianVersion('') |
| 311 | |
| 312 | # Old versions match routine |
| 313 | # ######## |
| 314 | # if _os in _vers: |
| 315 | # _v = _vers[_os] |
| 316 | # elif 'any' in _vers: |
| 317 | # _v = _vers['any'] |
| 318 | # else: |
| 319 | # _v = {} |
| 320 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 321 | # Finally, get specific version |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 322 | # _release = DebianVersion(_v[_mcp] if _mcp in _v else '') |
| 323 | for repo in _r_desc: |
| 324 | _all_tags.add(repo['tag']) |
| 325 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 326 | # Populate package info |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 327 | _m = _r_desc[0]["maintainer"] if _r_desc else 'n/a' |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 328 | _all_packages[_name] = { |
| 329 | "desc": _pkg_desc, |
Alex | d0391d4 | 2019-05-21 18:48:55 -0500 | [diff] [blame^] | 330 | "repos": _r_desc, |
| 331 | "maintainer": _m, |
| 332 | "is_mirantis": self.rm.is_mirantis( |
| 333 | _name, |
| 334 | tag=_tag_major |
| 335 | ), |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 336 | "results": {}, |
| 337 | "r": _release, |
Alex Savatieiev | 3db12a7 | 2019-03-22 16:32:31 -0500 | [diff] [blame] | 338 | } |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 339 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 340 | _cmp = VersionCmpResult( |
| 341 | _ver_ins, |
| 342 | _ver_can, |
| 343 | _all_packages[_name]['r'] |
| 344 | ) |
Alex | 3ebc563 | 2019-04-18 16:47:18 -0500 | [diff] [blame] | 345 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 346 | # shortcut to results |
| 347 | _res = _all_packages[_name]['results'] |
| 348 | # update status |
| 349 | if _cmp.status not in _res: |
| 350 | _res[_cmp.status] = {} |
| 351 | # update action |
| 352 | if _cmp.action not in _res[_cmp.status]: |
| 353 | _res[_cmp.status][_cmp.action] = {} |
| 354 | # update node |
| 355 | if node_name not in _res[_cmp.status][_cmp.action]: |
| 356 | _res[_cmp.status][_cmp.action][node_name] = {} |
| 357 | # put result |
| 358 | _res[_cmp.status][_cmp.action][node_name] = { |
| 359 | 'i': _ver_ins, |
| 360 | 'c': _ver_can, |
| 361 | 'res': _cmp, |
| 362 | 'raw': _value['raw'] |
| 363 | } |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 364 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 365 | self._packages = _all_packages |
Alex | d9fd85e | 2019-05-16 16:58:24 -0500 | [diff] [blame] | 366 | _progress.end() |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 367 | |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 368 | def create_report(self, filename, rtype, full=None): |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 369 | """ |
| 370 | Create static html showing packages diff per node |
| 371 | |
| 372 | :return: buff with html |
| 373 | """ |
Alex Savatieiev | 42b89fa | 2019-03-07 18:45:26 -0600 | [diff] [blame] | 374 | logger_cli.info("# Generating report to '{}'".format(filename)) |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 375 | if rtype == 'html': |
| 376 | _type = reporter.HTMLPackageCandidates() |
| 377 | elif rtype == 'csv': |
| 378 | _type = reporter.CSVAllPackages() |
| 379 | else: |
| 380 | raise ConfigException("Report type not set") |
Alex Savatieiev | d48994d | 2018-12-13 12:13:00 +0100 | [diff] [blame] | 381 | _report = reporter.ReportToFile( |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 382 | _type, |
savex | 4448e13 | 2018-04-25 15:51:14 +0200 | [diff] [blame] | 383 | filename |
| 384 | ) |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 385 | payload = { |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 386 | "nodes": salt_master.nodes, |
| 387 | "mcp_release": salt_master.mcp_release, |
| 388 | "openstack_release": salt_master.openstack_release |
Alex | 4148552 | 2019-04-12 17:26:18 -0500 | [diff] [blame] | 389 | } |
| 390 | payload.update(self.presort_packages(self._packages, full)) |
| 391 | _report(payload) |
Alex Savatieiev | 799bee3 | 2019-02-20 17:19:26 -0600 | [diff] [blame] | 392 | logger_cli.info("-> Done") |