#!/usr/bin/perl
use warnings;
use strict;

use Net::OpenSSH;
use Data::Dump qw(dump);
use List::Util qw(first);

my $arh = Net::OpenSSH->new('root@10.60.0.204');
my $dev = Net::OpenSSH->new('root@10.60.0.202');

sub on {
	my ($ssh,$command) = @_;
	warn "## ", $ssh->get_host, "> $command\n";
	if ( $command =~ m/zfs list/ ) {
		map {
			chomp; $_;
		} $ssh->capture($command);
	} else {
		$ssh->capture($command);
	}
}

print on $arh => 'zpool status';
print on $dev => 'zpool status';

my @arh = on $arh => 'zfs list -H -o name';
my @dev = on $dev => 'zfs list -H -o name';

warn "# ",dump( \@arh, \@dev );

my $from_pool = $arh[0];
my $to_pool   = $dev[0];

sub snapshots_from {
	my ($ssh) = @_;
	my $host = $ssh->get_host;

	my $snapshot;

	my @snapshots = on $ssh => 'zfs list -H -t snapshot -o name';
	die $ssh->error if $ssh->error;
	foreach my $s (@snapshots) {
		my ($fs,$name) = split(/\@/,$s);
		push @{ $snapshot->{$fs} }, $name;
	}

	warn "snapshots_from $host ",dump($snapshot),$/;

	return $snapshot;
}

foreach my $fs ( @arh ) {

	my $name = $fs;
	$name =~ s{^$from_pool/}{} || next; # FIXME skip top-level fs
	warn "? $name";

	my $arh_snapshot = snapshots_from $arh;
	if ( ! exists( $arh_snapshot->{$fs} ) ) {

		my $snapshot = $fs . '@send';
		print on $arh => "zfs snapshot $snapshot";
		die $arh->error if $arh->error;
		$arh_snapshot = snapshots_from $arh;
	}

	my $max_snapshot = $#{ $arh_snapshot->{$fs} };
	warn "$max_snapshot snapshots of $fs on arh\n";

	my $to_dev = "$to_pool/$name";

	foreach my $i ( 0 .. $max_snapshot ) {
		my $snap = $arh_snapshot->{$fs}->[$i] || die "no snap";

		my $dev_snapshot = snapshots_from $dev;
		if ( exists $dev_snapshot->{$to_dev} ) {
			if ( first { /^\Q$snap\E$/ } @{ $dev_snapshot->{$to_dev} } ) {
				warn "+ $name exists\n";
				next;
			} else {
				warn "- $name missing\n";
			}
		} else {
			warn "$name not found on target yet";
		}

		my $snapshot;
		if ( $i == 0 ) {
			$snapshot = "$from_pool/$name\@$snap";
		} else {
			my $prev = $arh_snapshot->{$fs}->[$i-1] || die "no prev";
			$snapshot = "-i $from_pool/$name\@$prev $from_pool/$name\@$snap";
		}

		warn "zfs transfer $snapshot -> $to_dev";

		my $t = time();

		my $recv = "nc -w 5 -l -p 8888 | zfs receive $to_dev";
		warn ">> $recv\n";
		my ($rin1,$pid1) = $dev->pipe_in($recv);
		warn ">> pid: $pid1";

		my $send = "zfs send $snapshot | nc -q 0 -w 5 10.60.0.202 8888";
		warn "<< $send\n";
		$arh->system($send);

		$t = time() - $t;
		warn "took $t seconds to complete\n";

		$dev->system("zfs set readonly=on $to_pool/$name") if $i == 0;
		die $dev->error if $dev->error;

	}

}	
