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

Source Code for Module entropy.client.interfaces.methods

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