Index: trunk/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- trunk/Nebulous-Server/lib/Nebulous/Server.pm	(revision 31370)
+++ trunk/Nebulous-Server/lib/Nebulous/Server.pm	(revision 39926)
@@ -1753,5 +1753,5 @@
 }
 
-sub find_instances
+sub find_instances_old
 {
     my $self = shift;
@@ -1820,4 +1820,157 @@
             # ext_id, name, available
             my $rows = $query->execute($key->path, $vol_name, 1);
+            unless ($rows > 0) {
+                $query->finish;
+                die("no instances on storage volume or volume is not avaiable for key: $key volume: $vol_name");
+            }
+        } else {
+            $query = $db->prepare_cached( $sql->get_object_instances );
+	    my $rows;
+            # ext_id, available
+	    if (defined($find_invalid)) {
+		$rows = $query->execute($key->path, 0);
+	    }
+	    else {
+		$rows = $query->execute($key->path, 1);
+	    }
+            unless ($rows > 0) {
+                $query->finish;
+                die("no instances available for key: $key");
+            }
+        }
+
+        while (my $row = $query->fetchrow_hashref) {
+            my $instance = $row->{ 'uri' };
+            push @locations, $instance if $instance;
+        }
+    };
+    if ($@) {
+        $db->rollback;
+        # handle soft volumes
+        if (defined $vol_name and not defined $key->hard_volume) {
+            $log->debug("retrying with 'any' volume");
+            return $self->find_instances($key->path, 'any');
+        }
+        $log->logdie("database error: $@");
+    }
+
+    # XXX remove this?
+    $log->logdie("no instances found") unless (scalar @locations);
+
+    $log->debug("found: @locations");
+
+    $log->debug("leaving");
+
+    return \@locations;
+}
+
+#sub find_instances_by_proximity
+sub find_instances
+{
+    my $self = shift;
+
+    my $log = $self->log;
+    $log->debug("entered - @_");
+
+    my ($key, $vol_name, $find_invalid) = validate_pos(@_,
+        {
+            type        => SCALAR,
+            callbacks   => {
+                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
+            },
+        },
+        {
+            type        => SCALAR|UNDEF,
+#            callbacks   => {
+#                # check that the volume name requested is valid
+#                'is valid volume name' => sub {
+#                    return 1 if not defined $_[0];
+#                    $self->_is_valid_volume_name($_[0])
+#                },
+#            },
+            optional    => 1,
+        },
+	{
+            # find_invalid
+	    type        => SCALAR|UNDEF,
+            optional    => 1,
+        }, 
+    );
+
+    my $sql = $self->sql;
+
+#    unless ($key) {
+#        $log->warn("key was undefined after validate_pos(), trying again...");
+#        return $self->find_instances(@_);
+#    }
+
+    # vol_name overrides the key implied volume
+    my ($h_vol_id, $h_cab_id, $h_site_id);
+    eval {
+        $key = parse_neb_key($key, $vol_name);
+    };
+    $log->logdie("$@") if $@;
+    $vol_name = $key->volume;
+
+    my $db  = $self->db($key);
+
+    # Convert possible alias to real volume.
+    if (defined $vol_name) {
+	my ($tmp_vol_id, $tmp_name, $tmp_host, $tmp_path);
+	eval {
+	    my $query = $db->prepare_cached( $sql->get_volume_by_alias );
+	    $query->execute( $vol_name );
+	    ($tmp_vol_id, $tmp_name, $tmp_host, $tmp_path) = $query->fetchrow_array;
+	    $query->finish;
+	};
+	$log->logdie("$@") if $@;
+#	$log->warn("CZW: find_instance: deref alias: $vol_name => $tmp_vol_id $tmp_name $tmp_host $tmp_path (key vol: $key" . $key->volume . ")");
+	if (defined $tmp_vol_id and defined $tmp_name and defined $tmp_host and defined $tmp_path) {
+	    if ($tmp_name ne $vol_name) {
+		$vol_name = $tmp_name;
+#		$key->volume = $vol_name;
+	    }
+	}
+
+
+    }
+
+    # the key's volume can't be validiated on input for this method so we have
+    # to check it after parsing the key
+    if (defined $vol_name
+        and not $self->_is_valid_volume_name($key, $vol_name)) {
+        if ($key->hard_volume) {
+            $log->logdie("$vol_name is not a valid volume name");
+        } else {
+            $log->warn( "$vol_name is not a known volume name" );
+            $vol_name = undef;
+        }
+    }
+
+    # Get the host volume information encoded in the vol_name.
+    # I am unhappy that we have three different if(defined($vol_name)) entries, but I don't see a better way.
+#    my ($h_vol_id, $h_cab_id, $h_site_id);
+    if (defined $vol_name) {
+	eval {
+	    my $query = $db->prepare_cached( $sql->get_site_info_by_name );
+	    $query->execute( $vol_name );
+	    ($h_vol_id, $h_cab_id, $h_site_id) = $query->fetchrow_array;
+	    $query->finish;
+	};
+	$log->logdie("$@") if $@;
+
+	unless(defined $h_vol_id and defined $h_cab_id and defined $h_site_id) {
+	    $vol_name = undef;
+	}
+    }
+
+    my @locations;
+    eval {
+        my $query;
+        if ($vol_name && $h_vol_id && $h_cab_id && $h_site_id) {
+            $query = $db->prepare_cached( $sql->get_object_instances_by_proximity );
+            # ext_id, name, available
+	    # host_vol_id host_cab_id host_site_id ext_id available
+            my $rows = $query->execute($h_vol_id, $h_cab_id, $h_site_id, $key->path, 1);
             unless ($rows > 0) {
                 $query->finish;
@@ -2341,5 +2494,5 @@
     my $db  = $self->db($key);
 
-    my ($vol_id, $vol_host, $vol_path, $xattr, $forbidden_cabinet);
+    my ($vol_id, $vol_host, $vol_path, $xattr, $forbidden_cabinet, $forbidden_site);
     eval {
         my $rows;
@@ -2352,7 +2505,10 @@
 	}
 	if ($rows == 1) {
-	    ($forbidden_cabinet) = $query->fetchrow_array;
+	    ($forbidden_cabinet, $forbidden_site) = $query->fetchrow_array;
 	    unless (defined($forbidden_cabinet)) {
 		$forbidden_cabinet = 0;
+	    }
+	    unless (defined($forbidden_site)) {
+		$forbidden_site = 0;
 	    }
 	    $query->finish;
@@ -2360,4 +2516,5 @@
 	else {
 	    $forbidden_cabinet = 0;
+	    $forbidden_site    = 0;
 	    $query->finish;
 	}
@@ -2366,9 +2523,15 @@
         $query = $db->prepare_cached( $sql->get_replication_volume_for_ext_id );
         # ext_id, %free, avaiable, allocate
-        $rows = $query->execute($key->path, $max_used_space, 1, 1, $forbidden_cabinet, $topfew_count);
+        $rows = $query->execute($key->path, $max_used_space, 1, 1, $forbidden_cabinet, $forbidden_site, $topfew_count);
         # XXX destinguish between non-existant and unaviable
         unless ($rows > 0) {
             $query->finish;
-            die("can't find a suitable storage volume to replicate $key to");
+	    # CZW: 2016-08-23 This wasn't right.  If we don't get an entry, we may have been too strict.
+	    #      I'm not fully convinced this is complete, as we may want to back out the cabinet criterion as well.
+	    #      In any case, I don't think we're generally in the situation where replication can't find a host.
+	    $rows = $query->execute($key->path, $max_used_space, 1, 1, $forbidden_cabinet, 0, $topfew_count);
+	    unless ($rows > 0) {
+		die("can't find a suitable storage volume to replicate $key to");
+	    }
         }
         # when matching by name we shouldn't ever match more than once
@@ -2490,7 +2653,9 @@
     $log->logdie("database error: $@") if $@;
 
+#    $log->warn("CZW: $vol_id $vol_path for >>$vol_name<<");
     if (defined $vol_id and defined $vol_path) {
         $log->debug( "found volume name $vol_name" );
         $log->debug( "leaving" );
+#	$log->warn("CZW: $vol_id $vol_path for >>$vol_name<<");
         return 1;
     } 
