[yum-commits] 8 commits - cli.py etc/yum-cron.conf etc/yum-cron-hourly.conf yumcommands.py yum-cron/yum-cron.py yum-cron/yum-hourly.cron.sh yum/__init__.py yum.spec yum/updateinfo.py

James Antill james at osuosl.org
Fri Apr 19 04:16:40 UTC 2013


 cli.py                      |   26 ++
 etc/yum-cron-hourly.conf    |    9 
 etc/yum-cron.conf           |    9 
 yum-cron/yum-cron.py        |   75 +++---
 yum-cron/yum-hourly.cron.sh |    2 
 yum.spec                    |    4 
 yum/__init__.py             |   38 +++
 yum/updateinfo.py           |  515 ++++++++++++++++++++++++++++++++++++++++++++
 yumcommands.py              |  427 ++++++++++++++++++++++++++++++++++++
 9 files changed, 1062 insertions(+), 43 deletions(-)

New commits:
commit 36ce5ff91ee0f1cecb51e4f0cd4b810353de7bd3
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:12:59 2013 -0400

    Add comment documentation about update_cmd to yum-cron.conf files.

diff --git a/etc/yum-cron-hourly.conf b/etc/yum-cron-hourly.conf
index ece67c4..b17448c 100644
--- a/etc/yum-cron-hourly.conf
+++ b/etc/yum-cron-hourly.conf
@@ -1,4 +1,13 @@
 [commands]
+#  What kind of update to use:
+# default                            = yum upgrade
+# security                           = yum --security upgrade
+# security-severity:Critical         = yum --sec-severity=Critical upgrade
+# minimal                            = yum --bugfix upgrade-minimal
+# minimal-security                   = yum --security upgrade-minimal
+# minimal-security-severity:Critical =  --sec-severity=Critical upgrade-minimal
+update_cmd = default
+
 # Whether a message should emitted when updates are available.
 update_messages = no
 
diff --git a/etc/yum-cron.conf b/etc/yum-cron.conf
index d19fce3..3221802 100644
--- a/etc/yum-cron.conf
+++ b/etc/yum-cron.conf
@@ -1,4 +1,13 @@
 [commands]
+#  What kind of update to use:
+# default                            = yum upgrade
+# security                           = yum --security upgrade
+# security-severity:Critical         = yum --sec-severity=Critical upgrade
+# minimal                            = yum --bugfix upgrade-minimal
+# minimal-security                   = yum --security upgrade-minimal
+# minimal-security-severity:Critical =  --sec-severity=Critical upgrade-minimal
+update_cmd = default
+
 # Whether a message should emitted when updates are available.
 update_messages = yes
 
commit b90840d1aa733811bbb9165272d09159a1018e35
Merge: d99089b f32d39f
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:07:40 2013 -0400

    Merge branch 'master' of ssh://yum.baseurl.org/srv/projects/yum/git/yum
    
    * 'master' of ssh://yum.baseurl.org/srv/projects/yum/git/yum: (4 commits)
      support socks4/5 proxies.  BZ 428588
      ...

commit d99089b628497bcaf24ae43e50a89e9254227096
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:06:57 2013 -0400

    Add update_cmd to yum-cron, so we can do security/minimal/etc. updates.

diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
index 30a147b..0b6af7d 100755
--- a/yum-cron/yum-cron.py
+++ b/yum-cron/yum-cron.py
@@ -701,6 +701,7 @@ class YumCronConfig(BaseConfig):
     email_host = Option("localhost")
     email_port = IntOption(25)
     update_messages = BoolOption(False)
+    update_cmd = Option("default")
     apply_updates = BoolOption(False)
     download_updates = BoolOption(False)
     yum_config_file = Option("/etc/yum.conf")
@@ -831,27 +832,37 @@ class YumCronBase(yum.YumBase):
            available
         """
         try:
-            updatesTuples = self.up.getUpdatesTuples()
-            # If there are no updates, return False
-            if not updatesTuples:
-                return False
-
-            # figure out the updates
-            for (new, old) in updatesTuples:
-                updates_available = True
-                updating = self.getPackageObject(new)
-                updated = self.rpmdb.searchPkgTuple(old)[0]
-            
-                self.tsInfo.addUpdate(updating, updated)
-
-            # and the obsoletes
-            if self.conf.obsoletes:
-                for (obs, inst) in self.up.getObsoletesTuples():
-                    obsoleting = self.getPackageObject(obs)
-                    installed = self.rpmdb.searchPkgTuple(inst)[0]
-                
-                    self.tsInfo.addObsoleting(obsoleting, installed)
-                    self.tsInfo.addObsoleted(installed, obsoleting)
+            #  Just call .update() because it does obsoletes loops, and group
+            # objects. etc.
+
+            update_cmd = self.opts.update_cmd
+            idx = update_cmd.find("security-severity:")
+            if idx != -1:
+                sevs       = update_cmd[idx + len("security-severity:"):]
+                update_cmd = update_cmd[:idx + len("security")]
+                self.updateinfo_filters['sevs'] = sevs.split(",")
+
+
+            if self.opts.update_cmd in ('minimal', 'minimal-security'):
+                if not updateinfo.update_minimal(self):
+                    return False
+                self.updateinfo_filters['bugfix'] = True
+            elif self.opts.update_cmd in ('default', 'security',
+                                          'default-security'):
+                if not self.update():
+                    return False
+            else:
+                # return False ?
+                self.opts.update_cmd = 'default'
+                if not self.update():
+                    return False
+
+            if self.opts.update_cmd.endswith("security"):
+                self.updateinfo_filters['security'] = True
+                updateinfo.remove_txmbrs(self)
+            elif self.opts.update_cmd == 'minimal':
+                self.updateinfo_filters['bugfix'] = True
+                updateinfo.remove_txmbrs(self)
 
         except Exception, e:
             self.emitCheckFailed("%s" %(e,))
commit d63aee13e8145852f970156ab9fe220367123eb1
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:06:25 2013 -0400

    Fixes for yum-cron: use .upinfo, group as objects, use quiet.

diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
index 089cad4..30a147b 100755
--- a/yum-cron/yum-cron.py
+++ b/yum-cron/yum-cron.py
@@ -10,7 +10,6 @@ from yum.config import BaseConfig, Option, IntOption, ListOption, BoolOption
 from yum.parser import ConfigPreProcessor
 from ConfigParser import ConfigParser, ParsingError
 from yum.constants import *
-from yum.update_md import UpdateMetadata
 from email.mime.text import MIMEText
 from yum.i18n import to_str, to_utf8, to_unicode, utf8_width, utf8_width_fill, utf8_text_fill
 from yum import  _, P_
@@ -796,6 +795,9 @@ class YumCronBase(yum.YumBase):
             if os.geteuid() != 0:
                 self.setCacheDir()
 
+            # Turn off the plugins line
+            self.preconf.debuglevel = 0
+
             # Create the configuration
             self.conf
 
@@ -820,22 +822,7 @@ class YumCronBase(yum.YumBase):
 
     def populateUpdateMetadata(self):
         """Populate the metadata for the packages in the update."""
-
-        self.updateMetadata = UpdateMetadata()
-        repos = []
-
-        for (new, old) in self.up.getUpdatesTuples():
-            pkg = self.getPackageObject(new)
-            if pkg.repoid not in repos:
-                repo = self.repos.getRepo(pkg.repoid)
-                repos.append(repo.id)
-                try: # grab the updateinfo.xml.gz from the repodata
-                    md = repo.retrieveMD('updateinfo')
-                except Exception: # can't find any; silently move on
-                    continue
-                md = gzip.open(md)
-                self.updateMetadata.add(md)
-                md.close()
+        self.upinfo
 
     def refreshUpdates(self):
         """Check whether updates are available.
