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