Index: trunk/Nebulous-Server/bin/nebdiskd
===================================================================
--- trunk/Nebulous-Server/bin/nebdiskd	(revision 28444)
+++ trunk/Nebulous-Server/bin/nebdiskd	(revision 28446)
@@ -224,4 +224,13 @@
                 }
             };
+
+            # fetch stats on the mounted device.  this has to be done AFTER
+            # we determine if it's a valid mountpoint incase
+            # is_mountpoint() invokes the automounter
+            my $dev_info = df($mountpoint, 1024);
+            unless (defined $dev_info) {
+		$valid_mountpoint = 0;
+            }
+
             if (!$valid_mountpoint) {
                 # try is_mountpoint() again if $retry > 1
@@ -254,5 +263,5 @@
             # we determine if it's a valid mountpoint incase
             # is_mountpoint() invokes the automounter
-            my $dev_info = df($mountpoint, 1024);
+            $dev_info = df($mountpoint, 1024);
             unless (defined $dev_info) {
                 $log->error("can't find device info for $mountpoint");
Index: trunk/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- trunk/Nebulous-Server/lib/Nebulous/Server.pm	(revision 28444)
+++ trunk/Nebulous-Server/lib/Nebulous/Server.pm	(revision 28446)
@@ -1598,4 +1598,58 @@
 }
 
+sub find_ext_id_by_volume
+{
+    my $self = shift;
+    my $log = $self->log;
+    $log->debug("entered - @_");
+    my ($vol_name,$limit) = validate_pos(@_,
+					{
+					    type      => SCALAR,
+# # 					    callbacks => {
+# # 						'is valid volume name' => sub {
+# # 						    return 1 if not defined $_[0];
+# # 						    $self->_is_valid_volume_name($_[0])
+# # 						},
+# 					    },
+					},
+					{
+					    type      => SCALAR|UNDEF,
+					    optional => 1,
+					},
+	);
+    unless (defined($limit)) {
+	$limit = 50000;
+    }
+
+    my $sql = $self->sql;
+    my @ext_ids;
+    my $db = $self->_db_for_index(0);
+    eval {
+	my $query;
+	$query = $db->prepare_cached( $sql->get_ext_id_by_vol_name );
+	my $rows = $query->execute($vol_name, 1,$limit);
+	unless ($rows > 0) {
+	    $query->finish;
+	    $log->logdie("no instances on storage volume or volume is not avaiable for volume: $vol_name");
+	}
+        while (my $row = $query->fetchrow_hashref) {
+            my $ext_id = $row->{ 'ext_id' };
+            push @ext_ids, $ext_id if $ext_id;
+        }
+    };
+    if ($@) {
+        $db->rollback;
+        $log->logdie("database error: $@");
+    }
+
+    # XXX remove this?
+    $log->logdie("no ext_ids found") unless (scalar @ext_ids);
+
+    $log->debug("found: \@ext_ids");
+
+    $log->debug("leaving");
+
+    return \@ext_ids;
+}
 
 sub find_instances
Index: trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm
===================================================================
--- trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 28444)
+++ trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 28446)
@@ -266,4 +266,18 @@
             AND mountedvol.name = ?
             AND mountedvol.available = ?
+    },
+    get_ext_id_by_vol_name => qq{
+        SELECT
+            ext_id
+        FROM instance
+        JOIN storage_object
+            USING (so_id)
+        JOIN mountedvol
+            USING(vol_id)
+        JOIN volume
+            USING(vol_id)
+        WHERE volume.name = ?
+            AND mountedvol.available = ?
+        LIMIT ?
     },
     # volume handler
Index: trunk/Nebulous/Build.PL
===================================================================
--- trunk/Nebulous/Build.PL	(revision 28444)
+++ trunk/Nebulous/Build.PL	(revision 28446)
@@ -113,7 +113,9 @@
         bin/neb-locate
         bin/neb-ls
+        bin/neb-migrate
         bin/neb-mv
         bin/neb-replicate
         bin/neb-rm
+        bin/neb-shift
         bin/neb-stat
         bin/neb-swap
Index: trunk/Nebulous/MANIFEST
===================================================================
--- trunk/Nebulous/MANIFEST	(revision 28444)
+++ trunk/Nebulous/MANIFEST	(revision 28446)
@@ -14,7 +14,9 @@
 bin/neb-insert
 bin/neb-ls
+bin/neb-migrate
 bin/neb-mv
 bin/neb-replicate
 bin/neb-rm
+bin/neb-shift
 bin/neb-stat
 bin/neb-swap
