# This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Copyright (c) 2007 Jonathan Dieter import rpmUtils.miscutils import os import commands class RpmItem: def __init__(self, nevra, f): (self.name, self.epoch, self.version, self.release, self.arch) = nevra self.epoch = str(self.epoch) self.filename = f def getNevra(self): return (self.name, self.epoch, self.version, self.release, self.arch) def __eq__(self, b): if b == None: return False try: # An RpmItem is equal to another RpmItem if the nevra's are equal if self.getNevra() == b.getNevra(): return True else: return False except: # An RpmItem is equal to a DrpmItem if the nevra is equal to the drpm's destination nevra if self.getNevra() == b.getDstNevra(): return True else: return False class DrpmItem: def __init__(self, src_nevra, dst_nevra, f): (self.src_name, self.src_epoch, self.src_version, self.src_release, self.src_arch) = src_nevra (self.dst_name, self.dst_epoch, self.dst_version, self.dst_release, self.dst_arch) = dst_nevra self.src_epoch = str(self.src_epoch) self.dst_epoch = str(self.dst_epoch) self.filename = f def exists(self): return self.filename != None def getSrcNevra(self): return (self.src_name, self.src_epoch, self.src_version, self.src_release, self.src_arch) def getDstNevra(self): return (self.dst_name, self.dst_epoch, self.dst_version, self.dst_release, self.dst_arch) def getSrcDstNevra(self): return [self.getSrcNevra(), self.getDstNevra()] def __eq__(self, b): if b == None: return False try: # A DrpmItem is equal to another DrpmItem if both source and destination nevra's are equal if self.getSrcDstNevra() == b.getSrcDstNevra(): return True else: return False except: # A DrpmItem is equal to an RpmItem if the destination nevra is equal to the rpm's nevra if self.getDstNevra() == b.getNevra(): return True else: return False class PackageList: def __init__(self, base_dir, drpm_dir, threshold, debug): self.base_dir = base_dir self.drpm_dir = drpm_dir self.threshold = threshold self.debug = debug self.rpm_list = {} self.sorted = False def addRpm(self, rpm): """Add RpmItem to PackageList""" self.sorted = False rpm_key = "%s.%s" % (rpm.name, rpm.arch) self.rpm_list.setdefault(rpm_key, {}) self.rpm_list[rpm_key].setdefault("rpms", []) found = False for found_rpm in self.rpm_list[rpm_key]["rpms"]: if rpm == found_rpm: found = True break if not found: self.rpm_list[rpm_key]["rpms"].append(rpm) def addDrpm(self, drpm): """Add DrpmItem to PackageList""" self.sorted = False rpm_key = "%s.%s" % (drpm.src_name, drpm.src_arch) self.rpm_list.setdefault(rpm_key, {}) self.rpm_list[rpm_key].setdefault("drpms", []) found = False for found_drpm in self.rpm_list[rpm_key]["drpms"]: if drpm == found_drpm: found = True break if not found: self.rpm_list[rpm_key]["drpms"].append(drpm) def sort(self): """Sort all rpms and deltarpms in reverse order. This is necessary before running any methods that try to work with rpms and deltarpms""" self.sorted = True for rpm_type in self.rpm_list.values(): for key in rpm_type.keys(): if key == "rpms" and len(rpm_type["rpms"]) > 1: def sortByEVR(rpm1, rpm2): (n1,e1,v1,r1,a1) = rpm1.getNevra() (n2,e2,v2,r2,a2) = rpm2.getNevra() rc = rpmUtils.miscutils.compareEVR((e1,v1,r1),(e2,v2,r2)) if rc == 0: return 0 if rc > 0: return -1 if rc < 0: return 1 rpm_type["rpms"].sort(sortByEVR) # highest first in list elif key == "drpms" and len(rpm_type["drpms"]) > 1: def sortByEVR(drpm1, drpm2, src=False): if src: (n1,e1,v1,r1,a1) = drpm1.getSrcNevra() (n2,e2,v2,r2,a2) = drpm2.getSrcNevra() else: (n1,e1,v1,r1,a1) = drpm1.getDstNevra() (n2,e2,v2,r2,a2) = drpm2.getDstNevra() rc = rpmUtils.miscutils.compareEVR((e1,v1,r1),(e2,v2,r2)) if rc == 0: if src: return 0 else: return sortByEVR(drpm1, drpm2, True) if rc > 0: return -1 if rc < 0: return 1 rpm_type["drpms"].sort(sortByEVR) # highest first in list def dump(self): """Dump list of all packages and their rpms and deltarpms""" if not self.sorted: self.sort() temp_list = [] for x in self.rpm_list.keys(): temp_list.append(x) temp_list.sort() for x in temp_list: print x if self.rpm_list[x].has_key("rpms"): print " RPMS:" for rpm in self.rpm_list[x]["rpms"]: print " %s:%s-%s" % (rpm.epoch, rpm.version, rpm.release) if self.rpm_list[x].has_key("drpms"): print " DRPMS:" for drpm in self.rpm_list[x]["drpms"]: print " %s:%s-%s => %s:%s-%s" % (drpm.src_epoch, drpm.src_version, drpm.src_release, drpm.dst_epoch, drpm.dst_version, drpm.dst_release) def __doWork(self, nevra1, nevra2, f1, deltaCommand): (n1,e1,v1,r1,a1) = nevra1 (n2,e2,v2,r2,a2) = nevra2 deltaRPMName = os.path.join(self.drpm_dir, '%s-%s-%s_%s-%s.%s.drpm' % (n1, v2, r2, v1, r1, a1)) deltaCommand += deltaRPMName if self.debug: print deltaCommand if os.path.exists("%s.dontdelta" % deltaRPMName): f = open("%s.dontdelta" % deltaRPMName, "r") drpmsize = f.readline()[:-1] drpmsize = int(drpmsize) if not self.__drpmIsWorthKeeping(drpmsize, f1): return False else: if self.debug: print "Original drpm is now worth creating...deleting %s%s.dontdelta" % (self.base_dir, deltaRPMName) try: os.unlink("%s.dontdelta" % deltaRPMName) except Exception, e: print "Error deleting %s.dontdelta" % os.path.basename(deltaRPMName), str(e) (code, out) = commands.getstatusoutput(deltaCommand) if code: print "Error genDeltaRPM for %s: exitcode was %s - Reported Error: %s" % (n1, code, out) return False else: # Check whether or not we should keep the drpm drpmsize = os.path.getsize(deltaRPMName) if not self.__drpmIsWorthKeeping(drpmsize, f1): if self.debug: print 'deleting %s' % (deltaRPMName) f = open("%s.dontdelta" % deltaRPMName, "w") f.write("%i\n" % os.path.getsize(deltaRPMName)) f.close() try: os.unlink(deltaRPMName) except Exception, e: print "Error deleting deltarpm %s" % os.path.basename(deltaRPMName), str(e) return False else: if e1 == e2: print 'Generated delta rpm for %s.%s - %s-%s => %s-%s' % (n1, a1, v2, r2, v1, r1) else: print 'Generated delta rpm for %s.%s - %s:%s-%s => %s:%s-%s' % (n1, a1, e2, v2, r2, e1, v1, r1) drpm = DrpmItem(nevra2, nevra1, deltaRPMName) self.addDrpm(drpm) self.sort() if self.debug: self.dump() return True def __combineDeltaRpm(self, combine_list, dstrpm): nevra1 = combine_list[-1].getDstNevra() nevra2 = combine_list[0].getSrcNevra() f1 = dstrpm.filename deltaCommand = 'combinedeltarpm ' for drpm in combine_list: deltaCommand += "%s " % drpm.filename return self.__doWork(nevra1, nevra2, f1, deltaCommand) def __buildDeltaRpm(self, srcrpm, dstrpm): nevra1 = dstrpm.getNevra() nevra2 = srcrpm.getNevra() f1 = dstrpm.filename f2 = srcrpm.filename deltaCommand = 'makedeltarpm %s %s ' % (f2, f1) return self.__doWork(nevra1, nevra2, f1, deltaCommand) def __makeDeltaRpm(self, src_nevra, dstrpm, package): foundSrc = False foundDst = False tempQueue = [] if package.has_key("drpms"): for drpm in package["drpms"]: if drpm.getDstNevra() == dstrpm.getNevra() and drpm.getSrcNevra() == src_nevra: return True if drpm.getSrcNevra() == src_nevra: for item in tempQueue: if drpm.getDstNevra() == item.getSrcNevra(): combine_list = [drpm, item] return self.__combineDeltaRpm(combine_list, dstrpm) elif drpm.getDstNevra() == dstrpm.getNevra(): tempQueue.append(drpm) for rpm in package["rpms"]: if rpm.getNevra() == src_nevra: return self.__buildDeltaRpm(rpm, dstrpm) print "Found nothing!" return False def makeDeltaRpms(self, DrpmsPerPackage=0, DoFirst=True, OnlyLastPackage=True): """Create deltarpms for packages. Attributes: OnlyLastPackage (Boolean): Only create deltarpms to point to last destination package DrpmsPerPackage (Int): Number of deltarpms to make for each package (0 = all possible) DoFirst (Boolean): Create deltarpm for first source package""" if not self.sorted: self.sort() temp_list = [] for x in self.rpm_list.keys(): temp_list.append(x) temp_list.sort() if self.debug: print "Building drpms" for rpm_name in temp_list: package = self.rpm_list[rpm_name] if not package.has_key("rpms") or len(package["rpms"]) == 0: if self.debug: print "No rpms for %s: Doing nothing" % rpm_name continue if (not package.has_key("drpms") or len(package["drpms"]) == 0) and len(package["rpms"]) == 1: if self.debug: print "Only one rpm available for %s: Doing nothing" % rpm_name continue build_list = [] for rpm in package["rpms"]: if [rpm.getNevra(), 1] not in build_list: build_list.append([rpm.getNevra(), 1]) if package.has_key("drpms"): for drpm in package["drpms"]: if [drpm.getSrcNevra(), 1] not in build_list and [drpm.getSrcNevra(), 0] not in build_list: build_list.append([drpm.getSrcNevra(), 0]) if [drpm.getDstNevra(), 1] not in build_list and [drpm.getDstNevra(), 0] not in build_list: build_list.append([drpm.getDstNevra(), 0]) def sortByEVR(rpm1, rpm2): (n1,e1,v1,r1,a1) = rpm1[0] (n2,e2,v2,r2,a2) = rpm2[0] rc = rpmUtils.miscutils.compareEVR((e1,v1,r1),(e2,v2,r2)) if rc == 0: return 0 if rc > 0: return -1 if rc < 0: return 1 build_list.sort(sortByEVR) if OnlyLastPackage: if build_list[0][1] != 1: continue rpm = None for x in package["rpms"]: if x.getNevra() == build_list[0][0]: rpm = x break if rpm == None: continue newpkg_list = [rpm] else: newpkg_list = [] for build in build_list[:-1]: if build[1] != 1: continue rpm = None for x in package["rpms"]: if x.getNevra() == build[0]: rpm = x break if rpm == None: continue newpkg_list.append(rpm) for newpkg in newpkg_list: start = False count = 0 do_last = True for build in build_list[:-1]: if start: self.__makeDeltaRpm(build[0], newpkg, package) if not self.sorted: self.sort() count += 1 if DrpmsPerPackage != 0: if count >= DrpmsPerPackage and not DoFirst: do_last = False break elif count >= DrpmsPerPackage - 1 and DoFirst: break if build[0] == newpkg.getNevra(): start = True if DoFirst == 1 and start and do_last: self.__makeDeltaRpm(build_list[-1][0], newpkg, package) if not self.sorted: self.sort() return def __drpmIsWorthKeeping(self, drpmsize, newrpm): newsize = os.path.getsize(newrpm) # Delete the drpm if it's too large if drpmsize > self.threshold * newsize: return False return True