Package entropy :: Module qa

Source Code for Module entropy.qa

  1  # -*- coding: utf-8 -*- 
  2  ''' 
  3      # DESCRIPTION: 
  4      # Entropy Object Oriented Interface 
  5   
  6      Copyright (C) 2007-2009 Fabio Erculiani 
  7   
  8      This program is free software; you can redistribute it and/or modify 
  9      it under the terms of the GNU General Public License as published by 
 10      the Free Software Foundation; either version 2 of the License, or 
 11      (at your option) any later version. 
 12   
 13      This program is distributed in the hope that it will be useful, 
 14      but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16      GNU General Public License for more details. 
 17   
 18      You should have received a copy of the GNU General Public License 
 19      along with this program; if not, write to the Free Software 
 20      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 21  ''' 
 22  # pylint ~ok 
 23  import os 
 24  import tempfile 
 25  from entropy.const import etpConst 
 26  from entropy.output import blue, darkgreen, red, darkred, bold, purple, brown 
 27  from entropy.exceptions import IncorrectParameter, PermissionDenied, \ 
 28      SystemDatabaseError 
 29  from entropy.i18n import _ 
 30  from entropy.core import SystemSettings 
 31   
32 -class QAInterface:
33 34 import entropy.tools as entropyTools 35 from entropy.misc import Lifo
36 - def __init__(self, OutputInterface):
37 38 self.Output = OutputInterface 39 self.SystemSettings = SystemSettings() 40 41 if not hasattr(self.Output, 'updateProgress'): 42 mytxt = _("Output interface has no updateProgress method") 43 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,)) 44 elif not callable(self.Output.updateProgress): 45 mytxt = _("Output interface has no updateProgress method") 46 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
47
48 - def test_depends_linking(self, idpackages, dbconn, repo = None):
49 50 repo = self.SystemSettings['repositories']['default_repository'] 51 52 scan_msg = blue(_("Now searching for broken depends")) 53 self.Output.updateProgress( 54 "[repo:%s] %s..." % ( 55 darkgreen(repo), 56 scan_msg, 57 ), 58 importance = 1, 59 type = "info", 60 header = red(" @@ ") 61 ) 62 63 broken = False 64 65 count = 0 66 maxcount = len(idpackages) 67 for idpackage in idpackages: 68 count += 1 69 atom = dbconn.retrieveAtom(idpackage) 70 scan_msg = "%s, %s:" % ( 71 blue(_("scanning for broken depends")), 72 darkgreen(atom), 73 ) 74 self.Output.updateProgress( 75 "[repo:%s] %s" % ( 76 darkgreen(repo), 77 scan_msg, 78 ), 79 importance = 1, 80 type = "info", 81 header = blue(" @@ "), 82 back = True, 83 count = (count, maxcount,) 84 ) 85 mydepends = dbconn.retrieveDepends(idpackage) 86 if not mydepends: 87 continue 88 for mydepend in mydepends: 89 myatom = dbconn.retrieveAtom(mydepend) 90 self.Output.updateProgress( 91 "[repo:%s] %s => %s" % ( 92 darkgreen(repo), 93 darkgreen(atom), 94 darkred(myatom), 95 ), 96 importance = 0, 97 type = "info", 98 header = blue(" @@ "), 99 back = True, 100 count = (count, maxcount,) 101 ) 102 mycontent = dbconn.retrieveContent(mydepend) 103 mybreakages = self.content_test(mycontent) 104 if not mybreakages: 105 continue 106 broken = True 107 self.Output.updateProgress( 108 "[repo:%s] %s %s => %s" % ( 109 darkgreen(repo), 110 darkgreen(atom), 111 darkred(myatom), 112 bold(_("broken libraries detected")), 113 ), 114 importance = 1, 115 type = "warning", 116 header = purple(" @@ "), 117 count = (count, maxcount,) 118 ) 119 for mylib in mybreakages: 120 self.Output.updateProgress( 121 "%s %s:" % ( 122 darkgreen(mylib), 123 red(_("needs")), 124 ), 125 importance = 1, 126 type = "warning", 127 header = brown(" ## ") 128 ) 129 for needed in mybreakages[mylib]: 130 self.Output.updateProgress( 131 "%s" % ( 132 red(needed), 133 ), 134 importance = 1, 135 type = "warning", 136 header = purple(" # ") 137 ) 138 return broken
139 140
141 - def scan_missing_dependencies(self, idpackages, dbconn, ask = True, 142 self_check = False, repo = None, black_list = None, 143 black_list_adder = None):
144 145 if repo == None: 146 repo = self.SystemSettings['repositories']['default_repository'] 147 148 if not isinstance(black_list, set): 149 black_list = set() 150 151 taint = False 152 scan_msg = blue(_("Now searching for missing RDEPENDs")) 153 self.Output.updateProgress( 154 "[repo:%s] %s..." % ( 155 darkgreen(repo), 156 scan_msg, 157 ), 158 importance = 1, 159 type = "info", 160 header = red(" @@ ") 161 ) 162 scan_msg = blue(_("scanning for missing RDEPENDs")) 163 count = 0 164 maxcount = len(idpackages) 165 for idpackage in idpackages: 166 count += 1 167 atom = dbconn.retrieveAtom(idpackage) 168 if not atom: 169 continue 170 self.Output.updateProgress( 171 "[repo:%s] %s: %s" % ( 172 darkgreen(repo), 173 scan_msg, 174 darkgreen(atom), 175 ), 176 importance = 1, 177 type = "info", 178 header = blue(" @@ "), 179 back = True, 180 count = (count, maxcount,) 181 ) 182 missing_extended, missing = self.get_missing_rdepends(dbconn, 183 idpackage, self_check = self_check) 184 missing -= black_list 185 for item in missing_extended.keys(): 186 missing_extended[item] -= black_list 187 if not missing_extended[item]: 188 del missing_extended[item] 189 if (not missing) or (not missing_extended): 190 continue 191 self.Output.updateProgress( 192 "[repo:%s] %s: %s %s:" % ( 193 darkgreen(repo), 194 blue("package"), 195 darkgreen(atom), 196 blue(_("is missing the following dependencies")), 197 ), 198 importance = 1, 199 type = "info", 200 header = red(" @@ "), 201 count = (count, maxcount,) 202 ) 203 for missing_data in missing_extended: 204 self.Output.updateProgress( 205 "%s:" % (brown(unicode(missing_data)),), 206 importance = 0, 207 type = "info", 208 header = purple(" ## ") 209 ) 210 for dependency in missing_extended[missing_data]: 211 self.Output.updateProgress( 212 "%s" % (darkred(dependency),), 213 importance = 0, 214 type = "info", 215 header = blue(" # ") 216 ) 217 if ask: 218 rc_ask = self.Output.askQuestion(_("Do you want to add them?")) 219 if rc_ask == "No": 220 continue 221 rc_ask = self.Output.askQuestion(_("Selectively?")) 222 if rc_ask == "Yes": 223 newmissing = set() 224 new_blacklist = set() 225 for dependency in missing: 226 self.Output.updateProgress( 227 "[repo:%s|%s] %s" % ( 228 darkgreen(repo), 229 brown(atom), 230 blue(dependency), 231 ), 232 importance = 0, 233 type = "info", 234 header = blue(" @@ ") 235 ) 236 rc_ask = self.Output.askQuestion(_("Want to add?")) 237 if rc_ask == "Yes": 238 newmissing.add(dependency) 239 else: 240 rc_ask = self.Output.askQuestion( 241 _("Want to blacklist?")) 242 if rc_ask == "Yes": 243 new_blacklist.add(dependency) 244 if new_blacklist and (black_list_adder != None): 245 black_list_adder(new_blacklist, repo = repo) 246 missing = newmissing 247 if missing: 248 taint = True 249 dbconn.insertDependencies(idpackage, missing) 250 dbconn.commitChanges() 251 self.Output.updateProgress( 252 "[repo:%s] %s: %s" % ( 253 darkgreen(repo), 254 darkgreen(atom), 255 blue(_("missing dependencies added")), 256 ), 257 importance = 1, 258 type = "info", 259 header = red(" @@ "), 260 count = (count, maxcount,) 261 ) 262 263 return taint
264
265 - def content_test(self, mycontent):
266 267 def is_contained(needed, content): 268 for item in content: 269 if os.path.basename(item) == needed: 270 return True 271 return False
272 273 mylibs = {} 274 for myfile in mycontent: 275 myfile = myfile.encode('raw_unicode_escape') 276 if not os.access(myfile, os.R_OK): 277 continue 278 if not os.path.isfile(myfile): 279 continue 280 if not self.entropyTools.is_elf_file(myfile): 281 continue 282 mylibs[myfile] = self.entropyTools.read_elf_dynamic_libraries( 283 myfile) 284 285 broken_libs = {} 286 for mylib in mylibs: 287 for myneeded in mylibs[mylib]: 288 # is this inside myself ? 289 if is_contained(myneeded, mycontent): 290 continue 291 found = self.resolve_dynamic_library(myneeded, mylib) 292 if found: 293 continue 294 if not broken_libs.has_key(mylib): 295 broken_libs[mylib] = set() 296 broken_libs[mylib].add(myneeded) 297 298 return broken_libs
299
300 - def resolve_dynamic_library(self, library, requiring_executable):
301 302 def do_resolve(mypaths): 303 found_path = None 304 for mypath in mypaths: 305 mypath = os.path.join(etpConst['systemroot']+mypath, library) 306 if not os.access(mypath, os.R_OK): 307 continue 308 if os.path.isdir(mypath): 309 continue 310 if not self.entropyTools.is_elf_file(mypath): 311 continue 312 found_path = mypath 313 break 314 return found_path
315 316 mypaths = self.entropyTools.collect_linker_paths() 317 found_path = do_resolve(mypaths) 318 319 if not found_path: 320 mypaths = self.entropyTools.read_elf_linker_paths( 321 requiring_executable) 322 found_path = do_resolve(mypaths) 323 324 return found_path 325
326 - def get_missing_rdepends(self, dbconn, idpackage, self_check = False):
327 328 rdepends = {} 329 rdepends_plain = set() 330 neededs = dbconn.retrieveNeeded(idpackage, extended = True) 331 ldpaths = set(self.entropyTools.collect_linker_paths()) 332 deps_content = set() 333 dependencies = self.get_deep_dependency_list(dbconn, idpackage, 334 atoms = True) 335 scope_cache = set() 336 337 def update_depscontent(mycontent, dbconn, ldpaths): 338 return set( \ 339 [x for x in mycontent if os.path.dirname(x) in ldpaths \ 340 and (dbconn.isNeededAvailable(os.path.basename(x)) > 0) ])
341 342 def is_in_content(myneeded, content): 343 for item in content: 344 item = os.path.basename(item) 345 if myneeded == item: 346 return True 347 return False 348 349 for dependency in dependencies: 350 match = dbconn.atomMatch(dependency) 351 if match[0] != -1: 352 mycontent = dbconn.retrieveContent(match[0]) 353 deps_content |= update_depscontent(mycontent, dbconn, ldpaths) 354 key, slot = dbconn.retrieveKeySlot(match[0]) 355 scope_cache.add((key, slot)) 356 357 key, slot = dbconn.retrieveKeySlot(idpackage) 358 mycontent = dbconn.retrieveContent(idpackage) 359 deps_content |= update_depscontent(mycontent, dbconn, ldpaths) 360 scope_cache.add((key, slot)) 361 362 idpackages_cache = set() 363 idpackage_map = {} 364 idpackage_map_reverse = {} 365 for needed, elfclass in neededs: 366 data_solved = dbconn.resolveNeeded(needed, elfclass = elfclass, 367 extended = True) 368 data_size = len(data_solved) 369 data_solved = set([x for x in data_solved if x[0] \ 370 not in idpackages_cache]) 371 if not data_solved or (data_size != len(data_solved)): 372 continue 373 374 if self_check: 375 if is_in_content(needed, mycontent): 376 continue 377 378 found = False 379 for data in data_solved: 380 if data[1] in deps_content: 381 found = True 382 break 383 if not found: 384 for data in data_solved: 385 r_idpackage = data[0] 386 key, slot = dbconn.retrieveKeySlot(r_idpackage) 387 if (key, slot) not in scope_cache: 388 if not dbconn.isSystemPackage(r_idpackage): 389 if not rdepends.has_key((needed, elfclass)): 390 rdepends[(needed, elfclass)] = set() 391 if not idpackage_map.has_key((needed, elfclass)): 392 idpackage_map[(needed, elfclass)] = set() 393 keyslot = "%s:%s" % (key, slot,) 394 obj = idpackage_map_reverse.setdefault( 395 keyslot, set()) 396 obj.add((needed, elfclass,)) 397 rdepends[(needed, elfclass)].add(keyslot) 398 idpackage_map[(needed, elfclass)].add(r_idpackage) 399 rdepends_plain.add(keyslot) 400 idpackages_cache.add(r_idpackage) 401 402 # now reduce dependencies 403 404 r_deplist = set() 405 for key in idpackage_map: 406 r_idpackages = idpackage_map.get(key) 407 for r_idpackage in r_idpackages: 408 r_deplist |= dbconn.retrieveDependencies(r_idpackage) 409 410 r_keyslots = set() 411 for r_dep in r_deplist: 412 m_idpackage, m_rc = dbconn.atomMatch(r_dep) 413 if m_rc != 0: 414 continue 415 keyslot = dbconn.retrieveKeySlotAggregated(m_idpackage) 416 if keyslot in rdepends_plain: 417 r_keyslots.add(keyslot) 418 419 rdepends_plain -= r_keyslots 420 for r_keyslot in r_keyslots: 421 keys = [x for x in idpackage_map_reverse.get(keyslot, set()) if \ 422 x in rdepends] 423 for key in keys: 424 rdepends[key].discard(r_keyslot) 425 if not rdepends[key]: 426 del rdepends[key] 427 428 return rdepends, rdepends_plain 429
430 - def get_deep_dependency_list(self, dbconn, idpackage, atoms = False):
431 432 mybuffer = self.Lifo() 433 matchcache = set() 434 depcache = set() 435 mydeps = dbconn.retrieveDependencies(idpackage) 436 for mydep in mydeps: 437 mybuffer.push(mydep) 438 mydep = mybuffer.pop() 439 440 while mydep: 441 442 if mydep in depcache: 443 mydep = mybuffer.pop() 444 continue 445 446 my_idpackage, my_rc = dbconn.atomMatch(mydep) 447 if atoms: 448 matchcache.add(mydep) 449 else: 450 matchcache.add(my_idpackage) 451 452 if my_idpackage != -1: 453 owndeps = dbconn.retrieveDependencies(my_idpackage) 454 for owndep in owndeps: 455 mybuffer.push(owndep) 456 457 depcache.add(mydep) 458 mydep = mybuffer.pop() 459 460 # always discard -1 in set 461 matchcache.discard(-1) 462 return matchcache
463
464 - def __analyze_package_edb(self, pkg_path):
465 466 from entropy.db import LocalRepository, dbapi2 467 fd, tmp_path = tempfile.mkstemp() 468 extract_path = self.entropyTools.extract_edb(pkg_path, tmp_path) 469 if extract_path is None: 470 os.remove(tmp_path) 471 os.close(fd) 472 return False # error! 473 try: 474 dbc = LocalRepository( 475 readOnly = False, 476 dbFile = tmp_path, 477 clientDatabase = True, 478 dbname = 'qa_testing', 479 xcache = False, 480 indexing = False, 481 OutputInterface = self.Output, 482 skipChecks = False 483 ) 484 except dbapi2.Error: 485 os.remove(tmp_path) 486 os.close(fd) 487 return False 488 489 valid = True 490 try: 491 dbc.validateDatabase() 492 except SystemDatabaseError: 493 valid = False 494 495 if valid: 496 try: 497 for idpackage in dbc.listAllIdpackages(): 498 dbc.retrieveContent(idpackage, extended = True, 499 formatted = True, insert_formatted = True) 500 except dbapi2.Error: 501 valid = False 502 503 dbc.closeDB() 504 os.remove(tmp_path) 505 os.close(fd) 506 507 return valid
508
509 - def entropy_package_checks(self, package_path):
510 qa_methods = [self.__analyze_package_edb] 511 for method in qa_methods: 512 qa_rc = method(package_path) 513 if not qa_rc: 514 return False 515 return True
516 517
518 -class ErrorReportInterface:
519 520 import entropy.tools as entropyTools
521 - def __init__(self, post_url):
522 from entropy.misc import MultipartPostHandler 523 import urllib2 524 self.url = post_url 525 self.opener = urllib2.build_opener(MultipartPostHandler) 526 self.generated = False 527 self.params = {} 528 529 sys_settings = SystemSettings() 530 proxy_settings = sys_settings['system']['proxy'] 531 mydict = {} 532 if proxy_settings['ftp']: 533 mydict['ftp'] = proxy_settings['ftp'] 534 if proxy_settings['http']: 535 mydict['http'] = proxy_settings['http'] 536 if mydict: 537 mydict['username'] = proxy_settings['username'] 538 mydict['password'] = proxy_settings['password'] 539 self.entropyTools.add_proxy_opener(urllib2, mydict) 540 else: 541 # unset 542 urllib2._opener = None
543
544 - def prepare(self, tb_text, name, email, report_data = "", description = ""):
545 546 import sys 547 from entropy.tools import getstatusoutput 548 self.params['arch'] = etpConst['currentarch'] 549 self.params['stacktrace'] = tb_text 550 self.params['name'] = name 551 self.params['email'] = email 552 self.params['version'] = etpConst['entropyversion'] 553 self.params['errordata'] = report_data 554 self.params['description'] = description 555 self.params['arguments'] = ' '.join(sys.argv) 556 self.params['uid'] = etpConst['uid'] 557 self.params['system_version'] = "N/A" 558 if os.access(etpConst['systemreleasefile'], os.R_OK): 559 f_rel = open(etpConst['systemreleasefile'], "r") 560 self.params['system_version'] = f_rel.readline().strip() 561 f_rel.close() 562 563 self.params['processes'] = getstatusoutput('ps auxf')[1] 564 self.params['lspci'] = getstatusoutput('/usr/sbin/lspci')[1] 565 self.params['dmesg'] = getstatusoutput('dmesg')[1] 566 self.params['locale'] = getstatusoutput('locale -v')[1] 567 568 self.generated = True
569 570 # params is a dict, key(HTTP post item name): value
571 - def submit(self):
572 if self.generated: 573 result = self.opener.open(self.url, self.params).read() 574 if result.strip() == "1": 575 return True 576 return False 577 else: 578 mytxt = _("Not prepared yet") 579 raise PermissionDenied("PermissionDenied: %s" % (mytxt,))
580