improve SLOT handling, add option for emerge-like resolver
authorgenone <genone@gentoo.org>
Wed, 30 May 2007 17:13:31 +0000 (17:13 -0000)
committergenone <genone@gentoo.org>
Wed, 30 May 2007 17:13:31 +0000 (17:13 -0000)
svn path=/; revision=403

trunk/ChangeLog
trunk/src/glsa-check/glsa-check
trunk/src/glsa-check/glsa.py

index e150ce2e23c26f05068d48dd7514e2c8dcbcd7b9..442a1a8964a3e49ef6f622aabd0a8cb6e94d6fb6 100644 (file)
@@ -1,3 +1,8 @@
+2007-05-30: Marius Mauch <genone@gentoo.org>
+       * glsa-check: check SLOT when selecting and displaying upgrades
+       * glsa-check: new --emergelike option to use the best version 
+       within the same SLOT instead of the one with the smallest delta.
+
 2007-05-21: Paul Varner <fuzzyray@gentoo.org>
        * echangelog: Add patch from genstef to fix issues from Bug 176337
 
index 6d2a11b9cbb349ba8db14acbf0543c650143c63c..5789547693d853e0009a2db7030a635657d5ea5b 100644 (file)
@@ -17,7 +17,7 @@ from getopt import getopt,GetoptError
 
 __program__ = "glsa-check"
 __author__ = "Marius Mauch <genone@gentoo.org>"
