[yum-commits] Branch 'yum-3_2_X' - 10 commits - cli.py docs/yum.8 docs/yum.conf.5 output.py yumcommands.py yum/config.py yum/depsolve.py yum/history.py yum.spec yum/update_md.py

James Antill james at osuosl.org
Thu Mar 25 20:07:14 UTC 2010


 cli.py           |   69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 docs/yum.8       |   14 +++++++++++
 docs/yum.conf.5  |    8 ++++++
 output.py        |   47 ++++++++++++++++++++++++++++++++-----
 yum.spec         |    2 -
 yum/config.py    |    1 
 yum/depsolve.py  |   11 ++++++++
 yum/history.py   |   34 +++++++++++++++++++++++++--
 yum/update_md.py |    4 ++-
 yumcommands.py   |   22 +++++++++++++++++
 10 files changed, 202 insertions(+), 10 deletions(-)

New commits:
commit 9c3b0e9ae6b86cd96ee8751fc9e131e063724b23
Author: James Antill <james at and.org>
Date:   Thu Mar 25 15:57:24 2010 -0400

    Add groupremove_leaf_only option

diff --git a/docs/yum.8 b/docs/yum.8
index 00bfd53..c6b8c42 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -189,6 +189,9 @@ Is used to remove all of the packages in a group, unlike "groupinstall" this
 will remove everything regardless of group_package_types. It is worth pointing
 out that packages can be in more than one group, so "groupinstall X Y" followed
 by "groupremove Y" does not do give you the same result as "groupinstall X".
