Package entropy :: Package client :: Package interfaces :: Module methods

Source Code for Module entropy.client.interfaces.methods

   1  # -*- coding: utf-8 -*- 
   2  """ 
   3   
   4      @author: Fabio Erculiani <lxnay@sabayonlinux.org> 
   5      @contact: lxnay@sabayonlinux.org 
   6      @copyright: Fabio Erculiani 
   7      @license: GPL-2 
   8   
   9      B{Entropy Package Manager Client Miscellaneous functions Interface}. 
  10   
  11  """ 
  12  from __future__ import with_statement 
  13  import os 
  14  import stat 
  15  import fcntl 
  16  import errno 
  17  import sys 
  18  import shutil 
  19  import time 
  20  import subprocess 
  21  import tempfile 
  22  from entropy.i18n import _ 
  23  from entropy.const import * 
  24  from entropy.exceptions import * 
  25  from entropy.db import dbapi2, EntropyRepository, EntropyRepository 
  26  from entropy.output import purple, bold, red, blue, darkgreen, darkred, brown 
  27   
  28   
29 -class RepositoryMixin:
30 31 __repo_error_messages_cache = set() 32 __repodb_cache = {} 33 _memory_db_instances = {} 34
35 - def validate_repositories(self, quiet = False):
36 self.MirrorStatus.clear() 37 self.__repo_error_messages_cache.clear() 38 39 # clear live masking validation cache, if exists 40 cl_id = self.sys_settings_client_plugin_id 41 client_metadata = self.SystemSettings.get(cl_id, {}) 42 if "masking_validation" in client_metadata: 43 client_metadata['masking_validation']['cache'].clear() 44 45 # valid repositories 46 del self.validRepositories[:] 47 for repoid in self.SystemSettings['repositories']['order']: 48 # open database 49 try: 50 51 dbc = self.open_repository(repoid) 52 dbc.listConfigProtectEntries() 53 dbc.validateDatabase() 54 self.validRepositories.append(repoid) 55 56 except RepositoryError: 57 58 if quiet: 59 continue 60 61 t = _("Repository") + " " + repoid + " " + \ 62 _("is not available") + ". " + _("Cannot validate") 63 t2 = _("Please update your repositories now in order to remove this message!") 64 self.updateProgress( 65 darkred(t), 66 importance = 1, 67 type = "warning" 68 ) 69 self.updateProgress( 70 purple(t2), 71 header = bold("!!! "), 72 importance = 1, 73 type = "warning" 74 ) 75 continue # repo not available 76 except (self.dbapi2.OperationalError, 77 self.dbapi2.DatabaseError,SystemDatabaseError,): 78 79 if quiet: 80 continue 81 82 t = _("Repository") + " " + repoid + " " + \ 83 _("is corrupted") + ". " + _("Cannot validate") 84 self.updateProgress( 85 darkred(t), 86 importance = 1, 87 type = "warning" 88 ) 89 continue 90 91 # to avoid having zillions of open files when loading a lot of EquoInterfaces 92 self.close_all_repositories(mask_clear = False)
93
94 - def __get_repository_cache_key(self, repoid):
95 return (repoid, etpConst['systemroot'],)
96
97 - def init_generic_memory_repository(self, repoid, description, package_mirrors = []):
98 dbc = self.open_memory_database(dbname = repoid) 99 repo_key = self.__get_repository_cache_key(repoid) 100 self._memory_db_instances[repo_key] = dbc 101 102 # add to self.SystemSettings['repositories']['available'] 103 repodata = { 104 'repoid': repoid, 105 'in_memory': True, 106 'description': description, 107 'packages': package_mirrors, 108 'dbpath': ':memory:', 109 } 110 self.add_repository(repodata) 111 return dbc
112
113 - def close_all_repositories(self, mask_clear = True):
114 for item in self.__repodb_cache: 115 # in-memory repositories cannot be closed 116 # otherwise everything will be lost, to 117 # effectively close these repos you 118 # must call remove_repository method 119 if item in self._memory_db_instances: 120 continue 121 self.__repodb_cache[item].closeDB() 122 self.__repodb_cache.clear() 123 124 # disable hooks during SystemSettings cleanup 125 # otherwise it makes entropy.client.interfaces.repository crazy 126 old_value = self._can_run_sys_set_hooks 127 self._can_run_sys_set_hooks = False 128 if mask_clear: 129 self.SystemSettings.clear() 130 self._can_run_sys_set_hooks = old_value
131 132
133 - def is_repository_connection_cached(self, repoid):
134 if (repoid,etpConst['systemroot'],) in self.__repodb_cache: 135 return True 136 return False
137
138 - def open_repository(self, repoid):
139 140 key = self.__get_repository_cache_key(repoid) 141 if not self.__repodb_cache.has_key(key): 142 dbconn = self.load_repository_database(repoid, xcache = self.xcache, 143 indexing = self.indexing) 144 try: 145 dbconn.checkDatabaseApi() 146 except (self.dbapi2.OperationalError, TypeError,): 147 pass 148 self.__repodb_cache[key] = dbconn 149 return dbconn 150 return self.__repodb_cache.get(key)
151
152 - def load_repository_database(self, repoid, xcache = True, indexing = True):
153 154 if isinstance(repoid,basestring): 155 if repoid.endswith(etpConst['packagesext']): 156 xcache = False 157 158 repo_data = self.SystemSettings['repositories']['available'] 159 if repoid not in repo_data: 160 t = _("bad repository id specified") 161 if repoid not in self.__repo_error_messages_cache: 162 self.updateProgress( 163 darkred(t), 164 importance = 2, 165 type = "warning" 166 ) 167 self.__repo_error_messages_cache.add(repoid) 168 raise RepositoryError("RepositoryError: %s" % (t,)) 169 170 if repo_data[repoid].get('in_memory'): 171 repo_key = self.__get_repository_cache_key(repoid) 172 conn = self._memory_db_instances.get(repo_key) 173 else: 174 dbfile = repo_data[repoid]['dbpath']+"/"+etpConst['etpdatabasefile'] 175 if not os.path.isfile(dbfile): 176 t = _("Repository %s hasn't been downloaded yet.") % (repoid,) 177 if repoid not in self.__repo_error_messages_cache: 178 self.updateProgress( 179 darkred(t), 180 importance = 2, 181 type = "warning" 182 ) 183 self.__repo_error_messages_cache.add(repoid) 184 raise RepositoryError("RepositoryError: %s" % (t,)) 185 186 conn = EntropyRepository( 187 readOnly = True, 188 dbFile = dbfile, 189 clientDatabase = True, 190 dbname = etpConst['dbnamerepoprefix']+repoid, 191 xcache = xcache, 192 indexing = indexing, 193 OutputInterface = self 194 ) 195 196 if (repoid not in etpConst['client_treeupdatescalled']) and \ 197 (self.entropyTools.is_root()) and \ 198 (not repoid.endswith(etpConst['packagesext'])): 199 # only as root due to Portage 200 try: 201 updated = self.repository_packages_spm_sync(repoid, conn) 202 except (self.dbapi2.OperationalError, self.dbapi2.DatabaseError): 203 updated = False 204 if updated: 205 self.clear_dump_cache(etpCache['world_update']) 206 self.clear_dump_cache(etpCache['critical_update']) 207 self.clear_dump_cache(etpCache['world']) 208 self.clear_dump_cache(etpCache['install']) 209 self.clear_dump_cache(etpCache['remove']) 210 return conn
211
212 - def get_repository_revision(self, reponame):
213 fname = self.SystemSettings['repositories']['available'][reponame]['dbpath']+"/"+etpConst['etpdatabaserevisionfile'] 214 revision = -1 215 if os.path.isfile(fname) and os.access(fname,os.R_OK): 216 with open(fname,"r") as f: 217 try: 218 revision = int(f.readline().strip()) 219 except (OSError, IOError, ValueError,): 220 pass 221 return revision
222
223 - def update_repository_revision(self, reponame):
224 r = self.get_repository_revision(reponame) 225 self.SystemSettings['repositories']['available'][reponame]['dbrevision'] = "0" 226 if r != -1: 227 self.SystemSettings['repositories']['available'][reponame]['dbrevision'] = str(r)
228
229 - def get_repository_db_file_checksum(self, reponame):
230 fname = self.SystemSettings['repositories']['available'][reponame]['dbpath']+"/"+etpConst['etpdatabasehashfile'] 231 mhash = "-1" 232 if os.path.isfile(fname) and os.access(fname,os.R_OK): 233 with open(fname,"r") as f: 234 try: 235 mhash = f.readline().strip().split()[0] 236 except (OSError, IOError, IndexError,): 237 pass 238 return mhash
239
240 - def add_repository(self, repodata):
241 product = self.SystemSettings['repositories']['product'] 242 branch = self.SystemSettings['repositories']['branch'] 243 # update self.SystemSettings['repositories']['available'] 244 try: 245 self.SystemSettings['repositories']['available'][repodata['repoid']] = {} 246 self.SystemSettings['repositories']['available'][repodata['repoid']]['description'] = repodata['description'] 247 except KeyError: 248 t = _("repodata dictionary is corrupted") 249 raise InvalidData("InvalidData: %s" % (t,)) 250 251 if repodata['repoid'].endswith(etpConst['packagesext']) or repodata.get('in_memory'): # dynamic repository 252 try: 253 # no need # self.SystemSettings['repositories']['available'][repodata['repoid']]['plain_packages'] = repodata['plain_packages'][:] 254 self.SystemSettings['repositories']['available'][repodata['repoid']]['packages'] = repodata['packages'][:] 255 smart_package = repodata.get('smartpackage') 256 if smart_package != None: 257 self.SystemSettings['repositories']['available'][repodata['repoid']]['smartpackage'] = smart_package 258 except KeyError: 259 raise InvalidData("InvalidData: repodata dictionary is corrupted") 260 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbpath'] = repodata.get('dbpath') 261 self.SystemSettings['repositories']['available'][repodata['repoid']]['pkgpath'] = repodata.get('pkgpath') 262 self.SystemSettings['repositories']['available'][repodata['repoid']]['in_memory'] = repodata.get('in_memory') 263 # put at top priority, shift others 264 self.SystemSettings['repositories']['order'].insert(0, repodata['repoid']) 265 else: 266 # XXX it's boring to keep this in sync with entropyConstants stuff, solutions? 267 self.SystemSettings['repositories']['available'][repodata['repoid']]['plain_packages'] = repodata['plain_packages'][:] 268 self.SystemSettings['repositories']['available'][repodata['repoid']]['packages'] = [x+"/"+product for x in repodata['plain_packages']] 269 self.SystemSettings['repositories']['available'][repodata['repoid']]['plain_database'] = repodata['plain_database'] 270 self.SystemSettings['repositories']['available'][repodata['repoid']]['database'] = repodata['plain_database'] + \ 271 "/" + product + "/database/" + etpConst['currentarch'] + "/" + branch 272 if not repodata['dbcformat'] in etpConst['etpdatabasesupportedcformats']: 273 repodata['dbcformat'] = etpConst['etpdatabasesupportedcformats'][0] 274 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbcformat'] = repodata['dbcformat'] 275 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbpath'] = etpConst['etpdatabaseclientdir'] + \ 276 "/" + repodata['repoid'] + "/" + product + "/" + etpConst['currentarch'] + "/" + branch 277 # set dbrevision 278 myrev = self.get_repository_revision(repodata['repoid']) 279 if myrev == -1: 280 myrev = 0 281 self.SystemSettings['repositories']['available'][repodata['repoid']]['dbrevision'] = str(myrev) 282 if repodata.has_key("position"): 283 self.SystemSettings['repositories']['order'].insert( 284 repodata['position'], repodata['repoid']) 285 else: 286 self.SystemSettings['repositories']['order'].append( 287 repodata['repoid']) 288 if not repodata.has_key("service_port"): 289 repodata['service_port'] = int(etpConst['socket_service']['port']) 290 if not repodata.has_key("ssl_service_port"): 291 repodata['ssl_service_port'] = int(etpConst['socket_service']['ssl_port']) 292 self.SystemSettings['repositories']['available'][repodata['repoid']]['service_port'] = repodata['service_port'] 293 self.SystemSettings['repositories']['available'][repodata['repoid']]['ssl_service_port'] = repodata['ssl_service_port'] 294 self.repository_move_clear_cache(repodata['repoid']) 295 # save new self.SystemSettings['repositories']['available'] to file 296 self.entropyTools.save_repository_settings(repodata) 297 self.SystemSettings.clear() 298 self.close_all_repositories() 299 self.validate_repositories()
300
301 - def remove_repository(self, repoid, disable = False):
302 303 done = False 304 if self.SystemSettings['repositories']['available'].has_key(repoid): 305 del self.SystemSettings['repositories']['available'][repoid] 306 done = True 307 308 if self.SystemSettings['repositories']['excluded'].has_key(repoid): 309 del self.SystemSettings['repositories']['excluded'][repoid] 310 done = True 311 312 # also early remove from validRepositories to avoid 313 # issues when reloading SystemSettings which is bound to Entropy Client 314 # SystemSettings plugin, which triggers calculate_world_updates, which 315 # triggers all_repositories_checksum, which triggers open_repository, 316 # which triggers load_repository_database, which triggers an unwanted 317 # output message => "bad repository id specified" 318 if repoid in self.validRepositories: 319 self.validRepositories.remove(repoid) 320 321 # ensure that all dbs are closed 322 self.close_all_repositories() 323 324 if done: 325 326 if repoid in self.SystemSettings['repositories']['order']: 327 self.SystemSettings['repositories']['order'].remove(repoid) 328 329 self.repository_move_clear_cache(repoid) 330 # save new self.SystemSettings['repositories']['available'] to file 331 repodata = {} 332 repodata['repoid'] = repoid 333 if disable: 334 self.entropyTools.save_repository_settings(repodata, disable = True) 335 else: 336 self.entropyTools.save_repository_settings(repodata, remove = True) 337 self.SystemSettings.clear() 338 339 repo_mem_key = self.__get_repository_cache_key(repoid) 340 mem_inst = self._memory_db_instances.pop(repo_mem_key, None) 341 if isinstance(mem_inst, EntropyRepository): 342 mem_inst.closeDB() 343 344 # reset db cache 345 self.close_all_repositories() 346 self.validate_repositories()
347
348 - def shift_repository(self, repoid, toidx):
349 # update self.SystemSettings['repositories']['order'] 350 self.SystemSettings['repositories']['order'].remove(repoid) 351 self.SystemSettings['repositories']['order'].insert(toidx, repoid) 352 self.entropyTools.write_ordered_repositories_entries( 353 self.SystemSettings['repositories']['order']) 354 self.SystemSettings.clear() 355 self.close_all_repositories() 356 self.repository_move_clear_cache(repoid) 357 self.validate_repositories()
358
359 - def enable_repository(self, repoid):
360 self.repository_move_clear_cache(repoid) 361 # save new self.SystemSettings['repositories']['available'] to file 362 repodata = {} 363 repodata['repoid'] = repoid 364 self.entropyTools.save_repository_settings(repodata, enable = True) 365 self.SystemSettings.clear() 366 self.close_all_repositories() 367 self.validate_repositories()
368
369 - def disable_repository(self, repoid):
370 # update self.SystemSettings['repositories']['available'] 371 done = False 372 try: 373 del self.SystemSettings['repositories']['available'][repoid] 374 done = True 375 except: 376 pass 377 378 if done: 379 try: 380 self.SystemSettings['repositories']['order'].remove(repoid) 381 except (IndexError,): 382 pass 383 # it's not vital to reset 384 # self.SystemSettings['repositories']['order'] counters 385 386 self.repository_move_clear_cache(repoid) 387 # save new self.SystemSettings['repositories']['available'] to file 388 repodata = {} 389 repodata['repoid'] = repoid 390 self.entropyTools.save_repository_settings(repodata, disable = True) 391 self.SystemSettings.clear() 392 393 self.close_all_repositories() 394 self.validate_repositories()
395
396 - def get_repository_settings(self, repoid):
397 try: 398 repodata = self.SystemSettings['repositories']['available'][repoid].copy() 399 except KeyError: 400 if not self.SystemSettings['repositories']['excluded'].has_key(repoid): 401 raise 402 repodata = self.SystemSettings['repositories']['excluded'][repoid].copy() 403 return repodata
404 405 # every tbz2 file that would be installed must pass from here
406 - def add_tbz2_to_repos(self, tbz2file):
407 atoms_contained = [] 408 basefile = os.path.basename(tbz2file) 409 db_dir = tempfile.mkdtemp() 410 dbfile = self.entropyTools.extract_edb(tbz2file, 411 dbpath = db_dir+"/packages.db") 412 if dbfile == None: 413 return -1, atoms_contained 414 etpSys['dirstoclean'].add(os.path.dirname(dbfile)) 415 # add dbfile 416 repodata = {} 417 repodata['repoid'] = basefile 418 repodata['description'] = "Dynamic database from " + basefile 419 repodata['packages'] = [] 420 repodata['dbpath'] = os.path.dirname(dbfile) 421 repodata['pkgpath'] = os.path.realpath(tbz2file) # extra info added 422 repodata['smartpackage'] = False # extra info added 423 424 mydbconn = self.open_generic_database(dbfile) 425 # read all idpackages 426 try: 427 myidpackages = mydbconn.listAllIdpackages() # all branches admitted from external files 428 except (AttributeError, self.dbapi2.DatabaseError, \ 429 self.dbapi2.IntegrityError, self.dbapi2.OperationalError,): 430 return -2, atoms_contained 431 if len(myidpackages) > 1: 432 repodata[basefile]['smartpackage'] = True 433 for myidpackage in myidpackages: 434 compiled_arch = mydbconn.retrieveDownloadURL(myidpackage) 435 if compiled_arch.find("/"+etpSys['arch']+"/") == -1: 436 return -3, atoms_contained 437 atoms_contained.append((int(myidpackage), basefile)) 438 439 self.add_repository(repodata) 440 self.validate_repositories() 441 if basefile not in self.validRepositories: 442 self.remove_repository(basefile) 443 return -4, atoms_contained 444 mydbconn.closeDB() 445 del mydbconn 446 return 0, atoms_contained
447
448 - def reopen_client_repository(self):
449 self.clientDbconn.closeDB() 450 self.open_client_repository() 451 # make sure settings are in sync 452 self.SystemSettings.clear()
453
454 - def open_client_repository(self):
455 456 def load_db_from_ram(): 457 self.safe_mode = etpConst['safemodeerrors']['clientdb'] 458 mytxt = "%s, %s" % (_("System database not found or corrupted"), 459 _("running in safe mode using empty database from RAM"),) 460 self.updateProgress( 461 darkred(mytxt), 462 importance = 1, 463 type = "warning", 464 header = bold("!!!"), 465 ) 466 conn = self.open_memory_database(dbname = etpConst['clientdbid']) 467 return conn
468 469 db_dir = os.path.dirname(etpConst['etpdatabaseclientfilepath']) 470 if not os.path.isdir(db_dir): os.makedirs(db_dir) 471 472 db_path = etpConst['etpdatabaseclientfilepath'] 473 if (not self.noclientdb) and (not os.path.isfile(db_path)): 474 conn = load_db_from_ram() 475 self.entropyTools.print_traceback(f = self.clientLog) 476 else: 477 try: 478 conn = EntropyRepository(readOnly = False, dbFile = db_path, 479 clientDatabase = True, dbname = etpConst['clientdbid'], 480 xcache = self.xcache, indexing = self.indexing, 481 OutputInterface = self 482 ) 483 except (self.dbapi2.DatabaseError,): 484 self.entropyTools.print_traceback(f = self.clientLog) 485 conn = load_db_from_ram() 486 else: 487 # validate database 488 if not self.noclientdb: 489 try: 490 conn.validateDatabase() 491 except SystemDatabaseError: 492 try: 493 conn.closeDB() 494 except: 495 pass 496 self.entropyTools.print_traceback(f = self.clientLog) 497 conn = load_db_from_ram() 498 499 self.clientDbconn = conn 500 return self.clientDbconn
501
502 - def client_repository_sanity_check(self):
503 self.updateProgress( 504 darkred(_("Sanity Check") + ": " + _("system database")), 505 importance = 2, 506 type = "warning" 507 ) 508 idpkgs = self.clientDbconn.listAllIdpackages() 509 length = len(idpkgs) 510 count = 0 511 errors = False 512 scanning_txt = _("Scanning...") 513 for x in idpkgs: 514 count += 1 515 self.updateProgress( 516 darkgreen(scanning_txt), 517 importance = 0, 518 type = "info", 519 back = True, 520 count = (count,length), 521 percent = True 522 ) 523 try: 524 self.clientDbconn.getPackageData(x) 525 except Exception ,e: 526 self.entropyTools.print_traceback() 527 errors = True 528 self.updateProgress( 529 darkred(_("Errors on idpackage %s, error: %s")) % (x,str(e)), 530 importance = 0, 531 type = "warning" 532 ) 533 534 if not errors: 535 t = _("Sanity Check") + ": %s" % (bold(_("PASSED")),) 536 self.updateProgress( 537 darkred(t), 538 importance = 2, 539 type = "warning" 540 ) 541 return 0 542 else: 543 t = _("Sanity Check") + ": %s" % (bold(_("CORRUPTED")),) 544 self.updateProgress( 545 darkred(t), 546 importance = 2, 547 type = "warning" 548 ) 549 return -1
550
551 - def open_generic_database(self, dbfile, dbname = None, xcache = None, 552 readOnly = False, indexing_override = None, skipChecks = False):
553 if xcache == None: 554 xcache = self.xcache 555 if indexing_override != None: 556 indexing = indexing_override 557 else: 558 indexing = self.indexing 559 if dbname == None: 560 dbname = etpConst['genericdbid'] 561 return EntropyRepository( 562 readOnly = readOnly, 563 dbFile = dbfile, 564 clientDatabase = True, 565 dbname = dbname, 566 xcache = xcache, 567 indexing = indexing, 568 OutputInterface = self, 569 skipChecks = skipChecks 570 )
571
572 - def open_memory_database(self, dbname = None):
573 if dbname == None: 574 dbname = etpConst['genericdbid'] 575 dbc = EntropyRepository( 576 readOnly = False, 577 dbFile = ':memory:', 578 clientDatabase = True, 579 dbname = dbname, 580 xcache = False, 581 indexing = False, 582 OutputInterface = self, 583 skipChecks = True 584 ) 585 dbc.initializeDatabase() 586 return dbc
587
588 - def backup_database(self, dbpath, backup_dir = None, silent = False, compress_level = 9):
589 590 if compress_level not in range(1,10): 591 compress_level = 9 592 593 backup_dir = os.path.dirname(dbpath) 594 if not backup_dir: backup_dir = os.path.dirname(dbpath) 595 dbname = os.path.basename(dbpath) 596 bytes_required = 1024000*300 597 if not (os.access(backup_dir,os.W_OK) and \ 598 os.path.isdir(backup_dir) and os.path.isfile(dbpath) and \ 599 os.access(dbpath,os.R_OK) and self.entropyTools.check_required_space(backup_dir, bytes_required)): 600 if not silent: 601 mytxt = "%s: %s, %s" % (darkred(_("Cannot backup selected database")),blue(dbpath),darkred(_("permission denied")),) 602 self.updateProgress( 603 mytxt, 604 importance = 1, 605 type = "error", 606 header = red(" @@ ") 607 ) 608 return False, mytxt 609 610 def get_ts(): 611 from datetime import datetime 612 ts = datetime.fromtimestamp(time.time()) 613 return "%s%s%s_%sh%sm%ss" % (ts.year,ts.month,ts.day,ts.hour,ts.minute,ts.second)
614 615 comp_dbname = "%s%s.%s.bz2" % (etpConst['dbbackupprefix'],dbname,get_ts(),) 616 comp_dbpath = os.path.join(backup_dir,comp_dbname) 617 if not silent: 618 mytxt = "%s: %s ..." % (darkgreen(_("Backing up database to")),blue(comp_dbpath),) 619 self.updateProgress( 620 mytxt, 621 importance = 1, 622 type = "info", 623 header = blue(" @@ "), 624 back = True 625 ) 626 import bz2 627 try: 628 self.entropyTools.compress_file(dbpath, comp_dbpath, bz2.BZ2File, compress_level) 629 except: 630 if not silent: 631 self.entropyTools.print_traceback() 632 return False, _("Unable to compress") 633 634 if not silent: 635 mytxt = "%s: %s" % (darkgreen(_("Database backed up successfully")),blue(comp_dbpath),) 636 self.updateProgress( 637 mytxt, 638 importance = 1, 639 type = "info", 640 header = blue(" @@ "), 641 back = True 642 ) 643 return True, _("All fine") 644
645 - def restore_database(self, backup_path, db_destination, silent = False):
646 647 bytes_required = 1024000*300 648 db_dir = os.path.dirname(db_destination) 649 if not (os.access(db_dir,os.W_OK) and os.path.isdir(db_dir) and \ 650 os.path.isfile(backup_path) and os.access(backup_path,os.R_OK) and \ 651 self.entropyTools.check_required_space(db_dir, bytes_required)): 652 653 if not silent: 654 mytxt = "%s: %s, %s" % (darkred(_("Cannot restore selected backup")), 655 blue(backup_path),darkred(_("permission denied")),) 656 self.updateProgress( 657 mytxt, 658 importance = 1, 659 type = "error", 660 header = red(" @@ ") 661 ) 662 return False, mytxt 663 664 if not silent: 665 mytxt = "%s: %s => %s ..." % (darkgreen(_("Restoring backed up database")), 666 blue(os.path.basename(backup_path)),blue(db_destination),) 667 self.updateProgress( 668 mytxt, 669 importance = 1, 670 type = "info", 671 header = blue(" @@ "), 672 back = True 673 ) 674 675 import bz2 676 try: 677 self.entropyTools.uncompress_file(backup_path, db_destination, bz2.BZ2File) 678 except: 679 if not silent: 680 self.entropyTools.print_traceback() 681 return False, _("Unable to unpack") 682 683 if not silent: 684 mytxt = "%s: %s" % (darkgreen(_("Database restored successfully")), 685 blue(db_destination),) 686 self.updateProgress( 687 mytxt, 688 importance = 1, 689 type = "info", 690 header = blue(" @@ "), 691 back = True 692 ) 693 self.purge_cache() 694 return True, _("All fine")
695
696 - def list_backedup_client_databases(self, client_dbdir = None):
697 if not client_dbdir: 698 client_dbdir = os.path.dirname(etpConst['etpdatabaseclientfilepath']) 699 return [os.path.join(client_dbdir,x) for x in os.listdir(client_dbdir) \ 700 if x.startswith(etpConst['dbbackupprefix']) and \ 701 os.access(os.path.join(client_dbdir,x),os.R_OK) 702 ]
703
704 - def run_repositories_post_branch_switch_hooks(self, old_branch, new_branch):
705 """ 706 This method is called whenever branch is successfully switched by user. 707 Branch is switched when user wants to upgrade the OS to a new 708 major release. 709 Any repository can be shipped with a sh script which if available, 710 handles system configuration to ease the migration. 711 712 @param old_branch: previously set branch 713 @type old_branch: string 714 @param new_branch: newly set branch 715 @type new_branch: string 716 @return: tuple composed by (1) list of repositories whose script has 717 been run and (2) bool describing if scripts exited with error 718 @rtype: tuple(set, bool) 719 """ 720 721 const_debug_write(__name__, 722 "run_repositories_post_branch_switch_hooks: called") 723 724 client_dbconn = self.clientDbconn 725 hooks_ran = set() 726 if client_dbconn is None: 727 const_debug_write(__name__, 728 "run_repositories_post_branch_switch_hooks: clientdb not avail") 729 return hooks_ran, True 730 731 errors = False 732 repo_data = self.SystemSettings['repositories']['available'] 733 repo_data_excl = self.SystemSettings['repositories']['available'] 734 all_repos = sorted(set(repo_data.keys() + repo_data_excl.keys())) 735 736 for repoid in all_repos: 737 738 const_debug_write(__name__, 739 "run_repositories_post_branch_switch_hooks: %s" % ( 740 repoid,) 741 ) 742 743 mydata = repo_data.get(repoid) 744 if mydata is None: 745 mydata = repo_data_excl.get(repoid) 746 747 if mydata is None: 748 const_debug_write(__name__, 749 "run_repositories_post_branch_switch_hooks: skipping %s" % ( 750 repoid,) 751 ) 752 continue 753 754 branch_mig_script = mydata['post_branch_hop_script'] 755 branch_mig_md5sum = '0' 756 if os.access(branch_mig_script, os.R_OK | os.F_OK): 757 branch_mig_md5sum = self.entropyTools.md5sum(branch_mig_script) 758 759 const_debug_write(__name__, 760 "run_repositories_post_branch_switch_hooks: script md5: %s" % ( 761 branch_mig_md5sum,) 762 ) 763 764 # check if it is needed to run post branch migration script 765 status_md5sums = client_dbconn.isBranchMigrationAvailable( 766 repoid, old_branch, new_branch) 767 if status_md5sums: 768 if branch_mig_md5sum == status_md5sums[0]: # its stored md5 769 const_debug_write(__name__, 770 "run_repositories_post_branch_switch_hooks: skip %s" % ( 771 branch_mig_script,) 772 ) 773 continue # skipping, already ran the same script 774 775 const_debug_write(__name__, 776 "run_repositories_post_branch_switch_hooks: preparing run: %s" % ( 777 branch_mig_script,) 778 ) 779 780 if branch_mig_md5sum != '0': 781 args = ["/bin/sh", branch_mig_script, repoid, 782 etpConst['systemroot'] + "/", old_branch, new_branch] 783 const_debug_write(__name__, 784 "run_repositories_post_branch_switch_hooks: run: %s" % ( 785 args,) 786 ) 787 proc = subprocess.Popen(args, stdin = sys.stdin, 788 stdout = sys.stdout, stderr = sys.stderr) 789 # it is possible to ignore errors because 790 # if it's a critical thing, upstream dev just have to fix 791 # the script and will be automagically re-run 792 br_rc = proc.wait() 793 const_debug_write(__name__, 794 "run_repositories_post_branch_switch_hooks: rc: %s" % ( 795 br_rc,) 796 ) 797 if br_rc != 0: 798 errors = True 799 800 const_debug_write(__name__, 801 "run_repositories_post_branch_switch_hooks: done") 802 803 # update metadata inside database 804 # overriding post branch upgrade md5sum is INTENDED 805 # here but NOT on the other function 806 # this will cause the post-branch upgrade migration 807 # script to be re-run also. 808 client_dbconn.insertBranchMigration(repoid, old_branch, new_branch, 809 branch_mig_md5sum, '0') 810 811 const_debug_write(__name__, 812 "run_repositories_post_branch_switch_hooks: db data: %s" % ( 813 (repoid, old_branch, new_branch, branch_mig_md5sum, '0',),) 814 ) 815 816 hooks_ran.add(repoid) 817 818 return hooks_ran, errors
819
820 - def run_repository_post_branch_upgrade_hooks(self, pretend = False):
821 """ 822 This method is called whenever branch is successfully switched by user 823 and all the updates have been installed (also look at: 824 run_repositories_post_branch_switch_hooks()). 825 Any repository can be shipped with a sh script which if available, 826 handles system configuration to ease the migration. 827 828 @param pretend: do not run hooks but just return list of repos whose 829 scripts should be run 830 @type pretend: bool 831 @return: tuple of length 2 composed by list of repositories whose 832 scripts have been run and errors boolean) 833 @rtype: tuple 834 """ 835 836 const_debug_write(__name__, 837 "run_repository_post_branch_upgrade_hooks: called" 838 ) 839 840 client_dbconn = self.clientDbconn 841 hooks_ran = set() 842 if client_dbconn is None: 843 return hooks_ran, True 844 845 repo_data = self.SystemSettings['repositories']['available'] 846 branch = self.SystemSettings['repositories']['branch'] 847 errors = False 848 849 for repoid in self.validRepositories: 850 851 const_debug_write(__name__, 852 "run_repository_post_branch_upgrade_hooks: repoid: %s" % ( 853 (repoid,), 854 ) 855 ) 856 857 mydata = repo_data.get(repoid) 858 if mydata is None: 859 const_debug_write(__name__, 860 "run_repository_post_branch_upgrade_hooks: repo data N/A") 861 continue 862 863 # check if branch upgrade script exists 864 branch_upg_script = mydata['post_branch_upgrade_script'] 865 branch_upg_md5sum = '0' 866 if os.access(branch_upg_script, os.R_OK | os.F_OK): 867 branch_upg_md5sum = self.entropyTools.md5sum(branch_upg_script) 868 869 const_debug_write(__name__, 870 "run_repository_post_branch_upgrade_hooks: script md5: %s" % ( 871 branch_upg_md5sum,) 872 ) 873 874 upgrade_data = client_dbconn.retrieveBranchMigration(branch) 875 if upgrade_data.get(repoid) is None: 876 # no data stored for this repository, skipping 877 const_debug_write(__name__, 878 "run_repository_post_branch_upgrade_hooks: %s: %s" % ( 879 repoid, "branch upgrade data not avail",) 880 ) 881 continue 882 repo_upgrade_data = upgrade_data[repoid] 883 884 const_debug_write(__name__, 885 "run_repository_post_branch_upgrade_hooks: upgrade data: %s" % ( 886 repo_upgrade_data,) 887 ) 888 889 for from_branch in sorted(repo_upgrade_data): 890 891 const_debug_write(__name__, 892 "run_repository_post_branch_upgrade_hooks: upgrade: %s" % ( 893 from_branch,) 894 ) 895 896 # yeah, this is run for every branch even if script 897 # which md5 is checked against is the same 898 # this makes the code very flexible 899 post_mig_md5, post_upg_md5 = repo_upgrade_data[from_branch] 900 if branch_upg_md5sum == post_upg_md5: 901 # md5 is equal, this means that it's been already run 902 const_debug_write(__name__, 903 "run_repository_post_branch_upgrade_hooks: %s: %s" % ( 904 "already run for from_branch", from_branch,) 905 ) 906 continue 907 908 hooks_ran.add(repoid) 909 910 if pretend: 911 const_debug_write(__name__, 912 "run_repository_post_branch_upgrade_hooks: %s: %s => %s" % ( 913 "pretend enabled, not actually running", 914 repoid, from_branch, 915 ) 916 ) 917 continue 918 919 const_debug_write(__name__, 920 "run_repository_post_branch_upgrade_hooks: %s: %s" % ( 921 "running upgrade script from_branch:", from_branch,) 922 ) 923 924 args = ["/bin/sh", branch_upg_script, repoid, 925 etpConst['systemroot'] + "/", from_branch, branch] 926 proc = subprocess.Popen(args, stdin = sys.stdin, 927 stdout = sys.stdout, stderr = sys.stderr) 928 mig_rc = proc.wait() 929 930 const_debug_write(__name__, 931 "run_repository_post_branch_upgrade_hooks: %s: %s" % ( 932 "upgrade script exit status", mig_rc,) 933 ) 934 935 if mig_rc != 0: 936 errors = True 937 938 # save branch_upg_md5sum in db 939 client_dbconn.setBranchMigrationPostUpgradeMd5sum(repoid, 940 from_branch, branch, branch_upg_md5sum) 941 942 const_debug_write(__name__, 943 "run_repository_post_branch_upgrade_hooks: %s: %s" % ( 944 "saved upgrade data", 945 (repoid, from_branch, branch, branch_upg_md5sum,), 946 ) 947 ) 948 949 return hooks_ran, errors
950 951
952 -class MiscMixin:
953 954 # resources lock file object container 955 RESOURCES_LOCK_F_REF = None 956 RESOURCES_LOCK_F_COUNT = 0 957
958 - def reload_constants(self):
961
962 - def setup_default_file_perms(self, filepath):
963 # setup file permissions 964 os.chmod(filepath,0664) 965 if etpConst['entropygid'] != None: 966 os.chown(filepath,-1,etpConst['entropygid'])
967
968 - def resources_create_lock(self):
969 acquired = self.create_pid_file_lock( 970 etpConst['locks']['using_resources']) 971 if acquired: 972 MiscMixin.RESOURCES_LOCK_F_COUNT += 1 973 return acquired
974
975 - def resources_remove_lock(self):
976 977 # decrement lock counter 978 if MiscMixin.RESOURCES_LOCK_F_COUNT > 0: 979 MiscMixin.RESOURCES_LOCK_F_COUNT -= 1 980 981 # if lock counter > 0, still locked 982 # waiting for other upper-level calls 983 if MiscMixin.RESOURCES_LOCK_F_COUNT > 0: 984 return 985 986 f_obj = MiscMixin.RESOURCES_LOCK_F_REF 987 if f_obj is not None: 988 fcntl.flock(f_obj.fileno(), fcntl.LOCK_UN) 989 990 if f_obj is not None: 991 f_obj.close() 992 MiscMixin.RESOURCES_LOCK_F_REF = None 993 994 if os.access(etpConst['locks']['using_resources'], os.F_OK): 995 os.remove(etpConst['locks']['using_resources'])
996
997 - def resources_check_lock(self):
998 return self.check_pid_file_lock(etpConst['locks']['using_resources'])
999
1000 - def check_pid_file_lock(self, pidfile):
1001 if not os.path.isfile(pidfile): 1002 return False # not locked 1003 f = open(pidfile) 1004 s_pid = f.readline().strip() 1005 f.close() 1006 try: 1007 s_pid = int(s_pid) 1008 except ValueError: 1009 return False # not locked 1010 # is it our pid? 1011 1012 mypid = os.getpid() 1013 if (s_pid != mypid) and const_pid_exists(s_pid): 1014 # is it running 1015 return True # locked 1016 return False
1017
1018 - def create_pid_file_lock(self, pidfile, mypid = None):
1019 1020 if MiscMixin.RESOURCES_LOCK_F_REF is not None: 1021 # already locked, reentrant lock 1022 return True 1023 1024 lockdir = os.path.dirname(pidfile) 1025 if not os.path.isdir(lockdir): 1026 os.makedirs(lockdir,0775) 1027 const_setup_perms(lockdir,etpConst['entropygid']) 1028 if mypid == None: 1029 mypid = os.getpid() 1030 1031 pid_f = open(pidfile, "w") 1032 try: 1033 fcntl.flock(pid_f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) 1034 except IOError, err: 1035 if err.errno not in (errno.EACCES, errno.EAGAIN,): 1036 # ouch, wtf? 1037 raise 1038 pid_f.close() 1039 return False # lock already acquired 1040 1041 pid_f.write(str(mypid)) 1042 pid_f.flush() 1043 MiscMixin.RESOURCES_LOCK_F_REF = pid_f 1044 return True
1045
1046 - def application_lock_check(self, silent = False):
1047 # check if another instance is running 1048 etpConst['applicationlock'] = False 1049 const_setup_entropy_pid(just_read = True) 1050 locked = self.entropyTools.application_lock_check(gentle = True) 1051 if locked: 1052 if not silent: 1053 self.updateProgress( 1054 red(_("Another Entropy instance is currently active, cannot satisfy your request.")), 1055 importance = 1, 1056 type = "error", 1057 header = darkred(" @@ ") 1058 ) 1059 return True 1060 return False
1061
1062 - def lock_check(self, check_function):
1063 1064 lock_count = 0 1065 max_lock_count = 600 1066 sleep_seconds = 0.5 1067 1068 # check lock file 1069 while 1: 1070 locked = check_function() 1071 if not locked: 1072 if lock_count > 0: 1073 self.updateProgress( 1074 blue(_("Resources unlocked, let's go!")), 1075 importance = 1, 1076 type = "info", 1077 header = darkred(" @@ ") 1078 ) 1079 # wait for other process to exit 1080 # 5 seconds should be enough 1081 time.sleep(5) 1082 break 1083 if lock_count >= max_lock_count: 1084 mycalc = max_lock_count*sleep_seconds/60 1085 self.updateProgress( 1086 blue(_("Resources still locked after %s minutes, giving up!")) % (mycalc,), 1087 importance = 1, 1088 type = "warning", 1089 header = darkred(" @@ ") 1090 ) 1091 return True # gave up 1092 lock_count += 1 1093 self.updateProgress( 1094 blue(_("Resources locked, sleeping %s seconds, check #%s/%s")) % ( 1095 sleep_seconds, 1096 lock_count, 1097 max_lock_count, 1098 ), 1099 importance = 1, 1100 type = "warning", 1101 header = darkred(" @@ "), 1102 back = True 1103 ) 1104 time.sleep(sleep_seconds) 1105 return False # yay!
1106
1107 - def backup_constant(self, constant_name):
1108 if etpConst.has_key(constant_name): 1109 myinst = etpConst[constant_name] 1110 if type(etpConst[constant_name]) in (list,tuple): 1111 myinst = etpConst[constant_name][:] 1112 elif type(etpConst[constant_name]) in (dict,set): 1113 myinst = etpConst[constant_name].copy() 1114 else: 1115 myinst = etpConst[constant_name] 1116 etpConst['backed_up'].update({constant_name: myinst}) 1117 else: 1118 t = _("Nothing to backup in etpConst with %s key") % (constant_name,) 1119 raise InvalidData("InvalidData: %s" % (t,))
1120
1121 - def set_priority(self, low = 0):
1122 return const_set_nice_level(low)
1123
1124 - def reload_repositories_config(self, repositories = None):
1125 if repositories is None: 1126 repositories = self.validRepositories 1127 for repoid in repositories: 1128 self.open_repository(repoid)
1129
1130 - def switch_chroot(self, chroot = ""):
1131 # clean caches 1132 self.purge_cache() 1133 self.close_all_repositories() 1134 if chroot.endswith("/"): 1135 chroot = chroot[:-1] 1136 etpSys['rootdir'] = chroot 1137 self.reload_constants() 1138 self.validate_repositories() 1139 self.reopen_client_repository() 1140 # keep them closed, since SystemSettings.clear() is called 1141 # above on reopen_client_repository() 1142 self.close_all_repositories() 1143 if chroot: 1144 try: 1145 self.clientDbconn.resetTreeupdatesDigests() 1146 except: 1147 pass 1148 # I don't think it's safe to keep them open 1149 # isn't it? 1150 self.closeAllSecurity() 1151 self.closeAllQA()
1152
1153 - def get_file_viewer(self):
1154 viewer = None 1155 if os.access("/usr/bin/less",os.X_OK): 1156 viewer = "/usr/bin/less" 1157 elif os.access("/bin/more",os.X_OK): 1158 viewer = "/bin/more" 1159 if not viewer: 1160 viewer = self.get_file_editor() 1161 return viewer
1162
1163 - def get_file_editor(self):
1164 editor = None 1165 if os.getenv("EDITOR"): 1166 editor = "$EDITOR" 1167 elif os.access("/bin/nano",os.X_OK): 1168 editor = "/bin/nano" 1169 elif os.access("/bin/vi",os.X_OK): 1170 editor = "/bin/vi" 1171 elif os.access("/usr/bin/vi",os.X_OK): 1172 editor = "/usr/bin/vi" 1173 elif os.access("/usr/bin/emacs",os.X_OK): 1174 editor = "/usr/bin/emacs" 1175 elif os.access("/bin/emacs",os.X_OK): 1176 editor = "/bin/emacs" 1177 return editor
1178
1179 - def add_user_package_set(self, set_name, set_atoms):
1180 1181 def _ensure_package_sets_dir(): 1182 sets_dir = etpConst['confsetsdir'] 1183 if not os.path.isdir(sets_dir): 1184 if os.path.lexists(sets_dir): 1185 os.remove(sets_dir) 1186 os.makedirs(sets_dir,0775) 1187 const_setup_perms(sets_dir, etpConst['entropygid'])
1188 1189 try: 1190 set_name = str(set_name) 1191 except (UnicodeEncodeError,UnicodeDecodeError,): 1192 raise InvalidPackageSet("InvalidPackageSet: %s %s" % (set_name,_("must be an ASCII string"),)) 1193 1194 if set_name.startswith(etpConst['packagesetprefix']): 1195 raise InvalidPackageSet("InvalidPackageSet: %s %s '%s'" % (set_name,_("cannot start with"),etpConst['packagesetprefix'],)) 1196 set_match, rc = self.package_set_match(set_name) 1197 if rc: return -1,_("Name already taken") 1198 1199 _ensure_package_sets_dir() 1200 set_file = os.path.join(etpConst['confsetsdir'],set_name) 1201 if os.path.isfile(set_file) and os.access(set_file,os.W_OK): 1202 try: 1203 os.remove(set_file) 1204 except OSError: 1205 return -2,_("Cannot remove the old element") 1206 if not os.access(os.path.dirname(set_file),os.W_OK): 1207 return -3,_("Cannot create the element") 1208 1209 f = open(set_file,"w") 1210 for x in set_atoms: f.write("%s\n" % (x,)) 1211 f.flush() 1212 f.close() 1213 self.SystemSettings['system_package_sets'][set_name] = set(set_atoms) 1214 return 0,_("All fine")
1215
1216 - def remove_user_package_set(self, set_name):
1217 1218 try: 1219 set_name = str(set_name) 1220 except (UnicodeEncodeError,UnicodeDecodeError,): 1221 raise InvalidPackageSet("InvalidPackageSet: %s %s" % (set_name,_("must be an ASCII string"),)) 1222 1223 if set_name.startswith(etpConst['packagesetprefix']): 1224 raise InvalidPackageSet("InvalidPackageSet: %s %s '%s'" % (set_name,_("cannot start with"),etpConst['packagesetprefix'],)) 1225 1226 set_match, rc = self.package_set_match(set_name) 1227 if not rc: return -1,_("Already removed") 1228 set_id, set_x, set_y = set_match 1229 1230 if set_id != etpConst['userpackagesetsid']: 1231 return -2,_("Not defined by user") 1232 set_file = os.path.join(etpConst['confsetsdir'],set_name) 1233 if os.path.isfile(set_file) and os.access(set_file,os.W_OK): 1234 os.remove(set_file) 1235 if set_name in self.SystemSettings['system_package_sets']: 1236 del self.SystemSettings['system_package_sets'][set_name] 1237 return 0,_("All fine") 1238 return -3,_("Set not found or unable to remove")
1239
1240 - def is_installed_idpackage_in_system_mask(self, idpackage):
1241 client_plugin_id = etpConst['system_settings_plugins_ids']['client_plugin'] 1242 mask_installed = self.SystemSettings[client_plugin_id]['system_mask']['repos_installed'] 1243 if idpackage in mask_installed: 1244 return True 1245 return False
1246
1247 - def get_branch_from_download_relative_uri(self, db_download_uri):
1248 return db_download_uri.split("/")[2]
1249
1250 - def swap_branch_in_download_relative_uri(self, new_branch, db_download_uri):
1251 cur_branch = self.get_branch_from_download_relative_uri(db_download_uri) 1252 return db_download_uri.replace("/%s/" % (cur_branch,), 1253 "/%s/" % (new_branch,))
1254
1255 - def unused_packages_test(self, dbconn = None):
1256 if dbconn == None: dbconn = self.clientDbconn 1257 return [x for x in dbconn.retrieveUnusedIdpackages() if self.validate_package_removal(x)]
1258
1259 - def get_licenses_to_accept(self, install_queue):
1260 if not install_queue: 1261 return {} 1262 licenses = {} 1263 cl_id = self.sys_settings_client_plugin_id 1264 repo_sys_data = self.SystemSettings[cl_id]['repositories'] 1265 1266 for match in install_queue: 1267 repoid = match[1] 1268 dbconn = self.open_repository(repoid) 1269 wl = repo_sys_data['license_whitelist'].get(repoid) 1270 if not wl: 1271 continue 1272 keys = dbconn.retrieveLicensedataKeys(match[0]) 1273 for key in keys: 1274 if key not in wl: 1275 found = self.clientDbconn.isLicenseAccepted(key) 1276 if found: 1277 continue 1278 if not licenses.has_key(key): 1279 licenses[key] = set() 1280 licenses[key].add(match) 1281 return licenses
1282
1283 - def get_text_license(self, license_name, repoid):
1284 dbconn = self.open_repository(repoid) 1285 text = dbconn.retrieveLicenseText(license_name) 1286 tempfile = self.entropyTools.get_random_temp_file() 1287 f = open(tempfile,"w") 1288 f.write(text) 1289 f.flush() 1290 f.close() 1291 return tempfile
1292
1293 - def set_branch(self, branch):
1294 """ 1295 Set new Entropy branch. This is NOT thread-safe. 1296 Please note that if you call this method all your 1297 repository instance references will become invalid. 1298 This is caused by close_all_repositories and SystemSettings 1299 clear methods. 1300 Once you changed branch, the repository databases won't be 1301 available until you fetch them (through Repositories class) 1302 1303 @param branch -- new branch 1304 @type branch basestring 1305 @return None 1306 """ 1307 self.Cacher.discard() 1308 self.Cacher.stop() 1309 self.purge_cache(showProgress = False) 1310 self.close_all_repositories() 1311 # etpConst should be readonly but we override the rule here 1312 # this is also useful when no config file or parameter into it exists 1313 etpConst['branch'] = branch 1314 self.entropyTools.write_new_branch(branch) 1315 # there are no valid repos atm 1316 del self.validRepositories[:] 1317 self.SystemSettings.clear() 1318 1319 # reset treeupdatesactions 1320 self.reopen_client_repository() 1321 self.clientDbconn.resetTreeupdatesDigests() 1322 self.validate_repositories(quiet = True) 1323 self.close_all_repositories() 1324 if self.xcache: 1325 self.Cacher.start()
1326
1327 - def get_meant_packages(self, search_term, from_installed = False, 1328 valid_repos = None):
1329 1330 if valid_repos is None: 1331 valid_repos = [] 1332 1333 pkg_data = [] 1334 atom_srch = False 1335 if "/" in search_term: 1336 atom_srch = True 1337 1338 if from_installed: 1339 if hasattr(self, 'clientDbconn'): 1340 if self.clientDbconn is not None: 1341 valid_repos.append(self.clientDbconn) 1342 1343 elif not valid_repos: 1344 valid_repos.extend(self.validRepositories[:]) 1345 1346 for repo in valid_repos: 1347 if isinstance(repo, basestring): 1348 dbconn = self.open_repository(repo) 1349 elif isinstance(repo, EntropyRepository): 1350 dbconn = repo 1351 else: 1352 continue 1353 pkg_data.extend([(x,repo,) for x in \ 1354 dbconn.searchSimilarPackages(search_term, atom = atom_srch)]) 1355 1356 return pkg_data
1357
1358 - def get_package_groups(self):
1359 """ 1360 Return Entropy Package Groups metadata. The returned dictionary 1361 contains information to make Entropy Client users to group packages 1362 into "macro" categories. 1363 1364 @return: Entropy Package Groups metadata 1365 @rtype: dict 1366 """ 1367 from entropy.spm.plugins.factory import get_default_class 1368 spm = get_default_class() 1369 groups = spm.get_package_groups().copy() 1370 1371 # expand metadata 1372 categories = self.list_repo_categories() 1373 for data in groups.values(): 1374 1375 exp_cats = set() 1376 for g_cat in data['categories']: 1377 exp_cats.update([x for x in categories if x.startswith(g_cat)]) 1378 data['categories'] = sorted(exp_cats) 1379 1380 return groups
1381
1382 - def list_repo_categories(self):
1383 categories = set() 1384 for repo in self.validRepositories: 1385 dbconn = self.open_repository(repo) 1386 catsdata = dbconn.listAllCategories() 1387 categories.update(set([x[1] for x in catsdata])) 1388 return categories
1389
1390 - def list_repo_packages_in_category(self, category):
1391 pkg_matches = [] 1392 for repo in self.validRepositories: 1393 dbconn = self.open_repository(repo) 1394 branch = self.SystemSettings['repositories']['branch'] 1395 catsdata = dbconn.searchPackagesByCategory(category, branch = branch) 1396 pkg_matches.extend([(x[1],repo,) for x in catsdata if (x[1],repo,) not in pkg_matches]) 1397 return pkg_matches
1398
1399 - def get_category_description_data(self, category):
1400 1401 data = {} 1402 for repo in self.validRepositories: 1403 try: 1404 dbconn = self.open_repository(repo) 1405 except RepositoryError: 1406 continue 1407 try: 1408 data = dbconn.retrieveCategoryDescription(category) 1409 except (self.dbapi2.OperationalError, self.dbapi2.IntegrityError,): 1410 continue 1411 if data: break 1412 1413 return data
1414
1415 - def list_installed_packages_in_category(self, category):
1416 pkg_matches = set([x[1] for x in self.clientDbconn.searchPackagesByCategory(category)]) 1417 return pkg_matches
1418
1419 - def get_package_match_config_protect(self, match, mask = False):
1420 1421 idpackage, repoid = match 1422 dbconn = self.open_repository(repoid) 1423 cl_id = self.sys_settings_client_plugin_id 1424 misc_data = self.SystemSettings[cl_id]['misc'] 1425 if mask: 1426 config_protect = set(dbconn.retrieveProtectMask(idpackage).split()) 1427 config_protect |= set(misc_data['configprotectmask']) 1428 else: 1429 config_protect = set(dbconn.retrieveProtect(idpackage).split()) 1430 config_protect |= set(misc_data['configprotect']) 1431 config_protect = [etpConst['systemroot']+x for x in config_protect] 1432 1433 return sorted(config_protect)
1434
1435 - def get_installed_package_config_protect(self, idpackage, mask = False):
1436 1437 if self.clientDbconn == None: 1438 return [] 1439 cl_id = self.sys_settings_client_plugin_id 1440 misc_data = self.SystemSettings[cl_id]['misc'] 1441 if mask: 1442 _pmask = self.clientDbconn.retrieveProtectMask(idpackage).split() 1443 config_protect = set(_pmask) 1444 config_protect |= set(misc_data['configprotectmask']) 1445 else: 1446 _protect = self.clientDbconn.retrieveProtect(idpackage).split() 1447 config_protect = set(_protect) 1448 config_protect |= set(misc_data['configprotect']) 1449 config_protect = [etpConst['systemroot']+x for x in config_protect] 1450 1451 return sorted(config_protect)
1452
1453 - def get_system_config_protect(self, mask = False):
1454 1455 if self.clientDbconn == None: 1456 return [] 1457 1458 # FIXME: workaround because this method is called 1459 # before misc_parser 1460 cl_id = self.sys_settings_client_plugin_id 1461 misc_data = self.SystemSettings[cl_id]['misc'] 1462 if mask: 1463 _pmask = self.clientDbconn.listConfigProtectEntries(mask = True) 1464 config_protect = set(_pmask) 1465 config_protect |= set(misc_data['configprotectmask']) 1466 else: 1467 _protect = self.clientDbconn.listConfigProtectEntries() 1468 config_protect = set(_protect) 1469 config_protect |= set(misc_data['configprotect']) 1470 config_protect = [etpConst['systemroot']+x for x in config_protect] 1471 1472 return sorted(config_protect)
1473
1474 - def inject_entropy_database_into_package(self, package_filename, data, treeupdates_actions = None):
1475 dbpath = self.get_tmp_dbpath() 1476 dbconn = self.open_generic_database(dbpath) 1477 dbconn.initializeDatabase() 1478 dbconn.addPackage(data, revision = data['revision']) 1479 if treeupdates_actions != None: 1480 dbconn.bumpTreeUpdatesActions(treeupdates_actions) 1481 dbconn.commitChanges() 1482 dbconn.closeDB() 1483 self.entropyTools.aggregate_edb(tbz2file = package_filename, dbfile = dbpath) 1484 return dbpath
1485
1486 - def get_tmp_dbpath(self):
1487 dbpath = etpConst['packagestmpdir']+"/"+str(self.entropyTools.get_random_number()) 1488 while os.path.isfile(dbpath): 1489 dbpath = etpConst['packagestmpdir']+"/"+str(self.entropyTools.get_random_number()) 1490 return dbpath
1491
1492 - def quickpkg(self, atomstring, savedir = None):
1493 if savedir == None: 1494 savedir = etpConst['packagestmpdir'] 1495 if not os.path.isdir(etpConst['packagestmpdir']): 1496 os.makedirs(etpConst['packagestmpdir']) 1497 # match package 1498 match = self.clientDbconn.atomMatch(atomstring) 1499 if match[0] == -1: 1500 return -1,None,None 1501 atom = self.clientDbconn.atomMatch(match[0]) 1502 pkgdata = self.clientDbconn.getPackageData(match[0]) 1503 resultfile = self.quickpkg_handler(pkgdata = pkgdata, dirpath = savedir) 1504 if resultfile == None: 1505 return -1,atom,None 1506 else: 1507 return 0,atom,resultfile
1508
1509 - def quickpkg_handler(self, pkgdata, dirpath, edb = True, 1510 portdbPath = None, fake = False, compression = "bz2", shiftpath = ""):
1511 1512 import tarfile 1513 1514 if compression not in ("bz2","","gz"): 1515 compression = "bz2" 1516 1517 # getting package info 1518 pkgtag = '' 1519 pkgrev = "~"+str(pkgdata['revision']) 1520 if pkgdata['versiontag']: pkgtag = "#"+pkgdata['versiontag'] 1521 pkgname = pkgdata['name']+"-"+pkgdata['version']+pkgrev+pkgtag # + version + tag 1522 pkgcat = pkgdata['category'] 1523 dirpath += "/"+pkgname+etpConst['packagesext'] 1524 if os.path.isfile(dirpath): 1525 os.remove(dirpath) 1526 tar = tarfile.open(dirpath,"w:"+compression) 1527 1528 if not fake: 1529 1530 contents = sorted([x for x in pkgdata['content']]) 1531 1532 # collect files 1533 for path in contents: 1534 # convert back to filesystem str 1535 encoded_path = path 1536 path = path.encode('raw_unicode_escape') 1537 path = shiftpath+path 1538 try: 1539 exist = os.lstat(path) 1540 except OSError: 1541 continue # skip file 1542 arcname = path[len(shiftpath):] # remove shiftpath 1543 if arcname.startswith("/"): 1544 arcname = arcname[1:] # remove trailing / 1545 ftype = pkgdata['content'][encoded_path] 1546 if str(ftype) == '0': ftype = 'dir' # force match below, '0' means databases without ftype 1547 if 'dir' == ftype and \ 1548 not stat.S_ISDIR(exist.st_mode) and \ 1549 os.path.isdir(path): # workaround for directory symlink issues 1550 path = os.path.realpath(path) 1551 1552 tarinfo = tar.gettarinfo(path, arcname) 1553 1554 if stat.S_ISREG(exist.st_mode): 1555 tarinfo.mode = stat.S_IMODE(exist.st_mode) 1556 tarinfo.type = tarfile.REGTYPE 1557 f = open(path) 1558 try: 1559 tar.addfile(tarinfo, f) 1560 finally: 1561 f.close() 1562 else: 1563 tar.addfile(tarinfo) 1564 1565 tar.close() 1566 1567 # appending xpak metadata 1568 import entropy.xpak as xpak 1569 Spm = self.Spm() 1570 1571 spm_name = self.entropyTools.remove_tag(pkgname) 1572 spm_name = self.entropyTools.remove_entropy_revision(spm_name) 1573 if portdbPath is None: 1574 spm_pkg = os.path.join(pkgcat, spm_name) 1575 dbbuild = Spm.get_installed_package_build_script_path(spm_pkg) 1576 dbdir = os.path.dirname(dbbuild) 1577 else: 1578 dbdir = os.path.join(portdbPath, pkgcat, spm_name) 1579 1580 if os.path.isdir(dbdir): 1581 tbz2 = xpak.tbz2(dirpath) 1582 tbz2.recompose(dbdir) 1583 1584 if edb: 1585 self.inject_entropy_database_into_package(dirpath, pkgdata) 1586 1587 if os.path.isfile(dirpath): 1588 return dirpath 1589 return None
1590 1591
1592 -class MatchMixin:
1593
1594 - def get_package_action(self, match):
1595 """ 1596 @input: matched atom (idpackage,repoid) 1597 @output: 1598 upgrade: int(2) 1599 install: int(1) 1600 reinstall: int(0) 1601 downgrade: int(-1) 1602 """ 1603 dbconn = self.open_repository(match[1]) 1604 pkgkey, pkgslot = dbconn.retrieveKeySlot(match[0]) 1605 results = self.clientDbconn.searchKeySlot(pkgkey, pkgslot) 1606 if not results: return 1 1607 1608 installed_idpackage = results[0][0] 1609 pkgver, pkgtag, pkgrev = dbconn.getVersioningData(match[0]) 1610 installedVer, installedTag, installedRev = self.clientDbconn.getVersioningData(installed_idpackage) 1611 pkgcmp = self.entropyTools.entropy_compare_versions((pkgver,pkgtag,pkgrev),(installedVer,installedTag,installedRev)) 1612 if pkgcmp == 0: 1613 return 0 1614 elif pkgcmp > 0: 1615 return 2 1616 return -1
1617
1618 - def get_masked_package_reason(self, match):
1619 idpackage, repoid = match 1620 dbconn = self.open_repository(repoid) 1621 idpackage, idreason = dbconn.idpackageValidator(idpackage) 1622 masked = False 1623 if idpackage == -1: masked = True 1624 return masked, idreason, self.SystemSettings['pkg_masking_reasons'].get(idreason)
1625
1626 - def get_match_conflicts(self, match):
1627 m_id, m_repo = match 1628 dbconn = self.open_repository(m_repo) 1629 conflicts = dbconn.retrieveConflicts(m_id) 1630 found_conflicts = set() 1631 for conflict in conflicts: 1632 my_m_id, my_m_rc = self.clientDbconn.atomMatch(conflict) 1633 if my_m_id != -1: 1634 # check if the package shares the same slot 1635 match_data = dbconn.retrieveKeySlot(m_id) 1636 installed_match_data = self.clientDbconn.retrieveKeySlot(my_m_id) 1637 if match_data != installed_match_data: 1638 found_conflicts.add(my_m_id) 1639 return found_conflicts
1640
1641 - def is_match_masked(self, match, live_check = True):
1642 m_id, m_repo = match 1643 dbconn = self.open_repository(m_repo) 1644 idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check) 1645 if idpackage != -1: 1646 return False 1647 return True
1648
1649 - def is_match_masked_by_user(self, match, live_check = True):
1650 # (query_status,masked?,) 1651 m_id, m_repo = match 1652 if m_repo not in self.validRepositories: return False 1653 dbconn = self.open_repository(m_repo) 1654 idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check) 1655 if idpackage != -1: return False #,False 1656 myr = self.SystemSettings['pkg_masking_reference'] 1657 user_masks = [myr['user_package_mask'],myr['user_license_mask'],myr['user_live_mask']] 1658 if idreason in user_masks: 1659 return True #,True 1660 return False #,True
1661
1662 - def is_match_unmasked_by_user(self, match, live_check = True):
1663 # (query_status,unmasked?,) 1664 m_id, m_repo = match 1665 if m_repo not in self.validRepositories: return False 1666 dbconn = self.open_repository(m_repo) 1667 idpackage, idreason = dbconn.idpackageValidator(m_id, live = live_check) 1668 if idpackage == -1: return False #,False 1669 myr = self.SystemSettings['pkg_masking_reference'] 1670 user_masks = [ 1671 myr['user_package_unmask'],myr['user_live_unmask'],myr['user_package_keywords'], 1672 myr['user_repo_package_keywords_all'], myr['user_repo_package_keywords'] 1673 ] 1674 if idreason in user_masks: 1675 return True #,True 1676 return False #,True
1677
1678 - def mask_match(self, match, method = 'atom', dry_run = False, clean_all_cache = False):
1679 if self.is_match_masked(match, live_check = False): return True 1680 methods = { 1681 'atom': self.mask_match_by_atom, 1682 'keyslot': self.mask_match_by_keyslot, 1683 } 1684 rc = self._mask_unmask_match(match, method, methods, dry_run = dry_run, clean_all_cache = clean_all_cache) 1685 if dry_run: # inject if done "live" 1686 self.SystemSettings['live_packagemasking']['unmask_matches'].discard(match) 1687 self.SystemSettings['live_packagemasking']['mask_matches'].add(match) 1688 return rc
1689
1690 - def unmask_match(self, match, method = 'atom', dry_run = False, clean_all_cache = False):
1691 if not self.is_match_masked(match, live_check = False): return True 1692 methods = { 1693 'atom': self.unmask_match_by_atom, 1694 'keyslot': self.unmask_match_by_keyslot, 1695 } 1696 rc = self._mask_unmask_match(match, method, methods, dry_run = dry_run, clean_all_cache = clean_all_cache) 1697 if dry_run: # inject if done "live" 1698 self.SystemSettings['live_packagemasking']['unmask_matches'].add(match) 1699 self.SystemSettings['live_packagemasking']['mask_matches'].discard(match) 1700 return rc
1701
1702 - def _mask_unmask_match(self, match, method, methods_reference, dry_run = False, clean_all_cache = False):
1703 1704 f = methods_reference.get(method) 1705 if not callable(f): 1706 raise IncorrectParameter('IncorrectParameter: %s: %s' % (_("not a valid method"),method,) ) 1707 1708 self.Cacher.discard() 1709 done = f(match, dry_run) 1710 if done and not dry_run: 1711 self.SystemSettings.clear() 1712 1713 # clear atomMatch cache anyway 1714 if clean_all_cache and not dry_run: 1715 self.clear_dump_cache(etpCache['world_available']) 1716 self.clear_dump_cache(etpCache['world_update']) 1717 self.clear_dump_cache(etpCache['critical_update']) 1718 1719 self.clear_dump_cache(etpCache['check_package_update']) 1720 self.clear_dump_cache(etpCache['filter_satisfied_deps']) 1721 self.clear_dump_cache(self.atomMatchCacheKey) 1722 self.clear_dump_cache(etpCache['dep_tree']) 1723 self.clear_dump_cache("%s/%s%s/" % (etpCache['dbMatch'],etpConst['dbnamerepoprefix'],match[1],)) 1724 self.clear_dump_cache("%s/%s%s/" % (etpCache['dbSearch'],etpConst['dbnamerepoprefix'],match[1],)) 1725 1726 cl_id = self.sys_settings_client_plugin_id 1727 self.SystemSettings[cl_id]['masking_validation']['cache'].clear() 1728 return done
1729
1730 - def unmask_match_by_atom(self, match, dry_run = False):
1731 m_id, m_repo = match 1732 dbconn = self.open_repository(m_repo) 1733 atom = dbconn.retrieveAtom(m_id) 1734 return self.unmask_match_generic(match, atom, dry_run = dry_run)
1735
1736 - def unmask_match_by_keyslot(self, match, dry_run = False):
1737 m_id, m_repo = match 1738 dbconn = self.open_repository(m_repo) 1739 keyslot = "%s:%s" % dbconn.retrieveKeySlot(m_id) 1740 return self.unmask_match_generic(match, keyslot, dry_run = dry_run)
1741
1742 - def mask_match_by_atom(self, match, dry_run = False):
1743 m_id, m_repo = match 1744 dbconn = self.open_repository(m_repo) 1745 atom = dbconn.retrieveAtom(m_id) 1746 return self.mask_match_generic(match, atom, dry_run = dry_run)
1747
1748 - def mask_match_by_keyslot(self, match, dry_run = False):
1749 m_id, m_repo = match 1750 dbconn = self.open_repository(m_repo) 1751 keyslot = "%s:%s" % dbconn.retrieveKeySlot(m_id) 1752 return self.mask_match_generic(match, keyslot, dry_run = dry_run)
1753
1754 - def unmask_match_generic(self, match, keyword, dry_run = False):
1755 self.clear_match_mask(match, dry_run) 1756 m_file = self.SystemSettings.get_setting_files_data()['unmask'] 1757 return self._mask_unmask_match_generic(keyword, m_file, dry_run = dry_run)
1758
1759 - def mask_match_generic(self, match, keyword, dry_run = False):
1760 self.clear_match_mask(match, dry_run) 1761 m_file = self.SystemSettings.get_setting_files_data()['mask'] 1762 return self._mask_unmask_match_generic(keyword, m_file, dry_run = dry_run)
1763
1764 - def _mask_unmask_match_generic(self, keyword, m_file, dry_run = False):
1765 exist = False 1766 if not os.path.isfile(m_file): 1767 if not os.access(os.path.dirname(m_file),os.W_OK): 1768 return False # cannot write 1769 elif not os.access(m_file, os.W_OK): 1770 return False 1771 elif not dry_run: 1772 exist = True 1773 1774 if dry_run: 1775 return True 1776 1777 content = [] 1778 if exist: 1779 f = open(m_file,"r") 1780 content = [x.strip() for x in f.readlines()] 1781 f.close() 1782 content.append(keyword) 1783 m_file_tmp = m_file+".tmp" 1784 f = open(m_file_tmp,"w") 1785 for line in content: 1786 f.write(line+"\n") 1787 f.flush() 1788 f.close() 1789 shutil.move(m_file_tmp,m_file) 1790 return True
1791
1792 - def clear_match_mask(self, match, dry_run = False):
1793 setting_data = self.SystemSettings.get_setting_files_data() 1794 masking_list = [setting_data['mask'],setting_data['unmask']] 1795 return self._clear_match_generic(match, masking_list = masking_list, dry_run = dry_run)
1796
1797 - def _clear_match_generic(self, match, masking_list = [], dry_run = False):
1798 1799 self.SystemSettings['live_packagemasking']['unmask_matches'].discard(match) 1800 self.SystemSettings['live_packagemasking']['mask_matches'].discard(match) 1801 1802 if dry_run: return 1803 1804 for mask_file in masking_list: 1805 if not (os.path.isfile(mask_file) and os.access(mask_file,os.W_OK)): continue 1806 f = open(mask_file,"r") 1807 newf = self.entropyTools.open_buffer() 1808 line = f.readline() 1809 while line: 1810 line = line.strip() 1811 if line.startswith("#"): 1812 newf.write(line+"\n") 1813 line = f.readline() 1814 continue 1815 elif not line: 1816 newf.write("\n") 1817 line = f.readline() 1818 continue 1819 mymatch = self.atom_match(line, packagesFilter = False) 1820 if mymatch == match: 1821 line = f.readline() 1822 continue 1823 newf.write(line+"\n") 1824 line = f.readline() 1825 f.close() 1826 tmpfile = mask_file+".w_tmp" 1827 f = open(tmpfile,"w") 1828 f.write(newf.getvalue()) 1829 f.flush() 1830 f.close() 1831 newf.close() 1832 shutil.move(tmpfile,mask_file)
1833