#!/usr/bin/perl
#
# Copyright (c) 2006 Zmanda Inc.  All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Contact information: Zmanda Inc, 505 N Mathlida Ave, Suite 120
# Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
#
#


use strict;
use warnings;
use File::Find;
use File::Basename;
use File::Path;
use File::Spec::Functions;
use POSIX qw(strftime);
use lib "/usr/local/lib/mysql-zrm";
use ZRM::Common;
use ZRM::MySQL;


#usage strings
my $USAGE_MANAGE_BACKUP_OPTIONS_STRING=
		"\t\t--convert-to-regular|--mount-snapshots|--remove-backup\n\t\t[--source-directory <source-directory>]\n";

my @MANAGE_OPT=qw/source-directory=s
		  dont-remove-index
                  remove-backup
		  convert-to-regular
		  mount-snapshots
                  /;
my %mountDetails;
my $parsedMountDetails = 0;
my $host;
my $plugin;
my $supportsRegularSnapshots = 1;

sub isRegularBackupsSupported()
{
        if( !defined $plugin ){
                return;
        }
        my $cmd = $plugin;
        $cmd .= " --action supports-regular";
        $cmd .= " 2>$LOGGER";
        if( $verbose ){
                &printLog( "checking if snapshot plugin supports regular snapshoting using command $cmd\n" );
        }
        my $out = &execCmdAndGetOutput( $cmd, "NO ERROR" );
        if( defined $out && $out =~ /^no\n$/ ){
                $supportsRegularSnapshots = 0;
                if( $inputs{"backup-type"} ne "quick" ){
                        &printAndDie( $plugin." supports only backup-type quick\n" );
                }
        }
}

# $_[0] file name
sub doParseMountDetails()
{
	if( $parsedMountDetails == 0 ){
		&parseGeneralOptionFile( $_[0], \%mountDetails );
		if( $backupset ne $mountDetails{"backup-set"} ){
			&printAndDie( "Backup set specified is wrong\n" );
		}
		delete $mountDetails{"backup-set"};
		$host = $mountDetails{"host"};
		delete $mountDetails{"host"};
		$plugin = $mountDetails{"snapshot-plugin"};
		delete $mountDetails{"snapshot-plugin"};
		$parsedMountDetails = 1;
		&isRegularBackupsSupported();
	}
}

# $_[0] backup directory
# $_[1] file containing mount details
sub removeSnapshots()
{
	&doParseMountDetails( $_[1] );
	&doRemoveInternalLinks($_[0], $host);
	foreach( keys%mountDetails ){
		my $tmpDir = $_;
                #umount
                my $umnt = $plugin;
                $umnt .= " --action umount --directory $tmpDir";
                if( $host ne "localhost" ){
                        $umnt .= " --host ".$host;
                }
                if( $verbose ) {
                        &printLog( "Unmounting\n" );
                        &printLog( $umnt."\n" );
                }
		$ENV{'ZRM_NO_ERROR'} = 1;
                my $r = system($umnt);
		delete $ENV{'ZRM_NO_ERROR'};
                if( $r > 0 ){
			if( $verbose == 1 ){
                        	&printLog( "Umount failed. Command used is ".$umnt."\n" );
			}
                }

		my $snp = $mountDetails{$_};
		my @a = split /;/, $snp;
		$snp = $a[0];
                my $rmcommand = $plugin;
                $rmcommand .= " --action remove-snapshot";
                if( $host ne "localhost" ){
                        $rmcommand .= " --host ".$host;
                }
                $rmcommand .= " --dev \"$snp\" --directory \"$tmpDir\" > $LOGGER 2>&1";
                if( $verbose ){
                        &printLog( "$rmcommand\n" );
                }
                $r = system( $rmcommand );
                if( $r > 0  ){
                        &printCommandOutputToLog( "ERROR", "remove-snapshot", $LOGGER );
                        &printAndDie( "remove snapshot failed. Command used is ".$rmcommand."\n" );

                }
	}
	&doRemoveMountDir($_[0], $host);
}

# $_[0] log file name
sub printFromOutputLog()
{
	unless( open( PLOG, $_[0] ) ){
		&printError( "Unable to open log file $_[0]\n" );
	}
	my @l = <PLOG>;
	close PLOG;
	foreach( @l ){
		print $_;
	}
}

