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