Package entropy :: Package client :: Package interfaces :: Module package

Source Code for Module entropy.client.interfaces.package

   1   
   2  # -*- coding: utf-8 -*- 
   3  ''' 
   4      # DESCRIPTION: 
   5      # Entropy Object Oriented Interface 
   6   
   7      Copyright (C) 2007-2009 Fabio Erculiani 
   8   
   9      This program is free software; you can redistribute it and/or modify 
  10      it under the terms of the GNU General Public License as published by 
  11      the Free Software Foundation; either version 2 of the License, or 
  12      (at your option) any later version. 
  13   
  14      This program is distributed in the hope that it will be useful, 
  15      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  16      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  17      GNU General Public License for more details. 
  18   
  19      You should have received a copy of the GNU General Public License 
  20      along with this program; if not, write to the Free Software 
  21      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  22  ''' 
  23  from __future__ import with_statement 
  24  import os 
  25  import subprocess 
  26  import time 
  27  import shutil 
  28  from entropy.const import * 
  29  from entropy.exceptions import * 
  30  from entropy.i18n import _ 
  31  from entropy.output import TextInterface, brown, blue, bold, darkgreen, \ 
  32      darkblue, red, purple, darkred, print_info, print_error, print_warning 
  33  from entropy.misc import TimeScheduled 
  34  from entropy.db import dbapi2, LocalRepository 
  35  from entropy.client.interfaces.client import Client 
  36  from entropy.cache import EntropyCacher 
  37   