sub mountAll()
{
	my $msgType = "INFO";
	if( $supportsRegularSnapshots == 0 ){
		$msgType = "ERROR";
	}
        foreach( keys%mountDetails ){
                my $tmpDir = $_;
                my $snp = $mountDetails{$_};
                my @a = split /;/, $snp;
                $snp = $a[0];
                my $fs = $a[1];
                my $command = $plugin;
                $command .= " --action mount";
                $command .= " --dev $snp";
                $command .= " --directory $tmpDir";
                $command .= " --fstype $fs";
                if( $host ne "localhost" ){
                        $command .= " --host ".$host;
                }
                $command .= " > $LOGGER 2>&1";
                if( $verbose ) {
                        &printLog( "Mounting snapshot\n" );
                        &printLog( $command."\n" );
                }
		$ENV{'ZRM_NO_ERROR'} = 1;
                my $r = system( $command );
		delete $ENV{'ZRM_NO_ERROR'};
                if( $r ) {
                        &printCommandOutputToLog( $msgType, "mount", $LOGGER );
                }elsif( $supportsRegularSnapshots == 0 ){
                        &printFromOutputLog( $LOGGER );
		}
        }
}

#$_[0] trgtDir
#$_[1] type of dir
#$_[2] host
sub removeOnRemote()
{
        my $destinationDirectory = $_[0];
        my @params;
        push @params, "remove-backup-data";
        push @params, "--backup-dir";
        push @params, $destinationDirectory;
        push @params, "--type-of-dir";
        push @params, $_[1];
        push @params, "--host";
        push @params, $_[2];
        my $cmd = $inputs{"copy-plugin"};
        my $r = system( $cmd, @params );
        if( $r != 0 ){
		&printAndDie( "Unable to remove snapshot data\n" );
        }
        return 0;
}

#$_[0] backup directory
#$_[1] host
sub doRemoveInternalLinks()
{
        my $dir = $_[0];
        if( $_[1] ne "localhost" && $supportsRegularSnapshots != 0 ){
                return &removeOnRemote( $dir, "LINKS", $_[1] );
        }
        $dir = catfile( $dir, $LINK_POINT );
        rmtree( $dir, 0, 0 );
}

#$_[0] backup directory
#$_[1] host
sub doRemoveMountDir()
{
        my $dir = $_[0];
        if( $_[1] ne "localhost" && $supportsRegularSnapshots != 0 ){
                return &removeOnRemote(  $dir, "MOUNTS", $_[1] );
        }
        $dir = catfile( $dir, $MOUNT_POINT );
        rmtree( $dir, 0, 0 );
}


# $_[0] backup directory to remove
sub removeBackupDirectory()
{
	my $file = $_[0]."/".$ZRM_MOUNT_DETAILS;
	if( -f $file ){
		&removeSnapshots( $_[0], $file );
	}
	unlink( $file );
	if( ! defined $inputs{"dont-remove-index"} ){
		rmtree( $_[0], 0, 0 );
	}
}

sub doCopyFromLinksUsingTar()
{
        my $srcDir = catfile( $inputs{"source-directory"}, $LINK_POINT );
        my $destDir = catfile( $inputs{"source-directory"} );
        my $srcFile = ".";
        return &copyUsingTar( $srcDir, $srcFile, $destDir );
}

#Returns total size of backed up data;
#Also calculates the md5 checksum of each;
sub totalSize()
{
        my $total_size = 0;
        my $dir = $_[0];
        find( {wanted => sub
                {
                        if( $_ ne "index" ){
                                $total_size += -s $_;
                        }
                },
                follow => 1}, $_[0]);
        return $total_size;
}

sub doMountSnapshots()
{
	my $file = $_[0]."/".$ZRM_MOUNT_DETAILS;
	if( ! -f $file ){
		&printAndDie( "Backup contained in $inputs{'source-directory'} is not a quick backup\n" );
		return;
	}
	&parseIndexFile( $inputs{"source-directory"}."/index" );
	&doParseMountDetails( $file );
	if( $supportsRegularSnapshots == 0 && defined $inputs{"host"} ){
		$host = $inputs{"host"};
	}
	if( ! defined $inputs{"host"} ){
		$inputs{"host"} = "localhost";
	}
	if( $supportsRegularSnapshots == 1 && $host ne $inputs{"host"} ){
		&printAndDie( "host specified does not match with host details in the backup\n" );
	}
	&mountAll();
}

