Logo Search packages:      
Sourcecode: sbackup version File versions  Download package

upgrade_backups.py

#!/usr/bin/python
#
# Simple Backup suit
#
# Running this command will upgrade a backup directory to latest format.
# This is also a backend for simple-restore-gnome GUI and sbackupd.
#
# Author: Aigars Mahinovs <aigarius@debian.org>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import tarfile, sys, re, os, os.path, shutil, datetime, filecmp, gzip, tempfile, zlib
import cPickle as pickle
import gettext
import traceback
from gettext import gettext as _
try:
    import gnomevfs
except ImportError:
    import gnome.vfs as gnomevfs


class SBUpgrade:
      def upgrade_target( self, target, purge=0 ):

            r = re.compile(r"^(\d{4})-(\d{2})-(\d{2})_(\d{2})[\:\.](\d{2})[\:\.](\d{2})\.\d+\..*?\.(.+)$")

            if self.islocal( target ):
                listing = os.listdir( target )
                listing = filter( r.search, listing )
            else:
                d = gnomevfs.open_directory( target )
                listing = []
                for f in d:
                    if f.type == 2 and f.name != "." and f.name != ".." and r.search( f.name ):
                        listing.append( f.name )
            
            listing.sort()
            listing.reverse()

            for adir in listing:
                  if not self.isdir( target+"/"+adir ):
                        continue
                  if ":" in adir:
                        print "I: Renaming directory: '"+adir+"' to '"+adir.replace( ":", "." )+"'"
                        self.rename( target+"/"+adir, adir.replace( ":", "." ) )
                        adir = adir.replace( ":", "." )
                  self.upgrade_tdir( target+"/"+adir )

            if not purge == 0:
                  self.purge( target, listing, purge )

      def upgrade_tdir( self, tdir ):
            good = True

            if self.exists( tdir+"/tree") and not self.exists( tdir+"/flist" ):
                  print _("I: Upgrading from v1.0 to v1.2: %s") % tdir
                  self.upgrade_v1( tdir )
                  good = False

            if self.exists(tdir + "/ver") :
                  ver = self.readfile( tdir + "/ver" )
            else : 
                  print ("W: Error reading %s/ver ! Ignoring incomplete or non-backup directory." % tdir)  
                  return
            
            try : 
                  major = int(ver[0])
                  minor = int(ver[2])
            except Exception, e:
                  print ("W: %s/ver doesn't contain valid value ! Ignoring incomplete or non-backup directory." % tdir)
                  print ("Error info : ")
                  traceback.print_exc()
                  self.delete(tdir+"/ver")
                  return 
                        
            if major < 2 and minor < 3:
                  print _("I: Upgrading to v1.3: %s") % tdir
                  self.upgrade_v13( tdir )
                  good = False
                  ver = "1.3\n"


            if int(ver[0]) < 2 and int(ver[2:]) < 4:
                  print _("I: Upgrading to v1.4: %s") % tdir
                  self.upgrade_v14( tdir )
                  good = False

            if self.exists( tdir+"/flist" ) and self.exists( tdir+"/fprops" ) and self.exists( tdir+"/files.tgz" ) and self.exists( tdir+"/ver" ):
                  return
            

      def upgrade_v1( self, tdir ):
            i = self.openfile(tdir+"/tree")
            bfiles = pickle.load( i )
            n = self.openfile( tdir+"/flist", True )
            p = self.openfile( tdir+"/fprops", True )
            for item in bfiles:
                  n.write( str(item[0])+"\000" )
                  p.write( str(item[1])+str(item[2])+str(item[3])+str(item[4])+str(item[5])+"\000" )
            p.close()
            n.close()
            i.close()
            v = self.openfile( tdir+"/ver", True )
            v.write("1.4\n")
            v.close()
            
      def upgrade_v13( self, tdir ):
            try:
                flist = gnomevfs.read_entire_file( tdir+"/flist" ).split( "\n" )
                fprops = gnomevfs.read_entire_file( tdir+"/fprops" ).split( "\n" )
                if len(flist)==len(fprops) and len(flist) > 1:
                  l = self.openfile(tdir+"/flist.v13", True)
                  p = self.openfile(tdir+"/fprops.v13", True)
                  for a,b in zip(flist,fprops):
                        l.write( a+"\000" )
                        p.write( b+"\000" )
                  l.close()
                  p.close()
                else:
                  print _("W: Damaged backup metainfo - disabling %s") % tdir
                  if self.exists(tdir+"/ver") : self.delete(tdir+"/ver")
                  
                self.rename(tdir+"/flist", "flist.old")
                self.rename(tdir+"/flist.v13", "flist")
                self.rename(tdir+"/fprops", "fprops.old")
                self.rename(tdir+"/fprops.v13", "fprops")
                v = self.openfile( tdir+"/ver", True )
                v.write("1.3\n")
                v.close()
            except:
                print _("W: Damaged backup metainfo - disabling %s") % tdir
                if self.exists(tdir+"/ver") : self.delete(tdir+"/ver")
                
            

      def upgrade_v14( self, tdir ):
            self.delete( tdir + "/ver" )
            
            if not self.exists( tdir+"/flist" ):
                  return False
            if not self.exists( tdir+"/fprops" ):
                  return False
            if not self.exists( tdir+"/files.tgz" ):
                  return False
            if not self.exists( tdir+"/excludes" ):
                  return False
            
            
            v = self.openfile( tdir+"/ver", True )
            v.write("1.4\n")
            v.close()


      def perm_secure( self, tdir ):
            self.chmod( tdir, 0750 )
            self.chmod( tdir+"/ver", 0640 )
            self.chmod( tdir+"/tree", 0640 )
            self.chmod( tdir+"/flist", 0640 )
            self.chmod( tdir+"/fprops", 0640 )
            self.chmod( tdir+"/files.tgz", 0640 )
            self.chmod( tdir+"/packages", 0640 )
            self.chmod( tdir+"/excludes", 0640 )
            self.chmod( tdir+"/base", 0640 )

      def purge( self, target, listing, purge ):
            r = re.compile(r"^(\d{4})-(\d{2})-(\d{2})_(\d{2})[\:\.](\d{2})[\:\.](\d{2})\.\d+\..*?\.(.+)$")

            topurge = []
            
            # Remove broken backup snapshots after first intact snapshot
            # TODO
            
            if purge == "log":
                  # Logarithmic purge
                  # Determine which incremental backup snapshots to remove
                  seenfull = 0
                  for e in listing:
                      if seenfull < 1 and e.endswith( ".ful" ):
                        seenfull += 1
                      elif seenfull > 1 and e.endswith( ".inc" ):
                        topurge.append( e )
                  
                  # Now for the fun part - expiring the full backup snapshots
                  # Only consider the full backups
                  
                  fulls = [x for x in listing if x.endswith( ".ful" )]
                  days = {}
                  
                  for adir in fulls:
                      m = r.search( adir )
                      dif = datetime.datetime.today() - datetime.datetime(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)),int(m.group(5)),int(m.group(6)))
                      if dif.days < 1:
                        # Keep all from last 24 hours
                        continue
                      if days.has_key( dif.days ):
                        topurge.append( days[dif.days] )
                        days[dif.days] = adir # Keep the earliest backup of each day
                      else:
                        days[dif.days] = adir
            
                  bdays = sorted(days.keys())
                  
                  for i in range( 1, 4 ):
                      week = [ x for x in bdays if x>(i*7) and x<=((i+1)*7) ]
                      week.sort()
                      week = week[:-1] # Keep earliest backup in a week
                      for aday in week:
                        topurge.append( days[aday] )

                  for i in range( 0, 12 ):
                      month = [ x for x in bdays if x>(28+i*30) and x<=(28+(i+1)*30) ]
                      month.sort()
                      month = month[:-1] # Keep earliest backup in a month
                      for aday in month:
                        topurge.append( days[aday] )
                  
                  bdays = [x for x in bdays if x>(28+12*30)] # Now for the really old backups
                  bdays.sort()
                  
                  years = {}
                  for aday in bdays:
                      year = int(aday/(28+12*30))
                      if years.has_key( year ):
                        topurge.append( days[aday] ) # Keep earliest backup of a year
                        years[year] = aday
                      else:
                        years[year] = aday
                  
            else:
                try: purge = int(purge)
                except: purge = 0
                if purge:
                  # Simple purge - remove all backups older then 'purge' days
                  for tdir in listing:
                      m = r.search( tdir )
                      if (datetime.date.today() - datetime.date(int(m.group(1)),int(m.group(2)),int(m.group(3)) ) ).days > purge:
                        topurge.append( tdir )
            
            for adir in topurge:
                self.delete( target+"/"+adir )
                      
                  

