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