+
+The groupremove_leaf_only configuration changes the behaviour of this command
+to only remove packages which aren't required by something else.
 .IP 
 .IP "\fBgroupinfo\fP"
 Is used to give the description and package list of a group (and which type
diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
index cb6f340..fe195af 100644
--- a/docs/yum.conf.5
+++ b/docs/yum.conf.5
@@ -167,6 +167,14 @@ repository will be used. If \fBoverwrite_groups\fR is `0' then the groups
 from all matching repositories will be merged together as one large group.
 
 .IP
+\fBgroupremove_leaf_only \fR
+Either `0' or `1'. Used to determine yum's behaviour when the groupremove
+command is run.  If \fBgroupremove_leaf_only\fR is `0' (default) then
+all packages in the group will be removed.  If \fBgroupremove_leaf_only\fR is
+`1' then only those packages in the group that aren't required by another
+package will be removed.
+
+.IP
 \fBenable_group_conditionals\fR
 Either `0' or `1'. Determines whether yum will allow the use of conditionals
 packages. Default is `1' (package conditionals are allowed).
diff --git a/output.py b/output.py
index 7b09695..452f8a2 100755
--- a/output.py
+++ b/output.py
@@ -1642,6 +1642,10 @@ class DepSolveProgressCallBack:
             _('--> Processing Dependency: %s for package: %s'), formatted_req,
             po)
     
+    def groupRemoveReq(self, po, hits):
+        self.verbose_logger.log(logginglevels.INFO_2,
+            _('---> Keeping package: %s'), po)
+
     def unresolved(self, msg):
         self.verbose_logger.log(logginglevels.INFO_2, _('--> Unresolved Dependency: %s'),
             msg)
diff --git a/yum/config.py b/yum/config.py
index c82fabc..e51e46f 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -657,6 +657,7 @@ class YumConf(StartupConf):
     enabled = BoolOption(True)
     enablegroups = BoolOption(True)
     enable_group_conditionals = BoolOption(True)
+    groupremove_leaf_only = BoolOption(False)
     group_package_types = ListOption(['mandatory', 'default'])
     
     timeout = FloatOption(30.0) # FIXME: Should use variation of SecondsOption
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 11c9f29..f654d84 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -886,6 +886,17 @@ class Depsolve(object):
             # FIXME: This is probably the best place to fix the postfix rename
             # problem long term (post .21) ... see compare_providers.
             for pkg, hits in self.tsInfo.getRequires(*prov).iteritems():
+                # See the docs, this is to make groupremove "more useful".
+                if (self.conf.groupremove_leaf_only and txmbr.groups and
+                    txmbr.output_state == TS_ERASE):
+                    cb = self.dsCallback
+                    if cb and hasattr(cb, 'groupRemoveReq'):
+                        cb.groupRemoveReq(pkg, hits)
+                    #  We don't undo anything else here ... hopefully that's
+                    # fine.
+                    self.tsInfo.remove(txmbr.pkgtup)
+                    return []
+
                 for hit in hits:
                     # See if the update solves the problem...
                     found = False
commit 5579a905155a6be3171f6968a24e691bafcac40b
Author: James Antill <james at and.org>
Date:   Thu Mar 25 11:53:51 2010 -0400

    Fix login user column output length, for history summary

diff --git a/output.py b/output.py
index c4fde7c..7b09695 100755
--- a/output.py
+++ b/output.py
@@ -1590,7 +1590,7 @@ to exit.
                 count, uiacts = self._history_uiactions(hpkgs)
                 uperiod = _period2user[period]
                 # Should probably use columns here, esp. for uiacts?
-                print fmt % (utf8_width_fill(name, 22, 22),
+                print fmt % (utf8_width_fill(name, 26, 26),
                              utf8_width_fill(uperiod, 19, 19),
                              utf8_width_fill(uiacts, 16, 16), count)
 
commit 1c2b28e36ef1d7051428ea57446cf800b388f36e
Author: James Antill <james at and.org>
Date:   Thu Mar 25 11:29:06 2010 -0400

    Show if transactions have error output, in history list

diff --git a/docs/yum.8 b/docs/yum.8
index ff7ed8f..00bfd53 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -289,6 +289,8 @@ if there was something not good with the transaction.
 .br
 .I \fB#\fR - The transaction completed, but with a non-zero status.
 .br
+.I \fBE\fR - The transaction completed fine, but had warning/error output during the transaction.
+.br
 
 .IP
 .IP "\fBcheck\fP"
diff --git a/output.py b/output.py
index aa5dc4d..c4fde7c 100755
--- a/output.py
+++ b/output.py
@@ -1310,6 +1310,9 @@ to exit.
                 rmark = lmark = '*'
             elif old.return_code:
                 rmark = lmark = '#'
+                # We don't check .errors, because return_code will be non-0
+            elif old.output:
+                rmark = lmark = 'E'
             if old.altered_lt_rpmdb:
                 rmark = '<'
             if old.altered_gt_rpmdb:
commit 7d04b0e6816cfb27e769bb19f44811f88523ed2b
Author: James Antill <james at and.org>
Date:   Mon Mar 22 17:43:25 2010 -0400

    Minor description fixup

diff --git a/yum.spec b/yum.spec
index 29870e5..3a724cd 100644
--- a/yum.spec
+++ b/yum.spec
@@ -29,7 +29,7 @@ Obsoletes: yum-plugin-allow-downgrade < 1.1.22-0
 %description
 Yum is a utility that can check for and automatically download and
 install updated RPM packages. Dependencies are obtained and downloaded 
-automatically prompting the user as necessary.
+automatically, prompting the user for permission as necessary.
 
 %package updatesd
 Summary: Update notification daemon
commit f2c3967ea8185541a3c68fad754d19b2dfd71d35
Author: James Antill <james at and.org>
Date:   Mon Mar 22 14:21:11 2010 -0400

     Don't traceback when we can't open the history DB (non-root users by default
    now), BZ 575917.
     Provide a .readable interface for callers to check.

diff --git a/yum/history.py b/yum/history.py
index 2707cac..3ef5f74 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -178,6 +178,7 @@ class YumHistory:
         self.conf = yum.misc.GenericHolder()
         self.conf.db_path  = os.path.normpath(root + '/' + db_path)
         self.conf.writable = False
+        self.conf.readable = True
 
         if not os.path.exists(self.conf.db_path):
             try:
@@ -214,7 +215,15 @@ class YumHistory:
 
     def _get_cursor(self):
         if self._conn is None:
-            self._conn = sqlite.connect(self._db_file)
+            if not self.conf.readable:
+                return None
+
+            try:
+                self._conn = sqlite.connect(self._db_file)
+            except sqlite.OperationalError:
+                self.conf.readable = False
+                return None
+
         return self._conn.cursor()
     def _commit(self):
         return self._conn.commit()
@@ -291,6 +300,8 @@ class YumHistory:
 
     def trans_with_pid(self, pid):
         cur = self._get_cursor()
+        if cur is None:
+            return None
         res = executeSQL(cur,
                          """INSERT INTO trans_with_pkgs
                          (tid, pkgtupid)
@@ -302,6 +313,8 @@ class YumHistory:
         if not hasattr(self, '_tid') or state is None:
             return # Not configured to run
         cur = self._get_cursor()
+        if cur is None:
+            return # Should never happen, due to above
         res = executeSQL(cur,
                          """INSERT INTO trans_data_pkgs
                          (tid, pkgtupid, state)
@@ -313,6 +326,8 @@ class YumHistory:
             return # Not configured to run
 
         cur = self._get_cursor()
+        if cur is None:
+            return # Should never happen, due to above
         res = executeSQL(cur,
                          """UPDATE trans_data_pkgs SET done = ?
                          WHERE tid = ? AND pkgtupid = ? AND state = ?
@@ -322,6 +337,8 @@ class YumHistory:
 
     def beg(self, rpmdb_version, using_pkgs, txmbrs):
         cur = self._get_cursor()
+        if cur is None:
+            return
         res = executeSQL(cur,
                          """INSERT INTO trans_beg
                             (timestamp, rpmdb_version, loginuid)
@@ -343,6 +360,8 @@ class YumHistory:
 
     def _log_errors(self, errors):
         cur = self._get_cursor()
+        if cur is None:
+            return
         for error in errors:
             error = to_unicode(error)
             executeSQL(cur,
@@ -356,6 +375,8 @@ class YumHistory:
             return # Not configured to run
 
         cur = self._get_cursor()
+        if cur is None:
+            return # Should never happen, due to above
         for error in msg.split('\n'):
             error = to_unicode(error)
             executeSQL(cur,
@@ -387,7 +408,11 @@ class YumHistory:
 
     def end(self, rpmdb_version, return_code, errors=None):
         assert return_code or not errors
+        if not hasattr(self, '_tid'):
+            return # Failed at beg() time
         cur = self._get_cursor()
+        if cur is None:
+            return # Should never happen, due to above
         res = executeSQL(cur,
                          """INSERT INTO trans_end
                             (tid, timestamp, rpmdb_version, return_code)
@@ -444,6 +469,8 @@ class YumHistory:
         """ Return a list of the last transactions, note that this includes
             partial transactions (ones without an end transaction). """
         cur = self._get_cursor()
+        if cur is None:
+            return []
         sql =  """SELECT tid,
                          trans_beg.timestamp AS beg_ts,
                          trans_beg.rpmdb_version AS beg_rv,
@@ -551,6 +578,10 @@ class YumHistory:
             packages al. la. "yum list". Returns transaction ids. """
         # Search packages ... kind of sucks that it's search not list, pkglist?
 
+        cur = self._get_cursor()
+        if cur is None:
+            return set()
+
         data = _setupHistorySearchSQL(patterns, ignore_case)
         (need_full, patterns, fields, names) = data
 
@@ -559,7 +590,6 @@ class YumHistory:
         for row in self._yieldSQLDataList(patterns, fields, ignore_case):
             pkgtupids.add(row[0])
 
-        cur = self._get_cursor()
         sql =  """SELECT tid FROM trans_data_pkgs WHERE pkgtupid IN """
         sql += "(%s)" % ",".join(['?'] * len(pkgtupids))
         params = list(pkgtupids)
commit b3ca56aed8f2949b73d07bf3c1aa90ae20793848
Author: James Antill <james at and.org>
Date:   Tue Mar 16 17:37:04 2010 -0400

    Add the provides data for each pkg, to format_missing_requires. Add relations.

diff --git a/output.py b/output.py
index b09730a..aa5dc4d 100755
--- a/output.py
+++ b/output.py
@@ -1660,24 +1660,52 @@ class DepSolveProgressCallBack:
         if not yb:
             return msg
         
+        def _msg_pkg(action, pkg, needname):
+            " Add a package to the message, including any provides matches. "
+            msg = _('\n    %s: %s (%s)') % (action, pkg, pkg.ui_from_repo)
+            needtup = (needname, None, (None, None, None))
+            done = False
+            for pkgtup in pkg.matchingPrcos('provides', needtup):
+                done = True
+                msg += _('\n        %s') % yum.misc.prco_tuple_to_string(pkgtup)
+            if not done:
+                msg += _('\n        Not found')
+            return msg
+
         ipkgs = set()
         for pkg in sorted(yb.rpmdb.getProvides(needname)):
-            ipkgs.add(pkg.pkgtup)
+            nevr = (pkg.name, pkg.epoch, pkg.version, pkg.release)
+            ipkgs.add(nevr)
             action = _('Installed')
-            if yb.tsInfo.getMembersWithState(pkg.pkgtup, TS_REMOVE_STATES):
+            rmed = yb.tsInfo.getMembersWithState(pkg.pkgtup, TS_REMOVE_STATES)
+            if rmed:
                 action = _('Removing')
-            msg += _('\n    %s: %s (%s)') % (action, pkg, pkg.ui_from_repo)
+            msg += _msg_pkg(action, pkg, needname)
+            # These should be the only three things we care about:
+            relmap = {'updatedby' : _('Updated By'),
+                      'downgradedby' : _('Downgraded By'),
+                      'obsoletedby' :  _('Obsoleted By'),
+                      }
+            for txmbr in rmed:
+                for (rpkg, rtype) in txmbr.relatedto:
+                    if rtype not in relmap:
+                        continue
+                    nevr = (rpkg.name, rpkg.epoch, rpkg.version, rpkg.release)
+                    ipkgs.add(nevr)
+                    msg += _msg_pkg(relmap[rtype], rpkg, needname)
+
         last = None
         for pkg in sorted(yb.pkgSack.getProvides(needname)):
             #  We don't want to see installed packages, or N packages of the
             # same version, from different repos.
-            if pkg.pkgtup in ipkgs or pkg.verEQ(last):
+            nevr = (pkg.name, pkg.epoch, pkg.version, pkg.release)
+            if nevr in ipkgs or (pkg.verEQ(last) and pkg.arch == last.arch):
                 continue
             last = pkg
             action = _('Available')
             if yb.tsInfo.getMembersWithState(pkg.pkgtup, TS_INSTALL_STATES):
                 action = _('Installing')
-            msg += _('\n    %s: %s (%s)') % (action, pkg, pkg.repoid)
+            msg += _msg_pkg(action, pkg, needname)
         return msg
     
     def procConflict(self, name, confname):
commit 008d74a13173bcb7005e7150ebc1367720078452
Author: James Antill <james at and.org>
Date:   Thu Mar 11 14:47:40 2010 -0500

     Return an error code if yum can't install any of the specified packages
    
     People want a bash scriptable way to say "X should be installed". So
    with this patch:
    
    . If _any_ package specified: is installed already; or is found and will
    be installed == yum returns 0 (or at least goes into depchecking etc.)
    
    . If _all_ package(s) are "not found", yum turns the message "Nothing to do"
    into an error (returns 1, atm).
    
    ...thus:
    
    1. yum install -y a b && echo worked
    
    This will echo "worked" if _either_ a or b is installed after yum
    finishes, but tries to install both.
    
    2. yum install a && yum install b && echo worked
    
    This will echo "worked" if both are installed (and if a is not
    available, b will not even try to be installed).

diff --git a/cli.py b/cli.py
index a34d205..cebbae3 100644
--- a/cli.py
+++ b/cli.py
@@ -595,6 +595,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         
         oldcount = len(self.tsInfo)
         
+        done = False
         for arg in userlist:
             if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                           os.path.exists(arg))):
@@ -609,8 +610,13 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                         self.term.MODE['bold'], arg,
                                         self.term.MODE['normal'])
                 self._maybeYouMeant(arg)
+            else:
+                done = True
         if len(self.tsInfo) > oldcount:
             return 2, [_('Package(s) to install')]
+
+        if not done:
+            return 1, [_('Nothing to do')]
         return 0, [_('Nothing to do')]
         
     def updatePkgs(self, userlist, quiet=0):
commit a33aa64f23ac3c197871e9100c99e9cccc3f58b1
Author: James Antill <james at and.org>
Date:   Thu Mar 11 11:47:33 2010 -0500

    Add a little more documentation to get_applicable_notices()

diff --git a/yum/update_md.py b/yum/update_md.py
index 54d4cd7..fc051b0 100644
--- a/yum/update_md.py
+++ b/yum/update_md.py
@@ -335,13 +335,15 @@ class UpdateMetadata(object):
     # has a BZ fix notice. All you can see is the BZ notice for the new "pkg-3"
     # with the above.
     #  So now instead you lookup based on the _installed_ pkg.pkgtup, and get
-    # two notices, in order: [(pkg-3, notice), (pkg-2, notice)]
+    # two notices, in order: [(pkgtup-3, notice), (pkgtup-2, notice)]
     # the reason for the sorting order is that the first match will give you
     # the minimum pkg you need to move to.
     def get_applicable_notices(self, pkgtup):
         """
         Retrieve any update notices which are newer than a
         given std. pkgtup (name, arch, epoch, version, release) tuple.
+        Returns: list of (pkgtup, notice) that are newer than the given pkgtup,
+                 in the order of newest pkgtups first.
         """
         oldpkgtup = pkgtup
         name = oldpkgtup[0]
commit cb59d97fba6765d03a908219d5258b3896f2ecb1
Author: James Antill <james at and.org>
Date:   Thu Mar 11 11:54:41 2010 -0500

    Add documentation for distro-sync command.

diff --git a/docs/yum.8 b/docs/yum.8
index 8d42d9d..ff7ed8f 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -27,6 +27,8 @@ gnome\-packagekit application\&.
 .br 
 .I \fR * upgrade [package1] [package2] [\&.\&.\&.] 
 .br
+.I \fR * distribution-synchronization [package1] [package2] [\&.\&.\&.] 
+.br
 .I \fR * remove | erase package1 [package2] [\&.\&.\&.]
 .br 
 .I \fR * list [\&.\&.\&.]
@@ -119,6 +121,13 @@ Running in verbose mode also shows obsoletes.
 Is the same as the update command with the \-\-obsoletes flag set. See update 
 for more details.
 .IP 
+.IP "\fBdistribution-synchronization\fP" "\fBdistro-sync\fP"
+Synchronizes the installed package set with the latest packages available, this
+is done by either obsoleting, upgrading or downgrading as appropriate. This will
+"normally" do the same thing as the upgrade command however if you have the
+package FOO installed at version 4, and the latest available is only
+version 3, then this command will \fBdowngrade\fP FOO to version 3.
+.IP 
 .IP "\fBremove\fP or \fBerase\fP"
 Are used to remove the specified packages from the system
 as well as removing any packages which depend on the package being
commit 203a39125e1de20500739c94e7c92d074d19790f
Author: James Antill <james at and.org>
Date:   Thu Mar 4 17:17:30 2010 -0500

     Add distro-sync command, to "force update" to the latest versions.
    
     This should be safe to go in for 3.2.27, as it's an entirely self
    contained command ... I've tried it here going on multiple directions,
    and it WMF(tm).

diff --git a/cli.py b/cli.py
index f5ed53d..a34d205 100644
--- a/cli.py
+++ b/cli.py
@@ -100,6 +100,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         self.registerCommand(yumcommands.VersionCommand())
         self.registerCommand(yumcommands.HistoryCommand())
         self.registerCommand(yumcommands.CheckRpmdbCommand())
+        self.registerCommand(yumcommands.DistroSyncCommand())
 
     def registerCommand(self, command):
         for name in command.getNames():
@@ -649,6 +650,68 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         else:
             return 0, [_('No Packages marked for Update')]
 
+    #  Note that we aren't in __init__ yet for a couple of reasons, but we 
+    # probably will get there for 3.2.28.
+    def distroSyncPkgs(self, userlist):
+        """ This does either upgrade/downgrade, depending on if the latest
+            installed version is older or newer. We allow "selection" but not
+            local packages (use tmprepo, or something). """
+
+        dupdates = []
+        ipkgs = {}
+        for pkg in sorted(self.rpmdb.returnPackages(patterns=userlist)):
+            ipkgs[pkg.name] = pkg
+
+        obsoletes = []
+        if self.conf.obsoletes:
+            obsoletes = self.up.getObsoletesTuples(newest=1)
+
+        for (obsoleting, installed) in obsoletes:
+            if installed[0] not in ipkgs:
+                continue
+            dupdates.extend(self.update(pkgtup=installed))
+        for (obsoleting, installed) in obsoletes:
+            if installed[0] not in ipkgs:
+                continue
+            del ipkgs[installed[0]]
+
+        apkgs = {}
+        for pkg in self.pkgSack.returnNewestByName():
+            if pkg.name not in ipkgs:
+                continue
+            apkgs[pkg.name] = pkg
+
+        for ipkgname in ipkgs:
+            if ipkgname not in apkgs:
+                continue
+
+            ipkg = ipkgs[ipkgname]
+            apkg = apkgs[ipkgname]
+            if ipkg.verEQ(apkg):
+                continue
+            if self.allowedMultipleInstalls(apkg):
+                found = False
+                for napkg in self.rpmdb.searchNames([apkg.name]):
+                    if napkg.verEQ(apkg):
+                        found = True
+                    elif napkg.verGT(apkg):
+                        dupdates.extend(self.remove(po=napkg))
+                if found:
+                    continue
+                dupdates.extend(self.install(pattern=apkg.name))
+            elif ipkg.verLT(apkg):
+                n,a,e,v,r = apkg.pkgtup
+                dupdates.extend(self.update(name=n, epoch=e, ver=v, rel=r))
+            else:
+                n,a,e,v,r = apkg.pkgtup
+                dupdates.extend(self.downgrade(name=n, epoch=e, ver=v, rel=r))
+
+        if dupdates:
+            msg = _('%d packages marked for Distribution Synchronization') % len(dupdates)
+            return 2, [msg]
+        else:
+            return 0, [_('No Packages marked for Distribution Synchronization')]
+
     def erasePkgs(self, userlist):
         """take user commands and populate a transaction wrapper with packages
            to be erased/removed"""
diff --git a/yumcommands.py b/yumcommands.py
index 35bd97c..88c047f 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -205,6 +205,28 @@ class UpdateCommand(YumCommand):
         except yum.Errors.YumBaseError, e:
             return 1, [str(e)]
 
+class DistroSyncCommand(YumCommand):
+    def getNames(self):
+        return ['distribution-synchronization', 'distro-sync']
+
+    def getUsage(self):
+        return _("[PACKAGE...]")
+
+    def getSummary(self):
+        return _("Synchronize installed packages to the latest available versions")
+
+    def doCheck(self, base, basecmd, extcmds):
+        checkRootUID(base)
+        checkGPGKey(base)
+
+    def doCommand(self, base, basecmd, extcmds):
+        self.doneCommand(base, _("Setting up Distribution Synchronization Process"))
+        try:
+            base.conf.obsoletes = 1
+            return base.distroSyncPkgs(extcmds)
+        except yum.Errors.YumBaseError, e:
+            return 1, [str(e)]
+
 def _add_pkg_simple_list_lens(data, pkg, indent=''):
     """ Get the length of each pkg's column. Add that to data.
         This "knows" about simpleList and printVer. """


More information about the Yum-commits mailing list