Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import csv 

2import os 

3import re 

4 

5from cfg_checker.common import config, const, logger_cli 

6from cfg_checker.common.settings import pkg_dir 

7 

8 

9class PkgVersions(object): 

10 _labels = [] 

11 _list = {} 

12 

13 dummy_desc = { 

14 "section": "unlisted", 

15 "app": "-", 

16 "repo": "-", 

17 "versions": {} 

18 } 

19 

20 def __init__(self): 

21 # preload csv file 

22 logger_cli.info("# Preloading specific MCP release versions") 

23 with open(os.path.join( 

24 pkg_dir, 

25 'versions', 

26 config.pkg_versions_map) 

27 ) as f: 

28 _reader = csv.reader(f, delimiter=',') 

29 # load packages 

30 for row in _reader: 

31 # load release version labels 

32 if _reader.line_num == 1: 

33 self._labels = [v for v in row[5:]] 

34 continue 

35 # package_name,component,application_or_service,repo,openstack_release,2018.4.0,2018.11.0,2019.2.0,2019.2.1,2019.2.2 

36 # reassign for code readability 

37 _pkg = row[0] 

38 _section = row[1] 

39 _app = row[2] 

40 _repo = row[3] 

41 # if release cell empty - use keyword 'any' 

42 _os_release = row[4] if len(row[4]) > 0 else 'any' 

43 

44 # prepare versions dict 

45 _l = self._labels 

46 _versions = {_l[i]: row[5+i] for i in range(0, len(row[5:]))} 

47 

48 if _pkg in self._list: 

49 if _os_release in self._list[_pkg]["versions"]: 

50 # all pkg/os_releases should be uniq. 

51 # If found, latest one used 

52 logger_cli.info( 

53 "-> WARNING: Duplicate package info found " 

54 "'{}' (line {})".format( 

55 _pkg, 

56 _reader.line_num 

57 ) 

58 ) 

59 else: 

60 # update pkg data in list 

61 self._list.update({ 

62 _pkg: { 

63 "section": _section, 

64 "app": _app, 

65 "repo": _repo, 

66 "versions": {} 

67 } 

68 }) 

69 

70 # and finally, update the versions for this release 

71 self._list[_pkg]["versions"].update({ 

72 _os_release: _versions 

73 }) 

74 

75 def __getitem__(self, pkg_name): 

76 if pkg_name in self._list: 

77 return self._list[pkg_name] 

78 else: 

79 # return self._dummy_desc 

80 return None 

81 

82 

83class DebianVersion(object): 

84 epoch = None 

85 epoch_status = const.VERSION_NA 

86 upstream = None 

87 upstream_rev = None 

88 upstream_status = const.VERSION_NA 

89 debian = None 

90 debian_rev = None 

91 debian_status = const.VERSION_NA 

92 

93 status = "" 

94 version = "" 

95 

96 @staticmethod 

97 def split_revision(version_fragment): 

98 # The symbols are -, +, ~ 

99 _symbols = ['-', '+', '~'] 

100 # nums, coz it is faster then regex 

101 _chars = [46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57] 

102 _ord_map = [ord(ch) not in _chars for ch in version_fragment] 

103 # if there is nothing to extract, return at once 

104 if not any([_s in version_fragment for _s in _symbols]) \ 

105 and not any(_ord_map): 

106 # no revisions 

107 return version_fragment, "" 

108 else: 

109 _main = _rev = "" 

110 # get indices 

111 _indices = [] 

112 for _s in _symbols: 

113 if _s in version_fragment: 

114 _indices.append(version_fragment.index(_s)) 

115 for _s in version_fragment: 

116 if ord(_s) not in _chars: 

117 _indices.append(version_fragment.index(_s)) 

118 # sort indices 

119 _indices.sort() 

120 # extract starting from the lowest one 

121 _main = version_fragment[:_indices[0]] 

122 _rev = version_fragment[_indices[0]:] 

123 return _main, _rev 

124 

125 def __init__(self, version_string): 

126 # save 

127 if len(version_string) < 1: 

128 self.epoch = "0" 

129 self.upstream = "0" 

130 self.debian = '' 

131 self.version = 'n/a' 

132 return 

133 else: 

134 # do parse the main versions 

135 _v = version_string 

136 # colon presence, means epoch present 

137 _e = _v.split(':', 1)[0] if ':' in _v else "0" 

138 # if epoch was there, upstream should be cut 

139 _m = _v if ':' not in _v else _v.split(':', 1)[1] 

140 # dash presence, means debian present 

