#!/usr/bin/env perl

# Use mandatory external modules
use strict;
use Cwd;
use File::Basename;
use Data::Dumper;
use POSIX;
use Config;
use Getopt::Long;

# Determine installation dir name
our $SELF_DIR = dirname(dirname(Cwd::abs_path(__FILE__)));

# Include parts of the system
require $SELF_DIR . '/lib/config.pm';
require $SELF_DIR . '/lib/log.pm';

#-----------------------------------------------------------------
#Parse options

my $config_file = "mmm_lvm.conf";
my $host_name;
my $copy_method;
my $dest_dir;

GetOptions( "config=s"      => \$config_file,
            "host=s"        => \$host_name,
            "copy-method=s" => \$copy_method,
            "dest-dir=s"    => \$dest_dir);

#-----------------------------------------------------------------
# Read config file
our $config = ReadConfig($config_file);

# Directories info parsing
my @clone_dirs : shared = split(/\,/, $config->{clone_dirs});
$config->{clone_dirs} = \@clone_dirs;
$config->{dest_dir} = $dest_dir if ($dest_dir ne '');

# Get params
$copy_method = $config->{default_copy_method} unless ($copy_method);

# Check host name
my $host = $config->{host}->{$host_name};
PrintUsage() unless ($host);

# Check copy method
my $method = $config->{copy_method}->{$copy_method};
PrintUsage() unless ($method);

LogNotice("Host: '$host_name'");
LogNotice("Copy method: '$copy_method'");
LogNotice("Destination: '$config->{dest_dir}'");

LogNotice("-----------------------------------------------------------------");
# Check destination directory
my $res = CheckDestinationDirectory();
unless ($res =~ /^OK/) {
    LogError("Destination directory error: $res");
    exit(1);
}

LogNotice("-----------------------------------------------------------------");
# Check remote ssh connection
my $res = CheckRemoteSshConnection($host_name);
unless ($res =~ /^OK/) {
    LogError("SSH Check Error: $res");
    exit(1);
}

LogNotice("-----------------------------------------------------------------");
# Create and mount remote snapshot
$res = CreateRemoteSnapshot($host_name);
unless ($res =~ /^OK/) {
    LogError("Can't create snapshot: $res");
    exit(1);
}

LogNotice("-----------------------------------------------------------------");
# Perform copying
if ($config->{copy_method}->{$copy_method}->{single_run} eq 'yes') {
    $res = CopyFilesFromRemoteSingleRun($host_name, $copy_method, \@clone_dirs);
    unless ($res =~ /^OK/) {
        LogError("Can't copy files from remote host directories (single run): $res");
    }
} else {
    foreach my $dir (@clone_dirs) {
        $res = CopyFilesFromRemote($host_name, $copy_method, $dir);
        unless ($res =~ /^OK/) {
            LogError("Can't copy files from remote host directory $dir: $res");
        }
    }
}

# copy _mmm dir from remote
$res = CopyFilesFromRemote($host_name, 'scp', '_mmm');
unless ($res =~ /^OK/) {
    LogError("Can't copy files from remote host _mmm directory: $res");
}

# save copy method to dump
$res = SaveCopyMethod();
unless ($res =~ /^OK/) {
    LogError("Can't save current copy method to _mmm directory: $res");
}

LogNotice("-----------------------------------------------------------------");
# Perform cleanup
$res = RemoveRemoteSnapshot($host_name);
unless ($res =~ /^OK/) {
    LogWarning("WARNING!!! Can't delete remote snapshot: $res");
}

LogNotice("-----------------------------------------------------------------");
LogNotice("Backup done!\n\n\n");

exit(0);


#-----------------------------------------------------------------
sub PrintUsage() {
    print "Usage: $0 [--config <config file>] --host <host> [--copy-method <method>] [--dest-dir <dir>]\n";
    print "Where:\n";
    print "  Host = " . join('|', keys(%{$config->{host}})) . "\n";
    print "  Copy method = " . join('|', keys(%{$config->{copy_method}})) . " (default = $config->{default_copy_method})\n\n";
    exit(1);
}

#-----------------------------------------------------------------
sub SaveCopyMethod() {
    my $dir = $config->{dest_dir} . '/_mmm';

    # Check config option
    if (! -d $dir) {
        return "ERROR: No local _mmm directory in destdir!";
    }
    
    open(F, ">$dir/copy_method.txt") || return "ERROR: I/O Error while saving method info!";
    print F $copy_method;
    close(F);
    
    return "OK: Sopy method saved";
}


#-----------------------------------------------------------------
sub CheckDestinationDirectory() {
    my $dir = $config->{dest_dir};

    # Check config option
    if (!$dir) {
        return "ERROR: No local directory specified. Please review your config file and put 'dest_dir' option there!";
    }

    LogNotice("Checking destination directory '$dir'...");
    
    # Check dir
    system("mkdir -p $dir");
    unless (-d $dir && -x $dir && -r $dir && -w $dir) {
        return "ERROR: Destination dir '$dir' has invalid permissions (show be readable/writable)!";
    }
    
    #FIXME: need to check free space, etc here
    
    return "OK: Directory is ok";
}