@@ -880,6 +867,9 @@ class YumCronBase(yum.YumBase):
         :return: Boolean indicating whether there are any updates to
            the group available
         """
+        if self.conf.group_command == 'objects':
+            return False
+
         update_available = False
         try:
             for group_string in self.opts.group_list:
commit c6a12f1b0597404a83cf20a741dcff5cd59c5360
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:04:32 2013 -0400

    Add updateinfo/update-minimal commands, and --security/etc. options.

diff --git a/cli.py b/cli.py
index 8ea5d1f..ba2e343 100755
--- a/cli.py
+++ b/cli.py
@@ -109,6 +109,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         self.registerCommand(yumcommands.LoadTransactionCommand())
         self.registerCommand(yumcommands.SwapCommand())
         self.registerCommand(yumcommands.RepoPkgsCommand())
+        self.registerCommand(yumcommands.UpdateinfoCommand())
+        self.registerCommand(yumcommands.UpdateMinimalCommand())
 
     def registerCommand(self, command):
         """Register a :class:`yumcommands.YumCommand` so that it can be called by
@@ -2130,6 +2132,14 @@ class YumOptionParser(OptionParser):
             self.base.conf.downloadonly = opts.dlonly
             self.base.conf.downloaddir = opts.dldir
 
+            # Store all the updateinfo filters somewhere...
+            self.base.updateinfo_filters['security'] = opts.security
+            self.base.updateinfo_filters['bugfix'] = opts.bugfix
+            self.base.updateinfo_filters['advs'] = self._splitArg(opts.advs)
+            self.base.updateinfo_filters['bzs']  = self._splitArg(opts.bzs)
+            self.base.updateinfo_filters['cves'] = self._splitArg(opts.cves)
+            self.base.updateinfo_filters['sevs'] = self._splitArg(opts.sevs)
+
             #  Treat users like root as much as possible:
             if not self.base.setCacheDir():
                 self.base.conf.cache = 1
@@ -2357,6 +2367,22 @@ class YumOptionParser(OptionParser):
         group.add_option("", "--setopt", dest="setopts", default=[],
                 action="append", help=_("set arbitrary config and repo options"))
 