-__version__ = "0.8"
+__version__ = "0.9"
 
 optionmap = [
 ["-l", "--list", "list all unapplied GLSA"],
@@ -27,6 +27,7 @@ optionmap = [
 ["-f", "--fix", "try to auto-apply this GLSA (experimental)"],
 ["-i", "--inject", "inject the given GLSA into the checkfile"],
 ["-n", "--nocolor", "disable colors (option)"],
+["-e", "--emergelike", "do not use a least-change algorithm (option)"],
 ["-h", "--help", "show this help message"],
 ["-V", "--version", "some information about this tool"],
 ["-v", "--verbose", "print more information (option)"],
@@ -67,6 +68,12 @@ try:
                if option in args:
                        list_cve = True
                        args.remove(option)
+       
+       least_change = True
+       for option in ["--emergelike", "-e"]:
+               if option in args:
+                       least_change = False
+                       args.remove(option)
 
        # sanity checking
        if len(args) <= 0:
@@ -125,6 +132,9 @@ from glsa import *
 
 glsaconfig = checkconfig(portage.config(clone=portage.settings))
 
+vardb = portage.db["/"]["vartree"].dbapi
+portdb = portage.db["/"]["porttree"].dbapi
+
 # Check that we really have a glsa dir to work on
 if not (os.path.exists(glsaconfig["GLSA_DIR"]) and os.path.isdir(glsaconfig["GLSA_DIR"])):
        sys.stderr.write(red("ERROR")+": GLSA_DIR %s doesn't exist. Please fix this.\n" % glsaconfig["GLSA_DIR"])
@@ -203,7 +213,7 @@ def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr):
                                fd1.write("... ")
                else:
                        for pkg in myglsa.packages.keys():
-                               mylist = portage.db["/"]["vartree"].dbapi.match(portage.dep_getkey(pkg))
+                               mylist = vardb.match(portage.dep_getkey(pkg))
                                if len(mylist) > 0:
                                        pkg = color(" ".join(mylist))
                                fd1.write(" " + pkg + " ")
@@ -230,7 +240,7 @@ if mode in ["dump", "fix", "inject", "pretend"]:
                        myglsa.dump()
                elif mode == "fix":
                        sys.stdout.write("fixing "+myid+"\n")
-                       mergelist = myglsa.getMergeList()
+                       mergelist = myglsa.getMergeList(least_change=least_change)
                        for pkg in mergelist:
                                sys.stdout.write(">>> merging "+pkg+"\n")
                                # using emerge for the actual merging as it contains the dependency
@@ -245,12 +255,16 @@ if mode in ["dump", "fix", "inject", "pretend"]:
                        myglsa.inject()
                elif mode == "pretend":
                        sys.stdout.write("Checking GLSA "+myid+"\n")
-                       mergelist = myglsa.getMergeList()
+                       mergelist = myglsa.getMergeList(least_change=least_change)
                        if mergelist:
                                sys.stdout.write("The following updates will be performed for this GLSA:\n")
                                for pkg in mergelist:
-                                       # we simplify a bit here
-                                       oldver = portage.db["/"]["vartree"].dbapi.match(portage.dep_getkey(pkg))[-1]
+                                       oldver = None
+                                       for x in vardb.match(portage.dep_getkey(pkg)):
+                                               if vardb.aux_get(x, ["SLOT"]) == portdb.aux_get(pkg, ["SLOT"]):
+                                                       oldver = x
+                                       if oldver == None:
+                                               raise ValueError("could not find old version for package %s" % pkg)
                                        oldver = oldver[len(portage.dep_getkey(oldver))+1:]
                                        sys.stdout.write("     " + pkg + " (" + oldver + ")\n")
                        else:
index 474f9f2bafc4ec30d3ceaab090e829b6f930d297..87a39e4cf5446bd6612c594e89c256f27b48e444 100644 (file)
@@ -288,7 +288,7 @@ def makeVersion(versionNode):
        return opMapping[versionNode.getAttribute("range")] \
                        +getText(versionNode, format="strip")
 
-def match(atom, portdbname):
+def match(atom, portdbname, match_type="default"):
        """
        wrapper that calls revisionMatch() or portage.dbapi.match() depending on 
        the given atom.
@@ -297,16 +297,22 @@ def match(atom, portdbname):
        @param  atom: a <~ or >~ atom or a normal portage atom that contains the atom to match against
        @type   portdb: portage.dbapi
        @param  portdb: one of the portage databases to use as information source
+       @type   match_type: string
+       @param  match_type: if != "default" passed as first argument to dbapi.xmatch 
+                               to apply the wanted visibility filters
        
        @rtype:         list of strings
        @return:        a list with the matching versions
        """
+       db = portage.db["/"][portdbname].dbapi
        if atom[2] == "~":
-               return revisionMatch(atom, portage.db["/"][portdbname].dbapi)
+               return revisionMatch(atom, db, match_type=match_type)
+       elif match_type == "default" or not hasattr(db, "xmatch"):
+               return db.match(atom)
        else:
-               return portage.db["/"][portdbname].dbapi.match(atom)
+               return db.xmatch(match_type, atom)
 
-def revisionMatch(revisionAtom, portdb):
+def revisionMatch(revisionAtom, portdb, match_type="default"):
        """
        handler for the special >~, >=~, <=~ and <~ atoms that are supposed to behave
        as > and < except that they are limited to the same version, the range only
@@ -316,11 +322,17 @@ def revisionMatch(revisionAtom, portdb):
        @param  revisionAtom: a <~ or >~ atom that contains the atom to match against
        @type   portdb: portage.dbapi
        @param  portdb: one of the portage databases to use as information source
+       @type   match_type: string
+       @param  match_type: if != "default" passed as first argument to portdb.xmatch 
+                               to apply the wanted visibility filters
        
        @rtype:         list of strings
        @return:        a list with the matching versions
        """
-       mylist = portdb.match(re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+       if match_type == "default" or not hasattr(portdb, "xmatch"):
+               mylist = portdb.match(re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+       else:
+               mylist = portdb.xmatch(match_type, re.sub("-r[0-9]+$", "", revisionAtom[2:]))
        rValue = []
        for v in mylist:
                r1 = portage.pkgsplit(v)[-1][1:]
@@ -330,7 +342,7 @@ def revisionMatch(revisionAtom, portdb):
        return rValue
                
 
-def getMinUpgrade(vulnerableList, unaffectedList):
+def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
        """
        Checks if the systemstate is matching an atom in
        I{vulnerableList} and returns string describing
@@ -344,6 +356,9 @@ def getMinUpgrade(vulnerableList, unaffectedList):
        @param  vulnerableList: atoms matching vulnerable package versions
        @type   unaffectedList: List of Strings
        @param  unaffectedList: atoms matching unaffected package versions
+       @type   minimize:       Boolean
+       @param  minimize:       True for a least-change upgrade, False for emerge-like algorithm
+       
        @rtype:         String | None
        @return:        the lowest unaffected version that is greater than
                                the installed version.
@@ -363,22 +378,22 @@ def getMinUpgrade(vulnerableList, unaffectedList):
                        install_unaffected = False
 
        if install_unaffected:
-               return []
+               return rValue
        
        for u in unaffectedList:
-               if u[2] == "~":
-                       mylist = revisionMatch(u, portage.db["/"]["porttree"].dbapi)
-               else:
-                       mylist = portage.db["/"]["porttree"].dbapi.xmatch("match-all", u)
+               mylist = match(u, "porttree", match_type="match-all")
                for c in mylist:
                        c_pv = portage.catpkgsplit(c)
                        i_pv = portage.catpkgsplit(portage.best(v_installed))
-                       if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 and (rValue == None or portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) < 0):
+                       if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
+                                       and (rValue == None or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0))) \
+                                       and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(portage.best(v_installed), ["SLOT"]):
                                rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
                                if c_pv[3] != "r0":             # we don't like -r0 for display
                                        rValue += "-"+c_pv[3]
        return rValue
 
+
 # simple Exception classes to catch specific errors
 class GlsaTypeException(Exception):
        def __init__(self, doctype):
@@ -602,20 +617,22 @@ class Glsa:
                        checkfile.close()
                return None
        
-       def getMergeList(self):
+       def getMergeList(self, least_change=True):
                """
                Returns the list of package-versions that have to be merged to
                apply this GLSA properly. The versions are as low as possible 
                while avoiding downgrades (see L{getMinUpgrade}).
                
+               @type   least_change: Boolean
+               @param  least_change: True if the smallest possible upgrade should be selected,
+                                       False for an emerge-like algorithm
                @rtype:         List of Strings
                @return:        list of package-versions that have to be merged
                """
                rValue = []
                for pkg in self.packages.keys():
                        for path in self.packages[pkg]:
-                               update = getMinUpgrade(path["vul_atoms"],
-                                                                               path["unaff_atoms"])
+                               update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], minimize=least_change)
                                if update:
                                        rValue.append(update)
                return rValue