38 -class Package:
39 40 import entropy.tools as entropyTools 41 import entropy.dump as dumpTools
42 - def __init__(self, EquoInstance):
43 44 if not isinstance(EquoInstance,Client): 45 mytxt = _("A valid Client instance or subclass is needed") 46 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,)) 47 self.Entropy = EquoInstance 48 49 self.Cacher = EntropyCacher() 50 self.infoDict = {} 51 self.prepared = False 52 self.matched_atom = () 53 self.valid_actions = ("source","fetch","multi_fetch","remove", 54 "remove_conflict","install","config" 55 ) 56 self.action = None 57 self.fetch_abort_function = None 58 self.xterm_title = ''
59
60 - def kill(self):
61 self.infoDict.clear() 62 self.matched_atom = () 63 self.valid_actions = () 64 self.action = None 65 self.prepared = False 66 self.fetch_abort_function = None
67
68 - def error_on_prepared(self):
69 if self.prepared: 70 mytxt = _("Already prepared") 71 raise PermissionDenied("PermissionDenied: %s" % (mytxt,))
72
73 - def error_on_not_prepared(self):
74 if not self.prepared: 75 mytxt = _("Not yet prepared") 76 raise PermissionDenied("PermissionDenied: %s" % (mytxt,))
77
78 - def check_action_validity(self, action):
79 if action not in self.valid_actions: 80 mytxt = _("Action must be in") 81 raise InvalidData("InvalidData: %s %s" % (mytxt,self.valid_actions,))
82
83 - def match_checksum(self, repository = None, checksum = None, 84 download = None, signatures = None):
85 86 self.error_on_not_prepared() 87 88 if repository == None: 89 repository = self.infoDict['repository'] 90 if checksum == None: 91 checksum = self.infoDict['checksum'] 92 if download == None: 93 download = self.infoDict['download'] 94 if signatures == None: 95 signatures = self.infoDict['signatures'] 96 97 def do_signatures_validation(signatures): 98 # check signatures, if available 99 if isinstance(signatures,dict): 100 for hash_type in sorted(signatures): 101 hash_val = signatures[hash_type] 102 # XXX workaround bug on unreleased 103 # entropy versions 104 if hash_val in signatures: 105 continue 106 elif hash_val == None: 107 continue 108 elif not hasattr(self.entropyTools, 109 'compare_%s' % (hash_type,)): 110 continue 111 112 self.Entropy.updateProgress( 113 "%s: %s" % (blue(_("Checking package hash")), 114 purple(hash_type.upper()),), 115 importance = 0, 116 type = "info", 117 header = red(" ## "), 118 back = True 119 ) 120 cmp_func = getattr(self.entropyTools, 121 'compare_%s' % (hash_type,)) 122 mydownload = os.path.join(etpConst['entropyworkdir'], 123 download) 124 valid = cmp_func(mydownload, hash_val) 125 if not valid: 126 self.Entropy.updateProgress( 127 "%s: %s %s" % ( 128 darkred(_("Package hash")), 129 purple(hash_type.upper()), 130 darkred(_("does not match the recorded one")), 131 ), 132 importance = 0, 133 type = "warning", 134 header = darkred(" ## ") 135 ) 136 return 1 137 self.Entropy.updateProgress( 138 "%s %s" % ( 139 purple(hash_type.upper()), 140 darkgreen(_("matches")), 141 ), 142 importance = 0, 143 type = "info", 144 header = " : " 145 ) 146 return 0
147 148 dlcount = 0 149 match = False 150 while dlcount <= 5: 151 152 self.Entropy.updateProgress( 153 blue(_("Checking package checksum...")), 154 importance = 0, 155 type = "info", 156 header = red(" ## "), 157 back = True 158 ) 159 160 dlcheck = self.Entropy.check_needed_package_download(download, 161 checksum = checksum) 162 if dlcheck == 0: 163 basef = os.path.basename(download) 164 self.Entropy.updateProgress( 165 "%s: %s" % ( 166 blue(_("Package checksum matches")), 167 darkgreen(basef), 168 ), 169 importance = 0, 170 type = "info", 171 header = red(" ## ") 172 ) 173 174 dlcheck = do_signatures_validation(signatures) 175 if dlcheck == 0: 176 self.infoDict['verified'] = True 177 match = True 178 break # file downloaded successfully 179 180 if dlcheck != 0: 181 dlcount += 1 182 self.Entropy.updateProgress( 183 darkred(_("Package checksum does not match. Redownloading... attempt #%s") % (dlcount,)), 184 importance = 0, 185 type = "warning", 186 header = darkred(" ## ") 187 ) 188 fetch = self.Entropy.fetch_file_on_mirrors( 189 repository, 190 self.Entropy.get_branch_from_download_relative_uri(download), 191 download, 192 checksum, 193 fetch_abort_function = self.fetch_abort_function 194 ) 195 if fetch != 0: 196 self.Entropy.updateProgress( 197 blue(_("Cannot properly fetch package! Quitting.")), 198 importance = 0, 199 type = "error", 200 header = darkred(" ## ") 201 ) 202 return fetch 203 204 # package is fetched, let's loop one more time 205 # to make sure to run all the checksum checks 206 continue 207 208 if not match: 209 mytxt = _("Cannot properly fetch package or checksum does not match. Try download latest repositories.") 210 self.Entropy.updateProgress( 211 blue(mytxt), 212 importance = 0, 213 type = "info", 214 header = red(" ## ") 215 ) 216 return 1 217 218 return 0
219
220 - def multi_match_checksum(self):
221 rc = 0 222 for repository, branch, download, digest, signatures in self.infoDict['multi_checksum_list']: 223 rc = self.match_checksum(repository, digest, download, signatures) 224 if rc != 0: break 225 return rc
226
227 - def __unpack_package(self):
228 229 if not self.infoDict['merge_from']: 230 self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Unpacking package: "+str(self.infoDict['atom'])) 231 else: 232 self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Merging package: "+str(self.infoDict['atom'])) 233 234 if os.path.isdir(self.infoDict['unpackdir']): 235 shutil.rmtree(self.infoDict['unpackdir'].encode('raw_unicode_escape')) 236 elif os.path.isfile(self.infoDict['unpackdir']): 237 os.remove(self.infoDict['unpackdir'].encode('raw_unicode_escape')) 238 os.makedirs(self.infoDict['imagedir']) 239 240 if not os.path.isfile(self.infoDict['pkgpath']) and not self.infoDict['merge_from']: 241 if os.path.isdir(self.infoDict['pkgpath']): 242 shutil.rmtree(self.infoDict['pkgpath']) 243 if os.path.islink(self.infoDict['pkgpath']): 244 os.remove(self.infoDict['pkgpath']) 245 self.infoDict['verified'] = False 246 rc = self.fetch_step() 247 if rc != 0: 248 return rc 249 250 if not self.infoDict['merge_from']: 251 252 # extract entropy database from package file 253 # in order to avoid having to read content data 254 # from the repository database, which, in future 255 # is allowed to not provide such info. 256 pkg_dbdir = os.path.dirname(self.infoDict['pkgdbpath']) 257 if not os.path.isdir(pkg_dbdir): 258 os.makedirs(pkg_dbdir, 0755) 259 # extract edb 260 self.entropyTools.extract_edb(self.infoDict['pkgpath'], 261 self.infoDict['pkgdbpath']) 262 263 unpack_tries = 3 264 while 1: 265 unpack_tries -= 1 266 try: 267 rc = self.entropyTools.spawn_function( 268 self.entropyTools.uncompress_tar_bz2, 269 self.infoDict['pkgpath'], 270 self.infoDict['imagedir'], 271 catchEmpty = True 272 ) 273 except EOFError: 274 self.Entropy.clientLog.log( 275 ETP_LOGPRI_INFO, ETP_LOGLEVEL_NORMAL, 276 "EOFError on " + self.infoDict['pkgpath'] 277 ) 278 rc = 1 279 except: 280 # this will make devs to actually catch the 281 # right exception and prepare a fix 282 self.Entropy.clientLog.log( 283 ETP_LOGPRI_INFO, 284 ETP_LOGLEVEL_NORMAL, 285 "Raising Unicode/Pickling Error for " + \ 286 self.infoDict['pkgpath'] 287 ) 288 rc = self.entropyTools.uncompress_tar_bz2( 289 self.infoDict['pkgpath'],self.infoDict['imagedir'], 290 catchEmpty = True 291 ) 292 if rc == 0: 293 break 294 if unpack_tries <= 0: 295 return rc 296 # otherwise, try to download it again 297 self.infoDict['verified'] = False 298 f_rc = self.fetch_step() 299 if f_rc != 0: 300 return f_rc 301 302 else: 303 304 pid = os.fork() 305 if pid > 0: 306 os.waitpid(pid, 0) 307 else: 308 self.__fill_image_dir(self.infoDict['merge_from'], 309 self.infoDict['imagedir']) 310 os._exit(0) 311 312 # unpack xpak ? 313 if os.path.isdir(self.infoDict['xpakpath']): 314 shutil.rmtree(self.infoDict['xpakpath']) 315 try: 316 os.rmdir(self.infoDict['xpakpath']) 317 except OSError: 318 pass 319 320 # create data dir where we'll unpack the xpak 321 os.makedirs(self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'],0755) 322 #os.mkdir(self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath']) 323 xpakPath = self.infoDict['xpakpath']+"/"+etpConst['entropyxpakfilename'] 324 325 if not self.infoDict['merge_from']: 326 if (self.infoDict['smartpackage']): 327 # we need to get the .xpak from database 328 xdbconn = self.Entropy.open_repository(self.infoDict['repository']) 329 xpakdata = xdbconn.retrieveXpakMetadata(self.infoDict['idpackage']) 330 if xpakdata: 331 # save into a file 332 f = open(xpakPath,"wb") 333 f.write(xpakdata) 334 f.flush() 335 f.close() 336 self.infoDict['xpakstatus'] = self.entropyTools.unpack_xpak( 337 xpakPath, 338 self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'] 339 ) 340 else: 341 self.infoDict['xpakstatus'] = None 342 del xpakdata 343 else: 344 self.infoDict['xpakstatus'] = self.entropyTools.extract_xpak( 345 self.infoDict['pkgpath'], 346 self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'] 347 ) 348 else: 349 # link xpakdir to self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'] 350 tolink_dir = self.infoDict['xpakpath']+"/"+etpConst['entropyxpakdatarelativepath'] 351 if os.path.isdir(tolink_dir): 352 shutil.rmtree(tolink_dir,True) 353 # now link 354 os.symlink(self.infoDict['xpakdir'],tolink_dir) 355 356 # create fake portage ${D} linking it to imagedir 357 portage_db_fakedir = os.path.join( 358 self.infoDict['unpackdir'], 359 "portage/"+self.infoDict['category'] + "/" + self.infoDict['name'] + "-" + self.infoDict['version'] 360 ) 361 362 os.makedirs(portage_db_fakedir,0755) 363 # now link it to self.infoDict['imagedir'] 364 os.symlink(self.infoDict['imagedir'],os.path.join(portage_db_fakedir,"image")) 365 366 return 0
367
368 - def __configure_package(self):
369 370 try: Spm = self.Entropy.Spm() 371 except: return 1 372 373 spm_atom = self.infoDict['key']+"-"+self.infoDict['version'] 374 myebuild = Spm.get_vdb_path()+spm_atom+"/"+self.infoDict['key'].split("/")[1]+"-"+self.infoDict['version']+etpConst['spm']['source_build_ext'] 375 if not os.path.isfile(myebuild): 376 return 2 377 378 self.Entropy.updateProgress( 379 brown(" Ebuild: pkg_config()"), 380 importance = 0, 381 header = red(" ##") 382 ) 383 384 try: 385 rc = Spm.spm_doebuild( 386 myebuild, 387 mydo = "config", 388 tree = "bintree", 389 cpv = spm_atom 390 ) 391 if rc == 1: 392 self.Entropy.clientLog.log( 393 ETP_LOGPRI_INFO, 394 ETP_LOGLEVEL_NORMAL, 395 "[PRE] ATTENTION Cannot properly run Spm pkg_config() for " + \ 396 str(spm_atom)+". Something bad happened." 397 ) 398 return 3 399 except Exception, e: 400 self.entropyTools.print_traceback() 401 self.Entropy.clientLog.log( 402 ETP_LOGPRI_INFO, 403 ETP_LOGLEVEL_NORMAL, 404 "[PRE] ATTENTION Cannot run Spm pkg_config() for "+spm_atom+"!! "+str(type(Exception))+": "+str(e) 405 ) 406 mytxt = "%s: %s %s. %s. %s: %s, %s" % ( 407 bold(_("QA")), 408 brown(_("Cannot run Spm pkg_config() for")), 409 bold(str(spm_atom)), 410 brown(_("Please report it")), 411 bold(_("Error")), 412 type(Exception), 413 e, 414 ) 415 self.Entropy.updateProgress( 416 mytxt, 417 importance = 0, 418 header = red(" ## ") 419 ) 420 return 1 421 422 return 0
423 424
425 - def __remove_package(self):
426 427 # clear on-disk cache 428 self.__clear_cache() 429 430 self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing package: %s" % (self.infoDict['removeatom'],)) 431 432 protected_removable_config_files = {} 433 # remove from database 434 if self.infoDict['removeidpackage'] != -1: 435 mytxt = "%s: " % (_("Removing from Entropy"),) 436 self.Entropy.updateProgress( 437 blue(mytxt) + red(self.infoDict['removeatom']), 438 importance = 1, 439 type = "info", 440 header = red(" ## ") 441 ) 442 protected_removable_config_files = self.Entropy.clientDbconn.retrieveAutomergefiles( 443 self.infoDict['removeidpackage'], get_dict = True 444 ) 445 self.__remove_package_from_database() 446 447 # Handle spm database 448 spm_atom = self.entropyTools.remove_tag(self.infoDict['removeatom']) 449 self.Entropy.clientLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing from Spm: "+str(spm_atom)) 450 self.__remove_package_from_spm_database(spm_atom) 451 452 self.__remove_content_from_system(protected_removable_config_files) 453 return 0
454
455 - def __remove_content_from_system(self, protected_removable_config_files):
456 457 sys_root = etpConst['systemroot'] 458 # load CONFIG_PROTECT and CONFIG_PROTECT_MASK 459 sys_settings = self.Entropy.SystemSettings 460 protect = self.Entropy.get_installed_package_config_protect( 461 self.infoDict['idpackage']) 462 mask = self.Entropy.get_installed_package_config_protect( 463 self.infoDict['idpackage'], mask = True) 464 sys_set_plg_id = \ 465 etpConst['system_settings_plugins_ids']['client_plugin'] 466 col_protect = sys_settings[sys_set_plg_id]['misc']['collisionprotect'] 467 468 # remove files from system 469 directories = set() 470 not_removed_due_to_collisions = set() 471 remove_content = sorted(self.infoDict['removecontent'], reverse = True) 472 for item in remove_content: 473 sys_root_item = sys_root+item 474 # collision check 475 if col_protect > 0: 476 477 if self.Entropy.clientDbconn.isFileAvailable(item) and os.path.isfile(sys_root_item): 478 # in this way we filter out directories 479 mytxt = red(_("Collision found during removal of")) + " " + sys_root_item + " - " 480 mytxt += red(_("cannot overwrite")) 481 self.Entropy.updateProgress( 482 mytxt, 483 importance = 1, 484 type = "warning", 485 header = red(" ## ") 486 ) 487 self.Entropy.clientLog.log( 488 ETP_LOGPRI_INFO, 489 ETP_LOGLEVEL_NORMAL, 490 "Collision found during remove of "+sys_root_item+" - cannot overwrite" 491 ) 492 not_removed_due_to_collisions.add(item) 493 continue 494 495 protected = False 496 if (not self.infoDict['removeconfig']) and (not self.infoDict['diffremoval']): 497 498 protected_item_test = sys_root_item 499 if isinstance(protected_item_test,unicode): 500 protected_item_test = protected_item_test.encode('utf-8') 501 502 in_mask, protected, x, do_continue = self._handle_config_protect( 503 protect, mask, None, protected_item_test, 504 do_allocation_check = False, do_quiet = True) 505 506 if do_continue: protected = True 507 508 # when files have not been modified by the user 509 # and they are inside a config protect directory 510 # we could even remove them directly 511 if in_mask: 512 513 oldprot_md5 = protected_removable_config_files.get(item) 514 if oldprot_md5 and os.path.exists(protected_item_test) and \ 515 os.access(protected_item_test, os.R_OK): 516 517 in_system_md5 = self.entropyTools.md5sum(protected_item_test) 518 if oldprot_md5 == in_system_md5: 519 mytxt = "%s: %s" % ( 520 darkgreen(_("Removing config file, never modified")), 521 blue(item),) 522 self.Entropy.updateProgress( 523 mytxt, 524 importance = 1, 525 type = "info", 526 header = red(" ## ") 527 ) 528 protected = False 529 do_continue = False 530 531 if protected: 532 self.Entropy.clientLog.log( 533 ETP_LOGPRI_INFO, 534 ETP_LOGLEVEL_VERBOSE, 535 "[remove] Protecting config file: "+sys_root_item 536 ) 537 mytxt = "[%s] %s: %s" % ( 538 red(_("remove")), 539 brown(_("Protecting config file")), 540 sys_root_item, 541 ) 542 self.Entropy.updateProgress( 543 mytxt, 544 importance = 1, 545 type = "warning", 546 header = red(" ## ") 547 ) 548 else: 549 try: 550 os.lstat(sys_root_item) 551 except OSError: 552 continue # skip file, does not exist 553 except UnicodeEncodeError: 554 mytxt = brown(_("This package contains a badly encoded file !!!")) 555 self.Entropy.updateProgress( 556 red("QA: ")+mytxt, 557 importance = 1, 558 type = "warning", 559 header = darkred(" ## ") 560 ) 561 continue # file has a really bad encoding 562 563 if os.path.isdir(sys_root_item) and os.path.islink(sys_root_item): 564 # S_ISDIR returns False for directory symlinks, so using os.path.isdir 565 # valid directory symlink 566 directories.add((sys_root_item,"link")) 567 elif os.path.isdir(sys_root_item): 568 # plain directory 569 directories.add((sys_root_item,"dir")) 570 else: # files, symlinks or not 571 # just a file or symlink or broken directory symlink (remove now) 572 try: 573 os.remove(sys_root_item) 574 # add its parent directory 575 dirfile = os.path.dirname(sys_root_item) 576 if os.path.isdir(dirfile) and os.path.islink(dirfile): 577 directories.add((dirfile,"link")) 578 elif os.path.isdir(dirfile): 579 directories.add((dirfile,"dir")) 580 except OSError: 581 pass 582 583 # removing files not removed from removecontent. 584 # it happened that boot services not removed due to 585 # collisions got removed from their belonging runlevels 586 # by postremove step. 587 # since this is a set, it is a mapped type, so every 588 # other instance around will feature this update 589 self.infoDict['removecontent'] -= not_removed_due_to_collisions 590 591 # now handle directories 592 directories = sorted(directories, reverse = True) 593 while 1: 594 taint = False 595 for directory, dirtype in directories: 596 mydir = "%s%s" % (sys_root,directory,) 597 if dirtype == "link": 598 try: 599 mylist = os.listdir(mydir) 600 if not mylist: 601 try: 602 os.remove(mydir) 603 taint = True 604 except OSError: 605 pass 606 except OSError: 607 pass 608 elif dirtype == "dir": 609 try: 610 mylist = os.listdir(mydir) 611 if not mylist: 612 try: 613 os.rmdir(mydir) 614 taint = True 615 except OSError: 616 pass 617 except OSError: 618 pass 619 620 if not taint: 621 break 622 del directories
623 624 625 ''' 626 @description: remove package entry from Spm database 627 @input spm package atom (cat/name+ver): 628 @output: 0 = all fine, <0 = error! 629 '''
630 - def __remove_package_from_spm_database(self, atom):
631 632 # handle spm support 633 try: 634 Spm = self.Entropy.Spm() 635 except: 636 return -1 # no Spm support ?? 637 638 portdb_dir = Spm.get_vdb_path() 639 remove_path = portdb_dir+atom 640 key = self.entropyTools.dep_getkey(atom) 641 others_installed = [x for x in Spm.search_keys(key) if \ 642 self.entropyTools.dep_getkey(x) == key] 643 if atom in others_installed: 644 others_installed.remove(atom) 645 slot = self.infoDict['slot'] 646 tag = self.infoDict['versiontag'] 647 if (tag == slot) and tag: slot = "0" 648 649 def do_rm_path_atomic(xpath): 650 for my_el in os.listdir(xpath): 651 my_el = os.path.join(xpath, my_el) 652 try: 653 os.remove(my_el) 654 except OSError: 655 pass 656 try: 657 os.rmdir(xpath) 658 except OSError: 659 pass
660 661 if os.path.isdir(remove_path): 662 do_rm_path_atomic(remove_path) 663 664 if others_installed: 665 666 for myatom in others_installed: 667 myslot = Spm.get_installed_package_slot(myatom) 668 if myslot != slot: 669 continue 670 mydir = portdb_dir+myatom 671 if not os.path.isdir(mydir): 672 continue 673 do_rm_path_atomic(mydir) 674 675 else: 676 677 world_file = Spm.get_world_file() 678 world_file_tmp = world_file+".entropy.tmp" 679 if os.access(world_file,os.W_OK) and os.path.isfile(world_file): 680 new = open(world_file_tmp,"w") 681 old = open(world_file,"r") 682 line = old.readline() 683 while line: 684 if line.find(key) != -1: 685 line = old.readline() 686 continue 687 if line.find(key+":"+slot) != -1: 688 line = old.readline() 689 continue 690 new.write(line) 691 line = old.readline() 692 new.flush() 693 new.close() 694 old.close() 695 os.rename(world_file_tmp,world_file) 696 697 return 0 698 699 ''' 700 @description: function that runs at the end of the package installation process, just removes data left by other steps 701 @output: 0 = all fine, >0 = error! 702 '''
703 - def _cleanup_package(self, unpack_dir):
704 # remove unpack dir 705 shutil.rmtree(unpack_dir,True) 706 try: os.rmdir(unpack_dir) 707 except OSError: pass 708 return 0
709
710 - def __remove_package_from_database(self, do_commit = False, do_cleanup = False):
711 self.error_on_not_prepared() 712 self.Entropy.clientDbconn.removePackage(self.infoDict['removeidpackage'], 713 do_commit = do_commit, do_cleanup = do_cleanup) 714 return 0
715
716 - def __clear_cache(self):
717 self.Entropy.clear_dump_cache(etpCache['advisories']) 718 self.Entropy.clear_dump_cache(etpCache['filter_satisfied_deps']) 719 self.Entropy.clear_dump_cache(etpCache['depends_tree']) 720 self.Entropy.clear_dump_cache(etpCache['check_package_update']) 721 self.Entropy.clear_dump_cache(etpCache['dep_tree']) 722 self.Entropy.clear_dump_cache(etpCache['dbMatch']+etpConst['clientdbid']+"/") 723 self.Entropy.clear_dump_cache(etpCache['dbSearch']+etpConst['clientdbid']+"/") 724 725 # clear caches, the bad way 726 self.Entropy.clear_dump_cache(etpCache['world_available']) 727 self.Entropy.clear_dump_cache(etpCache['world_update']) 728 self.Entropy.clear_dump_cache(etpCache['critical_update'])
729
730 - def __install_package(self):
731 732 # clear on-disk cache 733 self.__clear_cache() 734 735 self.Entropy.clientLog.log( 736 ETP_LOGPRI_INFO, 737 ETP_LOGLEVEL_NORMAL, 738 "Installing package: %s" % (self.infoDict['atom'],) 739 ) 740 741 already_protected_config_files = {} 742 if self.infoDict['removeidpackage'] != -1: 743 already_protected_config_files = self.Entropy.clientDbconn.retrieveAutomergefiles( 744 self.infoDict['removeidpackage'], get_dict = True 745 ) 746 747 # copy files over - install 748 # use fork? (in this case all the changed structures need to be pushed back) 749 rc = self.__move_image_to_system(already_protected_config_files) 750 if rc != 0: 751 return rc 752 del already_protected_config_files 753 754 # inject into database 755 mytxt = "%s: %s" % (blue(_("Updating database")),red(self.infoDict['atom']),) 756 self.Entropy.updateProgress( 757 mytxt, 758 importance = 1, 759 type = "info", 760 header = red(" ## ") 761 ) 762 newidpackage = self._install_package_into_database() 763 764 # remove old files and spm stuff 765 if self.infoDict['removeidpackage'] != -1: 766 # doing a diff removal 767 self.Entropy.clientLog.log( 768 ETP_LOGPRI_INFO, 769 ETP_LOGLEVEL_NORMAL, 770 "Remove old package: %s" % (self.infoDict['removeatom'],) 771 ) 772 self.infoDict['removeidpackage'] = -1 # disabling database removal 773 774 self.Entropy.clientLog.log( 775 ETP_LOGPRI_INFO, 776 ETP_LOGLEVEL_NORMAL, 777 "Removing Entropy and Spm database entry for %s" % ( 778 self.infoDict['removeatom'],) 779 ) 780 781 self.Entropy.updateProgress( 782 blue(_("Cleaning old package files...")), 783 importance = 1, 784 type = "info", 785 header = red(" ## ") 786 ) 787 self.__remove_package() 788 789 self.Entropy.clientLog.log( 790 ETP_LOGPRI_INFO, 791 ETP_LOGLEVEL_NORMAL, 792 "Installing new Spm database entry: %s" % (self.infoDict['atom'],) 793 ) 794 rc = self._install_package_into_spm_database(newidpackage) 795 796 return rc
797 798 ''' 799 @description: inject the database information into the Gentoo database 800 @output: 0 = all fine, !=0 = error! 801 '''
802 - def _install_package_into_spm_database(self, newidpackage):
803 804 # handle spm support 805 try: 806 Spm = self.Entropy.Spm() 807 except: 808 return -1 # no Spm support 809 810 portdb_dir = Spm.get_vdb_path() 811 if not os.path.isdir(portdb_dir): 812 os.makedirs(portdb_dir) 813 814 category = self.infoDict['category'] 815 name = self.infoDict['name'] 816 key = category + "/" + name 817 818 atomsfound = set() 819 dbdirs = os.listdir(portdb_dir) 820 if category in dbdirs: 821 catdirs = os.listdir(portdb_dir + "/" + category) 822 dirsfound = set([category + "/" + x for x in catdirs if \ 823 key == self.entropyTools.dep_getkey(category + "/" + x)]) 824 atomsfound.update(dirsfound) 825 826 ### REMOVE 827 # parse slot and match and remove 828 if atomsfound: 829 pkg_to_remove = None 830 for atom in atomsfound: 831 atomslot = Spm.get_installed_package_slot(atom) 832 # get slot from spm db 833 if atomslot == self.infoDict['slot']: 834 pkg_to_remove = atom 835 break 836 if pkg_to_remove: 837 remove_path = portdb_dir + pkg_to_remove 838 shutil.rmtree(remove_path, True) 839 try: 840 os.rmdir(remove_path) 841 except OSError: 842 pass 843 del atomsfound 844 845 # we now install it 846 xpak_rel_path = etpConst['entropyxpakdatarelativepath'] 847 if ((self.infoDict['xpakstatus'] != None) and \ 848 os.path.isdir( self.infoDict['xpakpath'] + "/" + xpak_rel_path)) or \ 849 self.infoDict['merge_from']: 850 851 if self.infoDict['merge_from']: 852 copypath = self.infoDict['xpakdir'] 853 if not os.path.isdir(copypath): 854 return 0 855 else: 856 copypath = self.infoDict['xpakpath'] + "/" + xpak_rel_path 857 858 if not os.path.isdir(portdb_dir + category): 859 os.makedirs(portdb_dir + category, 0755) 860 destination = portdb_dir + category + "/" + name + "-" + \ 861 self.infoDict['version'] 862 863 if os.path.isdir(destination): 864 shutil.rmtree(destination) 865 866 try: 867 shutil.copytree(copypath, destination) 868 except (IOError,), e: 869 mytxt = "%s: %s: %s: %s" % (red(_("QA")), 870 brown(_("Cannot update Portage database to destination")), 871 purple(destination),e,) 872 self.Entropy.updateProgress( 873 mytxt, 874 importance = 1, 875 type = "warning", 876 header = darkred(" ## ") 877 ) 878 879 # test if /var/cache/edb/counter is fine 880 if os.path.isfile(etpConst['edbcounter']): 881 try: 882 f = open(etpConst['edbcounter'],"r") 883 counter = int(f.readline().strip()) 884 f.close() 885 except: 886 # need file recreation, parse spm tree 887 counter = Spm.refill_counter() 888 else: 889 counter = Spm.refill_counter() 890 891 # write new counter to file 892 if os.path.isdir(destination): 893 counter += 1 894 f = open(destination+"/"+etpConst['spm']['xpak_entries']['counter'],"w") 895 f.write(str(counter)) 896 f.flush() 897 f.close() 898 f = open(etpConst['edbcounter'],"w") 899 f.write(str(counter)) 900 f.flush() 901 f.close() 902 # update counter inside clientDatabase 903 self.Entropy.clientDbconn.insertCounter(newidpackage, counter) 904 else: 905 mytxt = brown(_("Cannot update Portage counter, destination %s does not exist.") % (destination,)) 906 self.Entropy.updateProgress( 907 red("QA: ")+mytxt, 908 importance = 1, 909 type = "warning", 910 header = darkred(" ## ") 911 ) 912 913 user_inst_source = etpConst['install_sources']['user'] 914 if self.infoDict['install_source'] != user_inst_source: 915 self.Entropy.clientLog.log( 916 ETP_LOGPRI_INFO, 917 ETP_LOGLEVEL_NORMAL, 918 "Not updating Portage world file for: %s" % (self.infoDict['atom'],) 919 ) 920 # only user selected packages in Portage world file 921 return 0 922 923 # add to Portage world 924 # key: key 925 # slot: self.infoDict['slot'] 926 myslot = self.infoDict['slot'] 927 if (self.infoDict['versiontag'] == self.infoDict['slot']) and self.infoDict['versiontag']: 928 # usually kernel packages 929 myslot = "0" 930 keyslot = key+":"+myslot 931 world_file = Spm.get_world_file() 932 world_atoms = set() 933 934 if os.access(world_file,os.R_OK) and os.path.isfile(world_file): 935 f = open(world_file,"r") 936 world_atoms = set([x.strip() for x in f.readlines() if x.strip()]) 937 f.close() 938 else: 939 mytxt = brown(_("Cannot update Portage world file, destination %s does not exist.") % (world_file,)) 940 self.Entropy.updateProgress( 941 red("QA: ")+mytxt, 942 importance = 1, 943 type = "warning", 944 header = darkred(" ## ") 945 ) 946 return 0 947 948 try: 949 if keyslot not in world_atoms and \ 950 os.access(os.path.dirname(world_file),os.W_OK) and \ 951 self.entropyTools.istextfile(world_file): 952 world_atoms.discard(key) 953 world_atoms.add(keyslot) 954 world_atoms = sorted(list(world_atoms)) 955 world_file_tmp = world_file+".entropy_inst" 956 f = open(world_file_tmp,"w") 957 for item in world_atoms: 958 f.write(item+"\n") 959 f.flush() 960 f.close() 961 shutil.move(world_file_tmp,world_file) 962 except (UnicodeDecodeError,UnicodeEncodeError), e: 963 self.entropyTools.print_traceback(f = self.Entropy.clientLog) 964 mytxt = brown(_("Cannot update Portage world file, destination %s is corrupted.") % (world_file,)) 965 self.Entropy.updateProgress( 966 red("QA: ")+mytxt+": "+unicode(e), 967 importance = 1, 968 type = "warning", 969 header = darkred(" ## ") 970 ) 971 972 return 0
973 974 ''' 975 @description: injects package info into the installed packages database 976 @output: 0 = all fine, >0 = error! 977 '''
978 - def _install_package_into_database(self):
979 980 # fetch info 981 smart_pkg = self.infoDict['smartpackage'] 982 dbconn = self.Entropy.open_repository(self.infoDict['repository']) 983 984 if smart_pkg or self.infoDict['merge_from']: 985 986 data = dbconn.getPackageData(self.infoDict['idpackage'], 987 content_insert_formatted = True) 988 989 else: 990 991 # normal repositories 992 data = dbconn.getPackageData(self.infoDict['idpackage'], 993 get_content = False) 994 pkg_dbconn = self.Entropy.open_generic_database( 995 self.infoDict['pkgdbpath']) 996 # it is safe to consider that package dbs coming from repos 997 # contain only one entry 998 content = [] 999 for pkg_idpackage in pkg_dbconn.listAllIdpackages(): 1000 content += pkg_dbconn.retrieveContent( 1001 pkg_idpackage, extended = True, 1002 formatted = True, insert_formatted = True 1003 ) 1004 real_idpk = self.infoDict['idpackage'] 1005 content = [(real_idpk, x, y,) for orig_idpk, x, y in content] 1006 data['content'] = content 1007 pkg_dbconn.closeDB() 1008 1009 # this is needed to make postinstall trigger to work properly 1010 trigger_content = set((x[1] for x in data['content'])) 1011 self.infoDict['triggers']['install']['content'] = trigger_content 1012 1013 # open client db 1014 # always set data['injected'] to False 1015 # installed packages database SHOULD never have more 1016 # than one package for scope (key+slot) 1017 data['injected'] = False 1018 # spm counter will be set in self._install_package_into_spm_database() 1019 data['counter'] = -1 1020 # branch must be always set properly, it could happen it's not 1021 # when installing packages through their .tbz2s 1022 data['branch'] = self.Entropy.SystemSettings['repositories']['branch'] 1023 # there is no need to store needed paths into db 1024 if data.get('needed_paths'): 1025 del data['needed_paths'] 1026 1027 idpackage, rev, x = self.Entropy.clientDbconn.handlePackage( 1028 etpData = data, forcedRevision = data['revision'], 1029 formattedContent = True) 1030 1031 # update datecreation 1032 ctime = self.entropyTools.get_current_unix_time() 1033 self.Entropy.clientDbconn.setDateCreation(idpackage, str(ctime)) 1034 1035 # add idpk to the installedtable 1036 self.Entropy.clientDbconn.removePackageFromInstalledTable(idpackage) 1037 self.Entropy.clientDbconn.addPackageToInstalledTable(idpackage, 1038 self.infoDict['repository'], self.infoDict['install_source']) 1039 1040 automerge_data = self.infoDict.get('configprotect_data') 1041 if automerge_data: 1042 self.Entropy.clientDbconn.insertAutomergefiles(idpackage, 1043 automerge_data) 1044 1045 # clear depends table, this will make clientdb dependstable to be 1046 # regenerated during the next request (retrieveDepends) 1047 self.Entropy.clientDbconn.clearDependsTable() 1048 return idpackage
1049
1050 - def __fill_image_dir(self, mergeFrom, imageDir):
1051 1052 dbconn = self.Entropy.open_repository(self.infoDict['repository']) 1053 # this is triggered by merge_from infoDict metadata 1054 # even if repositories are allowed to not have content 1055 # metadata, in this particular case, it is mandatory 1056 package_content = dbconn.retrieveContent( 1057 self.infoDict['idpackage'], extended = True, formatted = True) 1058 contents = sorted(package_content) 1059 1060 # collect files 1061 for path in contents: 1062 # convert back to filesystem str 1063 encoded_path = path 1064 path = os.path.join(mergeFrom,encoded_path[1:]) 1065 topath = os.path.join(imageDir,encoded_path[1:]) 1066 path = path.encode('raw_unicode_escape') 1067 topath = topath.encode('raw_unicode_escape') 1068 1069 try: 1070 exist = os.lstat(path) 1071 except OSError: 1072 continue # skip file 1073 ftype = package_content[encoded_path] 1074 1075 if 'dir' == ftype and \ 1076 not stat.S_ISDIR(exist.st_mode) and \ 1077 os.path.isdir(path): 1078 # workaround for directory symlink issues 1079 path = os.path.realpath(path) 1080 1081 copystat = False 1082 # if our directory is a symlink instead, then copy the symlink 1083 if os.path.islink(path): 1084 tolink = os.readlink(path) 1085 if os.path.islink(topath): 1086 os.remove(topath) 1087 os.symlink(tolink,topath) 1088 elif os.path.isdir(path): 1089 if not os.path.isdir(topath): 1090 os.makedirs(topath) 1091 copystat = True 1092 elif os.path.isfile(path): 1093 if os.path.isfile(topath): 1094 os.remove(topath) # should never happen 1095 shutil.copy2(path,topath) 1096 copystat = True 1097 1098 if copystat: 1099 user = os.stat(path)[stat.ST_UID] 1100 group = os.stat(path)[stat.ST_GID] 1101 os.chown(topath,user,group) 1102 shutil.copystat(path,topath)
1103 1104
1105 - def __move_image_to_system(self, already_protected_config_files):
1106 1107 # load CONFIG_PROTECT and its mask 1108 repoid = self.infoDict['repository'] 1109 protect = self.Entropy.get_package_match_config_protect( 1110 self.matched_atom) 1111 mask = self.Entropy.get_package_match_config_protect( 1112 self.matched_atom, mask = True) 1113 sys_root = etpConst['systemroot'] 1114 sys_set_plg_id = \ 1115 etpConst['system_settings_plugins_ids']['client_plugin'] 1116 col_protect = self.Entropy.SystemSettings[sys_set_plg_id]['misc']['collisionprotect'] 1117 items_installed = set() 1118 1119 # setup imageDir properly 1120 imageDir = self.infoDict['imagedir'] 1121 encoded_imageDir = imageDir.encode('utf-8') 1122 movefile = self.entropyTools.movefile 1123 1124 # merge data into system 1125 for currentdir,subdirs,files in os.walk(encoded_imageDir): 1126 # create subdirs 1127 for subdir in subdirs: 1128 1129 imagepathDir = "%s/%s" % (currentdir,subdir,) 1130 rootdir = "%s%s" % (sys_root,imagepathDir[len(imageDir):],) 1131 1132 # handle broken symlinks 1133 if os.path.islink(rootdir) and not os.path.exists(rootdir):# broken symlink 1134 os.remove(rootdir) 1135 1136 # if our directory is a file on the live system 1137 elif os.path.isfile(rootdir): # really weird...! 1138 self.Entropy.clientLog.log( 1139 ETP_LOGPRI_INFO, 1140 ETP_LOGLEVEL_NORMAL, 1141 "WARNING!!! %s is a file when it should be a directory !! Removing in 20 seconds..." % (rootdir,) 1142 ) 1143 mytxt = darkred(_("%s is a file when should be a directory !! Removing in 20 seconds...") % (rootdir,)) 1144 self.Entropy.updateProgress( 1145 red("QA: ")+mytxt, 1146 importance = 1, 1147 type = "warning", 1148 header = red(" !!! ") 1149 ) 1150 self.entropyTools.ebeep(20) 1151 os.remove(rootdir) 1152 1153 # if our directory is a symlink instead, then copy the symlink 1154 if os.path.islink(imagepathDir): 1155 1156 # if our live system features a directory instead of 1157 # a symlink, we should consider removing the directory 1158 if (not os.path.islink(rootdir)) and os.path.isdir(rootdir): 1159 self.Entropy.clientLog.log( 1160 ETP_LOGPRI_INFO, 1161 ETP_LOGLEVEL_NORMAL, 1162 "WARNING!!! %s is a directory when it should be a symlink !! Removing in 20 seconds..." % (rootdir,) 1163 ) 1164 mytxt = darkred(_("%s is a directory when should be a symlink !! Removing in 20 seconds...") % (rootdir,)) 1165 self.Entropy.updateProgress( 1166 red("QA: ")+mytxt, 1167 importance = 1, 1168 type = "warning", 1169 header = red(" !!! ") 1170 ) 1171 self.entropyTools.ebeep(20) 1172 shutil.rmtree(rootdir, True) 1173 os.rmdir(rootdir) 1174 1175 tolink = os.readlink(imagepathDir) 1176 live_tolink = None 1177 if os.path.islink(rootdir): 1178 live_tolink = os.readlink(rootdir) 1179 if tolink != live_tolink: 1180 if os.path.lexists(rootdir): 1181 # at this point, it must be a file 1182 os.remove(rootdir) 1183 os.symlink(tolink, rootdir) 1184 1185 elif (not os.path.isdir(rootdir)) and (not os.access(rootdir,os.R_OK)): 1186 try: 1187 # we should really force a simple mkdir first of all 1188 os.mkdir(rootdir) 1189 except OSError: 1190 os.makedirs(rootdir) 1191 1192 1193 if not os.path.islink(rootdir) and os.access(rootdir,os.W_OK): 1194 # symlink doesn't need permissions, also until os.walk ends they might be broken 1195 # XXX also, added os.access() check because there might be directories/files unwritable 1196 # what to do otherwise? 1197 user = os.stat(imagepathDir)[stat.ST_UID] 1198 group = os.stat(imagepathDir)[stat.ST_GID] 1199 os.chown(rootdir,user,group) 1200 shutil.copystat(imagepathDir,rootdir) 1201 1202 items_installed.add(os.path.join(os.path.realpath(os.path.dirname(rootdir)),os.path.basename(rootdir))) 1203 1204 for item in files: 1205 1206 fromfile = "%s/%s" % (currentdir,item,) 1207 tofile = "%s%s" % (sys_root,fromfile[len(imageDir):],) 1208 1209 if col_protect > 1: 1210 todbfile = fromfile[len(imageDir):] 1211 myrc = self._handle_install_collision_protect(tofile, todbfile) 1212 if not myrc: 1213 continue 1214 1215 prot_old_tofile = tofile[len(sys_root):] 1216 pre_tofile = tofile[:] 1217 in_mask, protected, tofile, do_continue = self._handle_config_protect( 1218 protect, mask, fromfile, tofile) 1219 1220 # collect new config automerge data 1221 if in_mask and os.path.exists(fromfile): 1222 try: 1223 prot_md5 = self.entropyTools.md5sum(fromfile) 1224 self.infoDict['configprotect_data'].append( 1225 (prot_old_tofile,prot_md5,)) 1226 except (IOError,): 1227 pass 1228 1229 # check if it's really necessary to protect file 1230 if protected: 1231 1232 try: 1233 1234 # second task 1235 oldprot_md5 = already_protected_config_files.get( 1236 prot_old_tofile) 1237 1238 if oldprot_md5 and os.path.exists(pre_tofile) and \ 1239 os.access(pre_tofile, os.R_OK): 1240 1241 in_system_md5 = self.entropyTools.md5sum(pre_tofile) 1242 if oldprot_md5 == in_system_md5: 1243 # we can merge it, files, even if 1244 # contains changes have not been modified 1245 # by the user 1246 mytxt = "%s: %s" % ( 1247 darkgreen(_("Automerging config file, never modified")), 1248 blue(pre_tofile),) 1249 self.Entropy.updateProgress( 1250 mytxt, 1251 importance = 1, 1252 type = "info", 1253 header = red(" ## ") 1254 ) 1255 protected = False 1256 do_continue = False 1257 tofile = pre_tofile 1258 1259 except (IOError,): 1260 pass 1261 1262 if do_continue: 1263 continue 1264 1265 try: 1266 1267 if os.path.realpath(fromfile) == os.path.realpath(tofile) and os.path.islink(tofile): 1268 # there is a serious issue here, better removing tofile, happened to someone: 1269 try: # try to cope... 1270 os.remove(tofile) 1271 except OSError: 1272 pass 1273 1274 # if our file is a dir on the live system 1275 if os.path.isdir(tofile) and not os.path.islink(tofile): # really weird...! 1276 self.Entropy.clientLog.log( 1277 ETP_LOGPRI_INFO, 1278 ETP_LOGLEVEL_NORMAL, 1279 "WARNING!!! %s is a directory when it should be a file !! Removing in 20 seconds..." % (tofile,) 1280 ) 1281 mytxt = _("%s is a directory when it should be a file !! Removing in 20 seconds...") % (tofile,) 1282 self.Entropy.updateProgress( 1283 red("QA: ")+darkred(mytxt), 1284 importance = 1, 1285 type = "warning", 1286 header = red(" !!! ") 1287 ) 1288 self.entropyTools.ebeep(10) 1289 time.sleep(20) 1290 try: 1291 shutil.rmtree(tofile, True) 1292 os.rmdir(tofile) 1293 except: 1294 pass 1295 try: # if it was a link 1296 os.remove(tofile) 1297 except OSError: 1298 pass 1299 1300 # XXX 1301 # XXX moving file using the raw format like portage does 1302 # XXX 1303 done = movefile(fromfile, tofile, src_basedir = encoded_imageDir) 1304 if not done: 1305 self.Entropy.clientLog.log( 1306 ETP_LOGPRI_INFO, 1307 ETP_LOGLEVEL_NORMAL, 1308 "WARNING!!! Error during file move to system: %s => %s" % (fromfile,tofile,) 1309 ) 1310 mytxt = "%s: %s => %s, %s" % (_("File move error"),fromfile,tofile,_("please report"),) 1311 self.Entropy.updateProgress( 1312 red("QA: ")+darkred(mytxt), 1313 importance = 1, 1314 type = "warning", 1315 header = red(" !!! ") 1316 ) 1317 return 4 1318 1319 except IOError, e: 1320 # try to move forward, sometimes packages might be 1321 # fucked up and contain broken things 1322 if e.errno != 2: raise 1323 1324 items_installed.add(os.path.join(os.path.realpath(os.path.dirname(tofile)),os.path.basename(tofile))) 1325 if protected: 1326 # add to disk cache 1327 self.Entropy.FileUpdates.add_to_cache(tofile, quiet = True) 1328 1329 # this is useful to avoid the removal of installed files by __remove_package just because 1330 # there's a difference in the directory path, perhaps, which is not handled correctly by 1331 # LocalRepository.contentDiff for obvious reasons (think about stuff in /usr/lib and /usr/lib64, 1332 # where the latter is just a symlink to the former) 1333 if self.infoDict.get('removecontent'): 1334 my_remove_content = set([x for x in self.infoDict['removecontent'] \ 1335 if os.path.join(os.path.realpath( 1336 os.path.dirname("%s%s" % (sys_root,x,))),os.path.basename(x) 1337 ) in items_installed]) 1338 self.infoDict['removecontent'] -= my_remove_content 1339 1340 return 0
1341
1342 - def _handle_config_protect(self, protect, mask, fromfile, tofile, 1343 do_allocation_check = True, do_quiet = False):
1344 1345 protected = False 1346 tofile_before_protect = tofile 1347 do_continue = False 1348 in_mask = False 1349 1350 try: 1351 encoded_protect = [x.encode('raw_unicode_escape') for x in protect] 1352 if tofile in encoded_protect: 1353 protected = True 1354 in_mask = True 1355 elif os.path.dirname(tofile) in encoded_protect: 1356 protected = True 1357 in_mask = True 1358 else: 1359 tofile_testdir = os.path.dirname(tofile) 1360 old_tofile_testdir = None 1361 while tofile_testdir != old_tofile_testdir: 1362 if tofile_testdir in encoded_protect: 1363 protected = True 1364 in_mask = True 1365 break 1366 old_tofile_testdir = tofile_testdir 1367 tofile_testdir = os.path.dirname(tofile_testdir) 1368 1369 if protected: # check if perhaps, file is masked, so unprotected 1370 newmask = [x.encode('raw_unicode_escape') for x in mask] 1371 if tofile in newmask: 1372 protected = False 1373 in_mask = False 1374 elif os.path.dirname(tofile) in newmask: 1375 protected = False 1376 in_mask = False 1377 else: 1378 tofile_testdir = os.path.dirname(tofile) 1379 old_tofile_testdir = None 1380 while tofile_testdir != old_tofile_testdir: 1381 if tofile_testdir in newmask: 1382 protected = False 1383 in_mask = False 1384 break 1385 old_tofile_testdir = tofile_testdir 1386 tofile_testdir = os.path.dirname(tofile_testdir) 1387 1388 if not os.path.lexists(tofile): 1389 protected = False # file doesn't exist 1390 1391 # check if it's a text file 1392 if (protected) and os.path.isfile(tofile): 1393 protected = self.entropyTools.istextfile(tofile) 1394 in_mask = protected 1395 else: 1396 protected = False # it's not a file 1397 1398 # request new tofile then 1399 if protected: 1400 sys_set_plg_id = \ 1401 etpConst['system_settings_plugins_ids']['client_plugin'] 1402 client_settings = self.Entropy.SystemSettings[sys_set_plg_id]['misc'] 1403 if tofile not in client_settings['configprotectskip']: 1404 prot_status = True 1405 if do_allocation_check: 1406 tofile, prot_status = self.entropyTools.allocate_masked_file(tofile, fromfile) 1407 if not prot_status: 1408 protected = False 1409 else: 1410 oldtofile = tofile 1411 if oldtofile.find("._cfg") != -1: 1412 oldtofile = os.path.join(os.path.dirname(oldtofile), 1413 os.path.basename(oldtofile)[10:]) 1414 if not do_quiet: 1415 self.Entropy.clientLog.log( 1416 ETP_LOGPRI_INFO, 1417 ETP_LOGLEVEL_NORMAL, 1418 "Protecting config file: %s" % (oldtofile,) 1419 ) 1420 mytxt = red("%s: %s") % (_("Protecting config file"),oldtofile,) 1421 self.Entropy.updateProgress( 1422 mytxt, 1423 importance = 1, 1424 type = "warning", 1425 header = darkred(" ## ") 1426 ) 1427 else: 1428 if not do_quiet: 1429 self.Entropy.clientLog.log( 1430 ETP_LOGPRI_INFO, 1431 ETP_LOGLEVEL_NORMAL, 1432 "Skipping config file installation/removal, as stated in client.conf: %s" % (tofile,) 1433 ) 1434 mytxt = "%s: %s" % (_("Skipping file installation/removal"),tofile,) 1435 self.Entropy.updateProgress( 1436 mytxt, 1437 importance = 1, 1438 type = "warning", 1439 header = darkred(" ## ") 1440 ) 1441 do_continue = True 1442 1443 except Exception, e: 1444 self.entropyTools.print_traceback() 1445 protected = False # safely revert to false 1446 tofile = tofile_before_protect 1447 mytxt = darkred("%s: %s") % (_("Cannot check CONFIG PROTECTION. Error"),e,) 1448 self.Entropy.updateProgress( 1449 red("QA: ")+mytxt, 1450 importance = 1, 1451 type = "warning", 1452 header = darkred(" ## ") 1453 ) 1454 1455 return in_mask, protected, tofile, do_continue
1456 1457
1458 - def _handle_install_collision_protect(self, tofile, todbfile):
1459 avail = self.Entropy.clientDbconn.isFileAvailable(todbfile, get_id = True) 1460 if (self.infoDict['removeidpackage'] not in avail) and avail: 1461 mytxt = darkred(_("Collision found during install for")) 1462 mytxt += " %s - %s" % (blue(tofile),darkred(_("cannot overwrite")),) 1463 self.Entropy.updateProgress( 1464 red("QA: ")+mytxt, 1465 importance = 1, 1466 type = "warning", 1467 header = darkred(" ## ") 1468 ) 1469 self.Entropy.clientLog.log( 1470 ETP_LOGPRI_INFO, 1471 ETP_LOGLEVEL_NORMAL, 1472 "WARNING!!! Collision found during install for %s - cannot overwrite" % (tofile,) 1473 ) 1474 return False 1475 return True
1476
1477 - def sources_fetch_step(self):
1478 self.error_on_not_prepared() 1479 down_data = self.infoDict['download'] 1480 down_keys = down_data.keys() 1481 d_cache = set() 1482 rc = 0 1483 key_cache = [os.path.basename(x) for x in down_keys] 1484 for key in sorted(down_keys): 1485 key_name = os.path.basename(key) 1486 if key_name in d_cache: continue 1487 # first fine wins 1488 for url in down_data[key]: 1489 file_name = os.path.basename(url) 1490 if self.infoDict.get('fetch_path'): 1491 dest_file = os.path.join(self.infoDict['fetch_path'], 1492 file_name) 1493 else: 1494 dest_file = os.path.join(self.infoDict['unpackdir'], 1495 file_name) 1496 rc = self._fetch_source(url, dest_file) 1497 if rc == 0: 1498 d_cache.add(key_name) 1499 break 1500 key_cache.remove(key_name) 1501 if rc != 0 and key_name not in key_cache: 1502 break 1503 rc = 0 1504 1505 return rc
1506
1507 - def _fetch_source(self, url, dest_file):
1508 rc = 1 1509 try: 1510 mytxt = "%s: %s" % (blue(_("Downloading")),brown(url),) 1511 # now fetch the new one 1512 self.Entropy.updateProgress( 1513 mytxt, 1514 importance = 1, 1515 type = "info", 1516 header = red(" ## ") 1517 ) 1518 1519 rc, data_transfer, resumed = self.Entropy.fetch_file( 1520 url, 1521 None, 1522 None, 1523 False, 1524 fetch_file_abort_function = self.fetch_abort_function, 1525 filepath = dest_file 1526 ) 1527 if rc == 0: 1528 mytxt = blue("%s: ") % (_("Successfully downloaded from"),) 1529 mytxt += red(self.entropyTools.spliturl(url)[1]) 1530 mytxt += " %s %s/%s" % (_("at"),self.entropyTools.bytes_into_human(data_transfer),_("second"),) 1531 self.Entropy.updateProgress( 1532 mytxt, 1533 importance = 1, 1534 type = "info", 1535 header = red(" ## ") 1536 ) 1537 self.Entropy.updateProgress( 1538 "%s: %s" % (blue(_("Local path")),brown(dest_file),), 1539 importance = 1, 1540 type = "info", 1541 header = red(" # ") 1542 ) 1543 else: 1544 error_message = blue("%s: %s") % ( 1545 _("Error downloading from"), 1546 red(self.entropyTools.spliturl(url)[1]), 1547 ) 1548 # something bad happened 1549 if rc == -1: 1550 error_message += " - %s." % (_("file not available on this mirror"),) 1551 elif rc == -3: 1552 error_message += " - not found." 1553 elif rc == -100: 1554 error_message += " - %s." % (_("discarded download"),) 1555 else: 1556 error_message += " - %s: %s" % (_("unknown reason"),rc,) 1557 self.Entropy.updateProgress( 1558 error_message, 1559 importance = 1, 1560 type = "warning", 1561 header = red(" ## ") 1562 ) 1563 except KeyboardInterrupt: 1564 pass 1565 return rc
1566
1567 - def fetch_step(self):
1568 self.error_on_not_prepared() 1569 mytxt = "%s: %s" % (blue(_("Downloading archive")), 1570 red(os.path.basename(self.infoDict['download'])),) 1571 self.Entropy.updateProgress( 1572 mytxt, 1573 importance = 1, 1574 type = "info", 1575 header = red(" ## ") 1576 ) 1577 1578 rc = 0 1579 if not self.infoDict['verified']: 1580 rc = self.Entropy.fetch_file_on_mirrors( 1581 self.infoDict['repository'], 1582 self.Entropy.get_branch_from_download_relative_uri(self.infoDict['download']), 1583 self.infoDict['download'], 1584 self.infoDict['checksum'], 1585 fetch_abort_function = self.fetch_abort_function 1586 ) 1587 if rc != 0: 1588 mytxt = "%s. %s: %s" % ( 1589 red(_("Package cannot be fetched. Try to update repositories and retry")), 1590 blue(_("Error")), 1591 rc, 1592 ) 1593 self.Entropy.updateProgress( 1594 mytxt, 1595 importance = 1, 1596 type = "error", 1597 header = darkred(" ## ") 1598 ) 1599 return rc
1600
1601 - def multi_fetch_step(self):
1602 self.error_on_not_prepared() 1603 m_fetch_len = len(self.infoDict['multi_fetch_list']) 1604 mytxt = "%s: %s %s" % (blue(_("Downloading")),darkred(str(m_fetch_len)),_("archives"),) 1605 self.Entropy.updateProgress( 1606 mytxt, 1607 importance = 1, 1608 type = "info", 1609 header = red(" ## ") 1610 ) 1611 # fetch_files_on_mirrors(self, download_list, checksum = False, fetch_abort_function = None) 1612 rc, err_list = self.Entropy.fetch_files_on_mirrors( 1613 self.infoDict['multi_fetch_list'], 1614 self.infoDict['checksum'], 1615 fetch_abort_function = self.fetch_abort_function 1616 ) 1617 if rc != 0: 1618 mytxt = "%s. %s: %s" % ( 1619 red(_("Some packages cannot be fetched. Try to update repositories and retry")), 1620 blue(_("Error")), 1621 rc, 1622 ) 1623 self.Entropy.updateProgress( 1624 mytxt, 1625 importance = 1, 1626 type = "error", 1627 header = darkred(" ## ") 1628 ) 1629 for repo, branch, fname, cksum, signatures in err_list: 1630 self.Entropy.updateProgress( 1631 "[%s:%s|%s] %s" % (blue(repo),brown(branch), 1632 darkgreen(cksum),darkred(fname),), 1633 importance = 1, 1634 type = "error", 1635 header = darkred(" # ") 1636 ) 1637 return rc
1638
1639 - def fetch_not_available_step(self):
1640 self.Entropy.updateProgress( 1641 blue(_("Fetch for the chosen package is not available, unknown error.")), 1642 importance = 1, 1643 type = "info", 1644 header = red(" ## ") 1645 ) 1646 return 0
1647
1648 - def vanished_step(self):
1649 self.Entropy.updateProgress( 1650 blue(_("Installed package in queue vanished, skipping.")), 1651 importance = 1, 1652 type = "info", 1653 header = red(" ## ") 1654 ) 1655 return 0
1656
1657 - def checksum_step(self):
1658 self.error_on_not_prepared() 1659 return self.match_checksum()
1660
1661 - def multi_checksum_step(self):
1662 self.error_on_not_prepared() 1663 return self.multi_match_checksum()
1664
1665 - def unpack_step(self):
1666 self.error_on_not_prepared() 1667 1668 if not self.infoDict['merge_from']: 1669 mytxt = "%s: %s" % (blue(_("Unpacking package")),red(os.path.basename(self.infoDict['download'])),) 1670 self.Entropy.updateProgress( 1671 mytxt, 1672 importance = 1, 1673 type = "info", 1674 header = red(" ## ") 1675 ) 1676 else: 1677 mytxt = "%s: %s" % (blue(_("Merging package")),red(os.path.basename(self.infoDict['atom'])),) 1678 self.Entropy.updateProgress( 1679 mytxt, 1680 importance = 1, 1681 type = "info", 1682 header = red(" ## ") 1683 ) 1684 rc = self.__unpack_package() 1685 if rc != 0: 1686 if rc == 512: 1687 errormsg = "%s. %s. %s: 512" % ( 1688 red(_("You are running out of disk space")), 1689 red(_("I bet, you're probably Michele")), 1690 blue(_("Error")), 1691 ) 1692 else: 1693 errormsg = "%s. %s. %s: %s" % ( 1694 red(_("An error occured while trying to unpack the package")), 1695 red(_("Check if your system is healthy")), 1696 blue(_("Error")), 1697 rc, 1698 ) 1699 self.Entropy.updateProgress( 1700 errormsg, 1701 importance = 1, 1702 type = "error", 1703 header = red(" ## ") 1704 ) 1705 return rc
1706
1707 - def install_step(self):
1708 self.error_on_not_prepared() 1709 mytxt = "%s: %s" % (blue(_("Installing package")),red(self.infoDict['atom']),) 1710 self.Entropy.updateProgress( 1711 mytxt, 1712 importance = 1, 1713 type = "info", 1714 header = red(" ## ") 1715 ) 1716 rc = self.__install_package() 1717 if rc != 0: 1718 mytxt = "%s. %s. %s: %s" % ( 1719 red(_("An error occured while trying to install the package")), 1720 red(_("Check if your system is healthy")), 1721 blue(_("Error")), 1722 rc, 1723 ) 1724 self.Entropy.updateProgress( 1725 mytxt, 1726 importance = 1, 1727 type = "error", 1728 header = red(" ## ") 1729 ) 1730 return rc
1731
1732 - def remove_step(self):
1733 self.error_on_not_prepared() 1734 mytxt = "%s: %s" % (blue(_("Removing data")),red(self.infoDict['removeatom']),) 1735 self.Entropy.updateProgress( 1736 mytxt, 1737 importance = 1, 1738 type = "info", 1739 header = red(" ## ") 1740 ) 1741 rc = self.__remove_package() 1742 if rc != 0: 1743 mytxt = "%s. %s. %s: %s" % ( 1744 red(_("An error occured while trying to remove the package")), 1745 red(_("Check if you have enough disk space on your hard disk")), 1746 blue(_("Error")), 1747 rc, 1748 ) 1749 self.Entropy.updateProgress( 1750 mytxt, 1751 importance = 1, 1752 type = "error", 1753 header = red(" ## ") 1754 ) 1755 return rc
1756
1757 - def cleanup_step(self):
1758 self.error_on_not_prepared() 1759 mytxt = "%s: %s" % (blue(_("Cleaning")),red(self.infoDict['atom']),) 1760 self.Entropy.updateProgress( 1761 mytxt, 1762 importance = 1, 1763 type = "info", 1764 header = red(" ## ") 1765 ) 1766 self._cleanup_package(self.infoDict['unpackdir']) 1767 # we don't care if cleanupPackage fails since it's not critical 1768 return 0
1769
1770 - def logmessages_step(self):
1771 for msg in self.infoDict['messages']: 1772 self.Entropy.clientLog.write(">>> "+msg) 1773 return 0
1774
1775 - def postinstall_step(self):
1776 self.error_on_not_prepared() 1777 pkgdata = self.infoDict['triggers'].get('install') 1778 if pkgdata: 1779 trigger = self.Entropy.Triggers('postinstall',pkgdata, self.action) 1780 do = trigger.prepare() 1781 if do: 1782 trigger.run() 1783 trigger.kill() 1784 del pkgdata 1785 return 0
1786
1787 - def preinstall_step(self):
1788 self.error_on_not_prepared() 1789 pkgdata = self.infoDict['triggers'].get('install') 1790 if pkgdata: 1791 1792 trigger = self.Entropy.Triggers('preinstall',pkgdata, self.action) 1793 do = trigger.prepare() 1794 if self.infoDict.get("diffremoval") and do: 1795 # diffremoval is true only when the 1796 # removal is triggered by a package install 1797 remdata = self.infoDict['triggers'].get('remove') 1798 if remdata: 1799 r_trigger = self.Entropy.Triggers('preremove',remdata, self.action) 1800 r_trigger.prepare() 1801 r_trigger.triggers = [x for x in trigger.triggers if x not in r_trigger.triggers] 1802 r_trigger.kill() 1803 del remdata 1804 if do: 1805 trigger.run() 1806 trigger.kill() 1807 1808 del pkgdata 1809 return 0
1810
1811 - def preremove_step(self):
1812 self.error_on_not_prepared() 1813 remdata = self.infoDict['triggers'].get('remove') 1814 if remdata: 1815 trigger = self.Entropy.Triggers('preremove',remdata, self.action) 1816 do = trigger.prepare() 1817 if do: 1818 trigger.run() 1819 trigger.kill() 1820 del remdata 1821 return 0
1822
1823 - def postremove_step(self):
1824 self.error_on_not_prepared() 1825 remdata = self.infoDict['triggers'].get('remove') 1826 if remdata: 1827 1828 trigger = self.Entropy.Triggers('postremove',remdata, self.action) 1829 do = trigger.prepare() 1830 if self.infoDict['diffremoval'] and (self.infoDict.get("atom") != None) and do: 1831 # diffremoval is true only when the remove action is triggered by installPackages() 1832 pkgdata = self.infoDict['triggers'].get('install') 1833 if pkgdata: 1834 i_trigger = self.Entropy.Triggers('postinstall',pkgdata, self.action) 1835 i_trigger.prepare() 1836 i_trigger.triggers = [x for x in trigger.triggers if x not in i_trigger.triggers] 1837 i_trigger.kill() 1838 del pkgdata 1839 if do: 1840 trigger.run() 1841 trigger.kill() 1842 1843 del remdata 1844 return 0
1845
1846 - def removeconflict_step(self):
1847 self.error_on_not_prepared() 1848 for idpackage in self.infoDict['conflicts']: 1849 if not self.Entropy.clientDbconn.isIDPackageAvailable(idpackage): 1850 continue 1851 pkg = self.Entropy.Package() 1852 pkg.prepare((idpackage,),"remove_conflict", self.infoDict['remove_metaopts']) 1853 rc = pkg.run(xterm_header = self.xterm_title) 1854 pkg.kill() 1855 if rc != 0: 1856 return rc 1857 1858 return 0
1859
1860 - def config_step(self):
1861 self.error_on_not_prepared() 1862 mytxt = "%s: %s" % (blue(_("Configuring package")),red(self.infoDict['atom']),) 1863 self.Entropy.updateProgress( 1864 mytxt, 1865 importance = 1, 1866 type = "info", 1867 header = red(" ## ") 1868 ) 1869 rc = self.__configure_package() 1870 if rc == 1: 1871 mytxt = "%s. %s. %s: %s" % ( 1872 red(_("An error occured while trying to configure the package")), 1873 red(_("Make sure that your system is healthy")), 1874 blue(_("Error")), 1875 rc, 1876 ) 1877 self.Entropy.updateProgress( 1878 mytxt, 1879 importance = 1, 1880 type = "error", 1881 header = red(" ## ") 1882 ) 1883 elif rc == 2: 1884 mytxt = "%s. %s. %s: %s" % ( 1885 red(_("An error occured while trying to configure the package")), 1886 red(_("It seems that the Source Package Manager entry is missing")), 1887 blue(_("Error")), 1888 rc, 1889 ) 1890 self.Entropy.updateProgress( 1891 mytxt, 1892 importance = 1, 1893 type = "error", 1894 header = red(" ## ") 1895 ) 1896 return rc
1897
1898 - def run_stepper(self, xterm_header):
1899 if xterm_header == None: 1900 xterm_header = "" 1901 1902 if self.infoDict.has_key('remove_installed_vanished'): 1903 self.xterm_title += ' Installed package vanished' 1904 self.Entropy.setTitle(self.xterm_title) 1905 rc = self.vanished_step() 1906 return rc 1907 1908 if self.infoDict.has_key('fetch_not_available'): 1909 self.xterm_title += ' Fetch not available' 1910 self.Entropy.setTitle(self.xterm_title) 1911 rc = self.fetch_not_available_step() 1912 return rc 1913 1914 def do_fetch(): 1915 self.xterm_title += ' %s: %s' % (_("Fetching"),os.path.basename(self.infoDict['download']),) 1916 self.Entropy.setTitle(self.xterm_title) 1917 return self.fetch_step()
1918 1919 def do_multi_fetch(): 1920 self.xterm_title += ' %s: %s %s' % (_("Multi Fetching"), 1921 len(self.infoDict['multi_fetch_list']),_("packages"),) 1922 self.Entropy.setTitle(self.xterm_title) 1923 return self.multi_fetch_step() 1924 1925 def do_sources_fetch(): 1926 self.xterm_title += ' %s: %s' % (_("Fetching sources"),os.path.basename(self.infoDict['atom']),) 1927 self.Entropy.setTitle(self.xterm_title) 1928 return self.sources_fetch_step() 1929 1930 def do_checksum(): 1931 self.xterm_title += ' %s: %s' % (_("Verifying"),os.path.basename(self.infoDict['download']),) 1932 self.Entropy.setTitle(self.xterm_title) 1933 return self.checksum_step() 1934 1935 def do_multi_checksum(): 1936 self.xterm_title += ' %s: %s %s' % (_("Multi Verification"), 1937 len(self.infoDict['multi_checksum_list']),_("packages"),) 1938 self.Entropy.setTitle(self.xterm_title) 1939 return self.multi_checksum_step() 1940 1941 def do_unpack(): 1942 if not self.infoDict['merge_from']: 1943 mytxt = _("Unpacking") 1944 self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['download']),) 1945 else: 1946 mytxt = _("Merging") 1947 self.xterm_title += ' %s: %s' % (mytxt,os.path.basename(self.infoDict['atom']),) 1948 self.Entropy.setTitle(self.xterm_title) 1949 return self.unpack_step() 1950 1951 def do_remove_conflicts(): 1952 return self.removeconflict_step() 1953 1954 def do_install(): 1955 self.xterm_title += ' %s: %s' % (_("Installing"),self.infoDict['atom'],) 1956 self.Entropy.setTitle(self.xterm_title) 1957 return self.install_step() 1958 1959 def do_remove(): 1960 self.xterm_title += ' %s: %s' % (_("Removing"),self.infoDict['removeatom'],) 1961 self.Entropy.setTitle(self.xterm_title) 1962 return self.remove_step() 1963 1964 def do_logmessages(): 1965 return self.logmessages_step() 1966 1967 def do_cleanup(): 1968 self.xterm_title += ' %s: %s' % (_("Cleaning"),self.infoDict['atom'],) 1969 self.Entropy.setTitle(self.xterm_title) 1970 return self.cleanup_step() 1971 1972 def do_postinstall(): 1973 self.xterm_title += ' %s: %s' % (_("Postinstall"),self.infoDict['atom'],) 1974 self.Entropy.setTitle(self.xterm_title) 1975 return self.postinstall_step() 1976 1977 def do_preinstall(): 1978 self.xterm_title += ' %s: %s' % (_("Preinstall"),self.infoDict['atom'],) 1979 self.Entropy.setTitle(self.xterm_title) 1980 return self.preinstall_step() 1981 1982 def do_preremove(): 1983 self.xterm_title += ' %s: %s' % (_("Preremove"),self.infoDict['removeatom'],) 1984 self.Entropy.setTitle(self.xterm_title) 1985 return self.preremove_step() 1986 1987 def do_postremove(): 1988 self.xterm_title += ' %s: %s' % (_("Postremove"),self.infoDict['removeatom'],) 1989 self.Entropy.setTitle(self.xterm_title) 1990 return self.postremove_step() 1991 1992 def do_config(): 1993 self.xterm_title += ' %s: %s' % (_("Configuring"),self.infoDict['atom'],) 1994 self.Entropy.setTitle(self.xterm_title) 1995 return self.config_step() 1996 1997 steps_data = { 1998 "fetch": do_fetch, 1999 "multi_fetch": do_multi_fetch, 2000 "multi_checksum": do_multi_checksum, 2001 "sources_fetch": do_sources_fetch, 2002 "checksum": do_checksum, 2003 "unpack": do_unpack, 2004 "remove_conflicts": do_remove_conflicts, 2005 "install": do_install, 2006 "remove": do_remove, 2007 "logmessages": do_logmessages, 2008 "cleanup": do_cleanup, 2009 "postinstall": do_postinstall, 2010 "preinstall": do_preinstall, 2011 "postremove": do_postremove, 2012 "preremove": do_preremove, 2013 "config": do_config, 2014 } 2015 2016 rc = 0 2017 for step in self.infoDict['steps']: 2018 self.xterm_title = xterm_header 2019 rc = steps_data.get(step)() 2020 if rc != 0: break 2021 return rc 2022 2023 2024 ''' 2025 @description: execute the requested steps 2026 @input xterm_header: purely optional 2027 '''
2028 - def run(self, xterm_header = None):
2029 self.error_on_not_prepared() 2030 2031 gave_up = self.Entropy.lock_check(self.Entropy.resources_check_lock) 2032 if gave_up: 2033 return 20 2034 2035 locked = self.Entropy.application_lock_check() 2036 if locked: 2037 self.Entropy.resources_remove_lock() 2038 return 21 2039 2040 # lock 2041 self.Entropy.resources_create_lock() 2042 2043 try: 2044 rc = self.run_stepper(xterm_header) 2045 except: 2046 self.Entropy.resources_remove_lock() 2047 raise 2048 2049 # remove lock 2050 self.Entropy.resources_remove_lock() 2051 2052 if rc != 0: 2053 self.Entropy.updateProgress( 2054 blue(_("An error occured. Action aborted.")), 2055 importance = 2, 2056 type = "error", 2057 header = darkred(" ## ") 2058 ) 2059 return rc
2060 2061 ''' 2062 Install/Removal process preparation function 2063 - will generate all the metadata needed to run the action steps, creating infoDict automatically 2064 @input matched_atom(tuple): is what is returned by EquoInstance.atom_match: 2065 (idpackage,repoid): 2066 (2000,u'sabayonlinux.org') 2067 NOTE: in case of remove action, matched_atom must be: 2068 (idpackage,) 2069 NOTE: in case of multi_fetch, matched_atom can be a list of matches 2070 @input action(string): is an action to take, which must be one in self.valid_actions 2071 '''
2072 - def prepare(self, matched_atom, action, metaopts = {}):
2073 2074 self.error_on_prepared() 2075 self.check_action_validity(action) 2076 2077 self.action = action 2078 self.matched_atom = matched_atom 2079 self.metaopts = metaopts 2080 # generate metadata dictionary 2081 self.generate_metadata()
2082
2083 - def generate_metadata(self):
2084 self.error_on_prepared() 2085 self.check_action_validity(self.action) 2086 2087 if self.action == "fetch": 2088 self.__generate_fetch_metadata() 2089 elif self.action == "multi_fetch": 2090 self.__generate_multi_fetch_metadata() 2091 elif self.action in ("remove","remove_conflict"): 2092 self.__generate_remove_metadata() 2093 elif self.action == "install": 2094 self.__generate_install_metadata() 2095 elif self.action == "source": 2096 self.__generate_fetch_metadata(sources = True) 2097 elif self.action == "config": 2098 self.__generate_config_metadata() 2099 self.prepared = True
2100
2101 - def __generate_remove_metadata(self):
2102 self.infoDict.clear() 2103 idpackage = self.matched_atom[0] 2104 2105 if not self.Entropy.clientDbconn.isIDPackageAvailable(idpackage): 2106 self.infoDict['remove_installed_vanished'] = True 2107 return 0 2108 2109 self.infoDict['idpackage'] = idpackage 2110 self.infoDict['configprotect_data'] = [] 2111 self.infoDict['triggers'] = {} 2112 self.infoDict['removeatom'] = self.Entropy.clientDbconn.retrieveAtom(idpackage) 2113 self.infoDict['slot'] = self.Entropy.clientDbconn.retrieveSlot(idpackage) 2114 self.infoDict['versiontag'] = self.Entropy.clientDbconn.retrieveVersionTag(idpackage) 2115 self.infoDict['removeidpackage'] = idpackage 2116 self.infoDict['diffremoval'] = False 2117 removeConfig = False 2118 if self.metaopts.has_key('removeconfig'): 2119 removeConfig = self.metaopts.get('removeconfig') 2120 self.infoDict['removeconfig'] = removeConfig 2121 self.infoDict['removecontent'] = self.Entropy.clientDbconn.retrieveContent(idpackage) 2122 self.infoDict['triggers']['remove'] = self.Entropy.clientDbconn.getTriggerInfo(idpackage) 2123 self.infoDict['triggers']['remove']['removecontent'] = self.infoDict['removecontent'] 2124 self.infoDict['steps'] = [] 2125 self.infoDict['steps'].append("preremove") 2126 self.infoDict['steps'].append("remove") 2127 self.infoDict['steps'].append("postremove") 2128 2129 return 0
2130
2131 - def __generate_config_metadata(self):
2132 self.infoDict.clear() 2133 idpackage = self.matched_atom[0] 2134 2135 self.infoDict['atom'] = self.Entropy.clientDbconn.retrieveAtom(idpackage) 2136 key, slot = self.Entropy.clientDbconn.retrieveKeySlot(idpackage) 2137 self.infoDict['key'], self.infoDict['slot'] = key, slot 2138 self.infoDict['version'] = self.Entropy.clientDbconn.retrieveVersion(idpackage) 2139 self.infoDict['steps'] = [] 2140 self.infoDict['steps'].append("config") 2141 2142 return 0
2143
2144 - def __generate_install_metadata(self):
2145 self.infoDict.clear() 2146 2147 idpackage, repository = self.matched_atom 2148 self.infoDict['idpackage'] = idpackage 2149 self.infoDict['repository'] = repository 2150 2151 # fetch abort function 2152 if self.metaopts.has_key('fetch_abort_function'): 2153 self.fetch_abort_function = self.metaopts.pop('fetch_abort_function') 2154 2155 install_source = etpConst['install_sources']['unknown'] 2156 meta_inst_source = self.metaopts.get('install_source', install_source) 2157 if meta_inst_source in etpConst['install_sources'].values(): 2158 install_source = meta_inst_source 2159 self.infoDict['install_source'] = install_source 2160 2161 self.infoDict['configprotect_data'] = [] 2162 dbconn = self.Entropy.open_repository(repository) 2163 self.infoDict['triggers'] = {} 2164 self.infoDict['atom'] = dbconn.retrieveAtom(idpackage) 2165 self.infoDict['slot'] = dbconn.retrieveSlot(idpackage) 2166 self.infoDict['version'], self.infoDict['versiontag'], self.infoDict['revision'] = dbconn.getVersioningData(idpackage) 2167 self.infoDict['category'] = dbconn.retrieveCategory(idpackage) 2168 self.infoDict['download'] = dbconn.retrieveDownloadURL(idpackage) 2169 self.infoDict['name'] = dbconn.retrieveName(idpackage) 2170 self.infoDict['messages'] = dbconn.retrieveMessages(idpackage) 2171 self.infoDict['checksum'] = dbconn.retrieveDigest(idpackage) 2172 self.infoDict['signatures'] = dbconn.retrieveSignatures(idpackage) 2173 self.infoDict['accept_license'] = dbconn.retrieveLicensedataKeys(idpackage) 2174 self.infoDict['conflicts'] = self.Entropy.get_match_conflicts(self.matched_atom) 2175 2176 # fill action queue 2177 self.infoDict['removeidpackage'] = -1 2178 removeConfig = False 2179 if self.metaopts.has_key('removeconfig'): 2180 removeConfig = self.metaopts.get('removeconfig') 2181 2182 self.infoDict['remove_metaopts'] = { 2183 'removeconfig': True, 2184 } 2185 if self.metaopts.has_key('remove_metaopts'): 2186 self.infoDict['remove_metaopts'] = self.metaopts.get('remove_metaopts') 2187 2188 self.infoDict['merge_from'] = None 2189 mf = self.metaopts.get('merge_from') 2190 if mf != None: 2191 self.infoDict['merge_from'] = unicode(mf) 2192 self.infoDict['removeconfig'] = removeConfig 2193 2194 pkgkey = self.entropyTools.dep_getkey(self.infoDict['atom']) 2195 inst_match = self.Entropy.clientDbconn.atomMatch(pkgkey, matchSlot = self.infoDict['slot']) 2196 inst_idpackage = -1 2197 if inst_match[1] == 0: inst_idpackage = inst_match[0] 2198 self.infoDict['removeidpackage'] = inst_idpackage 2199 2200 if self.infoDict['removeidpackage'] != -1: 2201 avail = self.Entropy.clientDbconn.isIDPackageAvailable(self.infoDict['removeidpackage']) 2202 if avail: 2203 self.infoDict['removeatom'] = self.Entropy.clientDbconn.retrieveAtom(self.infoDict['removeidpackage']) 2204 else: 2205 self.infoDict['removeidpackage'] = -1 2206 2207 # smartpackage ? 2208 self.infoDict['smartpackage'] = False 2209 # set unpack dir and image dir 2210 if self.infoDict['repository'].endswith(etpConst['packagesext']): 2211 # do arch check 2212 compiled_arch = dbconn.retrieveDownloadURL(idpackage) 2213 if compiled_arch.find("/"+etpSys['arch']+"/") == -1: 2214 self.infoDict.clear() 2215 self.prepared = False 2216 return -1 2217 self.infoDict['smartpackage'] = self.Entropy.SystemSettings['repositories']['available'][self.infoDict['repository']]['smartpackage'] 2218 self.infoDict['pkgpath'] = self.Entropy.SystemSettings['repositories']['available'][self.infoDict['repository']]['pkgpath'] 2219 else: 2220 self.infoDict['pkgpath'] = etpConst['entropyworkdir']+"/"+self.infoDict['download'] 2221 self.infoDict['unpackdir'] = etpConst['entropyunpackdir']+"/"+self.infoDict['download'] 2222 self.infoDict['imagedir'] = etpConst['entropyunpackdir']+"/"+self.infoDict['download']+"/"+etpConst['entropyimagerelativepath'] 2223 2224 self.infoDict['pkgdbpath'] = os.path.join(self.infoDict['unpackdir'], 2225 "edb/pkg.db") 2226 2227 # spm xpak data 2228 self.infoDict['xpakpath'] = etpConst['entropyunpackdir'] + "/" + \ 2229 self.infoDict['download'] + "/" + \ 2230 etpConst['entropyxpakrelativepath'] 2231 if not self.infoDict['merge_from']: 2232 self.infoDict['xpakstatus'] = None 2233 self.infoDict['xpakdir'] = self.infoDict['xpakpath'] + "/" + \ 2234 etpConst['entropyxpakdatarelativepath'] 2235 else: 2236 self.infoDict['xpakstatus'] = True 2237 portdbdir = 'var/db/pkg' # XXX hard coded ? 2238 portdbdir = os.path.join(self.infoDict['merge_from'], portdbdir) 2239 portdbdir = os.path.join(portdbdir, self.infoDict['category']) 2240 portdbdir = os.path.join(portdbdir, self.infoDict['name'] + "-" + \ 2241 self.infoDict['version']) 2242 self.infoDict['xpakdir'] = portdbdir 2243 2244 # compare both versions and if they match, disable removeidpackage 2245 if self.infoDict['removeidpackage'] != -1: 2246 installedVer, installedTag, installedRev = self.Entropy.clientDbconn.getVersioningData(self.infoDict['removeidpackage']) 2247 pkgcmp = self.entropyTools.entropy_compare_versions( 2248 (self.infoDict['version'], self.infoDict['versiontag'], self.infoDict['revision'],), 2249 (installedVer, installedTag, installedRev,) 2250 ) 2251 if pkgcmp == 0: 2252 self.infoDict['removeidpackage'] = -1 2253 else: 2254 # differential remove list 2255 self.infoDict['diffremoval'] = True 2256 self.infoDict['removeatom'] = self.Entropy.clientDbconn.retrieveAtom(self.infoDict['removeidpackage']) 2257 self.infoDict['removecontent'] = self.Entropy.clientDbconn.contentDiff( 2258 self.infoDict['removeidpackage'], 2259 dbconn, 2260 idpackage 2261 ) 2262 self.infoDict['triggers']['remove'] = self.Entropy.clientDbconn.getTriggerInfo( 2263 self.infoDict['removeidpackage'] 2264 ) 2265 self.infoDict['triggers']['remove']['removecontent'] = self.infoDict['removecontent'] 2266 2267 # set steps 2268 self.infoDict['steps'] = [] 2269 if self.infoDict['conflicts']: 2270 self.infoDict['steps'].append("remove_conflicts") 2271 # install 2272 self.infoDict['steps'].append("unpack") 2273 # preinstall placed before preremove in order 2274 # to respect Spm order 2275 self.infoDict['steps'].append("preinstall") 2276 if (self.infoDict['removeidpackage'] != -1): 2277 self.infoDict['steps'].append("preremove") 2278 self.infoDict['steps'].append("install") 2279 if (self.infoDict['removeidpackage'] != -1): 2280 self.infoDict['steps'].append("postremove") 2281 self.infoDict['steps'].append("postinstall") 2282 self.infoDict['steps'].append("logmessages") 2283 self.infoDict['steps'].append("cleanup") 2284 2285 self.infoDict['triggers']['install'] = dbconn.getTriggerInfo(idpackage) 2286 self.infoDict['triggers']['install']['accept_license'] = self.infoDict['accept_license'] 2287 self.infoDict['triggers']['install']['unpackdir'] = self.infoDict['unpackdir'] 2288 self.infoDict['triggers']['install']['imagedir'] = self.infoDict['imagedir'] 2289 self.infoDict['triggers']['install']['xpakdir'] = self.infoDict['xpakdir'] 2290 2291 return 0
2292
2293 - def __generate_fetch_metadata(self, sources = False):
2294 self.infoDict.clear() 2295 2296 idpackage, repository = self.matched_atom 2297 dochecksum = True 2298 2299 # fetch abort function 2300 if self.metaopts.has_key('fetch_abort_function'): 2301 self.fetch_abort_function = self.metaopts.pop('fetch_abort_function') 2302 2303 if self.metaopts.has_key('dochecksum'): 2304 dochecksum = self.metaopts.get('dochecksum') 2305 2306 # fetch_path is the path where data should be downloaded 2307 # at the moment is implemented only for sources = True 2308 if self.metaopts.has_key('fetch_path'): 2309 fetch_path = self.metaopts.get('fetch_path') 2310 if self.entropyTools.is_valid_path(fetch_path): 2311 self.infoDict['fetch_path'] = fetch_path 2312 2313 self.infoDict['repository'] = repository 2314 self.infoDict['idpackage'] = idpackage 2315 dbconn = self.Entropy.open_repository(repository) 2316 self.infoDict['atom'] = dbconn.retrieveAtom(idpackage) 2317 if sources: 2318 self.infoDict['download'] = dbconn.retrieveSources(idpackage, extended = True) 2319 else: 2320 self.infoDict['checksum'] = dbconn.retrieveDigest(idpackage) 2321 self.infoDict['signatures'] = dbconn.retrieveSignatures(idpackage) 2322 self.infoDict['download'] = dbconn.retrieveDownloadURL(idpackage) 2323 2324 if not self.infoDict['download']: 2325 self.infoDict['fetch_not_available'] = True 2326 return 0 2327 2328 self.infoDict['verified'] = False 2329 self.infoDict['steps'] = [] 2330 if not repository.endswith(etpConst['packagesext']) and not sources: 2331 if self.Entropy.check_needed_package_download(self.infoDict['download'], None) < 0: 2332 self.infoDict['steps'].append("fetch") 2333 if dochecksum: 2334 self.infoDict['steps'].append("checksum") 2335 elif sources: 2336 self.infoDict['steps'].append("sources_fetch") 2337 2338 if sources: 2339 # create sources destination directory 2340 unpack_dir = etpConst['entropyunpackdir']+"/sources/"+self.infoDict['atom'] 2341 self.infoDict['unpackdir'] = unpack_dir 2342 if not self.infoDict.get('fetch_path'): 2343 if os.path.lexists(unpack_dir): 2344 if os.path.isfile(unpack_dir): 2345 os.remove(unpack_dir) 2346 elif os.path.isdir(unpack_dir): 2347 shutil.rmtree(unpack_dir,True) 2348 if not os.path.lexists(unpack_dir): 2349 os.makedirs(unpack_dir,0775) 2350 const_setup_perms(unpack_dir,etpConst['entropygid']) 2351 2352 else: 2353 # if file exists, first checksum then fetch 2354 if os.path.isfile(os.path.join(etpConst['entropyworkdir'],self.infoDict['download'])): 2355 # check size first 2356 repo_size = dbconn.retrieveSize(idpackage) 2357 f = open(os.path.join(etpConst['entropyworkdir'],self.infoDict['download']),"r") 2358 f.seek(0,os.SEEK_END) 2359 disk_size = f.tell() 2360 f.close() 2361 if repo_size == disk_size: 2362 self.infoDict['steps'].reverse() 2363 return 0
2364
2365 - def __generate_multi_fetch_metadata(self):
2366 self.infoDict.clear() 2367 2368 if not isinstance(self.matched_atom,list): 2369 raise IncorrectParameter("IncorrectParameter: " 2370 "matched_atom must be a list of tuples, not %s" % (type(self.matched_atom,)) 2371 ) 2372 2373 dochecksum = True 2374 2375 # meta options 2376 if self.metaopts.has_key('fetch_abort_function'): 2377 self.fetch_abort_function = self.metaopts.pop('fetch_abort_function') 2378 if self.metaopts.has_key('dochecksum'): 2379 dochecksum = self.metaopts.get('dochecksum') 2380 self.infoDict['checksum'] = dochecksum 2381 2382 matches = self.matched_atom 2383 self.infoDict['matches'] = matches 2384 self.infoDict['atoms'] = [] 2385 self.infoDict['repository_atoms'] = {} 2386 temp_fetch_list = [] 2387 temp_checksum_list = [] 2388 temp_already_downloaded_count = 0 2389 etp_workdir = etpConst['entropyworkdir'] 2390 for idpackage, repository in matches: 2391 if repository.endswith(etpConst['packagesext']): continue 2392 2393 dbconn = self.Entropy.open_repository(repository) 2394 myatom = dbconn.retrieveAtom(idpackage) 2395 2396 # general purpose metadata 2397 self.infoDict['atoms'].append(myatom) 2398 if not self.infoDict['repository_atoms'].has_key(repository): 2399 self.infoDict['repository_atoms'][repository] = set() 2400 self.infoDict['repository_atoms'][repository].add(myatom) 2401 2402 download = dbconn.retrieveDownloadURL(idpackage) 2403 digest = dbconn.retrieveDigest(idpackage) 2404 signatures = dbconn.retrieveSignatures(idpackage) 2405 repo_size = dbconn.retrieveSize(idpackage) 2406 orig_branch = self.Entropy.get_branch_from_download_relative_uri(download) 2407 if self.Entropy.check_needed_package_download(download, None) < 0: 2408 temp_fetch_list.append((repository, orig_branch, download, digest, signatures,)) 2409 continue 2410 elif dochecksum: 2411 temp_checksum_list.append((repository, orig_branch, download, digest, signatures,)) 2412 down_path = os.path.join(etp_workdir,download) 2413 if os.path.isfile(down_path): 2414 with open(down_path,"r") as f: 2415 f.seek(0,os.SEEK_END) 2416 disk_size = f.tell() 2417 if repo_size == disk_size: 2418 temp_already_downloaded_count += 1 2419 2420 self.infoDict['steps'] = [] 2421 self.infoDict['multi_fetch_list'] = temp_fetch_list 2422 self.infoDict['multi_checksum_list'] = temp_checksum_list 2423 if self.infoDict['multi_fetch_list']: 2424 self.infoDict['steps'].append("multi_fetch") 2425 if self.infoDict['multi_checksum_list']: 2426 self.infoDict['steps'].append("multi_checksum") 2427 if temp_already_downloaded_count == len(temp_checksum_list): 2428 self.infoDict['steps'].reverse() 2429 2430 return 0
2431