Viewing file: packageList.py (29.13 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/bin/python # # Package list mangling # # $Id: packageList.py,v 1.70 2005/05/17 17:43:25 alikins Exp $
import os import fnmatch import string import sys sys.path.insert(0, "/usr/share/rhn/") sys.path.insert(1,"/usr/share/rhn/up2date_client")
import rpm # temp home for all the package list related functions # move this to a class import up2dateUtils import up2dateLog import up2dateErrors import up2dateComps import config import rpmUtils import headers import rhnPackageInfo import transaction
from rhpl.translate import _ import rhpl.arch
#grab an instance of the config class shared cfg = config.initUp2dateConfig()
# get a reference to the log file object log = up2dateLog.initLog()
# idea... a class to wrap everything in here... # we add all the info we need, and then call PackageList.getTransaction or # PackageList.runTransaction or something # # needs to be able to handle adding packages for install/update/remove # and some set of standard options for those (--force?, --nodeps, --justdb,etc) # # At the very least, this needs to be able to do what getUpdatedPackageList does # # class PackageList: def __init__(self): # rpm transaction set flags self.nodeps = None self.force = None self.justdb = None # A hash keyed on the package name, and with a list of all the # packages with that name as a value self.__instPackagesHash = {} # A list of available packages self.__availPackagesList = [] # A hash keyed on the package name, None as value self.__forcePackagesHash = {} # A hash keyed on the package (n, v, r, e, a), a list of # (n, v, r, e, a, obsol name, obsol ver, obsol sense) as values self.__obsolPackagesHash = {} # list of packages we should update but are marked to # be skipped for some reason. for ui stuff # (names with possible wildcards) self.__skipPatternList = [] # Skipped packages self.__skippedPackagesList = [] self.__obsoletedPackagesList = [] self.__installedObsoletingPackageList = []
self.noMatchesForComps = [] self.noMatchesForGlobs = []
self.msgCallback = None self.progressCallback = None self.refreshCallback = None # bit of a kluge to make a better ui self.ignoreMsgCallback = 1
def __findLatestVersionAllArch(self, package_list): availPkgsDict = {} for pkg in package_list: pkgName = pkg[0]
if not availPkgsDict.has_key(pkgName): availPkgsDict[pkgName] = [pkg] continue
# Already seen this name ret = up2dateUtils.comparePackages(availPkgsDict[pkgName][0][:4], pkg[:4]) if ret > 0: # don't care, we already have a better version continue if ret < 0: # Better version, replace the existing one if availPkgsDict.has_key(pkgName): availPkgsDict[pkgName] = [pkg] continue # same version, different arch if ret == 0: if availPkgsDict.has_key(pkgName): availPkgsDict[pkgName].append(pkg) else: availPkgsDict[pkgName] = [pkg] continue # Package with a better architecture availPkgsDict[pkgName] = [pkg]
values = availPkgsDict.values() tmplist = [] for value in values: for i in value: tmplist.append(i) return tmplist
def run(self): # based on getpdatedPackageList installedPackageList = rpmUtils.getInstalledPackageList(getArch=1) self.addInstalledPackages(installedPackageList)
availList = rhnPackageInfo.getAvailableAllArchPackageList(self.msgCallback, self.progressCallback) if availList: self.addAvailablePackages(availList)
obsList = rhnPackageInfo.obsoletesList( msgCallback = self.msgCallback, progressCallback = self.progressCallback ) self.addObsoletePackages(obsList)
if not cfg["forceInstall"]: self.addSkipPatterns(cfg["pkgSkipList"])
# skipped packages and the packages to install # are self.getSkippedPackages and self.getPackagesToInstall
def addInstalledPackages(self, installedPackages): for p in installedPackages: pName = p[0] if not self.__instPackagesHash.has_key(pName): self.__instPackagesHash[pName] = [] self.__instPackagesHash[pName].append(p)
def addAvailablePackages(self, availablePackages): for p in availablePackages: self.__availPackagesList.append(p) #print self.__availPackagesList
def addForcePackages(self, forcePackages): for p in forcePackages: self.__forcePackagesHash[p] = None
def addGlobs(self, listOfGlobs): availList = rhnPackageInfo.getAvailablePackageList() availNames = map(lambda a: a[0], availList) matches = [] # list of stuff to ignore even if the user specified # glob strings tells us to, aka, "*-debuginfo" since # thats not what most users mean... I hope... if cfg.has_key('globStoppers'): unglobs = cfg['globStoppers'] else: unglobs = ['*-debuginfo']
# use a dict so we dont have to uniq later noMatches = {} for glob in listOfGlobs: for name in availNames: if fnmatch.fnmatch(name, glob): for unglob in unglobs: if not fnmatch.fnmatch(name, unglob): matches.append(name) else: # no point it adding it to the hash over and over if not noMatches.has_key(glob): noMatches[glob] = None else: if not noMatches.has_key(glob): noMatches[glob] = None
self.noMatchesForGlobs = noMatches.keys()
if matches: # self.noMatchesForGlobs = 0 self.addForcePackages(matches)
def addComps(self, listOfComps): comps = up2dateComps.initComps() matches = [] for comp in listOfComps: try: matches = matches + comps.pkgTree(comp) except KeyError: self.noMatchesForComps.append(comp) #uniq the list d = {} for i in matches: d[i] = i matches = d.keys() matches.sort()
if matches: self.addForcePackages(matches) def addObsoletePackages(self, obsoletePackages): if obsoletePackages == None or len(obsoletePackages) == 0: return for p in obsoletePackages: key = tuple(p[:5]) if not self.__obsolPackagesHash.has_key(key): self.__obsolPackagesHash[key] = [] self.__obsolPackagesHash[key].append(p)
self.__obsoletedPackageHash = {} for key in self.__obsolPackagesHash.keys(): for obsInfo in self.__obsolPackagesHash[key]: if not self.__obsoletedPackageHash.has_key(obsInfo[5]): self.__obsoletedPackageHash[obsInfo[5]] = [] self.__obsoletedPackageHash[obsInfo[5]].append(obsInfo) # foo[obsoletingpackage] = (obs info, with [5] being the thing obsoleted) # ugh
def addSkipPatterns(self, skipPatterns): for p in skipPatterns: self.__skipPatternList.append(p)
def getInstalledPackages(self): return self.__instPackagesHash
def getForcedPackages(self): return self.__forcePackagesHash def getObsoletedPackages(self): return self.__obsoletedPackagesList
def getInstalledObsoletingPackages(self): return self.__installedObsoletingPackageList
def getSkippedPackages(self): """ Returns a list of packages that have been skipped, together with the reason why they've been skipped """ # XXX return self.__skippedPackagesList
# Is this package outdated by a an already installed package? def __outdatedByInstalledPackage(self, p): for inst in self.__instPackagesHash[p[0]]: ret = up2dateUtils.comparePackages(inst, p) if ret < 0: log.log_debug("p: %s is newer than inst: %s" % ( p, inst)) else: # at least one of the installed versions is # new enough return 1
# No installed package is newer than p return 0
def __findOutdatedPackages(self): for p in self.__availPackagesList: pName = p[0]
# is this package already installed? if self.__instPackagesHash.has_key(pName): # if so, we need to do version compare stuff
# Installed # If the package is not outdated by an already installed # package, mark it as available for updates
# FIXME: we dont have color info here, but we really # need it. In the meantime, we kind of sorta pretend that # same arch is the same as same color. This works, but it # means that upgrades from i386 to i686 will fail. For the # moment, proper behaviour in the face of multilib is more # important, so we use that workaround.
# split packages into groups by arch archDict = {} for instPkg in self.__instPackagesHash[pName]: if archDict.has_key(instPkg[4]): archDict[instPkg[4]].append(instPkg) else: archDict[instPkg[4]]= [instPkg]
# find any updates need on a per arch basis # handling cases where the different arches have different # versions installed, and the upstream has different versions # available for each arch, or they match for arch in archDict.keys(): packagesToAdd = [] for pkg in archDict[arch]: if arch == p[4]: ret = up2dateUtils.comparePackages(pkg, p) elif arch == "noarch" or p[4] == "noarch": ret = up2dateUtils.comparePackages(pkg, p) else: continue if ret < 0: packagesToAdd.append(p) else: # some package of this arch installed is # newer than the package available of the same arch # so, dont upgrade it packagesToAdd = [] break for pkg in packagesToAdd: self.__addPackageToUpdate(pkg)
# this could well get called _alot_ def __findLatestPackage(self, name): pkgVersions = [] for p in self.__availPackagesList: if p[0] == name: pkgVersions.append(p)
pkgVersions.sort(up2dateUtils.comparePackages) pkgVersions.reverse() return pkgVersions[0]
def __findLatestPackages(self, name): pkgVersions = [] for p in self.__availPackagesList: if p[0] == name: pkgVersions.append(p)
latest = pkgVersions[0] latestlist = [] for pkgVersion in pkgVersions: ret = up2dateUtils.comparePackages(latest, pkgVersion) if ret < 0: #pkgVersion newer than latest latestlist = [pkgVersion] if ret == 0: latestlist.append(pkgVersion) return latestlist # is the package one of the packages we are installing/force upgrading? # assume we want to install the most recent def __findForcedPackages(self): fpHash = self.__forcePackagesHash for p in self.__availPackagesList: pName = p[0]
if self.packagesToUpdate.has_key(pName): # we've got this package installed, continue continue if fpHash.has_key(pName): # if a version of the package is installed # look at all the available versions and pick the most recent latestpkgs = self.__findLatestPackages(pName) for latest in latestpkgs: if self.__instPackagesHash.has_key(latest[0]): # and the installed version isnt the same or newer than # what we are forcing for instPkg in self.__instPackagesHash[latest[0]]: packagesToAdd = [] ret = up2dateUtils.comparePackages(instPkg, latest) if cfg['forcedArch']: arches = cfg['forcedArch'] if latest[4] in arches: if ret <= 0: packagesToAdd.append(latest) else: # see if the arches are the same, this would fall out # later in the arch selection code anyway if instPkg[4] == latest[4]: if ret < 0: packagesToAdd.append(latest) else: packagesToAdd = [] continue for pkg in packagesToAdd: self.__addPackageToUpdate(pkg) # package insnt installed, install the latest version else: #FIXME: replace with color info when we get it if cfg['forcedArch']: arches = cfg['forcedArch'] if latest[4] in arches: self.__addPackageToUpdate(latest) else: # this seems wrong, but the arch selection stuff later # on takes care of making sure the best arch is picked self.__addPackageToUpdate(latest)
def __addPackageToUpdate(self,p): pName = p[0] if not self.packagesToUpdate.has_key(pName): self.packagesToUpdate[pName] = [] # if we havent already added it to the list pkgsNVREA = map(lambda a: a[:5], self.packagesToUpdate[pName]) if not p[:5] in pkgsNVREA: # see if we flagged this as something not to update (aka, # something with mulitple installed versions, one of which is new enough # need to be per arch, and later per color self.packagesToUpdate[pName].append(p) # else: # print "wtf: %s" % p
def __findObsoletingPackages(self): for p in self.__availPackagesList: pName = p[0]
if self.packagesToUpdate.has_key(pName): # we're already updating this package found = 0 for packageToUpdate in self.packagesToUpdate[pName]: if p[:5] == packageToUpdate[:5]: found = 1 if found: # were already updating this package continue
key = tuple(p[:5]) if not self.__obsolPackagesHash.has_key(key): # Nothing to do here continue
# this package obsoletes something for obs in self.__obsolPackagesHash[key]: obsName, obsVersion, obsSense = obs[5:] if obsSense == "0" or obsSense == 0: # this package obsoletes all versions of # obsName if not self.__instPackagesHash.has_key(obsName): continue self.__addPackageToUpdate(p) # any given one package can obsolete multiple packages for obsP in self.__instPackagesHash[obsName]: self.__obsoletedPackagesList.append((obsP,self.__obsoletedPackageHash[obsName])) continue
# loop over all the installed versions if not self.__instPackagesHash.has_key(obsName): # Nothing to do continue # obsName is installed for inst in self.__instPackagesHash[obsName]: if up2dateUtils.isObsoleted(obs, inst, package=p): self.__addPackageToUpdate(p) for obsP in self.__instPackagesHash[obsName]: self.__obsoletedPackagesList.append((obsP,self.__obsoletedPackageHash[obsName]))
def isIa32E(self): # heh, dont ask. But if you must, see bz #155583 if rhpl.arch.getBaseArch() == "ia32e": return 1 return 0
def __findBestArch(self, plist): bestArchP = None for p in plist: archscore = rpm.archscore(p[4]) log.log_debug("archscore", archscore) if archscore <= 0: # Unsupported architecture continue # Dirty Dirty Dirty hack, see bz #155583 # basically, rpm.archscore thinks that "x86_64" is a compatiable arch for # ia32e boxes. Which it is. Unless it is a kernel. So if someome says "up2date kernel-smp", up2date # will let them. Because aside from telling you, there is no way to tell that that is not going to # get you a bootable kernel. So we hardcode around it. Ugh. if p[0] == "kernel-smp": if p[4] == "x86_64": if self.isIa32E(): # Haha! Fooled ya! Thats not actually a valid arch! Skip it. See above. continue if bestArchP and rpm.archscore(bestArchP[4]) <= archscore: # The currently best arch is better than (or the same as) # this one continue
# This is a better architecture bestArchP = p if bestArchP == None: log.log_me("Could not find an approriate arch for package %s, skipping" % plist) return None
if bestArchP: return [bestArchP] return None
def __findMatchingArch(self, plist, arches): bestArchPList = [] for p in plist: if p[4] in arches: bestArchPList.append(p) return bestArchPList
def __findBestArchPackages(self): hash = {}
for pkey, plist in self.packagesToUpdate.items(): # find all the packages that match the forced arch if cfg['forcedArch']: arches = cfg['forcedArch'] matches = [] for p in plist: if p[4] in arches: matches.append(p) for match in matches: self.packagesToUpdate[pkey] = matches continue
# ugly new mulitlib kluge... see if a particular arch # of a package is pulled in via a obsolete, if so, make sure # we don't remove it from the obs set added = 0 packagesToAdd = [] for p in plist: if self.__obsolPackagesHash.has_key(tuple(p[:5])): obsPkgs = self.__obsolPackagesHash[tuple(p[:5])] for obsPkg in obsPkgs: if p not in packagesToAdd: packagesToAdd.append(p) # try to pick only applicable arches of the packages were # adding because of obsoletes. This should handle the i686/athlon # kernel issues as well as the GDB/GDB64 issues. if len(packagesToAdd): arches = [] if self.__instPackagesHash.has_key(pkey): pkgs = self.__instPackagesHash[pkey] else: pkgs = []
for pkg in pkgs: if pkg[4] not in arches: arches.append(pkg[4])
if arches == []: newp = self.__findBestArch(packagesToAdd) else: newp = self.__findMatchingArch(packagesToAdd, arches)
if newp: self.packagesToUpdate[pkey] = newp # we had to add this because of wacky obs, so skip the rest continue
if self.__instPackagesHash.has_key(pkey): pkgs = self.__instPackagesHash[pkey] if len(pkgs) > 1: # we've currently got more than one copy of # a package installed... arches = [] for p in pkgs: if p[4] not in arches: arches.append(p[4])
# if we have more than one arch installed, # then just choose the updates for the approriate matching # arches if len(arches) > 1: newp = self.__findMatchingArch(plist,arches) self.packagesToUpdate[pkey] = newp continue elif len(pkgs) == 1: newp = self.__findMatchingArch(plist, [pkgs[0][4]]) # we might not have a matching arch anymore if not newp: newp = self.__findBestArch(plist) if newp: self.packagesToUpdate[pkey] = newp continue newp = self.__findBestArch(plist) # newp can be None at this point, if no packages apply to this # system if newp: self.packagesToUpdate[pkey] = newp # if we dont have an approriate arch, delete it from available packages else: log.log_me("The latest version of %s was not available for this arch. Skipping" % pkey) del self.packagesToUpdate[pkey]
# with the new mulitple repos we can have different versions of the same package # in different channels, we need to find the latest of the whole set
def __sortPackageList(self): # FIXME: change this so the values of self.packagesToUpdate can be lists? values = self.packagesToUpdate.values() tmplist = [] # flatten lists of lists for i in values: for j in i: tmplist.append(j) result = tmplist if len(result): result.sort(lambda a, b: cmp(a[0], b[0])) return result def getPackagesToInstall(self): # We put stuff in a hash to easily find packages hash = {}
# hash of packages we need to update self.packagesToUpdate = {}
self.__findOutdatedPackages() self.__findForcedPackages() self.__findObsoletingPackages() # self.__findLatestPackages()
self.__findBestArchPackages() # self.__handleForcePackages() result = self.__sortPackageList() result = self.__skipPackages(result) # print result return result
# list of failed deps if any # for ui stuff def failedDeps(self): pass
# either return a rpm.TransactionSet object # or some similar representation def getTransaction(self): pass
def __skipPackages(self, packageList): self.__skippedPackagesList = []
for p_i in range(len(packageList)): for pattern in self.__skipPatternList: if fnmatch.fnmatch(packageList[p_i][0], pattern): self.__skippedPackagesList.append((packageList[p_i], _("Pkg name/pattern")))
break
for p in self.__skippedPackagesList: packageList.remove(p[0])
packageList = self.__skipFiles(packageList) return packageList
def __skipFiles(self, packageList): removedList = [] if cfg['forceInstall']: return packageList
# need the db for the checkHeaderFiles ts = transaction.initReadOnlyTransaction() # ts = rpm.TransactionSet() # ts.setVerifySigFlags(8) p_i = 0 # FIXME: something seems wrong here, when we are # are checking for FileConfigExcludes, we try to load the # header of the installed version, which doesnt make sence # for the new package case h_list = buildHeaderList(packageList, msgCallback = self.msgCallback, progressCallback = self.progressCallback, refreshCallback = self.refreshCallback, ignoreMsgCallback = self.ignoreMsgCallback) for i in h_list: tmp_list = None tmp_list = rpmUtils.checkHeaderForFileConfigExcludes(i,packageList[p_i],ts) p_i = p_i + 1 if tmp_list: removedList = removedList + tmp_list
d = {} for i in removedList: d[tuple(i[0])] = i removedList = d.values()
for p in removedList: packageList.remove(p[0])
self.__skippedPackagesList = self.__skippedPackagesList + removedList return packageList
def getArchesOfAvailablePackages(availablePkgList,pkgName): archList = [] for pkg in availablePkgList: if pkgName == pkg[0]: archList.append(pkg[4])
return archList
def buildHeaderList(pkgList,msgCallback = None, progressCallback = None, refreshCallback = None, ignoreMsgCallback=None): headerList = headers.initHeaderList() h_list = [] count = 1 total = len(pkgList) if msgCallback: msgCallback(_("Fetching rpm headers")) for pkg in pkgList: hdr = headerList[pkg] if hdr: h_list.append(hdr) # I dont want to do this in the batch mode, but it's nice # for the gui. Ugly, but better ui... if msgCallback and not ignoreMsgCallback: msgCallback(_("Fetching rpm header: %s-%s-%s") % (hdr['name'],hdr['version'],hdr['release'])) if progressCallback: progressCallback(count, total) count = count + 1 return h_list
# side effects on pkgList # REFACTOR: this probabaly needs to stay module scope, as # depSolver.__skip calls it def removeSkipPackagesFromList(pkgList, msgCallback = None, progressCallback = None): removedList = [] pkgCount = len(pkgList)
if cfg["forceInstall"]: return removedList
if msgCallback != None: msgCallback(_("Removing packages marked to skip from list"))
pkgSkipList = cfg["pkgSkipList"] if cfg["forceInstall"]: pkgSkipList = []
for index in range(pkgCount): for pattern in pkgSkipList: if fnmatch.fnmatch(pkgList[index][0], pattern): removedList.append((pkgList[index], _("Pkg name/pattern"))) break
if progressCallback != None: progressCallback(index+1, pkgCount)
for pkg in removedList: pkgList.remove(pkg[0])
# just do a side effect on pkgList here, and return removedList # since we actually need the results of it return removedList
if __name__ == '__main__': installedPackageList = [] pList = PackageList() for i in range(20): installedPackageList.append("a%04d" % i, '1.0', '1', '', 'noarch') pList.addInstalledPackages(installedPackageList) availablePackageList = [] for i in range(30): for arch in ['noarch', 'i386', 'i586', 'i686', 'athlon']: availablePackageList.append("a%04d" % i, '2.0', '1', '', arch)
print len(installedPackageList), len(availablePackageList) pList.addAvailablePackages(availablePackageList)
forcePackageList = [] for i in range(10): forcePackageList.append("a%04d" % (20 + 5 * i)) # pList.addForcePackages(forcePackageList)
obsList = [ ('a0021', '2.0', '1', '', 'i686', 'a0019', '', 0), ('a0022', '2.0', '1', '', 'i686', 'a0018', '2.0-1', 2), ] pList.addObsoletePackages(obsList)
pList.addSkipPatterns(["a0017", "a001[3-5]"]) import time start = time.time() res = pList.getPackagesToInstall() print "Total time: %f" % (time.time() - start) print len(res) print res
|