summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmed Kamal <email.ahmedkamal@googlemail.com>2007-02-09 23:48:27 +0200
committerAhmed Kamal <email.ahmedkamal@googlemail.com>2007-02-09 23:48:27 +0200
commit9cc6347501e207ed796fd5f181f6a318c27cb45f (patch)
tree8cdaa835930082562a1cd79e78d956e99dbfb265
parentfbe55954c8378b7cfaa0c24aeb3bac5842f53c16 (diff)
downloadpresto-9cc6347501e207ed796fd5f181f6a318c27cb45f.zip
presto-9cc6347501e207ed796fd5f181f6a318c27cb45f.tar.gz
presto-9cc6347501e207ed796fd5f181f6a318c27cb45f.tar.xz
Initial code commit
-rw-r--r--README17
-rw-r--r--deltarpm.py513
-rw-r--r--yum-deltarpm.conf4
-rw-r--r--yum-deltarpm.py76
4 files changed, 609 insertions, 1 deletions
diff --git a/README b/README
index e7be26b..3e7853e 100644
--- a/README
+++ b/README
@@ -1 +1,16 @@
-Blank file for repo creation
+Presto: A project to add delta rpm support into yum for Fedora users
+https://hosted.fedoraproject.org/projects/presto/wiki/WikiStart
+Most of the code base has been written by Marcel Hild <mhild@redhat.com> as up2date/satellite-server delta rpm support. Code adaptation for yum done by Ahmed Kamal <email.ahmedkamal@googlemail.com>
+
+Installation:
+=============
+1- Make sure deltarpm is installed on your system (yum -y install deltarpm)
+2- Place the files in the following locations (/usr/share/deltarpm-python/deltarpm.py /usr/lib/yum-plugins/yum-deltarpm.py /etc/yum/pluginconf.d/yum-deltarpm.conf)
+3- Modify the conf file to point to any test repo you have setup
+4- Use the server.py script to generate drpms from different update levels of some rpms in your test repo
+5- Now install an old rpm from your repo using rpm, try updating it using yum. The plugin should kickin, try to download the drpm, reconstruct the full rpm, and yum should install that
+
+Notes:
+=====
+- The code is in a *very* early stage. Minimal testing has been done.
+- I (Ahmed) will probably be helping/maintaining this project, but probably I will not be the lead developer behind this
diff --git a/deltarpm.py b/deltarpm.py
new file mode 100644
index 0000000..aac0c02
--- /dev/null
+++ b/deltarpm.py
@@ -0,0 +1,513 @@
+# author: Lars Herrmann <herrmann@redhat.com>
+# Ahmed Kamal <email.ahmedkamal@googlemail.com>
+# license: GPL (see COPYING file in distribution)
+#
+# this module provides a python wrapper around deltarpm tools written by suse
+#
+# TODO: catch exceptions wherever possible and raise useful ones ;)
+# see TODO lines in methods
+
+MAKE='/usr/bin/makedeltarpm'
+APPLY='/usr/bin/applydeltarpm'
+SEQ_SUFFIX='seq'
+SUFFIX='drpm'
+
+# constants for up2date configuration
+# major flag to enable or disable use of delta rpms , used in patched up2date
+USE_DELTA='useDeltaRpms'
+# directory where to store generated rpms - depending on use up2date storage or temp storage for fetching
+STORAGE='storageDir'
+# directory where to store delta rpm files, normally temporarily
+DELTA_STORAGE='deltaStorageDir'
+# url to fetch delta rpm files from
+DELTA_URL='deltaRpmURL'
+# directory where local copies of oldrpms reside
+DELTA_OLDRPM_REPOSITORY='deltaOldRpmRepository'
+# flag indicates if downloading and verfying sequence files before downloading delta rpms - saves bandwidth
+DELTA_USE_SEQ='deltaUseSequences'
+# flag indicates if local copies of old rpms should be used
+DELTA_USE_OLDRPMS='deltaUseOldRpms'
+SIZELIMIT='deltaRpmSizeLimit'
+
+# default setting for STORAGE - same as with up2date
+DEFAULT_STORAGE='/var/spool/up2date'
+# default setting for DELTA_STORAGE
+DEFAULT_DELTA_STORAGE='/var/spool/up2date/deltarpms'
+
+# enable or disable to see verbose messages on stdout
+DEBUG=0
+
+import popen2
+import string
+import os
+import glob
+
+class Process:
+ """wrapper class to execute programs and return exitcode and output (stdout and stderr combined)"""
+ def __init__(self):
+ self.__stdout=None
+ self.__returncode=None
+ self.__command=None
+ self.__args=None
+
+ def run(self, command, *args):
+ self.__command=command
+ self.__args=args
+ cmdline=command+" "+string.join(args, " ")
+ if DEBUG:
+ print 'DEBUG: %s.%s: executing %s' % (self.__class__, 'run', cmdline)
+ pipe = popen2.Popen4(cmdline)
+ self.__stdout=pipe.fromchild.read()
+ retcode = pipe.wait()
+ if os.WIFEXITED(retcode):
+ self.__returncode = os.WEXITSTATUS(retcode)
+ else:
+ self.__returncode = retcode
+ # fallback to old implementation - works better ?
+ #stdoutp = os.popen(cmdline,'r',1)
+ #self.__stdout = stdoutp.read()
+ #retcode = stdoutp.close()
+ #if retcode is None:
+ # self.__returncode = 0
+ #else:
+ # self.__returncode = retcode
+
+ def getOutput(self):
+ return self.__stdout
+
+ def returnCode(self):
+ return self.__returncode
+
+class RpmDescription:
+ """Wrapper class to encapsulate RPM attributes"""
+
+ def __init__(self, name,version,release,arch, epoch=''):
+ """constructor: provide major attributes in correct order - epoch optional"""
+ self.name=name
+ self.version=version
+ self.release=release
+ self.epoch=epoch
+ self.arch=arch
+ if DEBUG:
+ print 'DEBUG: %s.%s: created: %s' % (self.__class__, '__init__', self)
+
+ def rhnFileName(self):
+ """return file name in e:nvr.a notation"""
+ if self.epoch:
+ return "%s:%s-%s-%s.%s" % (self.epoch, self.name, self.version, self.release, self.arch)
+ else:
+ return "%s-%s-%s.%s" % (self.name, self.version, self.release, self.arch)
+
+ def evr(self):
+ """return file name in e:vr notation as used in Satellite repository"""
+ if self.epoch:
+ return "%s:%s-%s" % (self.epoch, self.version, self.release)
+ else:
+ return "%s-%s" % (self.version, self.release)
+
+ def satellitePath(self):
+ """return file path as used in Satellite repository - relative to /var/satellite"""
+ return "%s/%s/%s/%s.rpm" % (self.name, self.evr(), self.arch, self.fileName())
+
+ def fileName(self):
+ """return file name in nvr.a notation as used in up2date storageDir"""
+ return "%s-%s-%s.%s" % (self.name, self.version, self.release, self.arch)
+
+ def __str__(self):
+ return self.rhnFileName()
+
+class DeltaRpmWrapper:
+ """wrapper around deltarpm binaries - implement methods for create, apply and verify delta rpms
+ - raises exceptions if exitcode of binaries was != 0"""
+
+ def __init__(self, storageDir, oldRpmDir=None):
+ """constructor - params: storageDir=path of delta rpm storage, oldRpmDir = path of full rpm repository (optional)"""
+ self.storageDir = storageDir
+ self.oldRpmDir = oldRpmDir
+ if DEBUG:
+ print 'DEBUG: %s.%s: created: %s' % (self.__class__, '__init__', self)
+
+ def __str__(self):
+ return "%s: storageDir=%s, oldRpmDir=%s" % (self.__class__, self.storageDir, self.oldRpmDir)
+
+ def create(self, oldrpm, newrpm):
+ """wraps execution of makedeltarpm -s seqfile oldrpm newrpm deltarpm
+ constructs file names and paths based on given RpmDescription and instance settings for directories"""
+ if DEBUG:
+ print 'DEBUG: %s.create(%s,%s)' % (self.__class__,oldrpm, newrpm)
+
+ # contruct filenames in satellite repository
+ oldrpmfile = "%s/%s" % (self.oldRpmDir, oldrpm.satellitePath())
+ newrpmfile = "%s/%s" % (self.oldRpmDir, newrpm.satellitePath())
+
+ # check if file exists
+ # this is an ugly workaround, where the epoch is 0, but satellite
+ # stores it as 0:package, so we try it with epoch = string "0"
+ # we do glob.glob here, because satellite path could also contain
+ # shell wildcards
+ if not glob.glob(oldrpmfile):
+ if not oldrpm.epoch:
+ oldrpm.epoch = '0'
+ oldrpmfile = "%s/%s" % (self.oldRpmDir, oldrpm.satellitePath())
+ if not glob.glob(newrpmfile):
+ if not newrpm.epoch:
+ newrpm.epoch = '0'
+ newrpmfile = "%s/%s" % (self.oldRpmDir, newrpm.satellitePath())
+
+ # construct filenames in deltarpm repository:
+ # /root/oldrpm/newrpm.{seq|rpm}
+ # with oldrpm|newrpm in e:nvr.a notation
+ deltarpm = newrpm.rhnFileName()
+ # files should go to /root/oldrpm
+ deltadir = "%s/%s" % (self.storageDir, oldrpm.rhnFileName())
+ # TODO check if is a directory
+ if not os.access(deltadir, os.F_OK):
+ if DEBUG:
+ print 'DEBUG: %s.create: mkdir(%s)' % (__name__, deltadir)
+ os.makedirs(deltadir)
+ # filenames
+ deltarpmfile = "%s/%s" % (deltadir, deltarpm)
+ p=Process()
+ p.run(MAKE, '-s', "%s.%s" % (deltarpmfile,SEQ_SUFFIX), oldrpmfile, newrpmfile, "%s.rpm" % deltarpmfile)
+ # save output into logfile
+ logfile = "%s.log" % deltarpmfile
+ fd = open(logfile,'w')
+ fd.write(p.getOutput())
+ print >> fd, "of: %s \nnf: %s" % (oldrpmfile, newrpmfile)
+
+ fd.close()
+ if p.returnCode():
+ raise Exception("%s.create: exitcode was %s - see %s" % (self.__class__,p.returnCode(), logfile))
+ return deltarpmfile
+
+ def apply(self, oldrpm, newrpm, deltarpmfile, useOldRpms = 0):
+ """wraps execution of applydeltarpm [-r oldrpm] deltarpm newrpm -
+ constructs file names and paths based on given RpmDescription and instance settings for directories"""
+ # args: RpmDescription
+ # TODO: test args for type == instance and __class__ == RpmDescription
+ # TODO: test without useOldRpms
+ if DEBUG:
+ print 'DEBUG: %s.apply(%s,%s,%s,%s)' % (self.__class__,oldrpm, newrpm, deltarpmfile, useOldRpms)
+ p=Process()
+ # targetrpm filename
+ newrpmfile = "%s/%s-%s-%s.%s.rpm" % (self.storageDir, newrpm.name, newrpm.version, newrpm.release, newrpm.arch)
+ if useOldRpms:
+ # TODO: check if self.oldRpmDir is set and exists !
+ oldrpmfile = "%s/%s-%s-%s.%s.rpm" % (self.oldRpmDir, oldrpm.name, oldrpm.version, oldrpm.release, oldrpm.arch)
+ p.run(APPLY, '-r', oldrpmfile, deltarpmfile, newrpmfile)
+ else:
+ p.run(APPLY, deltarpmfile, newrpmfile)
+ if p.returnCode():
+ # in case of error save output into logfile - will not be removed for further inspection
+ logfile = "%s.log" % deltarpmfile
+ fd = open(logfile,'w')
+ fd.write(p.getOutput())
+ fd.close()
+ raise Exception("%s.apply(%s) exitcode was %d - see %s" % (self.__class__, newrpm, p.returnCode(), logfile))
+ return newrpmfile
+
+ def verifySequence(self, sequencefile, oldrpm = None, useOldRpms = 0):
+ """wraps execution of applydeltarpm [-r oldrpm] -s seqfilecontent -
+ constructs file names and paths based on given RpmDescription and instance settings for directories"""
+ if DEBUG:
+ print 'DEBUG: %s.verify(%s,%s,%s)' % (self.__class__,sequencefile, oldrpm, useOldRpms)
+ # read sequencefile
+ fd = open(sequencefile)
+ # TODO: is strip safe here ? could remove other chars than the linebreak
+ content = string.strip(string.join(fd.readlines()))
+ fd.close()
+ p = Process()
+ if useOldRpms:
+ oldrpmfile = "%s/%s-%s-%s.%s.rpm" % (self.oldRpmDir, oldrpm.name, oldrpm.version, oldrpm.release, oldrpm.arch)
+ p.run(APPLY, '-s', content, '-r', oldrpmfile)
+ else:
+ p.run(APPLY, '-s', content)
+ if p.returnCode():
+ # in case of error save output into logfile - will not be removed for further inspection
+ logfile = "%s.log" % deltarpmfile
+ fd = open(logfile,'w')
+ fd.write(p.getOutput())
+ fd.close()
+ raise Exception("could not verify sequence of delta rpm: %d - see %s" % (p.returnCode(), logfile))
+class Fetcher:
+ """ abstract class to be derived from classes implementing fetching seq and rpm files """
+
+ def fetchSequence(self, oldrpm, targetrpm):
+ pass
+
+ def fetchDeltaRpm(self, oldrpm, targetrpm):
+ pass
+
+class HttpFetcher(Fetcher):
+ """ fetching seq and rpm files via http urls"""
+
+ def __init__(self, deltaUrl, destinationDir):
+ """constructor - params: deltaUrl = webapp-url, destinationDir=path to store files"""
+ self.deltaUrl = deltaUrl
+ self.destinationDir = destinationDir
+ if DEBUG:
+ print 'DEBUG: %s.%s: created: %s' % (self.__class__, '__init__', self)
+
+ def fetchSequence(self, oldrpm, targetrpm):
+ if DEBUG:
+ print 'DEBUG: %s.fetchSequence: : (%s,%s)' % (self.__class__, oldrpm, targetrpm)
+ return self.__fetchFile(oldrpm, targetrpm, SEQ_SUFFIX, 1)
+
+ # The following method has been disabled by Fedora Infrastructure team, as
+ # we will not be using a server side web service, rather, delta rpms will be generated
+ # periodically, and client side, will simply download them if applicable
+ def __DISABLED__fetchFile(self, oldrpm, targetrpm, suffix, sequence=0):
+ """private method - uses private module to do the http request to rely on http return code"""
+ import httppost
+ data={}
+ data['oldname'] = oldrpm.name
+ data['oldversion'] = oldrpm.version
+ data['oldrelease'] = oldrpm.release
+ # avoid that httplib would send 'None' and not empty string
+ if oldrpm.epoch:
+ data['oldepoch'] = oldrpm.epoch
+ else:
+ data['oldepoch']=''
+ data['oldarch'] = oldrpm.arch
+ data['newname'] = targetrpm.name
+ data['newversion'] = targetrpm.version
+ data['newrelease'] = targetrpm.release
+ if targetrpm.epoch:
+ data['newepoch'] = targetrpm.epoch
+ else:
+ data['newepoch'] = ''
+ data['newarch'] = targetrpm.arch
+ if sequence:
+ data['sequence']='1'
+
+ fd = httppost.send(self.deltaUrl, data, DEBUG)
+ content = fd.read()
+ fd.close()
+ dest = "%s/%s-%s-%s.%s.%s" % (self.destinationDir, targetrpm.name, targetrpm.version, targetrpm.release, targetrpm.arch, suffix)
+ fd=open(dest,'w')
+ fd.write(content)
+ fd.close
+ return dest
+ def __fetchFile(self, oldrpm, targetrpm, suffix, sequence=0):
+ """private method - uses private module to download delta rpms"""
+ import urllib2
+
+ if sequence:
+ data['sequence']='1'
+
+ drpmName = getDrpmName(oldrpm, targetrpm)
+ fullUrl = '%s%s.%s' % (self.deltaUrl,drpmName,suffix)
+ if DEBUG:
+ print 'DEBUG: oldrpm: %s, newrpm: %s, suffix: %s' % (oldrpm, targetrpm, suffix)
+ print 'DEBUG: %s.__fetchFile: : (%s)' % (self.__class__, fullUrl)
+ try:
+ fd = urllib2.urlopen(fullUrl)
+ except IOError, e:
+ if hasattr(e, 'reason'):
+ raise Exception ("Failed to download delta rpm from URL %s, error: %s" % (fullUrl,e.reason))
+ elif hasattr(e, 'code'):
+ raise Exception ("Failed to download delta rpm from URL %s, error: %s" % (fullUrl,e.code))
+ else:
+ content = fd.read()
+ fd.close()
+ dest = "%s/%s-%s-%s.%s.%s" % (self.destinationDir, targetrpm.name, targetrpm.version, targetrpm.release, targetrpm.arch, suffix)
+ fd=open(dest,'w')
+ fd.write(content)
+ fd.close
+ return dest
+
+ def fetchDeltaRpm(self, oldrpm, targetrpm):
+ if DEBUG:
+ print 'DEBUG: %s.fetchDeltaRpm: : (%s,%s)' % (self.__class__, oldrpm, targetrpm)
+ return self.__fetchFile(oldrpm, targetrpm, SUFFIX, 0)
+
+ def __str__(self):
+ return "%s: deltaUrl=%s, destinationDir=%s" % (self.__class__, self.deltaUrl, self.destinationDir)
+
+class TestFSFetcher(Fetcher):
+ """ fetching seq and rpm files from local filesystem - uses NOT same directory structure as DeltaRpmWrapper.create"""
+
+ def __init__(self, sourceDir, destinationDir):
+ self.sourceDir = sourceDir
+ self.destinationDir = destinationDir
+ if DEBUG:
+ print 'DEBUG: %s.%s: created: %s' % (self.__class__, '__init__', self)
+
+ def __str__(self):
+ return "%s: sourceDir=%s, destinationDir=%s" % (self.__class__, self.sourceDir, self.destinationDir)
+
+ def __copyFile(self, targetrpm, suffix):
+ source = "%s/%s-%s-%s.%s.%s" % (self.sourceDir, targetrpm.name, targetrpm.version, targetrpm.release, targetrpm.arch, suffix)
+ # construct new sequence filename
+ dest = "%s/%s-%s-%s.%s.%s" % (self.destinationDir, targetrpm.name, targetrpm.version, targetrpm.release, targetrpm.arch, suffix)
+ # copy content usind read/write
+ fr=open(source,'r')
+ content=fr.readlines()
+ fr.close()
+ fw=open(dest,'w')
+ fw.writelines(content)
+ fw.close()
+ return dest
+
+
+ def fetchSequence(self, oldrpm, targetrpm):
+ if DEBUG:
+ print 'DEBUG: %s.fetchSequence(%s,%s)' % (self.__class__, oldrpm, targetrpm)
+ return self.__copyFile(targetrpm, SEQ_SUFFIX)
+
+ def fetchDeltaRpm(self, oldrpm, targetrpm):
+ if DEBUG:
+ print 'DEBUG: %s.fetchDeltaRpm(%s,%s)' % (self.__class__, oldrpm, targetrpm)
+ return self.__copyFile(targetrpm, 'rpm')
+
+def getInstalled(targetrpm, sizelimit=0):
+ """retrieve description of installed version from rpm database"""
+ if DEBUG:
+ print 'DEBUG: %s.getInstalled(%s)' % (__name__, targetrpm)
+ import rpm
+ ts = rpm.TransactionSet()
+ # ts.setVSFlags(-1)
+ mi = ts.dbMatch('name', targetrpm.name)
+ oldrpm = None
+ count = 0
+ for h in mi:
+ oldrpmtmp = RpmDescription( h['name'], h['version'], h['release'], h['arch'], h['epoch'])
+ size = h['size']
+ #print "sizelimit: %d, size: %d" % (sizelimit, size)
+ if sizelimit > 0 and size > sizelimit:
+ raise Exception ("package %s bigger than limit (%d, %d)" % (targetrpm.name, size, sizelimit))
+ # TODO: add __cmp__ to RpmDescription to determine most current installed
+ # does not matter too much - for reconstruction any installed version is good
+ oldrpm = oldrpmtmp
+ count+=1
+ continue
+ if oldrpm:
+ if oldrpm > oldrpmtmp:
+ oldrpm = oldrpmtmp
+ else:
+ oldrpm = oldrpmtmp
+ # cleanup handles to free all rpmdb transactions - avoid db locking
+ del mi
+ del ts
+ if DEBUG:
+ print 'DEBUG: %s.getInstalled(%s): %s matches, using %s' % (__name__, targetrpm, count,oldrpm)
+ return oldrpm
+
+def getPackageFromDelta(cfg, rpmarray):
+ # method to be invoked within up2date
+ # return filename of regenerated newrpm
+ #
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta(%s)' % (__name__, rpmarray)
+ sizelimit=0
+ if cfg.has_key(SIZELIMIT):
+ sizelimit = cfg[SIZELIMIT]
+ # 1. retrieve relevant config from rhncfg
+ if cfg.has_key(STORAGE):
+ storageDir = cfg[STORAGE]
+ else:
+ storageDir = DEFAULT_STORAGE
+ # TODO: check if is directory
+ if not os.access(storageDir, os.F_OK):
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta: mkdir(%s)' % (__name__, storageDir)
+ os.makedirs(storageDir)
+ if cfg.has_key(DELTA_STORAGE):
+ deltaStorage = cfg[DELTA_STORAGE]
+ else:
+ deltaStorage = DEFAULT_DELTA_STORAGE
+ # TODO: check if is directory
+ if not os.access(deltaStorage, os.F_OK):
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta: mkdir(%s)' % (__name__, deltaStorage)
+ os.makedirs(deltaStorage)
+ if cfg.has_key(DELTA_URL):
+ deltaUrl = cfg[DELTA_URL]
+ else:
+ # without URL we can't do anything useful - raise exception and let up2date fall back to its own retrieval
+ raise "%s not configured" % DELTA_URL
+ oldRpms = None
+ if cfg.has_key(DELTA_OLDRPM_REPOSITORY):
+ oldRpms = cfg[DELTA_OLDRPM_REPOSITORY]
+ # use both config setting where old rpms could be and if they should be used
+ if cfg.has_key(DELTA_USE_OLDRPMS):
+ useOldRpms = cfg[DELTA_USE_OLDRPMS]
+ else:
+ # default is to NOT use old rpms
+ useOldRpms = 0
+ # if old rpms should be used, check if oldRpms is set , warn otherwise
+ if useOldRpms:
+ if not oldRpms:
+ print "warning: configuration inconsistent: cannot use ols rpms without path specified, check %s" % DELTA_OLDRPM_REPOSITORY
+ useOldRpms = 0
+ if cfg.has_key(DELTA_USE_SEQ):
+ useSeq = cfg[DELTA_USE_SEQ]
+ else:
+ # default is to NOT use sequence files
+ useSeq = 0
+ # 2. determine old rpm description
+ targetrpm = RpmDescription(rpmarray[0], rpmarray[1], rpmarray[2], rpmarray[4], rpmarray[3])
+ oldrpm = getInstalled(targetrpm, sizelimit)
+
+ # raise exception if package is not installed
+ if not oldrpm:
+ raise Exception("%s is not installed" % targetrpm.name)
+
+ # TODO: determine based on URL setting whih fetcher to use
+ fetcher = HttpFetcher(deltaUrl, deltaStorage)
+ #fetcher = TestFSFetcher('/tmp/deltasource', deltaStorage)
+ # wrapper takes only paths as constructor arguments,
+ # flags like useSeq or useOldRpms can be set on every method invocation
+ wrapper = DeltaRpmWrapper(storageDir, oldRpms)
+ # 3. ifSeq:
+ if useSeq:
+ # 3.1. download seq
+ seqfile = fetcher.fetchSequence(oldrpm, targetrpm)
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta: received seq in %s' % (__name__, seqfile)
+ # 3.2 verify seq
+ wrapper.verifySequence(seqfile, oldrpm, useOldRpms)
+ # 4. download deltarpm
+ deltafile = fetcher.fetchDeltaRpm(oldrpm, targetrpm)
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta: received rpm in %s' % (__name__, deltafile)
+ # 5. regenerate newrpm
+ newfile = wrapper.apply(oldrpm, targetrpm, deltafile, useOldRpms)
+ # output some statistics ;)
+ print "successfully reconstructed %s - %d bytes tranferred instead of %d" % (targetrpm, os.stat(deltafile).st_size, os.stat(newfile).st_size)
+ # done, cleanup
+ # 6. delete seq and deltarpm file if keepAfterInstall is not set
+ if cfg.has_key('keepAfterInstall') and cfg['keepAfterInstall']:
+ pass
+ else:
+ # let mkdir operation without try/except as failure would mean that something is really broken
+ # up2date would fallback to its retrieval and therefore not rely at all on this code ;)
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta: rm(%s)' % (__name__, deltafile)
+ os.unlink(deltafile)
+ if useSeq:
+ if DEBUG:
+ print 'DEBUG: %s.getPackageFromDelta: rm(%s)' % (__name__, seqfile)
+ os.unlink(seqfile)
+ return newfile
+
+def getDrpmName(oldrpm, targetrpm):
+ """Get delta rpm name from old, new rpms, and suffix"""
+ dver = "_".join([targetrpm.version, oldrpm.version] )
+ drel = "_".join([targetrpm.release ,oldrpm.release] )
+ drpmName = '%s-%s-%s.%s' % (oldrpm.name, dver, drel, oldrpm.arch)
+ return drpmName
+
+if __name__ == '__main__':
+ import sys
+ arg = sys.argv[1]
+ newrpm = RpmDescription(arg,'1.0.6','1.4.1','i386')
+ old = getInstalled(newrpm, 0)
+ old = getInstalled(newrpm, 10000000)
+
+ print old.rhnFileName()
+ #p = Process()
+ #p.run('find','/var/Satellite','-xdev')
+ #print p.getOutput()
+ #print p.returnCode()
+ print
diff --git a/yum-deltarpm.conf b/yum-deltarpm.conf
new file mode 100644
index 0000000..f3badd5
--- /dev/null
+++ b/yum-deltarpm.conf
@@ -0,0 +1,4 @@
+[main]
+enabled=1
+deltaRpmURL=http://localhost/delta/
+deltaStorageDir=/tmp/deltas/
diff --git a/yum-deltarpm.py b/yum-deltarpm.py
new file mode 100644
index 0000000..4b2ad10
--- /dev/null
+++ b/yum-deltarpm.py
@@ -0,0 +1,76 @@
+# author: Lars Herrmann <herrmann@redhat.com>
+# license: GPL (see COPYING file in distribution)
+#
+
+from yum.plugins import PluginYumExit, TYPE_INTERACTIVE
+
+import os
+import sys
+
+sys.path.append("/usr/share/deltarpm-python")
+import deltarpm
+
+deltarpm.DEBUG = 1
+
+requires_api_version = '2.1'
+plugin_type = (TYPE_INTERACTIVE,)
+
+
+def predownload_hook(conduit):
+ rpmdb = conduit.getRpmDB()
+ up2date_cfg={}
+
+ # Read configuration
+ conduit.info(2, 'Reading deltarpm configuration')
+ delta_rpm_url = conduit.confString('main', 'deltaRpmURL')
+
+ if not delta_rpm_url:
+ conduit.error(2, 'deltaRpmURL not set in deltarpm plugin config')
+
+ # set delta storage dir if provide, else fallback to default
+ delta_storage_dir = conduit.confString('main', 'deltaStorageDir')
+ if delta_storage_dir:
+ up2date_cfg[deltarpm.DELTA_STORAGE] = delta_storage_dir
+
+ # get up2date config and adjust some values for use with yum
+ ## up2date_cfg = config.initUp2dateConfig()
+ up2date_cfg[deltarpm.USE_DELTA] = 1
+ up2date_cfg[deltarpm.DELTA_URL] = delta_rpm_url
+
+ for p in conduit.getDownloadPackages():
+ # 0. Maybe rpm is already there?
+ #if os.path.exists(p.localpath):
+ # conduit.info(2, "target rpm already exists in cache - continue")
+ # continue
+
+ # 1. Is the package already installed?
+ installed = rpmdb.returnTupleByKeyword(name=p.name)
+ if not installed:
+ # Package not installed so fetching delta makes no sense
+ continue
+
+ # 2. Try to fetch deltarpm
+ filename = os.path.basename(p.localpath)
+ # Set the storage path for this package
+ up2date_cfg[deltarpm.STORAGE] = os.path.dirname(p.localpath)
+
+ try:
+ # invoke getPackageFromDelta method
+ # this method does all the work: reading config, fetching delta
+ # and reconstructing the new rpm. if something goes wrong, an
+ # exception is thrown
+ package = [p.name, p.version, p.release, p.epoch, p.arch]
+ # FIXME: handle this in deltarpm.py
+ if (package[3] == '0'):
+ package[3] = 0
+ deltarpm.getPackageFromDelta(up2date_cfg, package)
+
+ except Exception, e:
+ # Warn about failure
+ print "could not use delta rpm for %s: %s" % (filename, e)
+ # And remove anything we might have created
+ try:
+ os.unlink(p.localpath)
+ except OSError, msg:
+ pass
+ #raise PluginYumExit('exiting for testing in predownload hook ')