Blame SOURCES/BZ-1497351-versionlock-add-hint-and-status-cmd.patch

085af2
commit 691faec2bed8457edd19b8ad5587768bfbe4c653
085af2
Author: Michal Domonkos <mdomonko@redhat.com>
085af2
Date:   Mon May 28 16:49:10 2018 +0200
085af2
085af2
    docs: fix versionlock
085af2
    
085af2
    Fixes the formatting and clarifies the "exclude" subcommand (inspired
085af2
    by: https://illiterat.livejournal.com/8221.html).
085af2
085af2
diff --git a/docs/yum-versionlock.1 b/docs/yum-versionlock.1
085af2
index f7cd467..e14bbd5 100644
085af2
--- a/docs/yum-versionlock.1
085af2
+++ b/docs/yum-versionlock.1
085af2
@@ -16,22 +16,17 @@ This allows you to protect packages from being updated by newer versions.
085af2
 The plugin provides a command "versionlock" which allows you to view and edit
085af2
 the list of locked packages easily.
085af2
 .br
085af2
-.I  \fR yum versionlock add <package-wildcard>...
085af2
-.PP
085af2
+.IP "\fByum versionlock add <package-wildcard>...\fR"
085af2
 Add a versionlock for all of the packages in the rpmdb matching the given
085af2
 wildcards.
085af2
-.I  \fR yum versionlock exclude <package-wildcard>...
085af2
-.PP
085af2
-Add a exclude (within versionlock) for the latest versions of the
085af2
-packages in the available repos. matching the given wildcards.
085af2
-.I  \fR yum versionlock list
085af2
-.PP
085af2
+.IP "\fByum versionlock exclude <package-wildcard>...\fR"
085af2
+Opposite; disallow currently available versions of the packages matching the
085af2
+given wildcards.
085af2
+.IP "\fByum versionlock list\fR"
085af2
 List the current versionlock entries.
085af2
-.I  \fR yum versionlock delete <entry-wildcard>...
085af2
-.PP
085af2
+.IP "\fByum versionlock delete <entry-wildcard>...\fR"
085af2
 Remove any matching versionlock entries.
085af2
-.I  \fR yum versionlock clear
085af2
-.PP
085af2
+.IP "\fByum versionlock clear\fR"
085af2
 Remove all versionlock entries.
085af2
 
085af2
 .SH FILES
085af2
commit f761392dc6209ec17e718291c2319ea011923880
085af2
Author: Michal Domonkos <mdomonko@redhat.com>
085af2
Date:   Mon May 28 16:55:50 2018 +0200
085af2
085af2
    versionlock: add hint and "status" subcommand. BZ 1497351
085af2
    
085af2
    It is nice to be reminded of any versionlocks in effect when running yum
085af2
    transactions (esp. when doing a "yum update" and wondering why no
085af2
    updates were found).  This commit adds the following message that is
085af2
    printed in the excludes hook:
085af2
    
085af2
      Excluding X updates due to versionlock (use "yum versionlock status"
085af2
      to show them)
085af2
    
085af2
    Since it would be too annoying to have all the excludes printed every
085af2
    time, we only show their count (X) and provide the new "status"
085af2
    subcommand that allows the user to list them if they are interested.
085af2
    
085af2
    Note that even this short message might be annoying as it appears on
085af2
    every yum run, so we're also adding an option to disable it.
085af2
085af2
diff --git a/docs/yum-versionlock.1 b/docs/yum-versionlock.1
085af2
index e14bbd5..6190fea 100644
085af2
--- a/docs/yum-versionlock.1
085af2
+++ b/docs/yum-versionlock.1
085af2
@@ -24,6 +24,10 @@ Opposite; disallow currently available versions of the packages matching the
085af2
 given wildcards.
085af2
 .IP "\fByum versionlock list\fR"
085af2
 List the current versionlock entries.
085af2
+.IP "\fByum versionlock status\fR"
085af2
+List any available updates that are currently blocked by versionlock.
085af2
+That is, for each entry in the lock list, print the newest package available
085af2
+in the repos unless it is the particular locked/excluded version.
085af2
 .IP "\fByum versionlock delete <entry-wildcard>...\fR"
085af2
 Remove any matching versionlock entries.
085af2
 .IP "\fByum versionlock clear\fR"
085af2
diff --git a/docs/yum-versionlock.conf.5 b/docs/yum-versionlock.conf.5
085af2
index 78a6e49..b5be24e 100644
085af2
--- a/docs/yum-versionlock.conf.5
085af2
+++ b/docs/yum-versionlock.conf.5
085af2
@@ -26,7 +26,7 @@ character to the version.
085af2
 .B yum-versionlock.conf(5)
085af2
 utilizes configuration options in the form of
085af2
 .IP OPTION=VALUE
085af2
-.SH OPTION
085af2
+.SH OPTIONS
085af2
 .IP follow_obsoletes
085af2
 This option is a boolean flag which specifies if the versionlock plugin should
085af2
 look at all the obsoletes, and see if any of the packages specified have an
085af2
@@ -36,13 +36,20 @@ excluded. This option is off by default, as
085af2
 will take some time to do the obsoletes processing, and for non-rename
085af2
 obsoletes any issues you had which kept you at a specific version of a package
085af2
 should be different with another package.
085af2
-.SH OPTION
085af2
 .IP locklist
085af2
 This option is a string with points to the file which will have the versionlock
085af2
 information in it. Note that the file
085af2
 .B has
085af2
 to exist (or the versionlock plugin will make yum exit). However it can be
085af2
 empty.
085af2
+.IP show_hint
085af2
+This option is a boolean flag which specifies if the versionlock plugin should
085af2
+print a hint message whenever yum runs saying how many package updates
085af2
+available from the repos are being blocked due to versionlocks.
085af2
+This hint serves as a reminder that there are locks in effect and that you may
085af2
+want to reconsider them since newer versions of those packages have been
085af2
+released.
085af2
+Default is 1 (show the hint).
085af2
 .SH AUTHOR
085af2
 .RS
085af2
 Chitlesh Goorah <chitlesh@fedoraproject.org>
085af2
diff --git a/plugins/versionlock/versionlock.conf b/plugins/versionlock/versionlock.conf
085af2
index 4e997da..42e05c9 100644
085af2
--- a/plugins/versionlock/versionlock.conf
085af2
+++ b/plugins/versionlock/versionlock.conf
085af2
@@ -1,5 +1,7 @@
085af2
 [main]
085af2
 enabled = 1
085af2
 locklist = /etc/yum/pluginconf.d/versionlock.list
085af2
+#  Show a hint when any locked packages have updates available
085af2
+show_hint = 1
085af2
 #  Uncomment this to lock out "upgrade via. obsoletes" etc. (slower)
085af2
 # follow_obsoletes = 1
085af2
diff --git a/plugins/versionlock/versionlock.py b/plugins/versionlock/versionlock.py
085af2
index dfe4dd3..ad66855 100644
085af2
--- a/plugins/versionlock/versionlock.py
085af2
+++ b/plugins/versionlock/versionlock.py
085af2
@@ -42,6 +42,9 @@ _version_lock_excluder_B_nevr = set()
085af2
 # _version_lock_excluder_pkgtup = set()
085af2
 
085af2
 fileurl = None
085af2
+show_hint = True
085af2
+follow_obsoletes = False
085af2
+no_exclude = False
085af2
 
085af2
 def _read_locklist():
085af2
     locklist = []
085af2
@@ -73,6 +76,68 @@ def _match(ent, patterns):
085af2
                 return True
085af2
     return False
085af2
 
085af2
+def _get_updates(base):
085af2
+    """Return packages that update or obsolete anything in our locklist.
085af2
+
085af2
+    Returns a dict of locked_name->X, where X is either a package object or a
085af2
+    list of them.  If it's the former, it's the updating package.  If it's the
085af2
+    latter, it's the obsoleting packages (since multiple packages may obsolete
085af2
+    the same name).
085af2
+    """
085af2
+
085af2
+    updates = {}
085af2
+
085af2
+    # Read in the locked versions
085af2
+    locks = {}
085af2
+    for ent in _read_locklist():
085af2
+        (n, v, r, e, a) = splitFilename(ent)
085af2
+        if e and e[0] == '!':
085af2
+            e = e[1:]
085af2
+        elif e == '':
085af2
+            e = '0'
085af2
+        locks.setdefault(n, []).append((e, v, r))
085af2
+
085af2
+    # Process regular updates
085af2
+    #
085af2
+    # We are using searchNames() + packagesNewestByName() here instead of just
085af2
+    # returnNewestByName() because the former way is much, much faster for big
085af2
+    # name lists.
085af2
+    #
085af2
+    # The problem with returnNewestByName() is that it may easily end up
085af2
+    # querying all the packages in pkgSack which is terribly slow (takes
085af2
+    # seconds); all it takes is a "-" in a package name and more than
085af2
+    # PATTERNS_MAX (8 by default) package names to trigger that.
085af2
+    #
085af2
+    # Since we know that we only ever deal with names, we can just go straight
085af2
+    # to searchNames() to avoid the full query.
085af2
+    pkgs = base.pkgSack.searchNames(locks.keys())
085af2
+    for p in packagesNewestByName(pkgs):
085af2
+        name = p.name
085af2
+        evr = p.returnEVR()
085af2
+        if (evr.epoch, evr.version, evr.release) in locks[name]:
085af2
+            # This one is either the locked or excluded version, skip
085af2
+            continue
085af2
+        updates[name] = p
085af2
+
085af2
+    # Process obsoletes
085af2
+    tups = base.up.getObsoletesTuples() if follow_obsoletes else []
085af2
+    for new, old in tups:
085af2
+        nname = new[0]
085af2
+        oname = old[0]
085af2
+        if oname not in locks:
085af2
+            # Not our package, skip
085af2
+            continue
085af2
+        if nname in locks and new[2:] in locks[nname]:
085af2
+            # This one is either the locked or excluded version, skip
085af2
+            continue
085af2
+        # Only record obsoletes for any given package name
085af2
+        if oname not in updates or not isinstance(updates[oname], list):
085af2
+            updates[oname] = []
085af2
+        p = base.getPackageObject(new)
085af2
+        updates[oname].append(p)
085af2
+
085af2
+    return updates
085af2
+
085af2
 class VersionLockCommand:
085af2
     created = 1247693044
085af2
 
085af2
@@ -80,7 +145,7 @@ class VersionLockCommand:
085af2
         return ["versionlock"]
085af2
 
085af2
     def getUsage(self):
085af2
-        return '[add|exclude|list|delete|clear] [PACKAGE-wildcard]'
085af2
+        return '[add|exclude|list|status|delete|clear] [PACKAGE-wildcard]'
085af2
 
085af2
     def getSummary(self):
085af2
         return 'Control package version locks.'
085af2
@@ -93,7 +158,7 @@ class VersionLockCommand:
085af2
         if extcmds:
085af2
             if extcmds[0] not in ('add',
085af2
                                   'exclude', 'add-!', 'add!', 'blacklist',
085af2
-                                  'list', 'del', 'delete', 'clear'):
085af2
+                                  'list', 'status', 'del', 'delete', 'clear'):
085af2
                 cmd = 'add'