#-----------------------------------------------------------------
sub CheckRemoteSshConnection($) {
    my $host_name = shift;
    my $host = $config->{host}->{$host_name};
    
    # Check ssh connection
    my $ssh_host = $host->{ssh_user} . '@' . $host->{ip};
    my $check_cmd = "ssh $ssh_host date";
    LogNotice("Verifying ssh connection to remote host '$ssh_host' (command: $check_cmd)...");
    $res = system($check_cmd);
    if ($res) {
        return "Can't execute remote commands on host $host_name($ssh_host): $!\n";
    }
    
    return "OK: SSH connection works fine!";
}


#-----------------------------------------------------------------
sub CreateRemoteSnapshot($) {
    my $host_name = shift;
    my $host = $config->{host}->{$host_name};
    my $ssh_host = $host->{ssh_user} . '@' . $host->{ip};
    my $mmm_dir = $host->{mmm_dir} || "/usr/local/mmm";
    
    # Create and mount snapshot on remote host
    LogDebug("Executing create_snapshot on remote server '$host_name'...");
    chomp($res = `ssh "$ssh_host" "$mmm_dir/bin/lvm/create_snapshot"`);
    print "$res\n";
    my @res_lines = split(/\n/, $res);
    my $last_line = pop(@res_lines);

    return $last_line;
}

#-----------------------------------------------------------------
sub CopyFilesFromRemote($$$) {
    my $host_name = shift;
    my $copy_method = shift;
    my $subdir = shift;
    
    my $host = $config->{host}->{$host_name};
    my $ssh_host = $host->{ssh_user} . '@' . $host->{ip};
    
    LogNotice("Copying files from remote host $host_name with method $copy_method from subdir $subdir of snapshot...");
    my $copy = $config->{copy_method}->{$copy_method};
    my $copy_cmd = $copy->{command};
    
    LogDebug("Performing template substitutions on command...");
    $copy_cmd =~ s/%SSH_USER%/$host->{ssh_user}/ig;
    $copy_cmd =~ s/%IP%/$host->{ip}/ig;
    $copy_cmd =~ s/%SNAPSHOT%/$host->{lvm_mount_dir}/ig;
    $copy_cmd =~ s/%DEST_DIR%/$config->{dest_dir}/ig;
    $copy_cmd =~ s/%CLONE_DIR%/$subdir/ig;
    LogDebug("Final command: '$copy_cmd'");
    
    my $res = system($copy_cmd);
    if ($res) {
        return "ERROR: Can't perform copy operation: $!";
    }

    return "OK: Copy is finished!";
}

#-----------------------------------------------------------------
sub CopyFilesFromRemoteSingleRun($$$) {
    my $host_name = shift;
    my $copy_method = shift;
    my $clone_dirs = shift;
    @clone_dirs = @$clone_dirs;
    
    my $host = $config->{host}->{$host_name};
    my $ssh_host = $host->{ssh_user} . '@' . $host->{ip};
    
    LogNotice("Copying files from remote host $host_name with method $copy_method from remote directories of snapshot...");
    my $copy = $config->{copy_method}->{$copy_method};
    my $copy_cmd = $copy->{command};
    
    LogDebug("Performing template substitutions on command...");

    $copy_cmd =~ s/%SSH_USER%/$host->{ssh_user}/ig;
    $copy_cmd =~ s/%IP%/$host->{ip}/ig;
    $copy_cmd =~ s/%SNAPSHOT%/$host->{lvm_mount_dir}/ig;
    $copy_cmd =~ s/%DEST_DIR%/$config->{dest_dir}/ig;

    if ($copy_cmd =~ /!(.*)!/) {
        my $sub_tmpl = $1;
        my $sub_cmd = "";
        for my $dir (@clone_dirs) {
            my $partial = $sub_tmpl;
            $partial =~ s/%CLONE_DIR%/$dir/ig;
            $sub_cmd .= " $partial";
        }
        
        $copy_cmd =~ s/!.*!/$sub_cmd/;
    }
    
    LogDebug("Final command: '$copy_cmd'");
    
    my $res = system($copy_cmd);
    if ($res) {
        system("rdiff-backup --check-destination-dir '$config->{dest_dir}'");
        return "ERROR: Can't perform single-run copy operation: $!";
    }

    return "OK: Copy is finished!";
}


#-----------------------------------------------------------------
sub RemoveRemoteSnapshot($) {
    my $host_name = shift;
    my $host = $config->{host}->{$host_name};
    my $ssh_host = $host->{ssh_user} . '@' . $host->{ip};
    my $mmm_dir = $host->{mmm_dir} || "/usr/local/mmm";
    
    # Umount and remove snapshot on remote host
    LogDebug("Executing remove_snapshot on remote server '$host_name'...");
    chomp($res = `ssh "$ssh_host" "$mmm_dir/bin/lvm/remove_snapshot"`);
    print "$res\n";
    my @res_lines = split(/\n/, $res);
    my $last_line = pop(@res_lines);

    return $last_line;
}
