#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os
sys.path.append(
    os.path.join(os.path.dirname(os.path.realpath(__file__)), os.path.pardir, "src")
)
import optparse
from zfstools.connection import ZFSConnection
from zfstools.util import stderr, verbose_stderr, set_verbose
from zfstools.sync import recursive_replicate, optimize

#===================== configuration =====================

parser = optparse.OptionParser("usage: %prog [-onv] [-b BUFSIZE] [-l RATELIMIT] <srcdatasetname> <dstdatasetname>")
parser.add_option('-o', '--progress', action='store_true', dest='progress', default=False, help='show progress (depends on the executabilty of the \'bar\' a.k.a. \'clpbar\' program, or the \'pv\' program) (default: %default)')
parser.add_option('-l', '--rate-limit', action='store', dest='ratelimit', default=-1, type="int", help='rate limit in bytes per second (requires --progress) (default: %default which means no limit)')
parser.add_option('-n', '--dry-run', action='store_true', dest='dryrun', default=False, help='don\'t actually manipulate any file systems')
parser.add_option('-b', '--bufsize', action='store', dest='bufsize', default=-1, help='buffer size in bytes for network operations (default: %default i.e. OS default)')
parser.add_option('-C', '--force-compression', action='store_true', dest='force_compression', default=False, help='forcibly enable gzip compression for transfers via SSH (default: %default i.e. obey SSH configuration)')
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='be verbose (default: %default)')
parser.add_option('-t', '--trust', action='store_true', dest='trust', default=False, help='automatically trust unknown or mismatched SSH keys of remote hosts (default: %default)')
opts,args = parser.parse_args(sys.argv[1:])

try:
	bufsize = int(opts.bufsize)
	assert bufsize >= 16384 or bufsize == -1
except (ValueError,AssertionError),e:
	parser.error("bufsize must be an integer greater than 16384")

if len(args) == 2:
	try: source_host, source_dataset_name = args[0].split(":",1)
	except ValueError: source_host, source_dataset_name = "localhost",args[0]
	try: destination_host, destination_dataset_name = args[1].split(":",1)
	except ValueError: destination_host, destination_dataset_name = "localhost",args[1]
else:
	parser.error("arguments are wrong")

if opts.ratelimit != -1:
    if opts.ratelimit < 1024:
        parser.error("rate limit (%s) must be higher than 1024 bytes per second"%opts.ratelimit)
    if not opts.progress:
        parser.error("to apply a rate limit, you must specify --progress too")

set_verbose(opts.verbose)

#===================== end configuration =================

# ================ start program algorithm ===================

src_conn = ZFSConnection(source_host, trust=opts.trust)
dst_conn = ZFSConnection(destination_host, trust=opts.trust)

verbose_stderr("Replicating dataset %s:%s into %s:%s..." % (
		source_host,source_dataset_name,
		destination_host,destination_dataset_name))

verbose_stderr("Assessing that the source dataset exists...")
try:
	source_dataset = src_conn.pools.lookup(source_dataset_name)
except KeyError:
	stderr("Error: the source dataset does not exist.  Backup cannot continue.")
	sys.exit(2)


verbose_stderr("Assessing that the destination dataset exists...")
try:
	destination_dataset = dst_conn.pools.lookup(destination_dataset_name)
except KeyError:
	stderr("Error: the destination dataset does not exist.  Backup cannot continue.")
	sys.exit(2)

operation_schedule = recursive_replicate(source_dataset,destination_dataset)
optimized_operation_schedule = optimize(operation_schedule)

send_opts = []
receive_opts = []
if opts.verbose:
	send_opts.append("-v")
	receive_opts.append("-v")

verbose_stderr("=================================")
for op,src,dst,srcs,dsts in optimized_operation_schedule:
        #assert 0, (op,src,dst,srcs,dsts)

        source_dataset_path = src.get_path()

        if dst:
            destination_dataset_path = dst.get_path()
        else:
            commonbase = os.path.commonprefix([src.get_path(),source_dataset.get_path()])
            remainder = src.get_path()[len(commonbase):]
            destination_dataset_path = destination_dataset.get_path() + remainder

        if op == "create_stub":
            verbose_stderr("Creating (%s) %s in destination" % (op,destination_dataset_path))
            if not opts.dryrun:
                dst_conn.create_dataset(destination_dataset_path)

        else:
            destination_snapshot_path = dsts.get_path()
            if srcs:
                this_send_opts = ["-I", "@" + srcs.name]
            else:
                this_send_opts = []
            if "recursive" in op:
                this_send_opts.append("-R")
            verbose_stderr("Replicating (%s) %s to %s" % (op,source_dataset_path,destination_dataset_path))
            verbose_stderr("Base snapshot available in destination: %s" % srcs)
            verbose_stderr("Target snapshot available in source:    %s" % dsts)

            if not opts.dryrun:
                src_conn.transfer(
                    dst_conn,
                    destination_snapshot_path,
                    destination_dataset_path,
                    showprogress=opts.progress,
                    compression=opts.force_compression,
                    ratelimit=opts.ratelimit,
                    bufsize=bufsize,
                    send_opts=send_opts+this_send_opts,
                    receive_opts=receive_opts
                )
        verbose_stderr("=================================")

verbose_stderr("Replication complete.")
