Index: /trunk/tools/shuffle_otas.pl
===================================================================
--- /trunk/tools/shuffle_otas.pl	(revision 29378)
+++ /trunk/tools/shuffle_otas.pl	(revision 29378)
@@ -0,0 +1,146 @@
+#!/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 );
+foreach my $row (@{ $dr_get_ext_ids }) {
+    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 "Acceptible to move because ($max_ins_id,$current_ins_id,$current_vol_id,$forbidden_cab_id) != ($min_ins_id,$ok_vol_id,$ok_cab_id)\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