#format as HH:MM:SS given time in seconds
#if time is more than 86400 it will be formated as dd:HH:MM:SS
#$_[0] time in seconds
sub getFormatedTime()
{
	my $fmt = "%H:%M:%S";
	if( $_[0] >= 86400 ){
		$fmt = "%d:%H:%M:%S";
	}
	return strftime($fmt, $_[0], 0, 0, 0, 0, 0 );
}


sub doConvertBackup()
{
	my $file = $_[0]."/".$ZRM_MOUNT_DETAILS;
	if( ! -f $file ){
		&printAndDie( "Backup contained in $inputs{'source-directory'} is not a quick backup\n" );
		return;
	}
	&parseIndexFile( $inputs{"source-directory"}."/index" );
	&doParseMountDetails( $file );
	if( $supportsRegularSnapshots == 0 ){
		&printAndDie( "Snapshot plugin $plugin does not support converting quick backups to regular\n" );
		return;
	}
	if( ! defined $inputs{"host"} ){
		$inputs{"host"} = "localhost";
	}
	if( $host ne $inputs{"host"} ){
		&printAndDie( "host specified does not match with host details in the backup\n" );
	}
	&mountAll();
	if( $verbose == 1 ){
		&printLog( "Copying data to backup directory ".$inputs{"source-directory"} );
	}
	&doCopyFromLinksUsingTar();
	&removeBackupDirectory( $inputs{"source-directory"} );
	unlink( $inputs{"source-directory"}."/.nochecksum" );
	my $totSize = &totalSize( $inputs{"source-directory"} );
        my $SUF=" MB";
        $totSize = $totSize/1048576;
        if( $totSize > 1000 ){
                $totSize = $totSize/1024;
                $SUF=" GB";
        }
        $totSize = sprintf( "%0.2f", $totSize );
	$indexdata{"backup-size"}=$totSize.$SUF;
	if( (defined $inputs{"compress"} || defined $inputs{"encrypt"} ) ){
		$inputs{"destination"} = $inputs{"source-directory"};
                my $cet = time();
                my $r = &doCompressAndEncrypt();
                if( $r == 1 ){
                        &removeUncompressedBackup( $inputs{"source-directory"}, \%inputs );
                }
                my $cet1 = time() - $cet;
                $cet1 = &getFormatedTime( $cet1 );
                $indexdata{"compress-encrypt-time"}=$cet1;
        }
	$indexdata{"backup-type"}="converted";
	&writeIndexFile();
	open( T, ">".$inputs{"source-directory"}."/.checksum_pending" );
	close T;
	system( "/usr/local/bin/mysql-zrm-verify-backup --create-checksum --source-directory $inputs{'source-directory'} --backup-set $backupset 2>/dev/null" );
}

sub writeIndexFile()
{
	unless( open( TP, ">".$inputs{"source-directory"}."/index" ) ){
		&printAndDie( "Unable to modify index file\n" );
	}
	foreach( keys%indexdata ){
		print TP "$_=$indexdata{$_}\n";
	}
	close TP;
}

# $_[0] string to be put in index file
sub printIndex()
{
	my $s =  $_[0];
	my $r = "";
	my $v = "";
	if( $s =~ /(\S*)=(\S*)\n/ ){
        	$r = $1;
        	$v = $2;
	}
	$indexdata{$r} = $v;
}

#Sets up defaults
sub setupDefaults()
{
        $action = "manage-backup";
	$indexRoutine = \&printIndex;
        $USAGE_STRING = $USAGE_MANAGE_BACKUP_OPTIONS_STRING.$USAGE_COMMON_OPTIONS_STRING;
}

sub main()
{
        &setupDefaults();
        &initCommon(@MANAGE_OPT);
	&verifyInputs();
	&createConfigFile();
	if( ! defined $inputs{"source-directory"} ){
		&printAndDie( "Please specify --source-directory\n" );
	}
	if( ! defined $inputs{"remove-backup"} && ! defined $inputs{"convert-to-regular"} && ! defined $inputs{"mount-snapshots"} ){
		&printAndDie( "Please specify any one of --remove-backup or --convert-to-regular or --mount-snapshots\n" );
	}
	if( defined $inputs{"mount-snapshots"} ){
		&doMountSnapshots( $inputs{"source-directory"} );
	}elsif( defined $inputs{"convert-to-regular"} ){
		$inputs{"dont-remove-index"} = 1;
		&doConvertBackup( $inputs{"source-directory"} );
	}elsif( defined $inputs{"remove-backup"} ){
		&removeBackupDirectory( $inputs{"source-directory"} );
	}
        &my_exit();
}

&main();


