#!/usr/bin/env python
#
#  hot-backup.py: perform a "hot" backup of a Berkeley DB repository.
#                 (and clean old logfiles after backup completes.)
#
#  Subversion is a tool for revision control. 
#  See http://subversion.tigris.org for more information.
#    
# ====================================================================
# Copyright (c) 2000-2004 CollabNet.  All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.  The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
# This software consists of voluntary contributions made by many
# individuals.  For exact contribution history, see the revision
# history and logs, available at http://subversion.tigris.org/.
# ====================================================================

######################################################################

import sys, os, shutil, string, re, time

######################################################################
# Global Settings

# Subversion bin dir
bindir ="C:/Programme/Subversion/Bin/"

# Path to svnlook utility
svnlook = bindir + "svnlook"

# Path to svnadmin utility
svnadmin = bindir + "svnadmin"

# Number of backups to keep around (0 for "keep them all")
num_backups = 32

######################################################################
# Command line arguments


if len(sys.argv) != 3:
  print "Usage: ", os.path.basename(sys.argv[0]), " <repos_parent_path> <backup_path>"
  sys.exit(1)
  
# Path to parent dir
repo_parent_dir = sys.argv[1]

# Where to store the repository backup.  The backup will be placed in
# a *subdirectory* of this location, named after the youngest
# revision.
backup_dir = sys.argv[2]

filename = os.path.join(backup_dir,"hot-backup.log")
log = open( filename, 'a' )


m = "\nStarting hot backup .....\n" + time.strftime("%d-%m-%Y %H:%M:%S",time.localtime(time.time())) + "\n"
print m
log.write(m + "\n")
	

#Loop the repo parent folder
for list in os.listdir (repo_parent_dir):
	# Path to repository
    repo_dir = os.path.join(repo_parent_dir, list)
    if os.path.isdir(repo_dir):
        
	#repo_dir = sys.argv[1]

	repo = os.path.basename(os.path.abspath(repo_dir))
	
	
	######################################################################
	# Helper functions
	
	def comparator(a, b):
	  # We pass in filenames so there is never a case where they are equal.
	  regexp = re.compile("-(?P<revision>[0-9]+)(-(?P<increment>[0-9]+))?$")
	  matcha = regexp.search(a)
	  matchb = regexp.search(b)
	  reva = int(matcha.groupdict()['revision'])
	  revb = int(matchb.groupdict()['revision'])
	  if (reva < revb):
	    return -1
	  elif (reva > revb):
	    return 1
	  else:
	    inca = matcha.groupdict()['increment']
	    incb = matchb.groupdict()['increment']
	    if not inca:
	      return -1
	    elif not incb:
	      return 1;
	    elif (int(inca) < int(incb)):
	      return -1
	    else:
	      return 1
	
	######################################################################
	# Main
	
	m = "  Beginning hot backup of '"+ repo_dir + "'." 
	print m
	log.write(m + "\n")
	
	
	### Step 1: get the youngest revision.
	
	infile, outfile, errfile = os.popen3(svnlook + " youngest " + repo_dir)
	stdout_lines = outfile.readlines()
	stderr_lines = errfile.readlines()
	outfile.close()
	infile.close()
	errfile.close()
	
	youngest = string.strip(stdout_lines[0])
	m = "  Youngest revision is " + youngest
	print m
	log.write(m + "\n")
	
	### Step 2: Find next available backup path
	
	backup_subdir = os.path.join(backup_dir, repo + "-" + youngest)
	
	# If there is already a backup of this revision, then append the
	# next highest increment to the path. We still need to do a backup
	# because the repository might have changed despite no new revision
	# having been created. We find the highest increment and add one
	# rather than start from 1 and increment because the starting
	# increments may have already been removed due to num_backups.
	
	regexp = re.compile("^" + repo + "-" + youngest + "(-(?P<increment>[0-9]+))?$")
	directory_list = os.listdir(backup_dir)
	young_list = filter(lambda x: regexp.search(x), directory_list)
	if young_list:
	  young_list.sort(comparator)
	  increment = regexp.search(young_list.pop()).groupdict()['increment']
	  if increment:
	    backup_subdir = os.path.join(backup_dir, repo + "-" + youngest + "-"
	                                 + str(int(increment) + 1))
	  else:
	    backup_subdir = os.path.join(backup_dir, repo + "-" + youngest + "-1")
	
	### Step 3: Ask subversion to make a hot copy of a repository.
	###         copied last.
	
	m = "  Backing up repository to '" + backup_subdir + "'..."
	print m
	log.write(m + "\n")
	
	err_code = os.spawnl(os.P_WAIT, svnadmin, "svnadmin", "hotcopy", repo_dir, 
	                     backup_subdir, "--clean-logs")
	if(err_code != 0):
	  m = "  Unable to backup the repository: " + err_code + "\n"
	  print m
	  log.write(m + "\n")
	else:
	  m = "  Backup done."
	  print m
	  log.write(m + "\n")
	
	  ### Step 4: finally, remove all repository backups other than the last
	  ###         NUM_BACKUPS.
	
	  if num_backups > 0:
	    regexp = re.compile("^" + repo + "-[0-9]+(-[0-9]+)?$")
	    directory_list = os.listdir(backup_dir)
	    old_list = filter(lambda x: regexp.search(x), directory_list)
	    old_list.sort(comparator)
	    del old_list[max(0,len(old_list)-num_backups):]
	    for item in old_list:
	      old_backup_subdir = os.path.join(backup_dir, item)
	      m = "  Removing old backup: " + old_backup_subdir
	      print m
	      log.write(m + "\n")
	      shutil.rmtree(old_backup_subdir)

	  m = "  Finished " + repo_dir + ".\n"
	  print m
	  log.write(m + "\n")

m = "\nEnd hot backup .....\n" + time.strftime("%d-%m-%Y %H:%M:%S",time.localtime(time.time())) + "\n"
print m
log.write(m + "\n")
	
log.close()