+        # Updateinfo options...
+        group.add_option("--bugfix", action="store_true", 
+                help=_("Include bugfix relevant packages, in updates"))
+        group.add_option("--security", action="store_true", 
+                help=_("Include security relevant packages, in updates"))
+
+        group.add_option("--advisory", "--advisories", dest="advs", default=[],
+                action="append", help=_("Include packages needed to fix the given advisory, in updates"))
+        group.add_option("--bzs", default=[],
+                action="append", help=_("Include packages needed to fix the given BZ, in updates"))
+        group.add_option("--cves", default=[],
+                action="append", help=_("Include packages needed to fix the given CVE, in updates"))
+        group.add_option("--sec-severity", "--secseverity", default=[],
+                         dest="sevs", action="append",
+                help=_("Include security relevant packages matching the severity, in updates"))
+
         
 def _filtercmdline(novalopts, valopts, args):
     '''Keep only specific options from the command line argument list
diff --git a/yum.spec b/yum.spec
index a270de5..455c3ae 100644
--- a/yum.spec
+++ b/yum.spec
@@ -110,6 +110,8 @@ Obsoletes: yum-plugin-downloadonly <= 1.1.31-7.fc18
 Provides: yum-plugin-downloadonly = 3.4.3-44.yum
 Obsoletes: yum-presto < 3.4.3-66.yum
 Provides: yum-presto = 3.4.3-66.yum
+Obsoletes: yum-plugin-security < 1.1.32
+Provides: yum-plugin-security = 3.4.3-84.yum
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 
@@ -139,7 +141,7 @@ can notify you when they are available via email, syslog or dbus.
 %package cron
 Summary: Files needed to run yum updates as a cron job
 Group: System Environment/Base
-Requires: yum >= 3.0 cronie crontabs findutils
+Requires: yum >= 3.4.3-84 cronie crontabs findutils
 %if %{yum_cron_systemd}
 BuildRequires: systemd-units
 Requires(post): systemd
diff --git a/yumcommands.py b/yumcommands.py
index 968f231..e5f2363 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -13,6 +13,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 # Copyright 2006 Duke University 
+# Copyright 2013 Red Hat
 # Written by Seth Vidal
 
 """
@@ -34,6 +35,7 @@ import tempfile
 import glob
 
 import yum.config
+from yum import updateinfo
 
 def _err_mini_usage(base, basecmd):
     if basecmd not in base.yum_cli_commands:
@@ -484,7 +486,9 @@ class UpdateCommand(YumCommand):
         """
         self.doneCommand(base, _("Setting up Update Process"))
         try:
-            return base.updatePkgs(extcmds, update_to=(basecmd == 'update-to'))
+            ret = base.updatePkgs(extcmds, update_to=(basecmd == 'update-to'))
+            updateinfo.remove_txmbrs(base)
+            return ret
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
 
@@ -545,7 +549,9 @@ class DistroSyncCommand(YumCommand):
         self.doneCommand(base, _("Setting up Distribution Synchronization Process"))
         try:
             base.conf.obsoletes = 1
-            return base.distroSyncPkgs(extcmds)
+            ret = base.distroSyncPkgs(extcmds)
+            updateinfo.remove_txmbrs(base)
+            return ret
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
 
@@ -618,6 +624,12 @@ class InfoCommand(YumCommand):
             1 = we've errored, exit with error string
             2 = we've got work yet to do, onto the next stage
         """
+
+        if extcmds and extcmds[0] in ('updates', 'obsoletes'):
+            updateinfo.exclude_updates(base)
+        else:
+            updateinfo.exclude_all(base)
+
         try:
             highlight = base.term.MODE['bold']
             #  If we are doing: "yum info installed blah" don't do the highlight
@@ -1028,7 +1040,9 @@ class GroupsCommand(YumCommand):
             if cmd == 'install':
                 return base.installGroups(extcmds)
             if cmd == 'upgrade':
-                return base.installGroups(extcmds, upgrade=True)
+                ret = base.installGroups(extcmds, upgrade=True)
+                updateinfo.remove_txmbrs(base)
+                return ret
             if cmd == 'remove':
                 return base.removeGroups(extcmds)
 
@@ -1490,6 +1504,7 @@ class ProvidesCommand(YumCommand):
         """
         base.logger.debug("Searching Packages: ")
         try:
+            updateinfo.exclude_updates(base)
             return base.provides(extcmds)
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
@@ -1556,6 +1571,7 @@ class CheckUpdateCommand(YumCommand):
             1 = we've errored, exit with error string
             2 = we've got work yet to do, onto the next stage
         """
+        updateinfo.exclude_updates(base)
         obscmds = ['obsoletes'] + extcmds
         base.extcmds.insert(0, 'updates')
         result = 0
@@ -1591,6 +1607,12 @@ class CheckUpdateCommand(YumCommand):
                     base.updatesObsoletesList(obtup, 'obsoletes',
                                               columns=columns)
                 result = 100
+
+            # Add check_running_kernel call, if updateinfo is available.
+            if updateinfo._repos_downloaded(base.repos.listEnabled()):
+                def _msg(x):
+                    base.verbose_logger.info("%s", x)
+                updateinfo._check_running_kernel(base, base.upinfo, _msg)
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
         else:
@@ -1660,6 +1682,7 @@ class SearchCommand(YumCommand):
         """
         base.logger.debug(_("Searching Packages: "))
         try:
+            updateinfo.exclude_updates(base)
             return base.search(extcmds)
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
@@ -1743,7 +1766,9 @@ class UpgradeCommand(YumCommand):
         base.conf.obsoletes = 1
         self.doneCommand(base, _("Setting up Upgrade Process"))
         try:
-            return base.updatePkgs(extcmds, update_to=(basecmd == 'upgrade-to'))
+            ret = base.updatePkgs(extcmds, update_to=(basecmd == 'upgrade-to'))
+            updateinfo.remove_txmbrs(base)
+            return ret
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
 
@@ -1872,6 +1897,7 @@ class ResolveDepCommand(YumCommand):
         """
         base.logger.debug(_("Searching Packages for Dependency:"))
         try:
+            updateinfo.exclude_updates(base)
             return base.resolveDepCli(extcmds)
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
@@ -2010,6 +2036,7 @@ class DepListCommand(YumCommand):
         """
         self.doneCommand(base, _("Finding dependencies: "))
         try:
+            updateinfo.exclude_updates(base)
             return base.deplist(extcmds)
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
@@ -3489,6 +3516,7 @@ class RepoPkgsCommand(YumCommand):
                 num += len(txmbrs)
 
             if num:
+                updateinfo.remove_txmbrs(base)
                 return 2, P_('%d package to update', '%d packages to update',
                              num)
 
@@ -3499,6 +3527,7 @@ class RepoPkgsCommand(YumCommand):
                 num += len(txmbrs)
 
             if num:
+                updateinfo.remove_txmbrs(base)
                 return 2, P_('%d package to update', '%d packages to update',
                              num)
 
@@ -3676,3 +3705,393 @@ class RepoPkgsCommand(YumCommand):
         if cmd in ('info', 'list'):
             return InfoCommand().cacheRequirement(base, cmd, extcmds[2:])
         return 'write'
+
+# Using this a lot, so make it easier...
+_upi = updateinfo
+class UpdateinfoCommand(YumCommand):
+    # Old command names...
+    direct_cmds = {'list-updateinfo'    : 'list',
+                   'list-security'      : 'list',
+                   'list-sec'           : 'list',
+                   'info-updateinfo'    : 'info',
+                   'info-security'      : 'info',
+                   'info-sec'           : 'info',
+                   'summary-updateinfo' : 'summary'}
+
+    #  Note that this code (instead of using inheritance and multiple
+    # cmd classes) means that "yum help" only displays the updateinfo command.
+    # Which is what we want, because the other commands are just backwards
+    # compatible gunk we don't want the user using).
+    def getNames(self):
+        return ['updateinfo'] + sorted(self.direct_cmds.keys())
+
+    def getUsage(self):
+        return "[info|list|...] [security|...] [installed|available|all] [pkgs|id]"
+
+    def getSummary(self):
+        return "Acts on repository update information"
+
+    def doCheck(self, base, basecmd, extcmds):
+        pass
+
+    def list_show_pkgs(self, base, md_info, list_type, show_type,
+                       iname2tup, data, msg):
+        n_maxsize = 0
+        r_maxsize = 0
+        t_maxsize = 0
+        for (notice, pkgtup, pkg) in data:
+            n_maxsize = max(len(notice['update_id']), n_maxsize)
+            tn = notice['type']
+            if tn == 'security' and notice['severity']:
+                tn = notice['severity'] + '/Sec.'
+            t_maxsize = max(len(tn),                  t_maxsize)
+            if show_type:
+                for ref in _upi._ysp_safe_refs(notice['references']):
+                    if ref['type'] != show_type:
+                        continue
+                    r_maxsize = max(len(str(ref['id'])), r_maxsize)
+
+        for (notice, pkgtup, pkg) in data:
+            mark = ''
+            if list_type == 'all':
+                mark = '  '
+                if _upi._rpm_tup_vercmp(iname2tup[pkgtup[0]], pkgtup) >= 0:
+                    mark = 'i '
+            tn = notice['type']
+            if tn == 'security' and notice['severity']:
+                tn = notice['severity'] + '/Sec.'
+
+            if show_type and _upi._ysp_has_info_md(show_type, notice):
+                for ref in _upi._ysp_safe_refs(notice['references']):
+                    if ref['type'] != show_type:
+                        continue
+                    msg("%s %-*s %-*s %s" % (mark, r_maxsize, str(ref['id']),
+                                             t_maxsize, tn, pkg))
+            elif hasattr(pkg, 'name'):
+                print base.fmtKeyValFill("%s: " % pkg.name,
+                                         base._enc(pkg.summary))
+            else:
+                msg("%s%-*s %-*s %s" % (mark, n_maxsize, notice['update_id'],
+                                        t_maxsize, tn, pkg))
+
+    def info_show_pkgs(self, base, md_info, list_type, show_type,
+                       iname2tup, data, msg):
+        show_pkg_info_done = {}
+        for (notice, pkgtup, pkg) in data:
+            if notice['update_id'] in show_pkg_info_done:
+                continue
+            show_pkg_info_done[notice['update_id']] = notice
+
+            if hasattr(notice, 'text'):
+                debug_log_lvl = yum.logginglevels.DEBUG_3
+                vlog = base.verbose_logger
+                if vlog.isEnabledFor(debug_log_lvl):
+                    obj = notice.text(skip_data=[])
+                else:
+                    obj = notice.text()
+            else:
+                # Python-2.4.* doesn't understand str(x) returning unicode
+                obj = notice.__str__()
+
+            if list_type == 'all':
+                if _upi._rpm_tup_vercmp(iname2tup[pkgtup[0]], pkgtup) >= 0:
+                    obj = obj + "\n  Installed : true"
+                else:
+                    obj = obj + "\n  Installed : false"
+            msg(obj)
+
+    def summary_show_pkgs(self, base, md_info, list_type, show_type,
+                          iname2tup, data, msg):
+        def _msg(x):
+            base.verbose_logger.info("%s", x)
+        counts = {}
+        sev_counts = {}
+        show_pkg_info_done = {}
+        for (notice, pkgtup, pkg) in data:
+            if notice['update_id'] in show_pkg_info_done:
+                continue
+            show_pkg_info_done[notice['update_id']] = notice
+            counts[notice['type']] = counts.get(notice['type'], 0) + 1
+            if notice['type'] == 'security':
+                sev = notice['severity']
+                if sev is None:
+                    sev = ''
+                sev_counts[sev] = sev_counts.get(sev, 0) + 1
+
+        maxsize = 0
+        for T in ('newpackage', 'security', 'bugfix', 'enhancement'):
+            if T not in counts:
+                continue
+            size = len(str(counts[T]))
+            if maxsize < size:
+                maxsize = size
+        if not maxsize:
+            _upi._check_running_kernel(base, md_info, _msg)
+            return
+
+        outT = {'newpackage' : 'New Package',
+                'security' : 'Security',
+                'bugfix' : 'Bugfix',
+                'enhancement' : 'Enhancement'}
+        print "Updates Information Summary:", list_type
+        for T in ('newpackage', 'security', 'bugfix', 'enhancement'):
+            if T not in counts:
+                continue
+            n = outT[T]
+            if T == 'security' and len(sev_counts) == 1:
+                sn = sev_counts.keys()[0]
+                if sn != '':
+                    n = sn + " " + n
+            print "    %*u %s notice(s)" % (maxsize, counts[T], n)
+            if T == 'security' and len(sev_counts) != 1:
+                def _sev_sort_key(key):
+                    # We want these in order, from "highest" to "lowest".
+                    # Anything unknown is "higher". meh.
+                    return {'Critical' : "zz1",
+                            'Important': "zz2",
+                            'Moderate' : "zz3",
+                            'Low'      : "zz4",
+                            }.get(key, key)
+
+                for sn in sorted(sev_counts, key=_sev_sort_key):
+                    args = (maxsize, sev_counts[sn],sn or '?', outT['security'])
+                    print "        %*u %s %s notice(s)" % args
+        _upi._check_running_kernel(base, md_info, _msg)
+        self.show_pkg_info_done = {}
+
+    def _get_new_pkgs(self, md_info):
+        for notice in md_info.notices:
+            if notice['type'] != "newpackage":
+                continue
+            for upkg in notice['pkglist']:
+                for pkg in upkg['packages']:
+                    pkgtup = (pkg['name'], pkg['arch'], pkg['epoch'] or '0',
+                              pkg['version'], pkg['release'])
+                    yield (notice, pkgtup)
+
+    _cmd2filt = {"bugzillas" : "bugzilla",
+                 "bugzilla" : "bugzilla",
+                 "bzs" : "bugzilla",
+                 "bz" : "bugzilla",
+
+                 "sec" : "security",
+
+                 "cves" : "cve",
+                 "cve" : "cve",
+
+                 "newpackages" : "newpackage",
+                 "new-packages" : "newpackage",
+                 "newpackage" : "newpackage",
+                 "new-package" : "newpackage",
+                 "new" : "newpackage"}
+    for filt_type in _upi._update_info_types_:
+        _cmd2filt[filt_type] = filt_type
+
+    def doCommand(self, base, basecmd, extcmds):
+        if basecmd in self.direct_cmds:
+            subcommand = self.direct_cmds[basecmd]
+        elif extcmds and extcmds[0] in ('list', 'info', 'summary',
+                                        'remove-pkgs-ts', 'exclude-updates',
+                                        'exclude-all',
+                                        'check-running-kernel'):
+            subcommand = extcmds[0]
+            extcmds = extcmds[1:]
+        elif extcmds and extcmds[0] in self._cmd2filt:
+            subcommand = 'list'
+        elif extcmds:
+            subcommand = 'info'
+        else:
+            subcommand = 'summary'
+
+        if subcommand == 'list':
+            return self.doCommand_li(base, 'updateinfo list', extcmds,
+                                     self.list_show_pkgs)
+        if subcommand == 'info':
+            return self.doCommand_li(base, 'updateinfo info', extcmds,
+                                     self.info_show_pkgs)
+
+        if subcommand == 'summary':
+            return self.doCommand_li(base, 'updateinfo summary', extcmds,
+                                     self.summary_show_pkgs)
+
+        if subcommand == 'remove-pkgs-ts':
+            filters = None
+            if extcmds:
+                filters = updateinfo._args2filters(extcmds)
+            updateinfo.remove_txmbrs(base, filters)
+            return 0, [basecmd + ' ' + subcommand + ' done']
+
+        if subcommand == 'exclude-all':
+            filters = None
+            if extcmds:
+                filters = updateinfo._args2filters(extcmds)
+            updateinfo.exclude_all(base, filters)
+            return 0, [basecmd + ' ' + subcommand + ' done']
+
+        if subcommand == 'exclude-updates':
+            filters = None
+            if extcmds:
+                filters = updateinfo._args2filters(extcmds)
+            updateinfo.exclude_updates(base, filters)
+            return 0, [basecmd + ' ' + subcommand + ' done']
+
+        if subcommand == 'check-running-kernel':
+            def _msg(x):
+                base.verbose_logger.info("%s", x)
+            updateinfo._check_running_kernel(base, base.upinfo, _msg)
+            return 0, [basecmd + ' ' + subcommand + ' done']
+
+    def doCommand_li_new(self, base, list_type, extcmds, md_info, msg,
+                         show_pkgs):
+        done_pkgs = set()
+        data = []
+        for (notice, pkgtup) in sorted(self._get_new_pkgs(md_info),
+                                       key=lambda x: x[1][0]):
+            if extcmds and not _upi._match_sec_cmd(extcmds, pkgtup[0], notice):
+                continue
+            n = pkgtup[0]
+            if n in done_pkgs:
+                continue
+            ipkgs = list(reversed(sorted(base.rpmdb.searchNames([n]))))
+            if list_type in ('installed', 'updates') and not ipkgs:
+                done_pkgs.add(n)
+                continue
+            if list_type == 'available' and ipkgs:
+                done_pkgs.add(n)
+                continue
+
+            pkgs = base.pkgSack.searchPkgTuple(pkgtup)
+            if not pkgs:
+                continue
+            if list_type == "updates" and pkgs[0].verLE(ipkgs[0]):
+                done_pkgs.add(n)
+                continue
+            done_pkgs.add(n)
+            data.append((notice, pkgtup, pkgs[0]))
+        show_pkgs(base, md_info, list_type, None, {}, data, msg)
+
+    def _parse_extcmds(self, extcmds):
+        filt_type = None
+        show_type = None
+        if len(extcmds) >= 1:
+            filt_type = None
+            
+            if extcmds[0] in self._cmd2filt:
+                filt_type = self._cmd2filt[extcmds.pop(0)]
+            show_type = filt_type
+            if filt_type and filt_type in _upi._update_info_types_:
+                show_type = None
+        return extcmds, show_type, filt_type
+
+    def doCommand_li(self, base, basecmd, extcmds, show_pkgs):
+        md_info = base.upinfo
+        def msg(x):
+            #  Don't use: logger.log(logginglevels.INFO_2, x)
+            # or -q deletes everything.
+            print x
+
+        opts = _upi._updateinfofilter2opts(base.updateinfo_filters)
+        extcmds, show_type, filt_type = self._parse_extcmds(extcmds)
+
+        list_type = "available"
+        if extcmds and extcmds[0] in ("updates","available","installed", "all"):
+            list_type = extcmds.pop(0)
+
+        if filt_type == "newpackage":
+            # No filtering here, as we want what isn't installed...
+            self.doCommand_li_new(base, list_type, extcmds, md_info, msg,
+                                  show_pkgs)
+            return 0, [basecmd + ' new done']
+
+        opts.sec_cmds = extcmds
+        used_map = _upi._ysp_gen_used_map(base.updateinfo_filters)
+        iname2tup = {}
+        if False: pass
+        elif list_type in ('installed', 'all'):
+            name2tup = _upi._get_name2allpkgtup(base)
+            iname2tup = _upi._get_name2instpkgtup(base)
+        elif list_type == 'updates':
+            name2tup = _upi._get_name2oldpkgtup(base)
+        elif list_type == 'available':
+            name2tup = _upi._get_name2instpkgtup(base)
+
+        def _show_pkgtup(pkgtup):
+            name = pkgtup[0]
+            notices = reversed(md_info.get_applicable_notices(pkgtup))
+            for (pkgtup, notice) in notices:
+                if filt_type and not _upi._ysp_has_info_md(filt_type, notice):
+                    continue
+
+                if list_type == 'installed':
+                    # Remove any that are newer than what we have installed
+                    if _upi._rpm_tup_vercmp(iname2tup[name], pkgtup) < 0:
+                        continue
+
+                if _upi._ysp_should_filter_pkg(opts, name, notice, used_map):
+                    yield (pkgtup, notice)
+
+        data = []
+        for pkgname in sorted(name2tup):
+            for (pkgtup, notice) in _show_pkgtup(name2tup[pkgname]):
+                d = {}
+                (d['n'], d['a'], d['e'], d['v'], d['r']) = pkgtup
+                if d['e'] == '0':
+                    d['epoch'] = ''
+                else:
+                    d['epoch'] = "%s:" % d['e']
+                data.append((notice, pkgtup,
+                            "%(n)s-%(epoch)s%(v)s-%(r)s.%(a)s" % d))
+        show_pkgs(base, md_info, list_type, show_type, iname2tup, data, msg)
+
+        _upi._ysp_chk_used_map(used_map, msg)
+
+        return 0, [basecmd + ' done']
+
+
+class UpdateMinimalCommand(YumCommand):
+    def getNames(self):
+        return ['update-minimal', 'upgrade-minimal']
+
+    def getUsage(self):
+        return "[PACKAGE-wildcard]"
+
+    def getSummary(self):
+        return _("Works like upgrade, but goes to the 'newest' package match which fixes a problem that affects your system")
+
+    def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can run.
+        These include that the program is being run by the root user,
+        that there are enabled repositories with gpg keys, and that
+        this command is called with appropriate arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
+        checkRootUID(base)
+        checkGPGKey(base)
+
+    def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
+
+        num = len(base.tsInfo)
+        _upi.update_minimal(base, extcmds)
+        num -= len(base.tsInfo)
+        
+        if num > 0:
+            msg = '%d packages marked for minimal Update' % num
+            return 2, [msg]
+        else:
+            return 0, ['No Packages marked for minimal Update']
commit 286ab16df34d50aa814301b81d23977eadcc2840
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:03:34 2013 -0400

    Add updateinfo helper functions from plugin.

diff --git a/yum/updateinfo.py b/yum/updateinfo.py
new file mode 100644
index 0000000..7d37a76
--- /dev/null
+++ b/yum/updateinfo.py
@@ -0,0 +1,515 @@
+
+import os.path
+
+from yum.i18n import _, P_
+
+from yum.constants import *
+
+from yum.logginglevels import INFO_1
+
+import rpmUtils.miscutils
+
+import misc
+
+import fnmatch
+
+# newpackages is weird, in that we'll never display that because we filter to
+# things relevant to installed pkgs...
+_update_info_types_ = ("security", "bugfix", "enhancement",
+                       "recommended", "newpackage")
+
+def _rpm_tup_vercmp(tup1, tup2):
+    """ Compare two "std." tuples, (n, a, e, v, r). """
+    return rpmUtils.miscutils.compareEVR((tup1[2], tup1[3], tup1[4]),
+                                         (tup2[2], tup2[3], tup2[4]))
+
+
+def _ysp_safe_refs(refs):
+    """ Sometimes refs == None, if so return the empty list here. 
+        So we don't have to check everywhere. """
+    if not refs:
+        return []
+    return refs
+
+def _match_sec_cmd(sec_cmds, pkgname, notice):
+    for i in sec_cmds:
+        if fnmatch.fnmatch(pkgname, i):
+            return i
+        if notice['update_id'] == i:
+            return i
+    return None
+
+def _has_id(used_map, refs, ref_type, ref_ids):
+    ''' Check if the given ID is a match. '''
+    for ref in _ysp_safe_refs(refs):
+        if ref['type'] != ref_type:
+            continue
+        if ref['id'] not in ref_ids:
+            continue
+        used_map[ref_type][ref['id']] = True
+        return ref
+    return None
+    
+def _ysp_should_filter_pkg(opts, pkgname, notice, used_map):
+    """ Do the package filtering for should_show and should_keep. """
+    
+    rcmd = _match_sec_cmd(opts.sec_cmds, pkgname, notice)
+    if rcmd:
+        used_map['cmd'][rcmd] = True
+        return True
+    elif opts.advisory and notice['update_id'] in opts.advisory:
+        used_map['id'][notice['update_id']] = True
+        return True
+    elif (opts.severity and notice['type'] == 'security' and
+          notice['severity'] in opts.severity):
+        used_map['sev'][notice['severity']] = True
+        return True
+    elif opts.cve and _has_id(used_map, notice['references'], "cve", opts.cve):
+        return True
+    elif opts.bz and _has_id(used_map, notice['references'],"bugzilla",opts.bz):
+        return True
+    # FIXME: Add opts for enhancement/etc.? -- __update_info_types__
+    elif (opts.security and notice['type'] == 'security' and
+          (not opts.severity or 'severity' not in notice or
+           not notice['severity'])):
+        return True
+    elif opts.bugfixes and notice['type'] == 'bugfix':
+        return True
+    elif not (opts.advisory or opts.cve or opts.bz or
+              opts.security or opts.bugfixes or opts.sec_cmds or opts.severity):
+        return True # This is only possible from should_show_pkg
+
+    return False
+
+def _ysp_has_info_md(rname, md):
+    if rname in _update_info_types_:
+        if md['type'] == rname:
+            return md
+    for ref in _ysp_safe_refs(md['references']):
+        if ref['type'] != rname:
+            continue
+        return md
+
+def _no_options(opts):
+    return not (opts.security or opts.bugfixes or
+                opts.advisory or opts.bz or opts.cve or opts.severity)
+
+def _updateinfofilter2opts(updateinfo_filters):
+    opts = misc.GenericHolder()
+    opts.sec_cmds = []
+
+    opts.advisory = updateinfo_filters.get('advs', [])
+    opts.bz       = updateinfo_filters.get('bzs',  [])
+    opts.cve      = updateinfo_filters.get('cves', [])
+    opts.severity = updateinfo_filters.get('sevs', [])
+
+    opts.bugfixes = updateinfo_filters.get('bugfix', False)
+    opts.security = updateinfo_filters.get('security', False)
+
+    return opts
+
+def _args2filters(args):
+    # Basically allow args to turn into security filters, for shell command etc.
+
+    T_map = {'advs' : 'advs',
+             'advisory' : 'advs',
+             'advisories' : 'advs',
+
+             'bzs' : 'bzs',
+             'bz' : 'bzs',
+
+             'cves' : 'cves',
+             'cve' : 'cves',
+
+             'security-severity' : 'sevs',
+             'security-severities' : 'sevs',
+             'severity' : 'sevs',
+             'severities' : 'sevs',
+             'sevs' : 'sevs',
+             'sev' : 'sevs',
+
+             'security' : 'security',
+             'sec' : 'security',
+
+             'bugfix' : 'bugfix',
+             'bugfixes' : 'bugfix',
+             'bugs' : 'bugfix',
+
+             }
+
+    filters = {'security' : False, 'bugfix' : False}
+
+    for arg0 in args:
+        arg0 = arg0.replace(" ", ',')
+        T = 'advs'
+        if '=' in arg0:
+            T, arg1 = arg0.split('=', 1)
+        elif arg0 not in T_map:
+            arg1 = arg0
+        else:
+            T = arg0
+            arg1 = 'true'
+
+        if T not in T_map:
+            continue # Error message?
+
+        T = T_map[T]
+
+        if T in ('security', 'bugfix'):
+            filters[T] = not filters[T]
+        else:
+            filters[T] = filters.get(T, []) + arg1.split(',')
+        return filters
+
+def _ysp_gen_used_map(opts):
+    used_map = {'bugzilla' : {}, 'cve' : {}, 'id' : {}, 'cmd' : {}, 'sev' : {}}
+    if True:
+        return used_map
+    for i in opts.sec_cmds:
+        used_map['cmd'][i] = False
+    for i in opts.advisory:
+        used_map['id'][i] = False
+    for i in opts.bz:
+        used_map['bugzilla'][i] = False
+    for i in opts.cve:
+        used_map['cve'][i] = False
+    for i in opts.severity:
+        used_map['sev'][i] = False
+    return used_map
+
+def _ysp_chk_used_map(used_map, msg):
+    for i in used_map['cmd']:
+        if not used_map['cmd'][i]:
+            msg('No update information found for \"%s\"' % i)
+    for i in used_map['id']:
+        if not used_map['id'][i]:
+            msg('Advisory \"%s\" not found applicable for this system' % i)
+    for i in used_map['bugzilla']:
+        if not used_map['bugzilla'][i]:
+            msg('BZ \"%s\" not found applicable for this system' % i)
+    for i in used_map['cve']:
+        if not used_map['cve'][i]:
+            msg('CVE \"%s\" not found applicable for this system' % i)
+    for i in used_map['sev']:
+        if not used_map['sev'][i]:
+            msg('Severity \"%s\" not found applicable for this system' % i)
+
+
+def _get_name2pkgtup(base, pkgtups):
+    name2tup = {}
+    for pkgtup in pkgtups:
+        # Get the latest "old" pkgtups
+        if (pkgtup[0] in name2tup and
+            _rpm_tup_vercmp(name2tup[pkgtup[0]], pkgtup) > 0):
+            continue
+        name2tup[pkgtup[0]] = pkgtup
+    return name2tup
+def _get_name2oldpkgtup(base):
+    """ Get the pkgtups for all installed pkgs. which have an update. """
+    oupdates = map(lambda x: x[1], base.up.getUpdatesTuples())
+    return _get_name2pkgtup(base, oupdates)
+def _get_name2instpkgtup(base):
+    """ Get the pkgtups for all installed pkgs. """
+    return _get_name2pkgtup(base, base.rpmdb.simplePkgList())
+def _get_name2allpkgtup(base):
+    """ Get the pkgtups for all installed pkgs. and munge that to be the
+        first possible pkgtup. """
+    ofirst = [(pt[0], pt[1], '0','0','0') for pt in base.rpmdb.simplePkgList()]
+    return _get_name2pkgtup(base, ofirst)
+def _get_name2aallpkgtup(base):
+    """ Get the pkgtups for all available pkgs. and munge that to be the
+        first possible pkgtup. """
+    ofirst = [(pt[0], pt[1],'0','0','0') for pt in base.pkgSack.simplePkgList()]
+    return _get_name2pkgtup(base, ofirst)
+
+
+#  You might think we'd just call delPackage
+# and indeed that works for list updates etc.
+#
+# __but__ that doesn't work for dependancies on real updates
+#
+#  So to fix deps. we need to do it at the preresolve stage and take the
+# "transaction package list" and then remove packages from that.
+#
+# __but__ that doesn't work for lists ... so we do it two ways
+#
+def _ysp_should_keep_pkg(opts, pkgtup, md_info, used_map):
+    """ Do we want to keep this package to satisfy the security limits. """
+    name = pkgtup[0]
+    for (pkgtup, notice) in md_info.get_applicable_notices(pkgtup):
+        if _ysp_should_filter_pkg(opts, name, notice, used_map):
+            return True
+    return False
+
+def _repos_downloaded(repos):
+    dled = True
+    for repo in repos:
+        try:
+            data = repo.repoXML.getData('updateinfo');
+        except:
+            continue # No data is fine...
+
+        # Note that this doesn't check that it's decompressed...
+        path = repo.cachedir +'/'+ os.path.basename(data.location[1])
+        if not os.path.exists(path):
+            dled = False
+            break
+
+    return dled
+
+def _check_running_kernel(yb, md_info, msg):
+    kern_pkgtup = misc.get_running_kernel_pkgtup(yb.ts)
+    if kern_pkgtup[0] is None:
+        return
+
+    found_sec = False
+    for (pkgtup, notice) in md_info.get_applicable_notices(kern_pkgtup):
+        if found_sec or notice['type'] != 'security':
+            continue
+        found_sec = True
+        ipkg = yb.rpmdb.searchPkgTuple(pkgtup)
+        if not ipkg:
+            continue # Not installed
+        ipkg = ipkg[0]
+
+        e = ''
+        if kern_pkgtup[2] != '0':
+            e = '%s:' % kern_pkgtup[2]
+        rpkg = '%s-%s%s-%s.%s' % (kern_pkgtup[0], e,
+                                  kern_pkgtup[3], kern_pkgtup[4],
+                                  kern_pkgtup[1])
+
+        msg(_('Security: %s is an installed security update') % ipkg)
+        msg(_('Security: %s is the currently running version') % rpkg)
+        break
+
+def remove_txmbrs(base, filters=None):
+    '''
+    Remove packages from the transaction, using the updateinfo data.
+    '''
+    md_info = base.upinfo
+
+    def ysp_del_pkg(tspkg):
+        """ Deletes a package within a transaction. """
+        base.verbose_logger.log(INFO_1,
+                                _(" --> %s from %s removed (updateinfo)") %
+                                (tspkg.po, tspkg.po.ui_from_repo))
+        tsinfo.remove(tspkg.pkgtup)
+
+    if filters is None:
+        filters = base.updateinfo_filters
+    opts = _updateinfofilter2opts(filters)
+
+    if _no_options(opts):
+        return 0, 0, 0
+
+    tot = 0
+    cnt = 0
+    used_map = _ysp_gen_used_map(opts)
+    tsinfo = base.tsInfo
+    tspkgs = tsinfo.getMembers()
+    #  Ok, here we keep any pkgs that pass "ysp" tests, then we keep all
+    # related pkgs ... Ie. "installed" version marked for removal.
+    keep_pkgs = set()
+
+    count_states = set(TS_INSTALL_STATES + [TS_ERASE])
+    count_pkgs = set()
+    for tspkg in tspkgs:
+        if tspkg.output_state in count_states:
+            count_pkgs.add(tspkg.po)
+
+    name2tup = _get_name2oldpkgtup(base)
+    for tspkg in tspkgs:
+        if tspkg.output_state in count_states:
+            tot += 1
+        name = tspkg.po.name
+        if (name not in name2tup or
+            not _ysp_should_keep_pkg(opts, name2tup[name], md_info, used_map)):
+            continue
+        if tspkg.output_state in count_states:
+            cnt += 1
+        keep_pkgs.add(tspkg.po)
+
+    scnt = cnt
+    mini_depsolve_again = True
+    while mini_depsolve_again:
+        mini_depsolve_again = False
+
+        for tspkg in tspkgs:
+            if tspkg.po in keep_pkgs:
+                # Find any related pkgs, and add them:
+                for (rpkg, reason) in tspkg.relatedto:
+                    if rpkg not in keep_pkgs:
+                        if rpkg in count_pkgs:
+                            cnt += 1
+                        keep_pkgs.add(rpkg)
+                        mini_depsolve_again = True
+            else:
+                # If related to any keep pkgs, add us
+                for (rpkg, reason) in tspkg.relatedto:
+                    if rpkg in keep_pkgs:
+                        if rpkg in count_pkgs:
+                            cnt += 1
+                        keep_pkgs.add(tspkg.po)
+                        mini_depsolve_again = True
+                        break
+
+    for tspkg in tspkgs:
+        if tspkg.po not in keep_pkgs:
+            ysp_del_pkg(tspkg)
+
+    _ysp_chk_used_map(used_map, lambda x: base.verbose_logger.warn("%s", x))
+    
+    if cnt:
+        base.verbose_logger.log(INFO_1, _('%d package(s) needed (+%d related) for security, out of %d available') % (scnt, cnt - scnt, tot))
+    else:
+        base.verbose_logger.log(INFO_1, _('No packages needed for security; %d packages available') % tot)
+
+    return cnt, scnt, tot
+
+def exclude_updates(base, filters=None):
+    '''
+    Exclude all packages to do with updates, using the updateinfo data.
+    '''
+    
+    md_info = base.upinfo
+
+    def ysp_del_pkg(pkg, reason="updateinfo"):
+        """ Deletes a package from all trees that yum knows about """
+        base.verbose_logger.log(INFO_1,
+                                _(" --> %s from %s excluded (%s)") %
+                                (pkg,pkg.repoid, reason))
+        pkg.repo.sack.delPackage(pkg)
+
+    if filters is None:
+        filters = base.updateinfo_filters
+    opts = _updateinfofilter2opts(filters)
+
+    if _no_options(opts):
+        return 0, 0
+
+    used_map = _ysp_gen_used_map(opts)
+
+    # In theory the official API is:
+    #
+    # pkgs = base.pkgSack.returnPackages()
+    #
+    # ...however that is _extremely_ slow, deleting all packages. So we ask
+    # for the list of update packages, which is all we care about.    
+    upds = base.doPackageLists(pkgnarrow='updates')
+    pkgs = upds.updates
+    # In theory we don't need to do this in some cases, but meh.
+    upds = base.doPackageLists(pkgnarrow='obsoletes')
+    pkgs += upds.obsoletes
+
+    name2tup = _get_name2oldpkgtup(base)
+    
+    tot = 0
+    cnt = 0
+    for pkg in pkgs:
+        tot += 1
+        name = pkg.name
+        if (name not in name2tup or
+            not _ysp_should_keep_pkg(opts, name2tup[name], md_info, used_map)):
+            ysp_del_pkg(pkg)
+            continue
+        cnt += 1
+
+    _ysp_chk_used_map(used_map, lambda x: base.verbose_logger.warn("%s", x))
+
+    if cnt:
+        base.verbose_logger.log(INFO_1, _('%d package(s) needed for security, out of %d available') % (cnt, tot))
+    else:
+        base.verbose_logger.log(INFO_1, _('No packages needed for security; %d packages available' % tot))
+
+    return cnt, tot
+
+def exclude_all(base, filters=None):
+    '''
+    Exclude all packages, using the updateinfo data.
+    '''
+    
+    md_info = base.upinfo
+
+    def ysp_del_pkg(pkg, reason="updateinfo"):
+        """ Deletes a package from all trees that yum knows about """
+        base.verbose_logger.log(INFO_1,
+                                _(" --> %s from %s excluded (%s)") %
+                                (pkg,pkg.repoid, reason))
+        pkg.repo.sack.delPackage(pkg)
+
+    if filters is None:
+        filters = base.updateinfo_filters
+    opts = _updateinfofilter2opts(filters)
+
+    if _no_options(opts):
+        return 0, 0
+
+    used_map = _ysp_gen_used_map(opts)
+
+    pkgs = base.pkgSack.returnPackages()
+    name2tup = _get_name2aallpkgtup(base)
+    
+    tot = 0
+    cnt = 0
+    for pkg in pkgs:
+        tot += 1
+        name = pkg.name
+        if (name not in name2tup or
+            not _ysp_should_keep_pkg(opts, name2tup[name], md_info, used_map)):
+            ysp_del_pkg(pkg)
+            continue
+        cnt += 1
+
+    _ysp_chk_used_map(used_map, lambda x: base.verbose_logger.warn("%s", x))
+
+    if cnt:
+        base.verbose_logger.log(INFO_1, _('%d package(s) needed for security, out of %d available') % (cnt, tot))
+    else:
+        base.verbose_logger.log(INFO_1, _('No packages needed for security; %d packages available' % tot))
+
+    return cnt, tot
+
+def update_minimal(base, extcmds=[]):
+    """Mark the specified items to be updated, in the minimal way.
+    :param extcmds: the user specified arguments
+    :return: a list of transaction members added to the
+       transaction set by this function
+    """
+    txmbrs = []
+
+    used_map = _ysp_gen_used_map(base.updateinfo_filters)
+    opts     = _updateinfofilter2opts(base.updateinfo_filters)
+    ndata    = _no_options(opts)
+
+    # NOTE: Not doing obsoletes processing atm. ... maybe we should? --
+    # Also worth pointing out we don't go backwards for obsoletes in the:
+    # update --security case etc.
+
+    # obsoletes = base.up.getObsoletesTuples(newest=False)
+    # for (obsoleting, installed) in sorted(obsoletes, key=lambda x: x[0]):
+    #   pass
+
+    # Tuples == (n, a, e, v, r)
+    oupdates  = map(lambda x: x[1], base.up.getUpdatesTuples())
+    for oldpkgtup in sorted(oupdates):
+        data = base.upinfo.get_applicable_notices(oldpkgtup)
+        if ndata: # No options means pick the oldest update
+            data.reverse()
+
+        for (pkgtup, notice) in data:
+            name = pkgtup[0]
+            if extcmds and not _match_sec_cmd(extcmds, name, notice):
+                continue
+            if (not ndata and
+                not _ysp_should_filter_pkg(base, name, notice, used_map)):
+                continue
+            txmbrs.extend(base.update(name=pkgtup[0], arch=pkgtup[1],
+                                      epoch=pkgtup[2],
+                                      version=pkgtup[3], release=pkgtup[4]))
+            break
+
+    # _ysp_chk_used_map(used_map, msg)
+
+    return txmbrs
+
commit 61a84a1b187287ebbb8a4050b6f20a3b3d2bcf48
Author: James Antill <james at and.org>
Date:   Thu Apr 18 17:02:39 2013 -0400

    Add .upinfo property to Base.

diff --git a/yum/__init__.py b/yum/__init__.py
index d1ab41a..766f960 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -80,6 +80,7 @@ import yumRepo
 import callbacks
 import yum.history
 import yum.igroups
+import update_md
 
 import warnings
 warnings.simplefilter("ignore", Errors.YumFutureDeprecationWarning)
@@ -194,6 +195,7 @@ class YumBase(depsolve.Depsolve):
         self._pkgSack = None
         self._lockfile = None
         self._tags = None
+        self._upinfo = None
         self._ts_save_file = None
         self.skipped_packages = []   # packages skip by the skip-broken code
         self._not_found_a = {}
@@ -220,6 +222,8 @@ class YumBase(depsolve.Depsolve):
         self._cleanup = []
         self.exit_code = 0
 
+        self.updateinfo_filters = {}
+
     def __del__(self):
         self.close()
         self.closeRpmDB()
@@ -971,6 +975,35 @@ class YumBase(depsolve.Depsolve):
         self.verbose_logger.debug('tags time: %0.3f' % (time.time() - tag_st))
         return self._tags
         
+
+    def _getUpdateinfo(self):
+        """ create the Update Info object used to search/report the updateinfo
+            metadata"""
+
+        upi_st = time.time()
+        self.verbose_logger.log(logginglevels.DEBUG_4,
+                                _('Getting updateinfo metadata'))
+
+        if self._upinfo is None:
+            self._upinfo = update_md.UpdateMetadata()
+
+            for repo in self.repos.listEnabled():
+                if 'updateinfo' not in repo.repoXML.fileTypes():
+                    continue
+
+                self.verbose_logger.log(logginglevels.DEBUG_4,
+                    _('Adding Update Info from repository: %s'), repo)
+
+                try:
+                    self._upinfo.add(repo)
+                except Errors.RepoMDError, e:
+                    msg = _('Failed to add Update Info for repository: %s - %s') % (repo, exception2msg(e))
+                    self.logger.critical(msg)
+
+        self.verbose_logger.debug('updateinfo time: %0.3f' %
+                                  (time.time() - upi_st))
+        return self._upinfo
+
     def _getHistory(self):
         """auto create the history object that to access/append the transaction
            history information. """
@@ -1035,6 +1068,11 @@ class YumBase(depsolve.Depsolve):
                        fset=lambda self, value: setattr(self, "_tags",value),
                        fdel=lambda self: setattr(self, "_tags", None),
                        doc="Yum Package Tags Object")
+
+    upinfo = property(fget=lambda self: self._getUpdateinfo(),
+                      fset=lambda self, value: setattr(self, "_upinfo", value),
+                      fdel=lambda self: setattr(self, "_upinfo", None),
+                      doc="Yum Update Info Object")
     
     
     def doSackFilelistPopulate(self):
commit 1f613b7b3268aa083377ad54a5374cec31c45741
Author: Orion Poplawski <orion at nwra.com>
Date:   Wed Apr 17 10:35:38 2013 -0600

    yum-cron does not use -C to specify config file

diff --git a/yum-cron/yum-hourly.cron.sh b/yum-cron/yum-hourly.cron.sh
index 557dd4d..f0b77a9 100755
--- a/yum-cron/yum-hourly.cron.sh
+++ b/yum-cron/yum-hourly.cron.sh
@@ -8,4 +8,4 @@ if [[ ! -f /var/lock/subsys/yum-cron ]]; then
 fi
 
 # Action!
-exec /usr/sbin/yum-cron -C /etc/yum/yum-cron-hourly.conf
+exec /usr/sbin/yum-cron /etc/yum/yum-cron-hourly.conf


More information about the Yum-commits mailing list