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

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