#!/usr/bin/python
"""Use contents manifest to improve rsync performance by only syncing
directories which are out-of-date.

Updates content manifests in-place if you use a split manifest.  Otherwise
requires both source and destinations contents manifests.
"""
# Copyright (C) 2007-8 One Laptop Per Child Association, Inc.
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
# Written by C. Scott Ananian <cscott@laptop.org>

from __future__ import with_statement
import warnings
import sys
from bitfrost.contents.utils import verify, \
     SplitContents, UnifiedContents, \
     mkusermap, mkgroupmap, DIROBJECT
from bitfrost.update import VERSION_INFO
from bitfrost.update.irsync import rsync, sync_and_check

def main ():
    from optparse import OptionParser
    parser = OptionParser(usage='%prog [options] [source dir] [dest dir]')
    parser.add_option('-t', '--to-contents', dest='new_contents',default=None,
                      help='read result contents manifest from FILE, ' +
                      'instead of '+
                      'looking for '+DIROBJECT+' in each directory; '+
                      'requires the use of -o',
                      metavar='FILE')
    parser.add_option('-f', '--from-contents', dest='old_contents',default=None,
                      help='read original contents manifest from FILE, ' +
                      'instead of '+
                      'looking for '+DIROBJECT+' in each directory; '+
                      'requires the use of -f',
                      metavar='FILE')
    parser.add_option('-n', '--noopt', action='store_true', dest='noopt',
                      default=False, help='do rsync non-incrementally.')
    parser.add_option('-v','--verbose',action='count', dest='verbosity',
                      default=0, help='display progress.')
    parser.add_option('-p', '--passwd-file', dest='passwd_file',
                      default='/etc/passwd', metavar='PASSWD',
                      help='use PASSWD to lookup usernames for uids')
    parser.add_option('-g', '--group-file', dest='group_file',
                      default='/etc/group', metavar='GROUP',
                      help='use GROUP to lookup groupnames for gids')
    parser.add_option('--version',action='store_true',dest='version',
                      default=False,
                      help="display version and license information.")
    (options, args) = parser.parse_args()
    if options.version:
        print VERSION_INFO
    if not args:
        parser.error('specify source directory')
    if len(args) < 2:
        parser.error('specify destination directory')
    src = args[0]
    dst = args[1]

    # use split or unified manifest?
    if options.old_contents is not None or \
       options.new_contents is not None:
        if options.old_contents is None or options.new_contents is None:
            parser.error('both -f and -o are required.')
        old_contents = UnifiedContents(options.old_contents)
        new_contents = UnifiedContents(options.new_contents)
        old_contents[''] # side-effect: load subdirs
    else:
        old_contents = new_contents = SplitContents(dst)

    if options.verbosity > 0:
        print "Syncing:", dst
        print "From   :", src
    try:
        if options.noopt: raise RuntimeError # skip to fail-safe
        sync_and_check(src, dst, '', old_contents, new_contents, verbosity=options.verbosity)
        if options.verbosity > 0: print "Verifying:", dst
        # create user and group maps
        usermap = mkusermap(options.passwd_file)
        groupmap = mkgroupmap(options.group_file)
        # verify
        verify(dst, '', new_contents, None, None, usermap=usermap, groupmap=groupmap)
    except:
        # fail-safe: forget optimizations, do the hard thing
        if options.verbosity>0 and not options.noopt:
            print >>sys.stderr, "Verify failed.  Resyncing."
        rsync(src, dst, '', True, verbosity=options.verbosity)

if __name__ == '__main__': main ()
