Index: trunk/Nebulous-Server/Build.PL
===================================================================
--- trunk/Nebulous-Server/Build.PL	(revision 17772)
+++ trunk/Nebulous-Server/Build.PL	(revision 17815)
@@ -38,4 +38,5 @@
     script_files        => [qw(
         bin/neb-addvol
+        bin/neb-admin
         bin/neb-fsck
         bin/neb-initdb
Index: trunk/Nebulous-Server/Changes
===================================================================
--- trunk/Nebulous-Server/Changes	(revision 17772)
+++ trunk/Nebulous-Server/Changes	(revision 17815)
@@ -2,4 +2,5 @@
 
 0.12
+    - add neb-admin util
     - fix Nebulous::Server::setxattr_object() so that creating a new xattr with
       the "replace" flag works
Index: trunk/Nebulous-Server/MANIFEST
===================================================================
--- trunk/Nebulous-Server/MANIFEST	(revision 17772)
+++ trunk/Nebulous-Server/MANIFEST	(revision 17815)
@@ -8,4 +8,5 @@
 Todo
 bin/neb-addvol
+bin/neb-admin
 bin/neb-fsck
 bin/neb-initdb
Index: trunk/Nebulous-Server/bin/neb-admin
===================================================================
--- trunk/Nebulous-Server/bin/neb-admin	(revision 17772)
+++ trunk/Nebulous-Server/bin/neb-admin	(revision 17815)
@@ -3,5 +3,5 @@
 # Copyright (C) 2005-2008  Joshua Hoblitt
 #
-# $Id: neb-admin,v 1.1 2008-05-22 04:08:29 jhoblitt Exp $
+# $Id: neb-admin,v 1.2 2008-05-27 21:59:53 jhoblitt Exp $
 
 use strict;
@@ -19,5 +19,5 @@
 use Pod::Usage qw( pod2usage );
 
-my ($db, $dbuser, $dbpass, $server);
+my ($db, $dbuser, $dbpass, $server, $limit);
 
 $db     = $ENV{'NEB_DB'} unless $db;
@@ -31,4 +31,5 @@
     'pass=s'        => \$dbpass,
     'server|s=s'    => \$server,
+    'limit|l=i'     => \$limit,
 ) || pod2usage( 2 );
 
@@ -38,4 +39,7 @@
 pod2usage( -msg => "Required options: --db --user --pass", -exitval => 2 )
     unless $db && $dbuser && $dbpass;
+
+# set default limit to 5
+$limit ||= 5;
 
 my $neb = Nebulous::Client->new(
@@ -60,5 +64,6 @@
 
 # so_id, ext_id, instances, available_instances, need_recovery, recoverable
-my $query = $dbh->prepare( $sql->find_objects_with_unavailable_instances );
+my $query = $dbh->prepare( $sql->find_objects_with_unavailable_instances
+        . " LIMIT $limit" );
 $query->execute;
 
@@ -68,4 +73,6 @@
 }
 $query->finish;
+    
+print "replciate MULTI\n\n";
 
 foreach my $obj (@rows) {
@@ -75,4 +82,5 @@
     my $key = $obj->{ext_id};
     my $has = $obj->{instances};
+    my $vol_name = $obj->{volume_name};
     my $available = $obj->{available_instances} || 0;
     my $need_recovery = $obj->{need_recovery};
@@ -84,5 +92,5 @@
     # objects with only a single instance that are offline are unrecoverable so
     # we don't need to handle that special case
-    next unless $need_recovery;
+#    next unless $need_recovery;
     unless ($recoverable) {
         warn "so_id: $so_id key: \'$key\' ",
@@ -96,11 +104,49 @@
     next unless $need;
 
-    for (; $need > 0; $need--) {
-        warn "so_id: $so_id key: \'$key\' ",
-            "has $available available instances, target $copies -- replicating"
-            or warn "so_id: $so_id key: \'$key\' replication failed";
-
-        $need++;
+    my %md = (
+        key         => $key,
+        need_copies => $need,
+        volume_name => $vol_name,
+    );
+
+    print_metadata("replicate", \%md);
+    print "\n";
+}
+
+sub print_metadata
+{
+    my ($name, $pairs) = @_;
+
+    print "$name METADATA\n";
+
+    # format from formatMetadataItem() in pzMetadataConfig.c
+    foreach my $key (keys %$pairs) {
+        my $value = $pairs->{$key};
+        if (looks_like_number($value)) {
+            printf("   " . "%-15s  %-8s  %-15s\n", $key, "S32", $value);
+        } else {
+            printf("   " . "%-15s  %-8s  %-15s\n", $key, "STR", $value);
+        }
     }
+    
+    print "END\n";
+}
+
+# looks_like_number() was stolen from Scalar::Util
+#
+# Copyright (c) 1997-2006 Graham Barr <gbarr@pobox.com>. All rights reserved.
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as Perl itself.
+
+sub looks_like_number {
+  local $_ = shift;
+
+  # checks from perlfaq4
+  return 0 if !defined($_) or ref($_);
+  return 1 if (/^[+-]?\d+$/); # is a +/- integer
+  return 1 if (/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/); # a C float
+  return 1 if ($] >= 5.008 and /^(Inf(inity)?|NaN)$/i) or ($] >= 5.006001 and /^Inf$/i);
+
+  0;
 }
 
Index: trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm
===================================================================
--- trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 17772)
+++ trunk/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 17815)
@@ -1,5 +1,5 @@
 # Copyright (c) 2004  Joshua Hoblitt
 #
-# $Id: SQL.pm,v 1.51 2008-05-22 04:10:01 jhoblitt Exp $
+# $Id: SQL.pm,v 1.52 2008-05-27 21:59:53 jhoblitt Exp $
 
 package Nebulous::Server::SQL;
@@ -249,4 +249,5 @@
             ext_id,
             count(ins_id) as instances,
+            volume.name as volume_name,
             count(mountedvol.vol_id) as available_instances,
             count(mountedvol.vol_id) > 0 as recoverable,
@@ -255,4 +256,6 @@
         JOIN instance
             USING(so_id)
+        JOIN volume
+            USING(vol_id)
         LEFT JOIN storage_object_xattr
             ON storage_object.so_id = storage_object_xattr.so_id