Index: trunk/Nebulous/bin/neb-migrate
===================================================================
--- trunk/Nebulous/bin/neb-migrate	(revision 28446)
+++ trunk/Nebulous/bin/neb-migrate	(revision 28446)
@@ -0,0 +1,180 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2010  Joshua Hoblitt/Chris Waters
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.03';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my (
+    $server,
+    $limit,
+    $dest_volume,
+    $no_pretend,
+#    $recursive,
+);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'limit|l=s'     => \$limit,
+    'destination|d=s' => \$dest_volume,
+    'no_pretend'    => \$no_pretend,
+) || pod2usage( 2 );
+
+if ($limit && $limit <= 0) {
+    die "Limit must be positive.";
+}
+
+
+my $host = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+# Find the keys on this volume.
+my $keys;
+if ($limit) {
+    $keys = $neb->find_ext_id_by_volume($host,$limit);
+}
+else {
+    $keys = $neb->find_ext_id_by_volume($host);
+}
+
+# Print the keys on this volume, and if we're not pretending, do the neb-shift code to move them off it.
+if ($keys) {
+    foreach my $src (@{ $keys }) {
+	print "$src";
+	if ($no_pretend) {
+	    print "\t";
+	    my $uris = $neb->find_instances($src, "~$host");
+	    unless (defined $uris) {
+		die "No copy of $src exists on $host";
+	    }
+	    
+	    my $status;
+	    if ($dest_volume) {
+		$status = $neb->replicate( $src, $dest_volume);
+	    }
+	    else {
+		$status = $neb->replicate($src);
+	    }
+	    die "Replicate phase failed for $src ($host) ($dest_volume)" unless $status;
+	    
+	    $status = $neb->cull($src,$host);
+	    
+	    warn "Cull phase failed for $src from $host" unless $status;
+	    
+	    print "Success!";
+	}
+	print "\n";
+    }
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-migrate - list Nebulous keys on a specific volume and move them off it
+
+=head1 SYNOPSIS
+
+    neb-migrate [--server <URL>] [--limit <N>] [--no_pretend] <volume>
+
+=head1 DESCRIPTION
+
+This program list Nebulous keys found on volume <<volume>>, and will move them 
+to another volume if requested to do so.  
+
+=head1 OPTIONS
+
+=over 4
+
+Optional
+
+=item * --limit
+
+Limit the number of results.
+
+=item * --no_pretend
+
+Actually move the files off of the specified host.
+
+=item * --destination|-d <dest_volume>
+
+Specify the destination volume for the keys moved.
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Chris Waters <watersc1@ifa.hawaii.edu>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2010 Josh Hoblitt / Chris Waters.  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 as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+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.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: trunk/Nebulous/bin/neb-shift
===================================================================
--- trunk/Nebulous/bin/neb-shift	(revision 28446)
+++ trunk/Nebulous/bin/neb-shift	(revision 28446)
@@ -0,0 +1,146 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-shift,v 1.5 2008-01-22 21:40:39 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($volume, $server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'volume=s'      => \$volume,
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+my $src = shift;
+my $abandon_vol = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server <source key> <source volume>",
+        -exitval => 2 )
+        unless defined $server and defined $src and defined $abandon_vol;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $uris = $neb->find_instances($src, "~$abandon_vol");
+unless (defined $uris) {
+    die "No copy of $src exists on $abandon_vol";
+}
+
+my $status;
+if ($volume) {
+    $status = $neb->replicate( $src, $volume);
+}
+else {
+    $status = $neb->replicate($src);
+}
+die "Replicate phase failed for $src ($abandon_vol) ($volume)" unless $status;
+
+$status = $neb->cull($src,$abandon_vol);
+
+warn "Cull phase failed for $src from $abandon_vol" unless $status;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-shift - list Nebulous keys
+
+=head1 SYNOPSIS
+
+    neb-shift [--server <URL>] [--volume <destination volume name>] <source key> <source volume>
+
+=head1 DESCRIPTION
+
+Shift a Nebulous storage key from a specific source volume, optionally assigning it
+to a specific destination volume.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --volume <volume name>
+
+The volume to place the key's new storage instance on.
+
+Optional.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Joshua Hoblitt.  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 as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+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.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: trunk/Nebulous/lib/Nebulous/Client.pm
===================================================================
--- trunk/Nebulous/lib/Nebulous/Client.pm	(revision 28444)
+++ trunk/Nebulous/lib/Nebulous/Client.pm	(revision 28446)
@@ -790,4 +790,38 @@
 }
 
+sub find_ext_id_by_volume
+{
+    my $self = shift;
+    my ($vol_name, $limit) = validate_pos( @_,
+					   { 
+					       type => SCALAR,
+					   },
+					   {
+					       type => SCALAR|UNDEF,
+					       optional => 1,
+					   },
+	);
+    
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->find_ext_id_by_volume( $vol_name, $limit);
+    if ( $response->fault ) {
+	$self->set_err($response->faultstring);
+	if ($response->faultstring =~ /no instances on storage volume/) {
+	    $log->debug( "leaving" );
+	    return;
+	}
+
+	$log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $ext_ids = $response->result;
+    
+    $log->debug( "server found @$ext_ids" );
+    $log->debug( "leaving" );
+    
+    return($ext_ids);
+}
+
 sub find_instances_for_cull
 {
