[yum-cvs] 2 commits - cli.py docs/yum.conf.5 yum/config.py yum/__init__.py yum/rpmtrans.py

Seth Vidal skvidal at linux.duke.edu
Thu Aug 16 12:50:13 UTC 2007


 cli.py          |    4 -
 docs/yum.conf.5 |    4 +
 yum/__init__.py |    9 +++
 yum/config.py   |    3 -
 yum/rpmtrans.py |  151 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 5 files changed, 150 insertions(+), 21 deletions(-)

New commits:
commit bc6f746d416340b5dbe58f1028fee06d2e1e89aa
Merge: 13fa959... cbbaf1a...
Author: Seth Vidal <skvidal at fedoraproject.org>
Date:   Thu Aug 16 08:46:48 2007 -0400

    Merge branch 'master' of ssh://login.linux.duke.edu/home/groups/yum/git/yum
    
    * 'master' of ssh://login.linux.duke.edu/home/groups/yum/git/yum:
      Make the BaseConfig write method work better and make it support

commit 13fa959ab54b6fa6112f2064f7e8983fa15f3b11
Author: Seth Vidal <skvidal at fedoraproject.org>
Date:   Thu Aug 16 08:46:33 2007 -0400

    implement transaction journalling:
     transaction-done.datestamp and transaction-all.datestamp files
    will be made in /var/lib/yum (persistdir in the conf file). These files
    describe the entire transaction and which portions of it have finished.
    
    adds persistdir to configuration - for persistent information b/t runs - unlike cachedir
    fixes a couple of lingering bugs in the new rpm callback.

diff --git a/cli.py b/cli.py
index 683fb15..4459699 100644
--- a/cli.py
+++ b/cli.py
@@ -340,7 +340,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         for feature in ['diskspacecheck']: # more to come, I'm sure
             tsConf[feature] = getattr(self.conf, feature)
         
-        testcb = RPMTransaction(self.tsInfo)
+        testcb = RPMTransaction(self, test=True)
         
         self.initActionTs()
         # save our dsCallback out
@@ -374,7 +374,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         # put back our depcheck callback
         self.dsCallback = dscb
         # setup our rpm ts callback
-        cb = RPMTransaction(self.tsInfo, display=output.YumCliRPMCallBack)
+        cb = RPMTransaction(self, display=output.YumCliRPMCallBack)
         if self.conf.debuglevel < 2:
             cb.display.output = False
 
diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
index 8094b58..5f00b38 100644
--- a/docs/yum.conf.5
+++ b/docs/yum.conf.5
@@ -26,6 +26,10 @@ following options:
 Directory where yum should store its cache and db files. The default is
 `/var/cache/yum'.
 
+.IP \fBpersistdir\fR
+Directory where yum should store information that should persist over multiple
+runs. The default is `/var/lib/yum'.
+
 .IP \fBkeepcache\fR
 Either `1' or `0'. Determines whether or not yum keeps the cache
 of headers and packages after succesful installation.  Default is '1'
diff --git a/yum/__init__.py b/yum/__init__.py
index 80a9e52..33a0e76 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -573,6 +573,15 @@ class YumBase(depsolve.Depsolve):
 
         if not self.conf.keepcache:
             self.cleanUsedHeadersPackages()
+        
+        for i in ('ts_all_fn', 'ts_done_fn'):
+            if hasattr(cb, i):
+                fn = getattr(cb, i)
+                if os.path.exists(fn):
+                    try:
+                        os.unlink(fn)
+                    except (IOError, OSError), e:
+                        self.logger.critical('Failed to remove transaction file %s' % fn)
 
         self.plugins.run('posttrans')
         
diff --git a/yum/config.py b/yum/config.py
index 6ae7d0d..0af2099 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -474,6 +474,7 @@ class YumConf(StartupConf):
     recent = IntOption(7)
 
     cachedir = Option('/var/cache/yum')
+    persistdir = Option('/var/lib/yum')
     keepcache = BoolOption(True)
     logfile = Option('/var/log/yum.log')
     reposdir = ListOption(['/etc/yum/repos.d', '/etc/yum.repos.d'])
@@ -609,7 +610,7 @@ def readMainConfig(startupconf):
     yumconf.populate(startupconf._parser, 'main')
 
     # Apply the installroot to directory options
-    for option in ('cachedir', 'logfile'):
+    for option in ('cachedir', 'logfile', 'persistdir'):
         path = getattr(yumconf, option)
         setattr(yumconf, option, yumconf.installroot + path)
     
diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index 1c1d1b2..42c094f 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -18,8 +18,10 @@
 
 import rpm
 import os
-import sys
+import time
 import logging
+import types
+import sys
 from yum.constants import *
 
 
@@ -87,6 +89,7 @@ class RPMBaseCallback:
 
         
     def errorlog(self, msg):
+        # FIXME this should probably dump to the filelog, too
         print >> sys.stderr, msg
 
     def filelog(self, package, action):
@@ -111,10 +114,11 @@ class SimpleCliCallBack(RPMBaseCallback):
         self.lastpackage = package
 
 class RPMTransaction:
-    def __init__(self, tsInfo, display=NoOutputCallBack):
-        self.display = display()
-        self.tsInfo = tsInfo
-
+    def __init__(self, base, test=False, display=NoOutputCallBack):
+        self.display = display() # display callback
+        self.base = base # base yum object b/c we need so much
+        self.test = test # are we a test?
+        
         self.filehandles = {}
         self.total_actions = 0
         self.total_installed = 0
@@ -123,7 +127,8 @@ class RPMTransaction:
         self.total_removed = 0
         self.logger = logging.getLogger('yum.filelogging.RPMInstallCallback')
         self.filelog = False
-
+    
+        
     def _dopkgtup(self, hdr):
         tmpepoch = hdr['epoch']
         if tmpepoch is None: epoch = '0'
@@ -136,7 +141,101 @@ class RPMTransaction:
           hdr['release'], hdr['arch'])
 
         return handle
+    
+    def ts_done(self, package, action):
+        """writes out the portions of the transaction which have completed"""
+        
+        if self.test: return
+    
+        if not hasattr(self, '_ts_done'):
+            # need config variable to put this in a better path/name
+            te_fn = '%s/transaction-done.%s' % (self.base.conf.persistdir, self._ts_time)
+            self.ts_done_fn = te_fn
+            try:
+                self._ts_done = open(te_fn, 'w')
+            except (IOError, OSError), e:
+                self.display.errorlog('could not open ts_done file: %s' % e)
+                return
+        
+        # walk back through self._te_tuples
+        # make sure the package and the action make some kind of sense
+        # write it out and pop(0) from the list
         
+        (t,e,n,v,r,a) = self._te_tuples[0] # what we should be on
+
+        # make sure we're in the right action state
+        msg = 'ts_done state is %s %s should be %s %s' % (package, action, t, n)
+        if action in TS_REMOVE_STATES:
+            if t != 'erase':
+                self.display.errorlog(msg)
+        if action in TS_INSTALL_STATES:
+            if t != 'install':
+                self.display.errorlog(msg)
+                
+        # check the pkg name out to make sure it matches
+        if type(package) in types.StringTypes:
+            name = package
+        else:
+            name = package.name
+        
+        if n != name:
+            msg = 'ts_done name in te is %s should be %s' % (n, package)
+            self.display.errorlog(msg)
+
+        # hope springs eternal that this isn't wrong
+        msg = '%s %s:%s-%s-%s.%s\n' % (t,e,n,v,r,a)
+
+        self._ts_done.write(msg)
+        self._ts_done.flush()
+        self._te_tuples.pop(0)
+    
+    def ts_all(self):
+        """write out what our transaction will do"""
+        
+        # save the transaction elements into a list so we can run across them
+        if not hasattr(self, '_te_tuples'):
+            self._te_tuples = []
+
+        for te in self.base.ts:
+            n = te.N()
+            a = te.A()
+            v = te.V()
+            r = te.R()
+            e = te.E()
+            if e is None:
+                e = '0'
+            if te.Type() == 1:
+                t = 'install'
+            elif te.Type() == 2:
+                t = 'erase'
+            else:
+                t = te.Type()
+            
+            # save this in a list            
+            self._te_tuples.append((t,e,n,v,r,a))
+
+        # write to a file
+        self._ts_time = time.strftime('%Y-%m-%d.%H:%M.%S')
+        tsfn = '%s/transaction-all.%s' % (self.base.conf.persistdir, self._ts_time)
+        self.ts_all_fn = tsfn
+        try:
+            # fixme - we should probably be making this elsewhere but I'd
+            # rather that the transaction not fail so we do it here, anyway
+            if not os.path.exists(self.base.conf.persistdir):
+                os.makedirs(self.base.conf.persistdir) # make the dir, just in case
+            
+            fo = open(tsfn, 'w')
+        except (IOError, OSError), e:
+            self.display.errorlog('could not open ts_all file: %s' % e)
+            return
+        
+
+        for (t,e,n,v,r,a) in self._te_tuples:
+            msg = "%s %s:%s-%s-%s.%s\n" % (t,e,n,v,r,a)
+            fo.write(msg)
+        fo.flush()
+        fo.close()
+    
     def callback( self, what, bytes, total, h, user ):
         if what == rpm.RPMCALLBACK_TRANS_START:
             self._transStart( bytes, total, h )