# Helper functions

      def delete( self, uri ):
            if self.islocal( uri ):
                  if self.isdir( uri ):
                        shutil.rmtree( uri, False )
                        return True
                  else : 
                        os.unlink(uri)
                        return True
            else:
                  if not self.isdir( uri ):
                      gnomevfs.unlink( uri )
                  else:
                        d = gnomevfs.open_directory( uri )
                        for f in d:
                              if f.name=="." or f.name=="..":
                                    continue
                              if f.type==2:
                                    self.delete( uri+"/"+f.name )
                              else:
                                    gnomevfs.unlink( uri+"/"+f.name )
                        gnomevfs.remove_directory( uri )

      def isdir( self, uri ):
            try :
                  return ( gnomevfs.get_file_info( uri ).type == 2 )
            except Exception, e : 
                  print "W: " +str(e)
                  return False

      def rename( self, uri, name ):
            p = gnomevfs.get_file_info( uri )
            p.name = name
            gnomevfs.set_file_info( uri, p, 1 )

                        

      def chmod( self, uri, mode ):
            p = gnomevfs.get_file_info( uri )
            p.permissions = mode
            gnomevfs.set_file_info( uri, p, 2 )

      def permissions( self, uri ):
            return gnomevfs.get_file_info( uri ).permissions

      def exists( self, uri ):
            if self.islocal(uri):
                  return os.access( uri, os.F_OK )
            else:
                  return gnomevfs.exists( uri )

      def islocal( self, uri ):
            return gnomevfs.URI( uri ).is_local

      def openfile( self, uri, write=False ):
            if self.islocal( uri ):
                  if write:
                        return open( uri, "w" )
                  else:
                        return open( uri, "r" )
            else:
                  if write:
                        if self.exists( uri ):
                              return gnomevfs.open( uri, 2 )
                        else:
                              return gnomevfs.create( uri, 2 )
                  else:
                        return gnomevfs.open( uri, 1 )
                        
      def readfile(self, uri) :
            " Read a file from a given URI and return a string with the read content "
            if self.islocal( uri ) :
                  f = open( uri, "r" )
                  value = f.read()
                  f.close()
                  return str( value )
            else :
                  return str( gnomevfs.read_entire_file( uri ) )
            


if __name__ == "__main__":
      # i18n init
      gettext.textdomain("sbackup")

        if not len(sys.argv) in [2]:
                print _("""
Simple Backup suit command line backup format upgrade
Usage: upgrade-backup backup-target-url
Note: backup-target-url must not include the snapshot subdirectory name, for example:

   /var/backup/

Use simple-restore-gnome for more ease of use.
""")
                sys.exit(1)

      u = SBUpgrade()
        u.upgrade_target( sys.argv[1] )

Generated by  Doxygen 1.6.0   Back to index