diff --git a/yum-presto/presto.py b/yum-presto/presto.py index b545b9e..1b0c96c 100644 --- a/yum-presto/presto.py +++ b/yum-presto/presto.py @@ -21,7 +21,6 @@ # Copyright 2007 Red Hat, Inc. -- Jeremy Katz import os -import sys import subprocess import gzip import thread @@ -36,6 +35,7 @@ from yum.plugins import TYPE_CORE, PluginYumExit import yum.Errors import yum.misc from urlgrabber.grabber import URLGrabError +from urlgrabber.progress import format_number complete_download_size = 0 actual_download_size = 0 @@ -47,26 +47,30 @@ plugin_type = (TYPE_CORE,) pinfo = {} def verifyDelta(sequence, arch): + """checks that the deltarpm can be applied""" if subprocess.call(["/usr/bin/applydeltarpm", "-a", arch, "-C", "-s", sequence], close_fds=True): return False return True def applyDelta(deltarpmfile, newrpmfile, arch): + """applies the deltarpm to create the new rpm""" if subprocess.call(["/usr/bin/applydeltarpm", "-a", arch, deltarpmfile, newrpmfile], close_fds=True): return False return True def reconstruct(conduit, rpmlocal, rpmarch, deltalocal): + """logic around applyDelta""" retlist = "" global actual_download_size if not applyDelta(deltalocal, rpmlocal, rpmarch): - retlist += "Error rebuilding rpm from %s! Will download full package.\n" % os.path.basename(deltalocal) + retlist += "Error rebuilding rpm from %s! Will " \ + "download full package.\n" % os.path.basename(deltalocal) try: os.unlink(rpmlocal) - except: + except (OSError, IOError), e: pass else: # Calculate new download size @@ -76,24 +80,19 @@ def reconstruct(conduit, rpmlocal, rpmarch, deltalocal): actual_download_size = actual_download_size - rpm_size + drpm_size # Check to see whether or not we should keep the drpms - # FIXME: Is there any way to see whether or not a Boolean option was not set? - if conduit.confBool('main', 'neverkeepdeltas'): - delete = True - elif conduit.confBool('main', 'keepdeltas'): + delete = not conduit.getConf().keepcache + if conduit.confBool('main', 'keepdeltas'): delete = False - elif conduit.getConf().keepcache != 0: - delete = False - else: - delete = True if delete: try: os.unlink(deltalocal) - except: + except (OSError, IOError), e: pass return retlist class ReconstructionThread(threading.Thread): + """Threaded process to create rpms from deltarpms""" def __init__(self, queue, lock, run_function): threading.Thread.__init__(self) self.run_function = run_function @@ -128,28 +127,36 @@ class ReconstructionThread(threading.Thread): self.lock.release() -def getDelta(po, presto, rpmdb): +def getDelta(po, presto, conduit): """Does the package have a reasonable delta for us to use?""" global complete_download_size global actual_download_size + rpmdb = conduit.getRpmDB() # local packages don't make sense to use a delta for... if hasattr(po, 'pkgtype') and po.pkgtype == 'local': + conduit.info(5, "Package %s.%s is local, not using a delta" + % (po.name, po.arch)) return None if po.remote_url.startswith("file:/"): # kind of a hack, but file:/ repos are basically local + conduit.info(5, "Package %s.%s is in a file:// repo, not using a delta" + % (po.name, po.arch)) return None # if there's not presto info for the repo, we don't have a delta for # the package if not presto.has_key(po.repo.id): + conduit.info(5, "No delta information for repository %s." % po.repo.id) return None deltainfo = presto[po.repo.id] # any deltas for the new package in the repo? - nevra = "%s-%s:%s-%s.%s" %(po.name, po.epoch, po.version, + nevra = "%s-%s:%s-%s.%s" % (po.name, po.epoch, po.version, po.release, po.arch) if not deltainfo.has_key(nevra): + conduit.info(5, "Could not find delta rpm for package %s.%s." + % (po.name, po.arch)) return None deltas = deltainfo[nevra] @@ -161,21 +168,27 @@ def getDelta(po, presto, rpmdb): if po.verifyLocalPkg(): # we've got it. actual_download_size -= cursize complete_download_size -= cursize + conduit.info(5, "Already have package for %s.%s." + % (po.name, po.arch)) return None if cursize < totsize: # we have part of the file; do a reget + conduit.info(5, "Already have part of package for %s.%s." + % (po.name, po.arch)) return None os.unlink(local) # did we have a previous package of the same arch installed? installed = rpmdb.searchNevra(po.name, None, None, None, po.arch) if len(installed) == 0: + conduit.info(5, "Package for %s.%s of the same arch not installed." + % (po.name, po.arch)) return None # now, let's see if there's a delta for us... bestdelta = None for oldpo in installed: - evr = "%s:%s-%s" %(oldpo.epoch, oldpo.version, oldpo.release) + evr = "%s:%s-%s" % (oldpo.epoch, oldpo.version, oldpo.release) if not deltas.has_key(evr): continue delta = deltas[evr] @@ -188,7 +201,8 @@ def getDelta(po, presto, rpmdb): continue bestdelta = delta - + if not bestdelta: + conduit.info(5, "No delta rpm for %s.%s." % (po.name, po.arch)) return bestdelta @@ -212,7 +226,7 @@ def downloadPkgs(conduit, presto, download_pkgs=None): # see which deltas we need to download; if the delta is already # downloaded, we can start it reconstructing in the background for po in download_pkgs: - delta = getDelta(po, presto, conduit.getRpmDB()) + delta = getDelta(po, presto, conduit) if delta is None: continue @@ -231,7 +245,9 @@ def downloadPkgs(conduit, presto, download_pkgs=None): delta['checksum']) except URLGrabError, e: if po.repo.cache: - raise yum.Errors.RepoError, "Caching enabled and local cache for %s doesn't match checksum" %(deltapath,) + msg = "Caching enabled and local cache for " \ + "%sdoesn't match checksum" % deltapath + raise yum.Errors.RepoError, msg else: cursize = os.stat(deltapath)[6] totsize = long(delta['size']) @@ -268,7 +284,7 @@ def downloadPkgs(conduit, presto, download_pkgs=None): 'to download' % (deltadir,)) continue try: - text = "(%s/%s): %s" %(i, len(remote_pkgs), + text = "(%s/%s): %s" % (i, len(remote_pkgs), os.path.basename(delta['filename'])) deltafile = po.repo._getFile(url=po.basepath, relative=delta['filename'], @@ -305,12 +321,16 @@ def downloadPkgs(conduit, presto, download_pkgs=None): return errors class DeltaInfo(object): + """Base Delta rpm info object""" def __init__(self, elem): self.epoch = elem.get("oldepoch") self.version = elem.get("oldversion") self.release = elem.get("oldrelease") - - self.filename = self.sequence = self.size = self.checksum = self.checksum_type = None + self.filename = None + self.sequence = None + self.size = None + self.checksum = None + self.checksum_type = None for x in elem.getchildren(): if x.tag == "checksum": @@ -318,10 +338,12 @@ class DeltaInfo(object): setattr(self, x.tag, x.text) def evr(self): - return "%s:%s-%s" %(self.epoch, self.version, self.release) + return "%s:%s-%s" % (self.epoch, self.version, self.release) def __str__(self): - return "filename: %s, sequence: %s, size: %s, checksum (%s) = %s" % (self.filename, self.sequence, self.size, self.checksum_type, self.checksum) + return "filename: %s, sequence: %s, size: %s, checksum (%s) = %s" \ + % (self.filename, self.sequence, self.size, + self.checksum_type, self.checksum) def __getitem__(self, key): return getattr(self, key) @@ -339,7 +361,7 @@ class NewPackage(object): self.deltas[d.evr()] = d def nevra(self): - return "%s-%s:%s-%s.%s" %(self.name, self.epoch, self.version, + return "%s-%s:%s-%s.%s" % (self.name, self.epoch, self.version, self.release, self.arch) def __str__(self): @@ -366,46 +388,6 @@ class PrestoParser(object): def getDeltas(self): return self.deltainfo -def format_number(number, SI=False, space=''): - """Turn numbers into human-readable metric-like numbers""" - symbols = ['', # (none) - 'K', # kilo - 'M', # mega - 'G', # giga - 'T', # tera - 'P', # peta - 'E', # exa - 'Z', # zetta - 'Y'] # yotta - - if SI: step = 1000.0 - else: step = 1024.0 - - thresh = 999 - depth = 0 - - # we want numbers between - while number > thresh: - depth = depth + 1 - number = number / step - - # just in case someone needs more than 1000 yottabytes! - diff = depth - len(symbols) + 1 - if diff > 0: - depth = depth - diff - number = number * thresh**depth - - if type(number) == type(1) or type(number) == type(1L): - format = '%i%s%s' - elif number < 9.95: - # must use 9.95 for proper sizing. For example, 9.99 will be - # rounded to 10.0 with the .1f format string (which is too long) - format = '%.1f%s%s' - else: - format = '%.0f%s%s' - - return(format % (number, space, symbols[depth])) - # Configuration stuff def config_hook(conduit): # Add --disable-presto option @@ -431,8 +413,8 @@ def xpostreposetup_hook(conduit, repos=None): for active_repo in repos: try: deltamd = active_repo.retrieveMD("prestodelta") - except: - conduit.info(2, "No Presto metadata available for %s" %(active_repo,)) + except yum.Errors.RepoError, e: + conduit.info(2, "No Presto metadata available for %s" % active_repo) continue pinfo[active_repo.id] = PrestoParser(deltamd).getDeltas() @@ -490,6 +472,9 @@ def posttrans_hook(conduit): drpm_string = format_number(actual_download_size) rpm_string = format_number(complete_download_size) - conduit.info(2, "Size of all updates downloaded from Presto-enabled repositories: %s" % drpm_string) - conduit.info(2, "Size of updates that would have been downloaded if Presto wasn't enabled: %s" % rpm_string) - conduit.info(2, "This is a savings of %i percent" % (100 - ((actual_download_size * 100) / complete_download_size))) + conduit.info(2, "Size of all updates downloaded from Presto-enabled " \ + "repositories: %s" % drpm_string) + conduit.info(2, "Size of updates that would have been downloaded if " \ + "Presto wasn't enabled: %s" % rpm_string) + saveper = 100 - ((actual_download_size * 100) / complete_download_size) + conduit.info(2, "This is a savings of %i percent" % saveper)