@@ -165,17 +264,22 @@ class RPMTransaction:
         elif what == rpm.RPMCALLBACK_CPIO_ERROR:
             self._cpioError(bytes, total, h)
         elif what == rpm.RPMCALLBACK_UNPACK_ERROR:
-            self.unpackError(bytes, total, h)
+            self._unpackError(bytes, total, h)
     
     
     def _transStart(self, bytes, total, h):
         if bytes == 6:
             self.total_actions = total
-    
+            if self.test: return
+
+            self.ts_all() # write out what transaction will do
+
     def _transProgress(self, bytes, total, h):
         pass
+        
     def _transStop(self, bytes, total, h):
         pass
+
     def _instOpenFile(self, bytes, total, h):
         self.lastmsg = None
         hdr = None
@@ -190,6 +294,7 @@ class RPMTransaction:
             return fd
         else:
             self.display.errorlog("Error: No Header to INST_OPEN_FILE")
+            
     def _instCloseFile(self, bytes, total, h):
         hdr = None
         if h is not None:
@@ -197,11 +302,15 @@ class RPMTransaction:
             handle = self._makeHandle(hdr)
             os.close(self.filehandles[handle])
             fd = 0
-
+            if self.test: return
+            
             pkgtup = self._dopkgtup(hdr)
-            txmbrs = self.tsInfo.getMembers(pkgtup=pkgtup)
+            txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
             for txmbr in txmbrs:
                 self.display.filelog(txmbr.po, txmbr.output_state)
+                self.ts_done(txmbr.po, txmbr.output_state)
+                
+                
     
     def _instProgress(self, bytes, total, h):
         if h is not None:
@@ -214,14 +323,14 @@ class RPMTransaction:
             else:
                 hdr, rpmloc = h
                 pkgtup = self._dopkgtup(hdr)
-                txmbrs = self.tsInfo.getMembers(pkgtup=pkgtup)
+                txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
                 for txmbr in txmbrs:
                     action = txmbr.output_state
                     self.display.event(txmbr.po, action, bytes, total,
                                 self.complete_actions, self.total_actions)
     def _unInstStart(self, bytes, total, h):
         pass
-
+        
     def _unInstProgress(self, bytes, total, h):
         pass
     
@@ -233,10 +342,14 @@ class RPMTransaction:
             action = TS_ERASE
         else:
             action = TS_UPDATED                    
-            
+        
         self.display.event(h, action, 100, 100, self.complete_actions,
                             self.total_actions)
-
+        
+        if self.test: return # and we're done
+        self.ts_done(h, action)
+        
+        
     def _rePackageStart(self, bytes, total, h):
         pass
         
@@ -249,17 +362,19 @@ class RPMTransaction:
     def _cpioError(self, bytes, total, h):
         (hdr, rpmloc) = h
         pkgtup = self._dopkgtup(hdr)
-        txmbrs = self.tsInfo.getMembers(pkgtup=pkgtup)
+        txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
         for txmbr in txmbrs:
-            self.display.errorlog("Error in cpio payload of rpm package %s" % txmbr.po)
+            msg = "Error in cpio payload of rpm package %s" % txmbr.po
+            self.display.errorlog(msg)
             # FIXME - what else should we do here? raise a failure and abort?
     
     def _unpackError(self, bytes, total, h):
         (hdr, rpmloc) = h
         pkgtup = self._dopkgtup(hdr)
-        txmbrs = self.tsInfo.getMembers(pkgtup=pkgtup)
+        txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
         for txmbr in txmbrs:
-            self.display.errorlog("Error unpacking rpm package %s" % txmbr.po)
+            msg = "Error unpacking rpm package %s" % txmbr.po
+            self.display.errorlog(msg)
             # FIXME - should we raise? I need a test case pkg to see what the
             # right behavior should be
                 



More information about the Yum-cvs-commits mailing list