141 _d = _m.rsplit('-', 1)[1] if '-' in _m else '' 

142 # if debian was there, upstream version should be cut 

143 _m = _m if '-' not in _m else _m.rsplit('-', 1)[0] 

144 

145 self.epoch = _e 

146 self.upstream, self.upstream_rev = self.split_revision(_m) 

147 self.debian, self.debian_rev = self.split_revision(_d) 

148 self.version = version_string 

149 

150 # Following functions is a freestyle python mimic of apt's upstream, enjoy 

151 # https://github.com/chaos/apt/blob/master/apt/apt-pkg/deb/debversion.cc#L42 

152 # mimic produced in order not to pull any packages or call external code 

153 @staticmethod 

154 def _cmp_fragment(lhf, rhf): 

155 # search for difference 

156 # indices 

157 _li = _ri = 0 

158 # pre-calc len 

159 _lL = len(lhf) 

160 _rL = len(rhf) 

161 # bool for compare found 

162 _diff = False 

163 while _li < _lL and _ri < _rL: 

164 # iterate lists 

165 _num = lhf[_li] - rhf[_ri] 

166 if _num: 

167 return _num 

168 _li += 1 

169 _ri += 1 

170 

171 # diff found? lens equal? 

172 if not _diff and _lL != _rL: 

173 # lens not equal? Longer - later 

174 return _lL - _rL 

175 else: 

176 # equal 

177 return 0 

178 

179 def _cmp_num(self, lf, rf): 

180 # split fragments into lists 

181 _lhf = lf.split('.') if '.' in lf else list(lf) 

182 _rhf = rf.split('.') if '.' in rf else list(rf) 

183 # cast them to ints, delete empty strs 

184 _lhf = [int(n) for n in _lhf if len(n)] 

185 _rhf = [int(n) for n in _rhf if len(n)] 

186 

187 return self._cmp_fragment(_lhf, _rhf) 

188 

189 def _cmp_lex(self, lf, rf): 

190 def split_rev(_s): 

191 _out = [] 

192 _list = re.split(r'(\d+)', _s) 

193 # iterate and cast into num of possible 

194 for idx in range(0, len(_list)): 

195 try: 

196 # try to convert it to number 

197 _out.append(int(_list[idx])) 

198 except ValueError: 

199 # not a number 

200 _ords = [ord(n) for n in _list[idx]] 

201 _out += _ords 

202 return _out 

203 # split string into letters and numbers 

204 # and cast each item into its ORD value 

205 _lhf = split_rev(lf) 

206 _rhf = split_rev(rf) 

207 # _lhf = [ord(n) for n in lf] 

208 # _rhf = [ord(n) for n in rf] 

209 

210 return self._cmp_fragment(_lhf, _rhf) 

211 # end of cmps 

212 

213 # main part compared using splitted numbers 

214 # if equal, revision is compared using lexical comparizon 

215 def __lt__(self, v): 

216 _e = self._cmp_num(self.epoch, v.epoch) 

217 _u = self._cmp_num(self.upstream, v.upstream) 

218 _ul = self._cmp_lex(self.upstream_rev, v.upstream_rev) 

219 _d = self._cmp_num(self.debian, v.debian) 

220 _dl = self._cmp_lex(self.debian_rev, v.debian_rev) 

221 for n in [_e, _u, _ul, _d, _dl]: 

222 if n == 0: 

223 continue 

224 elif n < 0: 

225 return True 

226 elif n > 0: 

227 return False 

228 # if all is equal, it is still false 

229 return False 

230 

231 def __eq__(self, v): 

232 # compare all portions 

233 _result = [] 

234 _result.append(self._cmp_num(self.epoch, v.epoch)) 

235 _result.append(self._cmp_num(self.upstream, v.upstream)) 

236 _result.append(self._cmp_lex(self.upstream_rev, v.upstream_rev)) 

237 _result.append(self._cmp_num(self.debian, v.debian)) 

238 _result.append(self._cmp_lex(self.debian_rev, v.debian_rev)) 

239 # if there is any non-zero, its not equal 

240 return not any(_result) 

241 

242 def __gt__(self, v): 

243 _e = self._cmp_num(self.epoch, v.epoch) 

244 _u = self._cmp_num(self.upstream, v.upstream) 

245 _ul = self._cmp_lex(self.upstream_rev, v.upstream_rev) 

246 _d = self._cmp_num(self.debian, v.debian) 

247 _dl = self._cmp_lex(self.debian_rev, v.debian_rev) 

248 for n in [_e, _u, _ul, _d, _dl]: 

