VCL-1126 - allocate computer already having specified IP address for server reservations with specified IP

OS.pm: modified confirm_fixed_ip_is_available: added $computer_id argument when calling is_ip_assigned_query; added check for assigned computer having specified IP address and if so, skip ping check

utils.pm: modified is_ip_assigned_query: added 2nd (optional) argument for a computer ID; if computer ID is passed in, don't include that computer in query results

utils.php:
-modified isAvailable: added check for any mapped computers already having the requested IP address, and if so, limit mapped computers to just those and set a flag that mapped computers have been limited; at first check for no computers being available if mapped computer flag is set, use -4 return code instead of 0; if mapped computer flag set, skip removing recently reserved computers that may have failed
-modified debugIsAvailable: changed message for case 2 to say an unavailable computer has the requested IP instead of stating an overlapping server reservation has the address assigned
diff --git a/managementnode/lib/VCL/Module/OS.pm b/managementnode/lib/VCL/Module/OS.pm
index 32c25cf..fb0d6ec 100644
--- a/managementnode/lib/VCL/Module/OS.pm
+++ b/managementnode/lib/VCL/Module/OS.pm
@@ -1424,12 +1424,19 @@
 	my $server_request_fixed_ip = $self->data->get_server_request_fixed_ip(); 
 	
 	#check VCL computer table
-	if (is_ip_assigned_query($server_request_fixed_ip)) {
+	if (is_ip_assigned_query($server_request_fixed_ip, $computer_id)) {
 		notify($ERRORS{'WARNING'}, 0, "$server_request_fixed_ip is already assigned");
 		insertloadlog($reservation_id, $computer_id, "failed","$server_request_fixed_ip is already assigned");
 		return 0;
 	}
 
+	#check if the assigned computer has the specified address
+	my $retrieved_public_ip_address = $self->get_public_ip_address();
+	if ($retrieved_public_ip_address eq $server_request_fixed_ip) {
+		notify($ERRORS{'DEBUG'}, 0, "$server_request_fixed_ip is assigned to reserved computer, skipping ping test");
+		return 1;
+	}
+
 	#Is IP pingable	
 	if (_pingnode($server_request_fixed_ip)) {
 		notify($ERRORS{'WARNING'}, 0, "$server_request_fixed_ip is answering ping test");
diff --git a/managementnode/lib/VCL/utils.pm b/managementnode/lib/VCL/utils.pm
index 5f7b6ae..5204b82 100644
--- a/managementnode/lib/VCL/utils.pm
+++ b/managementnode/lib/VCL/utils.pm
@@ -11905,20 +11905,25 @@
 
 =head2 is_ip_assigned_query
 
-  Parameters  : IP address
+  Parameters  : IP address, Computer ID (optional)
   Returns     : boolean
-  Description : checks if IP address exists in db
+  Description : checks if IP address exists in db; ignores computer with
+                specified ID if supplied
 
 =cut
 
 sub is_ip_assigned_query {
 	
-	my ($ip_address) = @_;
+	my ($ip_address) = shift;
+	my ($computer_id) = shift;
 
    if (!defined($ip_address)) {
       notify($ERRORS{'WARNING'}, 0, "IPaddress  argument was not supplied");
       return;
-   }	
+   }
+	if (!defined($computer_id)) {
+		$computer_id = 0;
+	}
 
    my $select_statement = <<EOF;
 SELECT
@@ -11930,6 +11935,7 @@
 WHERE
 computer.IPaddress = '$ip_address' AND
 computer.stateid = state.id AND
+computer.id != $computer_id AND
 state.name != 'deleted' AND
 computer.vmhostid IS NOT NULL
 EOF
diff --git a/web/.ht-inc/utils.php b/web/.ht-inc/utils.php
index aaf3cbd..21c3304 100644
--- a/web/.ht-inc/utils.php
+++ b/web/.ht-inc/utils.php
@@ -4870,6 +4870,7 @@
 
 		// if $ip specified, only look at computers under management nodes that can
 		#   handle that network
+		$ipmaplimited = 0;
 		if($ip != '') {
 			$mappedmns = getMnsFromImage($imageid);
 			$mnnets = checkAvailableNetworks($ip);
@@ -4886,6 +4887,24 @@
 				return debugIsAvailable(0, 18, $start, $end, $imagerevisionid);
 			}
 			$mappedcomputers = implode(',', $newcompids);
+			// if existing, available, and mapped computer already has requested IP
+			//address, limit $mappedcomputers to just that one
+			$query = "SELECT c.id "
+			       . "FROM computer c "
+			       . "JOIN state s ON (c.stateid = s.id) "
+			       . "WHERE c.IPaddress = '$ip' AND "
+			       .       "c.id in ($mappedcomputers) AND "
+			       .       "s.name != 'deleted' AND "
+			       .       "c.deleted = 0 AND "
+			       .       "(c.type != 'virtualmachine' OR c.vmhostid IS NOT NULL)";
+			$tmpmapped = array();
+			$qh = doQuery($query);
+			while($row = mysqli_fetch_assoc($qh))
+				$tmpmapped[] = $row['id'];
+			if(count($tmpmapped)) {
+				$ipmaplimited = 1;
+				$mappedcomputers = implode(',', $tmpmapped);
+			}
 		}
 
 		#get computers for available schedules and platforms
@@ -4999,8 +5018,11 @@
 		}
 
 		# return 0 if no computers available
-		if(empty($computerids) && empty($blockids))
+		if(empty($computerids) && empty($blockids)) {
+			if($ipmaplimited)
+				return debugIsAvailable(-4, 2, $start, $end, $imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
 			return debugIsAvailable(0, 21, $start, $end, $imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
+		}
 
 		#remove computers from list that are already scheduled
 		$usedComputerids = array();
@@ -5142,7 +5164,7 @@
 		#   undetected failure
 		$failedids = getPossibleRecentFailures($userid, $imageid);
 		$shortened = 0;
-		if(! empty($failedids)) {
+		if(! empty($failedids) && ! $ipmaplimited) {
 			$origcomputerids = $computerids;
 			$origcurrentids = $currentids;
 			$origblockids = $blockids;
@@ -5231,7 +5253,7 @@
 			$msg = "invalid image id submitted - not found in images available to the user";
 			break;
 		case "2":
-			$msg = "an overlapping server reservation has the same fixed IP or MAC address";
+			$msg = "an unavailable computer has the requested fixed IP or MAC address";
 			break;
 		case "16":
 			$msg = "the requested fixed IP address is currently in use by a management node";