#!/usr/bin/env perl

use Carp;
use warnings;
use strict;

# Step 1: Get hostname
use Sys::Hostname;
my $host = hostname();

use IPC::Cmd 0.36 qw( can_run run );
use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
use Pod::Usage qw( pod2usage );
use DBI;

my $missing_tools;
my $nebshift = can_run('neb-shift') or (warn "Can't find neb-shift" and $missing_tools = 1);
my ($destination,$so_id_start,$so_id_end,$limit,$dbname,$verbose,$no_pretend);
$limit = 10;
$so_id_start = 0;
$so_id_end = 0;
GetOptions(
    'host=s'         => \$host,
    'destination|d=s' => \$destination,
    'so_id_start=s'    => \$so_id_start,
    'so_id_end=s'      => \$so_id_end,
    'limit=s'        => \$limit,
    'dbname=s'       => \$dbname,
    'verbose'        => \$verbose,
    'no_pretend'     => \$no_pretend,
    ) or pod2usage ( 2 );
pod2usage( -msg =>
"USAGE: shuffle_otas.pl <options>
        Options:
           --host <host>          Host to take from
           --destination <host>   Host to dump to
           --limit <N>            Number to move attempt to move
           --so_id_start <SO_ID>  so_id to start with
           --so_id_end   <SO_ID>  so_id to end with
           --dbname <db>          Database name
           --verbose              Print status checks
           --no_pretend           Actually move files.\n", -exitval => 2) 

    unless (defined $destination and defined $dbname);


# Step 2: Convert hostname into vol_id and check for availability
# Make configurable
use constant DB_SOCKET => '/var/run/mysqld/mysqld.sock';
my $dbserver = 'ippdb00';
my $dbuser = 'ipp';
my $dbpass = 'ipp';
my $db = DBI->connect("DBI:mysql:database=${dbname};host=${dbserver};" .
		   "mysql_socket=" . DB_SOCKET(),
		   ${dbuser},${dbpass},
		   { RaiseError => 1, AutoCommit => 1}
    ) or die "Unable to connect to database $DBI::errstr\n";

# This should be a nebulous function
my $sth_get_vol_id = "SELECT vol_id,volume.name,cab_id FROM mountedvol JOIN volume USING(vol_id) WHERE mountedvol.available = 1 AND mountedvol.allocate = 1 AND mountedvol.host = '$host'";

my $dr_get_vol_id = $db->selectall_arrayref( $sth_get_vol_id );
if ($#{ $dr_get_vol_id } > 0) {
    die "Too many volumes returned\n";
}
my $vol_id = shift(@{ ${ $dr_get_vol_id }[0] });
my $vol_name = shift(@{ ${ $dr_get_vol_id }[0] });

$sth_get_vol_id = "SELECT vol_id,volume.name,cab_id FROM mountedvol JOIN volume USING(vol_id) WHERE mountedvol.available = 1 AND mountedvol.allocate = 1 AND mountedvol.host = '$destination'";

$dr_get_vol_id = $db->selectall_arrayref( $sth_get_vol_id );
if ($#{ $dr_get_vol_id } > 0) {
    die "Too many volumes returned\n";
}
shift(@{ ${ $dr_get_vol_id }[0] });
my $destination_name = shift(@{ ${ $dr_get_vol_id }[0] });
my $forbidden_cab_id = shift(@{ ${ $dr_get_vol_id }[0] });

if ($verbose) {
    print STDERR "Got vol_id $vol_id vol_name $vol_name cab_id $forbidden_cab_id\n";
}
# Step 3: Get a list of replicated items with one instance on this host
# select * from instance JOIN storage_object USING(so_id) JOIN storage_object_xattr USING(so_id) where vol_id = 26 AND name = 'user.copies' AND value >= 2 limit 10;
# Step 4: Identify which are primary and which are secondary copies ...
# select * FROM (select K.vol_id AS here,K.ins_id,K.so_id,ext_id,value,II,instance.vol_id AS there from (select V.*,MAX(instance.ins_id) AS II from (select vol_id,ins_id,so_id,ext_id,value from storage_object JOIN storage_object_xattr USING(so_id) JOIN instance USING(so_id) where vol_id = 26 AND name = 'user.copies' AND value >= 2 limit 100) AS V LEFT OUTER JOIN instance USING(so_id) GROUP BY so_id) AS K JOIN instance ON II = instance.ins_id GROUP BY so_id) AS T WHERE here = there;

my $sth_get_ext_ids = " SELECT * FROM ( ";
$sth_get_ext_ids .= " SELECT K.vol_id AS here,K.ins_id,K.so_id,ext_id,value, ";
$sth_get_ext_ids .= " MAXins_id,instance.vol_id AS there FROM ( ";
$sth_get_ext_ids .= " SELECT V.*,MAX(instance.ins_id) AS MAXins_id FROM ( ";
$sth_get_ext_ids .= " SELECT vol_id,ins_id,so_id,ext_id,value FROM ";
$sth_get_ext_ids .= " storage_object JOIN storage_object_xattr USING(so_id) ";
$sth_get_ext_ids .= " JOIN instance USING(so_id) ";
$sth_get_ext_ids .= "  WHERE vol_id = $vol_id AND name = 'user.copies' AND value >= 2 ";
if ($so_id_start > 0) {
    $sth_get_ext_ids .= " AND so_id > $so_id_start ";
}
if ($so_id_end > 0) {
    $sth_get_ext_ids .= " AND so_id < $so_id_end ";
}
if ($limit > 0) {
    $sth_get_ext_ids .= " limit $limit ";
}
$sth_get_ext_ids .= " ) AS V ";
$sth_get_ext_ids .= " LEFT OUTER JOIN instance USING(so_id) GROUP BY so_id ) AS K ";
$sth_get_ext_ids .= " JOIN instance on MAXins_id = instance.ins_id GROUP BY so_id ) AS T ";
$sth_get_ext_ids .= " where here = there ";

if ($verbose) {
    print STDERR "$sth_get_ext_ids\n";
}
my $dr_get_ext_ids = $db->selectall_arrayref( $sth_get_ext_ids );
my $counter = 0;
foreach my $row (@{ $dr_get_ext_ids }) {
    $counter++;
    my ($current_vol_id,$current_ins_id,$so_id,$ext_id,$count,
	$max_ins_id,$check_vol_id) = @{ $row };
    if (($current_vol_id != $check_vol_id)||($count < 2)) {
	warn "Skipping row for $ext_id due to impossible situation.";
    }
    if ($current_vol_id == $vol_id) {
	my $sth_get_excludes = "SELECT MINins_id,vol_id,cab_id FROM (SELECT MIN(ins_id) AS MINins_id FROM instance WHERE so_id = $so_id GROUP BY so_id) AS I JOIN instance ON ins_id = MINins_id JOIN volume USING(vol_id) WHERE cab_id != $forbidden_cab_id";
	my $dr_get_excludes = $db->selectall_arrayref( $sth_get_excludes );
	if ($#{ $dr_get_excludes } >= 0) {
	    my ($min_ins_id,$ok_vol_id,$ok_cab_id) = @{ ${ $dr_get_excludes }[0] };
	    
	    if ($ok_cab_id != $forbidden_cab_id) {
		if ($verbose) {
		    print STDERR "Acceptable to move because ($max_ins_id,$current_ins_id,$current_vol_id,$forbidden_cab_id) != ($min_ins_id,$ok_vol_id,$ok_cab_id) Count: $counter\n";
		}
		my $cmd = "$nebshift --volume $destination_name $ext_id $vol_name";
		print "$cmd\n";
		if ($no_pretend) {
		    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
			run(command => $cmd, verbose => $verbose);
		    unless ($success) {
			$error_code = (($error_code >> 8) or 4);
			die("Unable to perform nebshift: $error_code $ext_id\n");
		    }
		}		    
	    }
	}	
    }
}


# Step 5: Call neb-shift to move file around.
# neb-shift --volume ipp00[5-7] $ext_id $hostname
