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