249 if n == 0: 

250 continue 

251 elif n > 0: 

252 return True 

253 elif n < 0: 

254 return False 

255 # if all is equal, it is still false 

256 return False 

257 

258 def update_parts(self, target, status): 

259 # updating parts of version statuses 

260 if self._cmp_num(self.epoch, target.epoch) != 0: 

261 self.epoch_status = status 

262 else: 

263 self.epoch_status = const.VERSION_OK 

264 

265 if self._cmp_num(self.upstream, target.upstream) != 0 \ 

266 or self._cmp_lex(self.upstream_rev, target.upstream_rev) != 0: 

267 self.upstream_status = status 

268 else: 

269 self.upstream_status = const.VERSION_OK 

270 

271 if self._cmp_lex(self.debian, target.debian) != 0 \ 

272 or self._cmp_lex(self.debian_rev, target.debian_rev) != 0: 

273 self.debian_status = status 

274 else: 

275 self.debian_status = const.VERSION_OK 

276 

277 

278class VersionCmpResult(object): 

279 status = "" 

280 action = "" 

281 

282 source = None 

283 target = None 

284 

285 def __init__(self, i, c, r): 

286 # compare three versions and write a result 

287 self.source = i 

288 self.status = const.VERSION_NA 

289 self.action = const.ACT_NA 

290 

291 # Check if there is a release version present 

292 if r and len(r.version) > 0 and r.version != 'n/a': 

293 # I < C, installed version is older 

294 if i < c: 

295 self.target = c 

296 if i == r: 

297 # installed version is equal vs release version 

298 self.status = const.VERSION_OK 

299 self.action = const.ACT_UPGRADE 

300 elif i > r: 

301 # installed version is newer vs release version 

302 self.status = const.VERSION_UP 

303 self.action = const.ACT_UPGRADE 

304 elif i < r and r < c: 

305 # installed version is older vs release version 

306 self.status = const.VERSION_WARN 

307 self.action = const.ACT_NEED_UP 

308 self.target = r 

309 elif i < r and c == r: 

310 # installed version is older vs release version 

311 self.status = const.VERSION_WARN 

312 self.action = const.ACT_NEED_UP 

313 self.target = c 

314 elif c < r: 

315 # installed and repo versions older vs release version 

316 self.status = const.VERSION_WARN 

317 self.action = const.ACT_REPO 

318 # I > C 

319 # installed version is newer 

320 elif i > c: 

321 self.target = c 

322 if c == r: 

323 # some unknown version installed 

324 self.status = const.VERSION_WARN 

325 self.action = const.ACT_NEED_DOWN 

326 elif c > r: 

327 # installed and repo versions newer than release 

328 self.status = const.VERSION_UP 

329 self.action = const.ACT_NEED_DOWN 

330 elif c < r and r < i: 

331 # repo is older vs release and both older vs installed 

332 self.status = const.VERSION_UP 

333 self.action = const.ACT_REPO 

334 elif c < r and r == i: 

335 # repo is older vs release, but release version installed 

336 self.status = const.VERSION_OK 

337 self.action = const.ACT_REPO 

338 elif i < r: 

339 # both repo and installed older vs release, new target 

340 self.status = const.VERSION_DOWN 

341 self.action = const.ACT_REPO 

342 self.target = r 

343 # I = C 

344 # installed and linked repo is inline, 

345 elif i == c: 

346 self.target = c 

347 if i < r: 

348 # both are intact, new target possible 

349 self.status = const.VERSION_OK 

350 self.action = const.ACT_REPO 

351 self.target = r 

352 elif i > r: 

353 # both are newer, same target 

354 self.status = const.VERSION_UP 

355 self.action = const.ACT_NA 

356 elif i == r: 

357 # all is ok 

358 self.status = const.VERSION_OK 

359 self.action = const.ACT_NA 

360 else: 

361 # no release version present 

362 self.target = c 

363 if i < c: 

364 self.status = const.VERSION_OK 

365 self.action = const.ACT_UPGRADE 

366 elif i > c: 

367 self.status = const.VERSION_UP 

368 self.action = const.ACT_NEED_DOWN 

369 elif i == c: 

370 self.status = const.VERSION_OK 

371 self.action = const.ACT_NA 

372 

373 # and we need to update per-part status 

374 self.source.update_parts(self.target, self.status) 

375 

376 @staticmethod 

377 def deb_lower(_s, _t): 

378 if _t.debian and _t.debian > _s.debian: 

379 return True 

380 else: 

381 return False