085af2
             else:
085af2
                 cmd = {'del'       : 'delete',
085af2
@@ -190,6 +255,19 @@ class VersionLockCommand:
085af2
             os.rename(tmpfilename, filename)
085af2
             return 0, ['versionlock deleted: ' + str(count)]
085af2
 
085af2
+        if cmd == 'status':
085af2
+            global no_exclude
085af2
+            no_exclude = True
085af2
+            updates = _get_updates(base)
085af2
+            for name, value in updates.iteritems():
085af2
+                if isinstance(value, list):
085af2
+                    value = set(p.envr + '.*' for p in value)
085af2
+                    for v in value:
085af2
+                        print '%s (replacing %s)' % (v, name)
085af2
+                    continue
085af2
+                print value.envr + '.*'
085af2
+            return 0, ['versionlock status done']
085af2
+
085af2
         assert cmd == 'list'
085af2
         for ent in _read_locklist():
085af2
             print ent
085af2
@@ -201,8 +279,12 @@ class VersionLockCommand:
085af2
 
085af2
 def config_hook(conduit):
085af2
     global fileurl
085af2
+    global follow_obsoletes
085af2
+    global show_hint
085af2
 
085af2
     fileurl = conduit.confString('main', 'locklist')
085af2
+    follow_obsoletes = conduit.confBool('main', 'follow_obsoletes', default=False)
085af2
+    show_hint = conduit.confBool('main', 'show_hint', default=True)
085af2
 
085af2
     if hasattr(conduit._base, 'registerCommand'):
085af2
         conduit.registerCommand(VersionLockCommand())
085af2
@@ -227,6 +309,9 @@ def _add_versionlock_blacklist(conduit):
085af2
     ape(None, exid + str(3), 'exclude.marked')
085af2
 
085af2
 def exclude_hook(conduit):
085af2
+    if no_exclude:
085af2
+        return
085af2
+
085af2
     conduit.info(3, 'Reading version lock configuration')
085af2
 
085af2
     if not fileurl:
085af2
@@ -250,8 +335,7 @@ def exclude_hook(conduit):
085af2
         _version_lock_excluder_n.add(n)
085af2
         _version_lock_excluder_nevr.add("%s-%s:%s-%s" % (n, e, v, r))
085af2
 
085af2
-    if (_version_lock_excluder_n and
085af2
-        conduit.confBool('main', 'follow_obsoletes', default=False)):
085af2
+    if (_version_lock_excluder_n and follow_obsoletes):
085af2
         #  If anything obsoletes something that we have versionlocked ... then
085af2
         # remove all traces of that too.
085af2
         for (pkgtup, instTup) in conduit._base.up.getObsoletesTuples():
085af2
@@ -259,6 +343,18 @@ def exclude_hook(conduit):
085af2
                 continue
085af2
             _version_lock_excluder_n.add(pkgtup[0].lower())
085af2
 
085af2
+    total = len(_get_updates(conduit._base)) if show_hint else 0
085af2
+    if total:
085af2
+        if total > 1:
085af2
+            suffix = 's'
085af2
+            what = 'them'
085af2
+        else:
085af2
+            suffix = ''
085af2
+            what = 'it'
085af2
+        conduit.info(2, 'Excluding %d update%s due to versionlock '
085af2
+                        '(use "yum versionlock status" to show %s)'
085af2
+                        % (total, suffix, what))
085af2
+
085af2
     if _version_lock_excluder_n:
085af2
         _add_versionlock_whitelist(conduit)
085af2
     if _version_lock_excluder_B_nevr: