曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
#! /usr/bin/python -tt
# 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 Library 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 2005 Duke University
# Written by Seth Vidal
# TODOS: make all the package relationships deal with package objects
# search by package object for TransactionData, etc.
# provide a real TransactionData.remove(txmbr) method, It should
# remove the given txmbr and iterate to remove all those in depedent relationships
# with the given txmbr.
"""
Classes and functions for manipulating a transaction to be passed
to rpm.
"""
from constants import *
from packageSack import PackageSack, PackageSackVersion
from packages import YumInstalledPackage
from sqlitesack import YumAvailablePackageSqlite
import Errors
import warnings
import misc
import time
class GetProvReqOnlyPackageSack(PackageSack):
def __init__(self, need_files=False):
PackageSack.__init__(self)
self._need_index_files = need_files
def __addPackageToIndex_primary_files(self, obj):
for ftype in obj.returnFileTypes(primary_only=True):
for file in obj.returnFileEntries(ftype, primary_only=True):
self._addToDictAsList(self.filenames, file, obj)
def __addPackageToIndex_files(self, obj):
for ftype in obj.returnFileTypes():
for file in obj.returnFileEntries(ftype):
self._addToDictAsList(self.filenames, file, obj)
def _addPackageToIndex(self, obj):
for (n, fl, (e,v,r)) in obj.returnPrco('provides'):
self._addToDictAsList(self.provides, n, obj)
for (n, fl, (e,v,r)) in obj.returnPrco('requires'):
self._addToDictAsList(self.requires, n, obj)
if self._need_index_files:
self.__addPackageToIndex_files(obj)
else:
self.__addPackageToIndex_primary_files(obj)
def __buildFileIndexes(self):
for repoid in self.pkgsByRepo:
for obj in self.pkgsByRepo[repoid]:
self.__addPackageToIndex_files(obj)
def searchFiles(self, name):
if not self._need_index_files and not misc.re_primary_filename(name):
self._need_index_files = True
if self.indexesBuilt:
self.filenames = {}
self.__buildFileIndexes()
return PackageSack.searchFiles(self, name)
class TransactionData:
"""Data Structure designed to hold information on a yum Transaction Set"""
def __init__(self):
self.flags = []
self.vsflags = []
self.probFilterFlags = []
self.root = '/'
self.pkgdict = {} # key = pkgtup, val = list of TransactionMember obj
self._namedict = {} # name -> list of TransactionMember obj
self._unresolvedMembers = set()
self.debug = 0
self.changed = False
self.installonlypkgs = []
self.state_counter = 0
self.tm_created = time.time()
self.tm_changed = time.time()
self.conditionals = {} # key = pkgname, val = list of pos to add
self.rpmdb = None
self._pkgSack = None
self._pkgSackCtor = None
self.pkgSackPackages = 0
self.localSack = PackageSack()
self._inSack = GetProvReqOnlyPackageSack()
# lists of txmbrs in their states - just placeholders
self.instgroups = []
self.removedgroups = []
self.instenvironments = []
self.removedenvironments = []
self.removed = []
self.installed = []
self.updated = []
self.obsoleted = []
self.depremoved = []
self.depinstalled = []
self.depupdated = []
self.reinstalled = []
self.downgraded = []
self.failed = []
self._future_rpmdbv = None
self._check_future_rpmdbv = None
def _changed(self):
self.changed = True
self.state_counter += 1
self.tm_changed = time.time()
def __len__(self):
return len(self.pkgdict)
def __iter__(self):
if hasattr(self.getMembers(), '__iter__'):
return self.getMembers().__iter__()
else:
return iter(self.getMembers())
def _getPkgSack(self):
if self._pkgSack is not None:
return self._pkgSack
if self._pkgSackCtor is not None:
self._pkgSack = self._pkgSackCtor()
return self._pkgSack
pkgSack = property(fget=lambda self: self._getPkgSack(),
fset=lambda self, value: setattr(self, "_pkgSack",value),
fdel=lambda self: setattr(self, "_pkgSack", None),
doc="Package sack object")
def debugprint(self, msg):
if self.debug:
print msg
def getMembersWithState(self, pkgtup=None, output_states=None):
return filter(lambda p: p.output_state in output_states,
self.getMembers(pkgtup))
def getMembers(self, pkgtup=None):
"""takes an optional package tuple and returns all transaction members
matching, no pkgtup means it returns all transaction members"""
returnlist = []
if pkgtup is None:
for members in self.pkgdict.itervalues():
returnlist.extend(members)
elif pkgtup in self.pkgdict:
returnlist.extend(self.pkgdict[pkgtup])
return returnlist
# The order we resolve things in _matters_, so for sanity sort the list
# otherwise .i386 can be different to .x86_64 etc.
def getUnresolvedMembers(self):
return list(sorted(self._unresolvedMembers))
def markAsResolved(self, txmbr):
self._unresolvedMembers.discard(txmbr)
def resetResolved(self, hard=False):
if hard or len(self) < len(self._unresolvedMembers):
self._unresolvedMembers.clear()
self._unresolvedMembers.update(self.getMembers())
return True
return False
def getMode(self, name=None, arch=None, epoch=None, ver=None, rel=None):
"""returns the mode of the first match from the transaction set,
otherwise, returns None"""
txmbrs = self.matchNaevr(name=name, arch=arch, epoch=epoch, ver=ver, rel=rel)
if not len(txmbrs):
return None
states = []
for txmbr in txmbrs:
states.append(txmbr.ts_state)
if 'u' in states:
return 'u'
elif 'i' in states:
return 'i'
else:
return states[0]
def matchNaevr(self, name=None, arch=None, epoch=None, ver=None, rel=None):
"""returns the list of packages matching the args above"""
if name is None:
txmbrs = self.getMembers()
else:
txmbrs = self._namedict.get(name, [])
if arch is None and epoch is None and ver is None and rel is None:
return txmbrs[:]
result = []
for txmbr in txmbrs:
(n, a, e, v, r) = txmbr.pkgtup
# Name is done above
if arch is not None and arch != a:
continue
if epoch is not None and epoch != e:
continue
if ver is not None and ver != v:
continue
if rel is not None and rel != r:
continue
result.append(txmbr)
return result
def deselect(self, pattern):
""" Remove these packages from the transaction. This is more user
orientated than .remove(). Used from kickstart/install -blah. """
# We don't have a returnPackages() here, so just try the "simple"
# specifications. Pretty much 100% hit rate on kickstart.
txmbrs = self.matchNaevr(pattern)
if not txmbrs:
na = pattern.rsplit('.', 2)
if len(na) == 2:
txmbrs = self.matchNaevr(na[0], na[1])
if not txmbrs:
pkgs = []
if self._inSack is not None:
pkgs = self._inSack.returnPackages(patterns=[pattern])
if not pkgs and self.pkgSack is not None:
pkgs = self.pkgSack.returnPackages(patterns=[pattern])
if not pkgs:
pkgs = self.rpmdb.returnPackages(patterns=[pattern])
for pkg in pkgs:
txmbrs.extend(self.getMembers(pkg.pkgtup))
# Now we need to do conditional group packages, so they don't
# get added later on. This is hacky :(
for req, cpkgs in self.conditionals.iteritems():
if pkg in cpkgs:
cpkgs.remove(pkg)
self.conditionals[req] = cpkgs
for txmbr in txmbrs:
self.remove(txmbr.pkgtup)
return txmbrs
def _isLocalPackage(self, txmember):
# Is this the right criteria?
# FIXME: This is kinda weird, we really want all local pkgs to be in a
# special pkgsack before this point ... so that "yum up ./*.rpm" works.
# Also FakePackage() sets it off ... which is confusing and not what
# happens IRL.
return txmember.ts_state in ('u', 'i') and not isinstance(txmember.po, (YumInstalledPackage, YumAvailablePackageSqlite))
# NOTE: This is a copy of Depsolve.allowedMultipleInstalls() because
# we can't get to YumBase from here. Use that as the main/public API.
def _allowedMultipleInstalls(self, po):
"""takes a packageObject, returns 1 or 0 depending on if the package
should/can be installed multiple times with different vers
like kernels and kernel modules, for example"""
if po.name in self.installonlypkgs:
return True
provides = po.provides_names
if filter (lambda prov: prov in self.installonlypkgs, provides):
return True
return False
def add(self, txmember):
"""add a package to the transaction"""
for oldpo in txmember.updates:
self.addUpdated(oldpo, txmember.po)
if txmember.pkgtup not in self.pkgdict:
self.pkgdict[txmember.pkgtup] = []
else:
self.debugprint("Package: %s.%s - %s:%s-%s already in ts" % txmember.pkgtup)
for member in self.pkgdict[txmember.pkgtup]:
if member.ts_state == txmember.ts_state:
self.debugprint("Package in same mode, skipping.")
return
self.pkgdict[txmember.pkgtup].append(txmember)
self._namedict.setdefault(txmember.name, []).append(txmember)
self._changed()
if self._isLocalPackage(txmember):
self.localSack.addPackage(txmember.po)
elif isinstance(txmember.po, YumAvailablePackageSqlite):
self.pkgSackPackages += 1
if self._inSack is not None and txmember.output_state in TS_INSTALL_STATES:
if not txmember.po.have_fastReturnFileEntries():
# In theory we could keep this on if a "small" repo. fails
self._inSack = None
else:
self._inSack.addPackage(txmember.po)
if txmember.name in self.conditionals:
for pkg in self.conditionals[txmember.name]:
if self.rpmdb.contains(po=pkg):
continue
for condtxmbr in self.install_method(po=pkg):
condtxmbr.setAsDep(po=txmember.po)
self._unresolvedMembers.add(txmember)
def remove(self, pkgtup):
"""remove a package from the transaction"""
if pkgtup not in self.pkgdict:
self.debugprint("Package: %s not in ts" %(pkgtup,))
return
for txmbr in self.pkgdict[pkgtup]:
txmbr.po.state = None
if self._isLocalPackage(txmbr):
self.localSack.delPackage(txmbr.po)
elif isinstance(txmbr.po, YumAvailablePackageSqlite):
self.pkgSackPackages -= 1
if self._inSack is not None and txmbr.output_state in TS_INSTALL_STATES:
self._inSack.delPackage(txmbr.po)
self._namedict[txmbr.name].remove(txmbr)
self._unresolvedMembers.add(txmbr)
del self.pkgdict[pkgtup]
if not self._namedict[pkgtup[0]]:
del self._namedict[pkgtup[0]]
self._changed()
def exists(self, pkgtup):
"""tells if the pkg is in the class"""
if pkgtup in self.pkgdict:
if len(self.pkgdict[pkgtup]) != 0:
return 1
return 0
def isObsoleted(self, pkgtup):
"""true if the pkgtup is marked to be obsoleted"""
if self.exists(pkgtup):
for txmbr in self.getMembers(pkgtup=pkgtup):
if txmbr.output_state == TS_OBSOLETED:
return True
return False
def makelists(self, include_reinstall=False, include_downgrade=False):
"""returns lists of transaction Member objects based on mode:
updated, installed, erased, obsoleted, depupdated, depinstalled
deperased"""
self.instgroups = []
self.removedgroups = []
self.instenvironments = []
self.removedenvironments = []
self.removed = []
self.installed = []
self.updated = []
self.obsoleted = []
self.depremoved = []
self.depinstalled = []
self.depupdated = []
self.reinstalled = []
self.downgraded = []
self.failed = []
for txmbr in self.getMembers():
if txmbr.output_state == TS_UPDATE:
if txmbr.isDep:
self.depupdated.append(txmbr)
else:
self.updated.append(txmbr)
elif txmbr.output_state in (TS_INSTALL, TS_TRUEINSTALL):
if include_reinstall and txmbr.reinstall:
self.reinstalled.append(txmbr)
continue
if include_downgrade and txmbr.downgrades:
self.downgraded.append(txmbr)
continue
if txmbr.groups:
for g in txmbr.groups:
if g not in self.instgroups:
self.instgroups.append(g)
if txmbr.environments:
for evg in txmbr.environments:
if evg not in self.instenvironments:
self.instenvironments.append(evg)
# "user" here is kind of a hack ... we really shouldn't have
# .isDep == True, but this probably doesn't hurt.
if txmbr.isDep and txmbr.reason != 'user':
self.depinstalled.append(txmbr)
else:
self.installed.append(txmbr)
elif txmbr.output_state == TS_ERASE:
if include_downgrade and txmbr.downgraded_by:
continue
for g in txmbr.groups:
if g not in self.instgroups:
self.removedgroups.append(g)
for evg in txmbr.environments:
if evg not in self.instenvironments:
self.removedenvironments.append(evg)
if txmbr.isDep:
self.depremoved.append(txmbr)
else:
self.removed.append(txmbr)
elif txmbr.output_state == TS_OBSOLETED:
self.obsoleted.append(txmbr)
elif txmbr.output_state == TS_OBSOLETING:
self.installed.append(txmbr)
elif txmbr.output_state == TS_FAILED:
self.failed.append(txmbr)
else:
pass
self.updated.sort()
self.installed.sort()
self.removed.sort()
self.obsoleted.sort()
self.depupdated.sort()
self.depinstalled.sort()
self.depremoved.sort()
self.instgroups.sort()
self.removedgroups.sort()
self.instenvironments.sort()
self.removedenvironments.sort()
self.reinstalled.sort()
self.downgraded.sort()
self.failed.sort()
def addInstall(self, po):
"""adds a package as an install but in mode 'u' to the ts
takes a packages object and returns a TransactionMember Object"""
if self._allowedMultipleInstalls(po):
return self.addTrueInstall(po)
txmbr = TransactionMember(po)
txmbr.current_state = TS_AVAILABLE
txmbr.output_state = TS_INSTALL
txmbr.po.state = TS_INSTALL
txmbr.ts_state = 'u'
if self.rpmdb.contains(po=txmbr.po):
txmbr.reinstall = True
self.findObsoletedByThisMember(txmbr)
self.add(txmbr)
return txmbr
def addTrueInstall(self, po):
"""adds a package as an install
takes a packages object and returns a TransactionMember Object"""
txmbr = TransactionMember(po)
txmbr.current_state = TS_AVAILABLE
txmbr.output_state = TS_TRUEINSTALL
txmbr.po.state = TS_INSTALL
txmbr.ts_state = 'i'
if self.rpmdb.contains(po=txmbr.po):
txmbr.reinstall = True
self.add(txmbr)
return txmbr
def addErase(self, po):
"""adds a package as an erasure
takes a packages object and returns a TransactionMember Object"""
txmbr = TransactionMember(po)
txmbr.current_state = TS_INSTALL
txmbr.output_state = TS_ERASE
txmbr.po.state = TS_INSTALL
txmbr.ts_state = 'e'
self.add(txmbr)
return txmbr
def addUpdate(self, po, oldpo=None):
"""adds a package as an update
takes a packages object and returns a TransactionMember Object"""
if self._allowedMultipleInstalls(po):
return self.addTrueInstall(po)
txmbr = TransactionMember(po)
txmbr.current_state = TS_AVAILABLE
txmbr.output_state = TS_UPDATE
txmbr.po.state = TS_UPDATE
txmbr.ts_state = 'u'
if oldpo:
txmbr.relatedto.append((oldpo, 'updates'))
txmbr.updates.append(oldpo)
self.add(txmbr)
self.findObsoletedByThisMember(txmbr)
return txmbr
def addDowngrade(self, po, oldpo):
"""adds a package as an downgrade takes a packages object and returns
a pair of TransactionMember Objects"""
itxmbr = self.addErase(oldpo)
itxmbr.relatedto.append((po, 'downgradedby'))
itxmbr.downgraded_by.append(po)
atxmbr = self.addInstall(po)
if not atxmbr: # Fail?
self.remove(itxmbr.pkgtup)
return None
if 'reason' in oldpo.yumdb_info:
atxmbr.reason = oldpo.yumdb_info.reason
atxmbr.relatedto.append((oldpo, 'downgrades'))
atxmbr.downgrades.append(oldpo)
return (itxmbr, atxmbr)
def addUpdated(self, po, updating_po):
"""adds a package as being updated by another pkg
takes a packages object and returns a TransactionMember Object"""
txmbr = TransactionMember(po)
txmbr.current_state = TS_INSTALL
txmbr.output_state = TS_UPDATED
txmbr.po.state = TS_UPDATED
txmbr.ts_state = 'ud'
txmbr.relatedto.append((updating_po, 'updatedby'))
txmbr.updated_by.append(updating_po)
if 'reason' in po.yumdb_info: # Propbably worthless, but...
txmbr.reason = po.yumdb_info.reason
self.add(txmbr)
return txmbr
def addObsoleting(self, po, oldpo):
"""adds a package as an obsolete over another pkg
takes a packages object and returns a TransactionMember Object"""
txmbr = TransactionMember(po)
txmbr.current_state = TS_AVAILABLE
txmbr.output_state = TS_OBSOLETING
txmbr.po.state = TS_OBSOLETING
txmbr.ts_state = 'u'
txmbr.relatedto.append((oldpo, 'obsoletes'))
txmbr.obsoletes.append(oldpo)
if self.rpmdb.contains(po=txmbr.po):
txmbr.reinstall = True
self.add(txmbr)
return txmbr
def addObsoleted(self, po, obsoleting_po):
"""adds a package as being obsoleted by another pkg
takes a packages object and returns a TransactionMember Object"""
txmbr = TransactionMember(po)
txmbr.current_state = TS_INSTALL
txmbr.output_state = TS_OBSOLETED
txmbr.po.state = TS_OBSOLETED
txmbr.ts_state = 'od'
txmbr.relatedto.append((obsoleting_po, 'obsoletedby'))
txmbr.obsoleted_by.append(obsoleting_po)
if 'reason' in po.yumdb_info: # Propbably worthless, but...
txmbr.reason = po.yumdb_info.reason
self.add(txmbr)
for otxmbr in self.getMembersWithState(obsoleting_po.pkgtup,
[TS_OBSOLETING]):
if po in otxmbr.obsoletes:
continue
otxmbr.relatedto.append((po, 'obsoletes'))
otxmbr.obsoletes.append(po)
return txmbr
def setDatabases(self, rpmdb, pkgSack, pkgSackCtor=None):
self.rpmdb = rpmdb
self._pkgSack = pkgSack
self._pkgSackCtor = pkgSackCtor
def getNewProvides(self, name, flag=None, version=(None, None, None)):
"""return dict { packages -> list of matching provides }
searches in packages to be installed"""
result = { }
if not self.pkgSackPackages:
pass
elif self._inSack is None:
for pkg, hits in self.pkgSack.getProvides(name, flag, version).iteritems():
if self.getMembersWithState(pkg.pkgtup, TS_INSTALL_STATES):
result[pkg] = hits
else:
for pkg, hits in self._inSack.getProvides(name, flag, version).iteritems():
result[pkg] = hits
result.update(self.localSack.getProvides(name, flag, version))
return result
def getOldProvides(self, name, flag=None, version=(None, None, None)):
"""return dict { packages -> list of matching provides }
searches in packages already installed and not going to be removed"""
result = { }
for pkg, hits in self.rpmdb.getProvides(name, flag, version).iteritems():
if not self.getMembersWithState(pkg.pkgtup, TS_REMOVE_STATES):
result[pkg] = hits
return result
def getProvides(self, name, flag=None, version=(None, None, None)):
"""return dict { packages -> list of matching provides }"""
result = self.getOldProvides(name, flag, version)
result.update(self.getNewProvides(name, flag, version))
return result
def getNewRequires(self, name, flag=None, version=(None, None, None)):
"""return dict { packages -> list of matching provides }
searches in packages to be installed"""
result = { }
if not self.pkgSackPackages:
pass
elif self._inSack is None:
for pkg, hits in self.pkgSack.getRequires(name, flag, version).iteritems():
if self.getMembersWithState(pkg.pkgtup, TS_INSTALL_STATES):
result[pkg] = hits
else:
for pkg, hits in self._inSack.getRequires(name, flag, version).iteritems():
result[pkg] = hits
result.update(self.localSack.getRequires(name, flag, version))
return result
def getOldRequires(self, name, flag=None, version=(None, None, None)):
"""return dict { packages -> list of matching provides }
searches in packages already installed and not going to be removed"""
result = { }
for pkg, hits in self.rpmdb.getRequires(name, flag, version).iteritems():
if not self.getMembersWithState(pkg.pkgtup, TS_REMOVE_STATES):
result[pkg] = hits
return result
def getRequires(self, name, flag=None, version=(None, None, None)):
"""return dict { packages -> list of matching provides }"""
result = self.getOldRequires(name, flag, version)
result.update(self.getNewRequires(name, flag, version))
return result
def futureRpmDBVersion(self):
""" Return a simple version for the future rpmdb. Works like
rpmdb.simpleVersion(main_only=True)[0], but for the state the rpmdb
will be in after the transaction. """
if self._future_rpmdbv is not None:
sc, ret = self._future_rpmdbv
if sc == self.state_counter:
return ret
pkgs = self.rpmdb.returnPackages()
_reinstalled_pkgtups = {}
for txmbr in self.getMembersWithState(None, TS_INSTALL_STATES):
# reinstalls have to use their "new" checksum data, in case it's
# different.
if txmbr.reinstall:
_reinstalled_pkgtups[txmbr.po.pkgtup] = txmbr.po
pkgs.append(txmbr.po)
self.rpmdb.preloadPackageChecksums()
main = PackageSackVersion()
pkg_checksum_tups = []
for pkg in sorted(pkgs):
if pkg.repoid != 'installed':
# Paste from PackageSackBase.simpleVersion()
csum = pkg.returnIdSum()
main.update(pkg, csum)
pkg_checksum_tups.append((pkg.pkgtup, csum))
continue
# Installed pkg, see if it's about to die
if self.getMembersWithState(pkg.pkgtup, TS_REMOVE_STATES):
continue
# ...or die and be risen again (Zombie!)
if pkg.pkgtup in _reinstalled_pkgtups:
continue
# Paste from rpmdb.simpleVersion()
ydbi = pkg.yumdb_info
csum = None
if 'checksum_type' in ydbi and 'checksum_data' in ydbi:
csum = (ydbi.checksum_type, ydbi.checksum_data)
# We need all the pkgtups, so we even save the ones without a
# checksum.
pkg_checksum_tups.append((pkg.pkgtup, csum))
main.update(pkg, csum)
self.rpmdb.transactionCachePackageChecksums(pkg_checksum_tups)
self._future_rpmdbv = (self.state_counter, main)
return main
def findObsoletedByThisMember(self, txmbr):
"""addObsoleted() pkgs for anything that this txmbr will obsolete"""
# this is mostly to keep us in-line with what will ACTUALLY happen
# when rpm hits the obsoletes, whether we added them or not
for obs_n in txmbr.po.obsoletes_names:
for pkg in self.rpmdb.searchNevra(name=obs_n):
if pkg.obsoletedBy([txmbr.po]):
self.addObsoleted(pkg, txmbr.po)
txmbr.output_state = TS_OBSOLETING
txmbr.po.state = TS_OBSOLETING
class ConditionalTransactionData(TransactionData):
"""A transaction data implementing conditional package addition"""
def __init__(self):
warnings.warn("ConditionalTransactionData will go away in a future "
"version of Yum.", Errors.YumFutureDeprecationWarning)
TransactionData.__init__(self)
class SortableTransactionData(TransactionData):
"""A transaction data implementing topological sort on it's members"""
def __init__(self):
# Cache of sort
self._sorted = []
# Current dependency path
self.path = []
# List of loops
self.loops = []
TransactionData.__init__(self)
def _visit(self, txmbr):
self.path.append(txmbr.name)
txmbr.sortColour = TX_GREY
for po in txmbr.depends_on:
vertex = self.getMembers(pkgtup=po.pkgtup)[0]
if vertex.sortColour == TX_GREY:
self._doLoop(vertex.name)
if vertex.sortColour == TX_WHITE:
self._visit(vertex)
txmbr.sortColour = TX_BLACK
self._sorted.insert(0, txmbr.pkgtup)
def _doLoop(self, name):
self.path.append(name)
loop = self.path[self.path.index(self.path[-1]):]
if len(loop) > 2:
self.loops.append(loop)
def add(self, txmember):
txmember.sortColour = TX_WHITE
TransactionData.add(self, txmember)
self._sorted = []
def remove(self, pkgtup):
TransactionData.remove(self, pkgtup)
self._sorted = []
def sort(self):
if self._sorted:
return self._sorted
self._sorted = []
# loop over all members
for txmbr in self.getMembers():
if txmbr.sortColour == TX_WHITE:
self.path = [ ]
self._visit(txmbr)
self._sorted.reverse()
return self._sorted
class TransactionMember:
"""Class to describe a Transaction Member (a pkg to be installed/
updated/erased)."""
def __init__(self, po):
# holders for data
self.po = po # package object
self.current_state = None # where the package currently is (repo, installed)
self.ts_state = None # what state to put it into in the transaction set
self.output_state = None # what state to list if printing it
self.isDep = 0
self.reason = 'user' # reason for it to be in the transaction set
self.process = None # I think this is used nowhere by nothing - skv 2010/11/03
self.relatedto = [] # ([relatedpkg, relationship)]
self.depends_on = []
self.obsoletes = []
self.obsoleted_by = []
self.updates = []
self.updated_by = []
self.downgrades = []
self.downgraded_by = []
self.reinstall = False
self.groups = [] # groups it's in
self.environments = [] # Env. groups it's in
self.repopkg = None # repo pkg "group" it was removed/installed by
self._poattr = ['pkgtup', 'repoid', 'name', 'arch', 'epoch', 'version',
'release']
for attr in self._poattr:
val = getattr(self.po, attr)
setattr(self, attr, val)
if po.repoid == 'installed':
# We want to load these so that we can auto hardlink in the same
# new values. Because of the hardlinks it should be really cheap
# to load them ... although it's still a minor hack.
po.yumdb_info.get('from_repo')
po.yumdb_info.get('releasever')
po.yumdb_info.get('changed_by')
def setAsDep(self, po=None, relonly=False):
"""sets the transaction member as a dependency and maps the dep into the
relationship list attribute"""
if not relonly:
self.isDep = 1
if po:
self.relatedto.append((po, 'dependson'))
self.depends_on.append(po)
def removeDep(self, po):
"""sets the transaction member as a dependency and maps the dep into the
relationship list attribute"""
nrelto = []
for data in self.relatedto:
if data[0] == po:
continue
nrelto.append(data)
self.relatedto = nrelto
ndepon = []
for data in self.depends_on:
if data == po:
continue
ndepon.append(data)
self.depends_on = ndepon
def __cmp__(self, other):
return cmp(self.po, other.po)
def __hash__(self):
return object.__hash__(self)
def __str__(self):
return "%s.%s %s:%s-%s - %s" % (self.name, self.arch, self.epoch,
self.version, self.release, self.ts_state)
def __repr__(self):
return "<%s : %s (%s)>" % (self.__class__.__name__, str(self),hex(id(self)))
def _dump(self):
msg = ["mbr: %s,%s,%s,%s,%s %s\n" %
(self.name, self.arch, self.epoch, self.version, self.release,
self.current_state),
" repo: %s\n" % self.po.repo.id,
" ts_state: %s\n" % self.ts_state,
" output_state: %s\n" % self.output_state,
" isDep: %s\n" % bool(self.isDep),
" reason: %s\n" % self.reason,
# " process: %s\n" % self.process,
" reinstall: %s\n" % bool(self.reinstall)]
if self.relatedto:
msg += [" relatedto:"]
for (po, rel) in self.relatedto:
pkgorigin = 'a'
if isinstance(po, YumInstalledPackage):
pkgorigin = 'i'
msg += [" %s,%s,%s,%s,%s@%s:%s" %
(po.name, po.arch, po.epoch, po.version, po.release,
pkgorigin, rel)]
msg += ["\n"]
for lst in ['depends_on', 'obsoletes', 'obsoleted_by', 'downgrades',
'downgraded_by', 'updates', 'updated_by']:
thislist = getattr(self, lst)
if thislist:
msg += [" %s:" % lst]
for po in thislist:
pkgorigin = 'a'
if isinstance(po, YumInstalledPackage):
pkgorigin = 'i'
msg += [" %s,%s,%s,%s,%s@%s" %
(po.name, po.arch, po.epoch, po.version,
po.release, pkgorigin)]
msg += ["\n"]
if self.groups:
msg += [" groups: %s\n" % ' '.join(self.groups)]
if self.environments:
msg += [" environments: %s\n" % ' '.join(self.environments)]
if self.repopkg:
msg += [" repopkg: %s\n" % self.repopkg]
return ''.join(msg)