Index: /trunk/tools/eam/rebalance/md5clean.mana
===================================================================
--- /trunk/tools/eam/rebalance/md5clean.mana	(revision 41383)
+++ /trunk/tools/eam/rebalance/md5clean.mana	(revision 41383)
@@ -0,0 +1,57 @@
+#!/usr/bin/env mana
+
+# USAGE: rebalance.mana (source) (target) (Ngoal)
+# source : partition name (e.g., /data/ipp070.0/nebulous)
+# target : partition name (e.g., /data/ipp133.0/rebalance/ipp070.0/nebulous)
+# Ngoal  : disk fraction target 
+
+$EXEC = exec
+
+# names are defined for a single src & tgt pair
+macro run.md5clean
+  if ($0 != 3)
+    echo "USAGE: run.md5clean (srchost) (srcvol)"
+    echo "srchost : e.g., ipp070"
+    echo "srcvol  : e.g., 0 (for /data/ipp070.0/nebulous)"
+    break
+  end
+
+  local srchost srcvol
+
+  $srchost = $1
+  $srcvol = $2
+
+  $source = /data/$srchost.$srcvol/nebulous
+
+  $myhost = `hostname`
+  if ("$myhost" != "$srchost")
+    echo "please run on $srchost"
+    break
+  end
+
+  for i 0 0x100
+    sprintf srcdir "%s/%02x" $source $i
+    mkdir $srcdir/md5sums
+
+    list files -glob $srcdir/*.OTA_md5sums
+    if ($files:n == 0) 
+     echo "no md5sum files in $srcdir, skipping"
+     next
+    end
+
+     echo mv $srcdir/*.OTA_md5sums $srcdir/md5sums/
+    $EXEC mv $srcdir/*.OTA_md5sums $srcdir/md5sums/
+  end
+end
+
+if ($SCRIPT) 
+  if ($argv:n != 2) 
+    echo "USAGE: md5clean.mana (srchost) (srcvol)"
+    echo "srchost : e.g., ipp070"
+    echo "srcvol  : e.g., 0 (for /data/ipp070.0/nebulous)"
+    exit 2
+  end
+
+  run.md5clean $argv:0 $argv:1
+  exit 0
+end
Index: /trunk/tools/eam/rebalance/neb_check_reinsert.pl
===================================================================
--- /trunk/tools/eam/rebalance/neb_check_reinsert.pl	(revision 41383)
+++ /trunk/tools/eam/rebalance/neb_check_reinsert.pl	(revision 41383)
@@ -0,0 +1,192 @@
+#! /usr/bin/env perl
+
+# TESTRUN can be 1, 2, 3, 4 to get further in the tests
+my $TESTRUN = 2;
+
+use DBI;
+use Carp;
+use Pod::Usage qw( pod2usage );
+
+use strict;
+use warnings FATAL => qw( all );
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+
+my ($tgthost,$tgtvol,$subdir,$movelist);
+
+GetOptions(
+    'tgthost=s' => \$tgthost,
+    'tgtvol=s'  => \$tgtvol,
+    'subdir=s'  => \$subdir,
+    'list=s'    => \$movelist) || pod2usage(2);
+
+my ( $dbname, $dbhost, $dbport, $dbpass, $dbuser );
+my $conffile = '/data/ippc19.0/home/eugene/rebalance.20200605/nebulous.config';
+
+if (-e $conffile) {
+#   printf STDERR "found config\n";
+    open(IN,$conffile);
+    while(<IN>) {
+        chomp;
+        if ($_ =~ /NEB_NAME /) { $dbname = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_HOST /) { $dbhost = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_USER /) { $dbuser = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_PASS /) { $dbpass = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_PORT /) { $dbport = (split /\s+/,$_)[2]; }
+    }
+    close(IN);
+    print STDERR  "$dbname $dbhost $dbport $dbuser $dbpass\n";
+}
+
+pod2usage( -msg => "Cannot configure nebulous database", -exitval => 2) unless
+    defined $dbname && defined $dbhost && defined $dbport && defined $dbuser && defined $dbpass;
+
+pod2usage( -msg => "Cannot determine target machine and volume", -exitval => 2) unless
+    defined $tgthost && defined $tgtvol;
+
+pod2usage( -msg => "Cannot determine subdir or list of files which have been moved", -exitval => 2) unless
+    defined $movelist && defined $subdir;
+
+my $host_check = `hostname`; chomp($host_check);
+
+if ($host_check ne $tgthost) { die "Required to run on $tgthost, not on $host_check\n"; }
+
+my $time = time();
+
+my $dsn;
+if ($dbport eq "NONE") {
+    $dsn = "DBI:mysql:database=$dbname:host=$dbhost";
+} else {
+    $dsn = "DBI:mysql:database=$dbname:host=$dbhost:port=$dbport";
+}
+
+my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { RaiseError => 1, AutoCommit => 1} ) or die "Unable to connect to database $DBI::errstr\n";
+
+# example:
+# tgthost : ipp130
+# tgtvol  : 0 
+
+# Get the vol_id of the (local) destination.
+
+my $tgt_volume = "${tgthost}.${tgtvol}";
+my $tgt_subdir = "/export/${tgthost}.${tgtvol}/nebulous/${subdir}";
+
+my $sql_get_volume_id = "SELECT vol_id, name, host, path FROM volume WHERE name = ?";
+my $sql_update_instance = "UPDATE instance SET vol_id = ?, uri = ? WHERE ins_id = ?";
+
+my $query = $dbh->prepare( $sql_get_volume_id );
+
+$query->execute( $tgt_volume );
+my ($tgt_vol_id, $tgt_vol_name, $tgt_vol_host, $tgt_vol_path) = $query->fetchrow_array;
+$query->finish;
+
+print "LOG INIT $tgthost $tgtvol $tgt_vol_id\n";
+
+# load the list of files from movelist
+
+open (MOVELIST, $movelist) or die "cannot open list of files $movelist\n";
+my @movefiles = <MOVELIST>;
+close (MOVELIST);
+
+my $otime = $time;
+$time = time();
+my $dtime = $time - $otime;
+
+for (my $i = 0; $i < @movefiles; $i++) {
+
+    my $movefile = $movefiles[$i];
+    chomp $movefile;
+    
+    # the filename should have the following form:
+    # NN/DDDDDDDDDD.WWW:WWW:WWWW:WWW:
+    # NN : 2-hexdigit name of the subsubdir
+    # DDDDD : instance ID (long)
+    # WWW:WWW:WWW : elements of the filename, with : replacing / in the neb storage_object name
+
+    # I want to check two things:
+    # 1) does the file exist at /data/$tgthost.$tgtvol/nebulous/$subdir/$movefile
+    # 2) does the vol_id of the instance_id match our vol_id ?
+
+    unless (-e "$tgt_subdir/$movefile") {
+	print "MISSING FILE: $tgt_subdir/$movefile\n";
+	next;
+    }
+
+    # print "movefile: $movefile\n";
+
+    my ($ins_id) = $movefile =~ m|[0-9a-fA-F][0-9a-fA-F]/(\d+)\..*|;
+    my $summary = "$tgt_subdir/$movefile";
+
+    if ( ! defined ($ins_id)) {
+	print "ERROR : INVALID ID: $summary\n";
+	next;
+    }
+
+    if ( $ins_id =~ m/\D/ ) {
+	print "ERROR : INVALID ID: $summary\n";
+	next;
+    }
+    $summary = "$summary -> $ins_id";
+    
+    # check that this instance exists in DB (if not, need to add it)
+    my $vol_id_query = "SELECT vol_id FROM instance WHERE ins_id = ${ins_id}";
+    
+    my $rowref = $dbh->selectall_arrayref( $vol_id_query );
+    my @array = @$rowref;
+    my $Nrows = @array;
+
+    if ($Nrows == 0) { # We should find this instance
+	print "ERROR : NO DB ENTRY: $summary\n";
+	next;
+    }
+
+    if ($Nrows > 1) { # We should only find one instance
+	print "ERROR : TOO MANY INSTANCE ENTRIES: $summary\n";
+	next;
+    }
+
+    my $valref = $array[0];
+    my @values = @$valref;
+    
+    my $Nvalue = @values;
+
+    if ($Nvalue != 1) {
+	print "ERROR : INVALID DB RESPONSE: $summary\n";
+	next;
+    }
+
+    my $vol_id_actual = $values[0];
+
+    ## print "NROWS: $Nrows\n";
+    ## foreach my $valref (@array) {
+    ## 	my @values = @$valref;
+    ## 	foreach my $value (@values) {
+    ## 	    print "VAL: $value, ";
+    ## 	}
+    ## 	print "\n";
+    ## }
+
+    if ($vol_id_actual != $tgt_vol_id) {
+	print "ERROR : INVALID VOL_ID: $summary\n";
+	next;
+    }
+
+    print "PASSED INSTANCE CHECK: $summary\n";
+
+} # END loop over movefiles
+
+# example of using the return value of arrayref:
+
+# my $rowref = $dbh->selectall_arrayref( $validate_ins_id_query );
+# my @array = @$rowref;
+# my $Nrows = @array;
+
+# print "NROWS: $Nrows\n";
+
+# foreach my $valref (@array) {
+#     my @values = @$valref;
+#     foreach my $value (@values) {
+#         print "VAL: $value, ";
+#     }
+#     print "\n";
+# }
+
Index: /trunk/tools/eam/rebalance/neb_reinsert.pl
===================================================================
--- /trunk/tools/eam/rebalance/neb_reinsert.pl	(revision 41383)
+++ /trunk/tools/eam/rebalance/neb_reinsert.pl	(revision 41383)
@@ -0,0 +1,222 @@
+#! /usr/bin/env perl
+
+# TESTRUN can be
+#   0 : NOT A TEST RUN : do the full jobs
+#   1 : just print the move commands
+#   2 : just do one file
+#   3 : just do one subsubdir
+my $TESTRUN = 0;
+
+use DBI;
+use Carp;
+use Pod::Usage qw( pod2usage );
+
+use strict;
+use warnings FATAL => qw( all );
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+
+# XXX change name "host" to something like tgt?
+my ($tgthost,$tgtvol,$srchost,$srcvol,$dir,$holding);
+
+GetOptions(
+    'tgthost=s' => \$tgthost,
+    'tgtvol=s'  => \$tgtvol,
+
+    'srchost=s' => \$srchost,
+    'srcvol=s'  => \$srcvol,
+    'dir=s'     => \$dir,
+    'holding=s' => \$holding) || pod2usage(2 );
+
+my ( $dbname, $dbhost, $dbport, $dbpass, $dbuser );
+my $conffile = '/data/ippc19.0/home/eugene/rebalance.20200605/nebulous.config';
+
+if (-e $conffile) {
+#   printf STDERR "found config\n";
+    open(IN,$conffile);
+    while(<IN>) {
+        chomp;
+        if ($_ =~ /NEB_NAME /) { $dbname = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_HOST /) { $dbhost = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_USER /) { $dbuser = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_PASS /) { $dbpass = (split /\s+/,$_)[2]; }
+        if ($_ =~ /NEB_PORT /) { $dbport = (split /\s+/,$_)[2]; }
+    }
+    close(IN);
+    print STDERR  "$dbname $dbhost $dbport $dbuser $dbpass\n";
+}
+
+pod2usage( -msg => "Cannot configure nebulous database", -exitval => 2) unless
+    defined $dbname && defined $dbhost && defined $dbport && defined $dbuser && defined $dbpass;
+
+pod2usage( -msg => "Cannot determine target machine and volume", -exitval => 2) unless
+    defined $tgthost && defined $tgtvol;
+
+pod2usage( -msg => "Cannot determine source machine and volume", -exitval => 2) unless
+    defined $srchost && defined $srcvol;
+
+pod2usage( -msg => "Cannot determine target directory or holding", -exitval => 2) unless
+    defined $dir && defined $holding;
+
+my $host_check = `hostname`; chomp($host_check);
+
+if ($host_check ne $tgthost) { die "ERROR : Required to run on $tgthost, not on $host_check\n"; }
+
+my $time = time();
+
+my $dsn;
+if ($dbport eq "NONE") {
+    $dsn = "DBI:mysql:database=$dbname:host=$dbhost";
+} else {
+    $dsn = "DBI:mysql:database=$dbname:host=$dbhost:port=$dbport";
+}
+
+my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { RaiseError => 1, AutoCommit => 1} ) or die "ERROR : Unable to connect to database $DBI::errstr\n";
+
+# example:
+# srcpath : /data/ippb01.0/nebulous/00
+# tmppath : /data/ippb22.0/rebalance/ippb01.0/nebulous/00
+# tgtpath : /data/ippb22.0/nebulous/00
+
+my $tmppath = "/export/${tgthost}.${tgtvol}/$holding/${srchost}.${srcvol}/nebulous/${dir}";
+
+my @subdirectories = <$tmppath/*>;
+
+# Get the vol_id of the (local) destination.
+
+my $tgt_volume = "${tgthost}.${tgtvol}";
+my $src_volume = "${srchost}.${srcvol}";
+
+my $sql_get_volume_id = "SELECT vol_id, name, host, path FROM volume WHERE name = ?";
+my $sql_update_instance = "UPDATE instance SET vol_id = ?, uri = ? WHERE ins_id = ?";
+
+my $query = $dbh->prepare( $sql_get_volume_id );
+
+$query->execute( $tgt_volume );
+my ($tgt_vol_id, $tgt_vol_name, $tgt_vol_host, $tgt_vol_path) = $query->fetchrow_array;
+$query->finish;
+
+$query->execute( $src_volume );
+my ($src_vol_id, $src_vol_name, $src_vol_host, $src_vol_path) = $query->fetchrow_array;
+$query->finish;
+
+print "LOG INIT $tgthost $tgtvol $srchost $srcvol $dir $tgt_vol_id $src_vol_id\n";
+
+foreach my $tmp_subdir (@subdirectories) {
+
+    my $tgt_subdir = $tmp_subdir;
+    $tgt_subdir =~ s%/$holding/${srchost}.${srcvol}/nebulous%/nebulous%;
+
+    my @files = <${tmp_subdir}/*>;
+    my $otime = $time;
+
+    $time = time();
+    my $dtime = $time - $otime;
+
+    print "LOG BEGIN $tmp_subdir $time $dtime\n";
+    for (my $i = 0; $i <= $#files; $i++) {
+#   for (my $i = 0; $i < 10; $i++) {
+
+        my $tmp_file = $files[$i];
+        my ($ins_id);
+        $ins_id = $tmp_file;
+        $ins_id =~ s/^$tmp_subdir\///;
+        $ins_id =~ s/\..*//;
+
+        # Write log of what we plan to do.
+        my $tgt_file = $tmp_file;
+        $tgt_file =~ s%/$holding/${srchost}.${srcvol}/nebulous%/nebulous%;
+
+        my $db_uri = $tgt_file;
+        $db_uri =~ s%^/export/%/data/%;
+        $db_uri = 'file://' . $db_uri;
+
+        my $summary = "$ins_id $tmp_file $tgt_file $db_uri";
+
+        # Do move of file.
+
+        # check if file exists at new location:
+        if (-e $tgt_file) {
+	    if (! -l $tmp_file) { die "ERROR : INCONSISTENT STATE: TGT FILE $tgt_file exists, but $tmp_file is not a link"; }
+
+	    my $linkvalue = readlink $tmp_file;
+	    # print "LINK: $linkvalue\n";
+
+	    if ($linkvalue ne $tgt_file) { die "ERROR : INCONSISTENT STATE: TMP LINK $tmp_file does not match TGT FILE $tgt_file"; }
+
+            print "FILE ALREADY FOUND: $summary\n";
+            next;
+	    # if file is already found, check the instance?
+        }
+
+	# if TGT FILE does not exist, TMP FILE should NOT be a link:
+	if (-l $tmp_file) { die "ERROR : INCONSISTENT STATE: TGT FILE $tgt_file does not exist, but $tmp_file is a link"; }
+
+        # check that this instance exists in DB (if not, need to add it)
+        my $validate_ins_id_query = "SELECT ins_id FROM instance WHERE vol_id = ${src_vol_id} AND ins_id = ${ins_id}";
+        # print "SQL: $validate_ins_id_query\n";
+        if ( $ins_id =~ m/\D/ ) {
+            print "ERROR : INVALID ID: $summary\n";
+            next;
+        }
+
+        my $rowref = $dbh->selectall_arrayref( $validate_ins_id_query );
+        my @array = @$rowref;
+        my $Nrows = @array;
+
+        if ($Nrows == 0) { # We expect one instance on the source
+            print "ERROR : NO DB ENTRY: $summary\n";
+            next;
+        }
+
+        # print "PASSED DB CHECK: $#instances\n";
+
+        # XXX SKIP actual ops for now
+        if ($TESTRUN && ($TESTRUN < 2)) {
+            print "mv $tmp_file $tgt_file\n";
+            print "ln -s $tgt_file $tmp_file\n";
+            next;
+        }
+
+	# move the file to the TGT location
+        system("mv $tmp_file $tgt_file");
+        unless (-e $tgt_file) { die "ERROR : problem moving file $tmp_file to $tgt_file\n $summary"; }
+
+	# link the TGT file to the TMP location
+        system("ln -s $tgt_file $tmp_file");
+        unless (-e $tmp_file && -l $tmp_file) { die "ERROR : problem linking file $tmp_file to $tgt_file\n $summary"; }
+
+	## XXX skip db changes for now (need to revert the moves above in this case)
+	## print "SKIP db changes\n";
+	## next;
+
+        # Register database change:
+        $query = $dbh->prepare( $sql_update_instance );
+        $query->execute( $tgt_vol_id, $db_uri, $ins_id );
+        $query->finish;
+        print "GOOD: $summary\n";
+
+        if ($TESTRUN && ($TESTRUN < 3)) { die; } # die after one file
+    } # END loop over files
+    rmdir($tmp_subdir);
+
+    if ($TESTRUN && ($TESTRUN < 4)) { die; } # die after one subdir
+} # END loop over subdirectories
+
+rmdir($tmppath);
+
+# example of using the return value of arrayref:
+
+# my $rowref = $dbh->selectall_arrayref( $validate_ins_id_query );
+# my @array = @$rowref;
+# my $Nrows = @array;
+
+# print "NROWS: $Nrows\n";
+
+# foreach my $valref (@array) {
+#     my @values = @$valref;
+#     foreach my $value (@values) {
+#         print "VAL: $value, ";
+#     }
+#     print "\n";
+# }
+
Index: /trunk/tools/eam/rebalance/rebalance.mana
===================================================================
--- /trunk/tools/eam/rebalance/rebalance.mana	(revision 41383)
+++ /trunk/tools/eam/rebalance/rebalance.mana	(revision 41383)
@@ -0,0 +1,376 @@
+#!/usr/bin/env mana
+
+# USAGE: rebalance.mana (source) (target) (Ngoal)
+# source : partition name (e.g., /data/ipp070.0/nebulous)
+# target : partition name (e.g., /data/ipp133.0/rebalance/ipp070.0/nebulous)
+# Ngoal  : disk fraction target 
+
+$EXEC = exec
+
+$BIGSIZE = 30M
+$TB_BYTE = 1024^4
+$VERSION = NONE
+
+$NSUBDIR = 1
+# $NSUBDIR = 0x100
+
+macro run.find
+  if ($0 != 7)
+    echo "USAGE: run.find (srchost) (srcvol) (tgthost) (tgtvol) (VERSION) Fraction)"
+    echo "srchost, tgthost : e.g., ipp070"
+    echo "srcvol, tgtvol   : e.g., 0 (for /data/ipp070.0/nebulous)"
+    echo "Fraction : disk fraction target"
+    break
+  end
+
+  local srchost srcvol tgthost tgtvol Fraction
+
+  $srchost = $1
+  $srcvol = $2
+  $tgthost = $3
+  $tgtvol = $4
+  $VERSION = $5
+  $Fraction = $6
+
+  # generate the full paths:
+  $source = /data/$srchost.$srcvol/nebulous
+  $target = /data/$tgthost.$tgtvol/rebalance/$srchost.$srcvol/nebulous
+
+  ## make sure target exists
+  mkdir $target
+
+  ## this script must be run on the same machine as the source
+  ## partitions
+
+  $result = `df -l $source | tail -n 1`
+  if ("$result" == "") 
+    echo "invalid source partition or partition is not local"
+    echo "source: $source"
+    break
+  end
+
+  list word -split $result
+
+  $Nbytes = 1024*1024*((1 - $Fraction)*$word:1 - $word:3)
+
+  if ($Nbytes < 0)
+    echo "source partition is already below goal fraction"
+    echo "Nbytes to remove : $Nbytes"
+    break
+  end
+
+  # calculate the number of bytes to remove per subdir
+  $NremoveSub = $Nbytes / 256
+
+  echo "** removing {$NremoveSub/$TB_BYTE} TB per nebulous subdir for total of {$Nbytes/$TB_BYTE} TB"
+
+  # list subdirs -glob $source/[0-9,a-f][0-9,a-f]
+
+  for i 0 $NSUBDIR
+    sprintf srcdir "%s/%02x" $source $i
+    sprintf tgtdir "%s/%02x" $target $i
+    # echo $srcdir
+
+    # we save the temp and working files in e.g., /data/ipp070.0/nebulous/00/rebalance
+    mkdir $srcdir/rebalance
+
+    # generate initial file list (all big files)
+    sprintf filelist %s/rebalance/bigfiles.%s.txt $srcdir $VERSION
+    sprintf movelist %s/rebalance/movefiles.%s.txt $srcdir $VERSION
+    sprintf movelog  %s/rebalance/movefiles.%s.log $srcdir $VERSION
+    sprintf subdirset "%s/0?" $srcdir
+    exec find $subdirset -size +$BIGSIZE > $filelist
+    list files -x "/bin/ls -l `cat $filelist` | sort -k 5nr"
+
+    # generate subset list to match goal size
+    $Ntotal = 0
+    $j = 0
+    while (($Ntotal < $NremoveSub) && ($j < $files:n))
+      list word -split $files:$j
+      $keep:$j = $word:8
+      $Ntotal += $word:4
+      # echo $word:4 $Ntotal : $NremoveSub $keep:$j
+      $j ++
+    end
+    $keep:n = $j
+
+    # did we find enough?
+    if ($Ntotal < $NremoveSub) 
+      echo "WARNING: only found {$Ntotal/$TB_BYTE} TB of {$NremoveSub/$TB_BYTE} TB (increase BIGSIZE)"
+    else
+      echo "found {$Ntotal/$TB_BYTE} TB of {$NremoveSub/$TB_BYTE} TB"
+    end
+
+    # write out the files to a file
+    exec /bin/rm -f $movelist
+    output $movelist
+    for j 0 $keep:n
+      strsub $keep:$j $srcdir/ "" 
+      # echo $keep:$j
+    end
+    output stdout
+
+    echo "saved list in $movelist"
+  end
+end
+
+macro run.rsync
+  if ($0 != 6)
+    echo "USAGE: run.rsync (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+    echo "srchost, tgthost : e.g., ipp070"
+    echo "srcvol, tgtvol   : e.g., 0 (for /data/ipp070.0/nebulous)"
+    break
+  end
+
+  local srchost srcvol tgthost tgtvol
+
+  $srchost = $1
+  $srcvol = $2
+  $tgthost = $3
+  $tgtvol = $4
+  $VERSION = $5
+
+  # generate the full paths:
+  $source = /data/$srchost.$srcvol/nebulous
+  $target = /data/$tgthost.$tgtvol/rebalance/$srchost.$srcvol/nebulous
+
+# list subdirs -glob $source/[0-9,a-f][0-9,a-f]
+# for i 0 $subdirs:n
+
+  for i 0 $NSUBDIR
+    sprintf srcdir "%s/%02x" $source $i
+    sprintf tgtdir "%s/%02x" $target $i
+    # echo $srcdir
+
+    # generate initial file list (all big files)
+    sprintf movelist %s/rebalance/movefiles.%s.txt $srcdir $VERSION
+    sprintf movelog  %s/rebalance/movefiles.%s.log $srcdir $VERSION
+
+    echo "rsyncing $srcdir/ to $tgtdir/ : $movelog"
+    exec rsync -auv --files-from=$movelist $srcdir/ $tgtdir/ >& $movelog
+  end
+end
+
+# names are defined for a single src & tgt pair
+macro run.insert
+  if ($0 != 6)
+    echo "USAGE: run.insert (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+    echo "srchost, tgthost : e.g., ipp070"
+    echo "srcvol, tgtvol   : e.g., 0 (for /data/ipp070.0/nebulous)"
+    break
+  end
+
+  local srchost tgthost srcvol tgtvol
+
+  $srchost = $1
+  $srcvol = $2
+
+  $tgthost = $3
+  $tgtvol = $4
+  $VERSION = $5
+  
+  $myhost = `hostname`
+  if ("$myhost" != "$tgthost")
+    echo "please run on $tgthost"
+    break
+  end
+
+  # generate the full paths:
+  $source = /data/$srchost.$srcvol/nebulous
+  $target = /data/$tgthost.$tgtvol/rebalance/$srchost.$srcvol/nebulous
+
+  for i 0 $NSUBDIR
+    sprintf dirname "%02x" $i
+
+    sprintf srcdir "%s/%02x" $source $i
+    sprintf tgtdir "%s/%02x" $target $i
+
+    sprintf logname  %s/rebalance/insert.%s.log $srcdir $VERSION
+
+    echo "insert from $tgtdir/ : $logname"
+    $EXEC ./neb_reinsert.pl --tgthost $tgthost --tgtvol $tgtvol --srchost $srchost --srcvol $srcvol --holding rebalance --dir $dirname > $logname
+  end
+
+  $NtotalError = 0
+  for i 0 $NSUBDIR
+    sprintf srcdir "%s/%02x" $source $i
+    sprintf logname  %s/rebalance/insert.%s.log $srcdir $VERSION
+    # count total number of errors
+    $NgroupError = `grep ERROR $logname | wc -l`
+    $NtotalError += $NgroupError
+  end
+
+  if ($NtotalError) 
+    echo "ERROR: $NtotalError errors with insert"
+  else
+    echo "SUCCESS: no insert errors detected"
+  end
+end
+
+# names are defined for a single src & tgt pair
+macro run.check
+  if ($0 != 6)
+    echo "USAGE: run.check (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+    echo "srchost, tgthost : e.g., ipp070"
+    echo "srcvol, tgtvol   : e.g., 0 (for /data/ipp070.0/nebulous)"
+    break
+  end
+
+  local srchost tgthost srcvol tgtvol
+
+  $srchost = $1
+  $srcvol = $2
+
+  $source = /data/$srchost.$srcvol/nebulous
+
+  $tgthost = $3
+  $tgtvol = $4
+  $VERSION = $5
+  
+  $myhost = `hostname`
+  if ("$myhost" != "$tgthost")
+    echo "please run on $tgthost"
+    break
+  end
+
+  # I have a list of files in 'movelist' which I want to double-check
+  # the instances corresponding to those files should be on tgt not src
+
+  for i 0 $NSUBDIR
+    sprintf srcdir "%s/%02x" $source $i
+    sprintf subdir "%02x" $i
+
+    # generate initial file list (all big files)
+    sprintf movelist %s/rebalance/movefiles.%s.txt $srcdir $VERSION
+    sprintf checklog %s/rebalance/movefiles.%s.chk $srcdir $VERSION
+
+     echo ./neb_check_reinsert.pl --tgthost $tgthost --tgtvol $tgtvol --subdir $subdir --list $movelist > $checklog
+    $EXEC ./neb_check_reinsert.pl --tgthost $tgthost --tgtvol $tgtvol --subdir $subdir --list $movelist > $checklog
+  end
+
+  $NtotalError = 0
+  for i 0 $NSUBDIR
+    sprintf srcdir "%s/%02x" $source $i
+    sprintf logname %s/rebalance/movefiles.%s.chk $srcdir $VERSION
+    # count total number of errors
+    $NgroupError = `grep ERROR $logname | wc -l`
+    $NtotalError += $NgroupError
+  end
+
+  if ($NtotalError > 0) 
+    echo "ERROR: $NtotalError errors with check"
+  else
+    echo "SUCCESS: no check errors detected"
+  end
+end
+
+macro run.trash
+  if ($0 != 6)
+    echo "USAGE: run.insert (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+    echo "srchost, tgthost : e.g., ipp070"
+    echo "srcvol, tgtvol   : e.g., 0 (for /data/ipp070.0/nebulous)"
+    break
+  end
+
+  local srchost tgthost srcvol tgtvol
+
+  $srchost = $1
+  $srcvol = $2
+
+  $source = /data/$srchost.$srcvol/nebulous
+  $VERSION = $5
+
+  $myhost = `hostname`
+  if ("$myhost" != "$srchost")
+    echo "please run on $srchost"
+    break
+  end
+
+# list subdirs -glob $source/[0-9,a-f][0-9,a-f]
+# for i 0 $subdirs:n
+
+  for i 0 $NSUBDIR
+    sprintf trashdir "/data/%s.%s/nebulous/%02x/trash" $srchost $srcvol $i
+    mkdir $trashdir
+
+    sprintf srcdir "%s/%02x" $source $i
+    # echo $srcdir
+
+    # list of files which have been moved & checked
+    sprintf movelist %s/rebalance/movefiles.%s.txt $srcdir $VERSION
+
+    list files -x "cat $movelist"
+    for j 0 $files:n
+      # move the file to the trash directory
+      echo mv $srcdir/$files:$j $trashdir
+      exec mv $srcdir/$files:$j $trashdir
+    end
+  end
+end
+
+if ($SCRIPT) 
+  if ($argv:n < 6) 
+    echo "USAGE: rebalance.mana (MODE) (srchost) (srcvol) (tgthost) (tgtvol) (VERSION) [options]"
+    echo ""
+    echo "MODE: find, rsync, insert, check, trash"
+    echo ""
+    echo "srchost, tgthost : e.g., ipp070"
+    echo "srcvol, tgtvol   : e.g., 0 (for /data/ipp070.0/nebulous)"
+    echo ""
+    echo "VERSION : free-form string (e.g., YYYYMMDD)"
+    exit 2
+  end
+
+  #### find ####
+  if ("$argv:0" == "find")
+    if ($argv:n != 7)
+      echo "USAGE: rebalance.mana (find) (srchost) (srcvol) (tgthost) (tgtvol) (VERSION) (Fraction)"
+      echo "Fraction : disk fraction target"
+      break
+    end
+    run.find $argv:1 $argv:2 $argv:3 $argv:4 $argv:5 $argv:6
+    exit 0
+  end
+
+  #### rsync ####
+  if ("$argv:0" == "rsync")
+    if ($argv:n != 6)
+      echo "USAGE: rebalance.mana (rsync) (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+      break
+    end
+    run.rsync $argv:1 $argv:2 $argv:3 $argv:4 $argv:5
+    exit 0
+  end
+
+  #### insert ####
+  if ("$argv:0" == "insert")
+    if ($argv:n != 6)
+      echo "USAGE: rebalance.mana (insert) (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+      break
+    end
+    run.insert $argv:1 $argv:2 $argv:3 $argv:4 $argv:5
+    exit 0
+  end
+
+  #### check ####
+  if ("$argv:0" == "check")
+    if ($argv:n != 6)
+      echo "USAGE: rebalance.mana (check) (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+      break
+    end
+    run.check $argv:1 $argv:2 $argv:3 $argv:4 $argv:5
+    exit 0
+  end
+
+  #### trash ####
+  if ("$argv:0" == "trash")
+    if ($argv:n != 6)
+      echo "USAGE: rebalance.mana (trash) (srchost) (srcvol) (tgthost) (tgtvol) (VERSION)"
+      break
+    end
+    run.trash $argv:1 $argv:2 $argv:3 $argv:4 $argv:5
+    exit 0
+  end
+
+  exit 0
+end
