[yum-commits] Branch 'yum-3_2_X' - 3 commits - test/complexremovetests.py test/testbase.py yum/__init__.py
James Antill
james at osuosl.org
Mon Jun 6 15:12:54 UTC 2011
test/complexremovetests.py | 85 +++++++++++++++++++++++++++++++++++++++++++++
test/testbase.py | 47 ++++++++++++++++++++++--
yum/__init__.py | 66 ++++++++++++++++++++++++++++++++--
3 files changed, 189 insertions(+), 9 deletions(-)
New commits:
commit b79cd7f945fcc8a47c0a861185d8510bcb645770
Author: Casey Jao <cjao at ugcs.caltech.edu>
Date: Mon Jun 6 07:09:20 2011 -0700
Add complex remove tests for new _remove_old_deps code.
diff --git a/test/complexremovetests.py b/test/complexremovetests.py
new file mode 100644
index 0000000..ecb5f85
--- /dev/null
+++ b/test/complexremovetests.py
@@ -0,0 +1,85 @@
+from testbase import *
+
+class ComplexRemoveTests(OperationsTests):
+ # Three testcases. A -> B means that B requires A.
+ # 1) 0 -> L1 -> (R1 -> R2, L2 -> L3) where everything but L3 was
+ # dep-installed (removing L3 should remove everything);
+ # 2) 0 -> L1 -> (R1 -> R2, L2 -> L3) where everything but L3 and R2 was
+ # dep-installed (removing L3 should only remove L3 and L2);
+ # 3) C1 <--> C2 -> C3 where C1 and C2 were dep-installed (removing
+ # C3 should remove everything)
+ @staticmethod
+ def buildPkgs(pkgs, *args):
+ pkgs.node0 = FakePackage('foo', '2.5', '1.1', '0', 'noarch')
+ pkgs.node0.addFile('/bin/foo')
+ pkgs.node0.yumdb_info.reason = 'dep'
+
+ pkgs.lnode1 = FakePackage('bar1', '1.0')
+ pkgs.lnode1.addRequires('foo')
+ pkgs.lnode1.addRequiresPkg(pkgs.node0)
+ pkgs.node0.addRequiringPkg(pkgs.lnode1)
+ pkgs.lnode1.yumdb_info.reason = 'dep'
+
+ pkgs.lnode2 = FakePackage('bar2', '1.0')
+ pkgs.lnode2.addRequires('bar1')
+ pkgs.lnode2.addRequiresPkg(pkgs.lnode1)
+ pkgs.lnode1.addRequiringPkg(pkgs.lnode2)
+ pkgs.lnode2.yumdb_info.reason = 'dep'
+
+ pkgs.lnode3 = FakePackage('bar3', '1.0')
+ pkgs.lnode3.addRequires('bar2')
+ pkgs.lnode3.addRequiresPkg(pkgs.lnode2)
+ pkgs.lnode2.addRequiresPkg(pkgs.lnode3)
+ pkgs.lnode3.yumdb_info.reason = 'user'
+
+ pkgs.rnode1 = FakePackage('baz1', '1.0')
+ pkgs.rnode1.addRequires('bar1')
+ pkgs.rnode1.addRequiresPkg(pkgs.lnode1)
+ pkgs.lnode1.addRequiringPkg(pkgs.rnode1)
+ pkgs.rnode1.yumdb_info.reason = 'dep'
+
+ pkgs.rnode2 = FakePackage('baz2', '1.0')
+ pkgs.rnode2.addRequires('baz1')
+ pkgs.rnode2.addRequiresPkg(pkgs.rnode1)
+ pkgs.rnode1.addRequiringPkg(pkgs.rnode2)
+ pkgs.rnode2.yumdb_info.reason = 'dep'
+
+ pkgs.cycle1 = FakePackage('cycle1', '1.0')
+ pkgs.cycle1.yumdb_info.reason = 'dep'
+
+ pkgs.cycle2 = FakePackage('cycle2', '1.0')
+ pkgs.cycle2.yumdb_info.reason = 'dep'
+
+ pkgs.cycle3 = FakePackage('cycle3', '1.0')
+ pkgs.cycle3.yumdb_info.reason = 'user'
+
+
+ pkgs.cycle1.addRequires('cycle2')
+ pkgs.cycle1.addRequiresPkg(pkgs.cycle2)
+ pkgs.cycle2.addRequiringPkg(pkgs.cycle1)
+
+ pkgs.cycle2.addRequires('cycle1')
+ pkgs.cycle2.addRequiringPkg(pkgs.cycle1)
+ pkgs.cycle1.addRequiringPkg(pkgs.cycle2)
+
+ pkgs.cycle3.addRequires('cycle2')
+ pkgs.cycle3.addRequiresPkg(pkgs.cycle2)
+ pkgs.cycle2.addRequiringPkg(pkgs.cycle3)
+
+ def testRemoveCycle(self):
+ p = self.pkgs
+ res, msg = self.runOperation(['remove', 'cycle3'], [p.cycle1, p.cycle2, p.cycle3], [])
+ self.assertResult( () )
+
+ def testRemoveTree(self):
+ p = self.pkgs
+ res, msg = self.runOperation(['remove', 'bar3'], [p.node0, p.lnode1, p.lnode2, p.lnode3, p.rnode1, p.rnode2], [])
+ self.assertResult( () )
+
+ def testRemoveNeededRevdeps(self):
+ p = self.pkgs
+ p.rnode2.yumdb_info.reason = 'user'
+ res, msg = self.runOperation(['remove', 'bar3'], [p.node0, p.lnode1, p.lnode2, p.lnode3, p.rnode1, p.rnode2], [])
+ p.rnode2.yumdb_info.reason = 'dep'
+ self.assertResult( (p.node0, p.lnode1, p.rnode1, p.rnode2) )
+
commit 664049895f916bf6e6bd3f81abebfbc55ad572ae
Author: Casey Jao <cjao at ugcs.caltech.edu>
Date: Mon Jun 6 07:08:46 2011 -0700
Make FakePackage use a simulated RPMDBAdditionalDataPackage for yumdb_info.
diff --git a/test/testbase.py b/test/testbase.py
index 12025ce..d0f22be 100644
--- a/test/testbase.py
+++ b/test/testbase.py
@@ -84,6 +84,39 @@ class FakeRepo(object):
else:
return 0
+class FakeYumDBInfo(object):
+ """Simulate some functionality of RPMAdditionalDataPackage"""
+ _auto_hardlink_attrs = set(['checksum_type', 'reason',
+ 'installed_by', 'changed_by',
+ 'from_repo', 'from_repo_revision',
+ 'from_repo_timestamp', 'releasever',
+ 'command_line'])
+
+ def __init__(self, conf=None, pkgdir=None, yumdb_cache=None):
+ self.db = {}
+ for attr in self._auto_hardlink_attrs:
+ self.db[attr] = ''
+
+ def __getattr__(self, attr):
+ return self.db[attr]
+
+ def __setattr__(self, attr, value):
+ if not attr.startswith("db"):
+ self.db[attr] = value
+ else:
+ object.__setattr__(self, attr, value)
+
+ def __iter__(self, show_hidden=False):
+ for item in self.db:
+ yield item
+
+ def get(self, attr, default=None):
+ try:
+ res = self.db[attr]
+ except AttributeError:
+ return default
+ return res
+
class FakePackage(packages.YumAvailablePackage):
def __init__(self, name, version='1.0', release='1', epoch='0', arch='noarch', repo=None):
@@ -102,18 +135,24 @@ class FakePackage(packages.YumAvailablePackage):
self.epoch = epoch
self.arch = arch
self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
- self.yumdb_info = {}
+ self.yumdb_info = FakeYumDBInfo()
self.prco['provides'].append((name, 'EQ', (epoch, version, release)))
# Just a unique integer
self.id = self.__hash__()
self.pkgKey = self.__hash__()
-
+
+ self.required_pkgs = []
+ self.requiring_pkgs = []
def addProvides(self, name, flag=None, evr=(None, None, None)):
self.prco['provides'].append((name, flag, evr))
def addRequires(self, name, flag=None, evr=(None, None, None)):
self.prco['requires'].append((name, flag, evr))
+ def addRequiresPkg(self, pkg):
+ self.required_pkgs.append(pkg)
+ def addRequiringPkg(self, pkg):
+ self.requiring_pkgs.append(pkg)
def addConflicts(self, name, flag=None, evr=(None, None, None)):
self.prco['conflicts'].append((name, flag, evr))
def addObsoletes(self, name, flag=None, evr=(None, None, None)):
@@ -121,9 +160,9 @@ class FakePackage(packages.YumAvailablePackage):
def addFile(self, name, ftype='file'):
self.files[ftype].append(name)
def required_packages(self):
- return []
+ return self.required_pkgs
def requiring_packages(self):
- return []
+ return self.requiring_pkgs
class _Container(object):
pass
commit 96f3a7d325045d2234310faa779c1b0d17227d18
Author: Casey Jao <cjao at ugcs.caltech.edu>
Date: Mon Jun 6 07:07:20 2011 -0700
Fix _remove_old_deps to deal with chains of revdeps.
diff --git a/yum/__init__.py b/yum/__init__.py
index 77c4d00..ab2bb9d 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -5363,6 +5363,11 @@ class YumBase(depsolve.Depsolve):
found_leaves = set()
checked = set()
beingremoved = [ t.po for t in self.tsInfo.getMembersWithState(output_states=TS_REMOVE_STATES) ]
+ # cache previously examined packages
+ okay_to_remove = {}
+ for i in self.rpmdb.returnPackages():
+ okay_to_remove[i] = True
+
for pkg in beingremoved: # for each package required by the pkg being removed
#print 'removal: %s' % pkg.name
for required in pkg.required_packages():
@@ -5370,23 +5375,26 @@ class YumBase(depsolve.Depsolve):
# continue # if we've already checked it, skip it.
#checked.add(required)
if required.yumdb_info.get('reason', '') != 'dep': # if the required pkg is not a dep, then skip it
+ okay_to_remove[required] = False
continue
if required in beingremoved:
continue
+ if self._has_needed_revdeps(required, beingremoved, okay_to_remove):
+ continue
still_needed = False
for requiring in required.requiring_packages(): # so we have required deps - look at all the pkgs which require them
if requiring == required: # if they are self-requiring skip them
continue
- if requiring not in beingremoved: # if the requiring pkg is not in the list of pkgs being removed then it is still needed
- still_needed = True
- break
+
# go through the stuff in the ts to be installed - make sure none of that needs the required pkg, either.
for (provn,provf,provevr) in required.provides:
if self.tsInfo.getNewRequires(provn, provf, provevr).keys():
still_needed = True
+ okay_to_remove[required] = False
break
for fn in required.filelist + required.dirlist:
if self.tsInfo.getNewRequires(fn, None,(None,None,None)).keys():
+ okay_to_remove[required] = False
still_needed = True
break
@@ -5406,5 +5414,53 @@ class YumBase(depsolve.Depsolve):
beingremoved.append(txmbr.po)
found_leaves.add(txmbr)
self.verbose_logger.log(logginglevels.INFO_2, "Found and removing %s unneeded dependencies" % len(found_leaves))
-
-
+
+ # Checks if pkg has any reverse deps which cannot be removed.
+ # Currently this only checks the install reason for each revdep,
+ # but we may want to check for other reasons that would
+ # prevent the revdep from being removed (e.g. protected)
+ def _has_needed_revdeps(self, pkg, beingremoved, ok_to_remove):
+ # check if we've already found this package to have user-installed deps
+ if not ok_to_remove[pkg]:
+ # Debugging output
+ self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has been visited already and cannot be removed."), pkg)
+ return True
+ # Debugging output
+ self.verbose_logger.log(logginglevels.DEBUG_2, _("Examining revdeps of %s"), pkg)
+ # track which pkgs we have visited already
+ visited = {}
+ for po in self.rpmdb.returnPackages():
+ visited[po] = False
+ # no need to consider packages that are already being removed
+ for po in beingremoved:
+ visited[po] = True
+ stack = []
+ stack.append(pkg)
+ # depth-first search
+ while stack:
+ curpkg = stack[-1]
+ if not visited[curpkg]:
+ if not ok_to_remove[curpkg]:
+ # Debugging output
+ self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has been visited already and cannot be removed."), pkg)
+ ok_to_remove[pkg] = False
+ return True
+ if curpkg.yumdb_info.get('reason', '') != 'dep':
+ # Debugging output
+ self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has revdep %s which was user-installed."), pkg, curpkg)
+ ok_to_remove[pkg] = False
+ return True
+ visited[curpkg] = True
+ all_leaves_visited = True
+ leaves = curpkg.requiring_packages()
+ for leaf in leaves:
+ if not visited[leaf]:
+ stack.append(leaf)
+ all_leaves_visited = False
+ break
+ if all_leaves_visited:
+ stack.pop()
+ # Debugging output
+ self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has no user-installed revdeps."), pkg)
+ return False
+
More information about the Yum-commits
mailing list