You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2015/02/03 18:26:49 UTC

svn commit: r1656840 - in /vcl/trunk/managementnode/lib/VCL: Module/Provisioning/VMware/VIM_SSH.pm Module/State.pm utils.pm

Author: arkurth
Date: Tue Feb  3 17:26:49 2015
New Revision: 1656840

URL: http://svn.apache.org/r1656840
Log:
VCL-16
Added utils.pm::get_reservation_management_node_hostname. This is used to display more useful information when a cluster request fails.

Changed utils.pm::set_reservation_lastcheck to allow multiple reservation IDs to be passed. Updated call in VIM_SSH.pm.

Updated utils.pm::reservation_being_processed to also retrieve the parent reservation ID from the database for cluster requests. Duplicate processes were being forked if a child reservation already ran before the parent finished.

Removed call to update_request_state in State.pm::reservation_failed. This gets handled by state_exit. It was not checking if the reservation was the parent.

Updated State.pm::wait_for_all_reservations_to_begin to display information about which management nodes the reservations belong to.

Updated State.pm::wait_for_child_reservations_to_exit to check for both a computerloadlog 'begin' and 'exited' entry for each reservation. It was only checking for 'exited'. As a result, the loop ran until it timed out if any child reservation process had never started and added a 'begin' entry.


VCL-767
Updated utils.pm::determine_remote_connection_target to check the argument for a '@' sign.  run_scp_command is passing user@x.x.x.x. This was failing. If it detects this, the 'user@' is removed from the argument.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
    vcl/trunk/managementnode/lib/VCL/Module/State.pm
    vcl/trunk/managementnode/lib/VCL/utils.pm

Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm?rev=1656840&r1=1656839&r2=1656840&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm Tue Feb  3 17:26:49 2015
@@ -248,7 +248,7 @@ sub _run_vim_cmd {
 				my $request_end_time_epoch = convert_to_epoch_seconds($self->data->get_request_end_time());
 				my $current_time_epoch = time;
 				my $reservation_lastcheck_epoch = ($request_end_time_epoch-(20*60));
-				set_reservation_lastcheck($reservation_id, $reservation_lastcheck_epoch);
+				set_reservation_lastcheck($reservation_lastcheck_epoch, $reservation_id);
 			}
 			return;
 		}

Modified: vcl/trunk/managementnode/lib/VCL/Module/State.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/State.pm?rev=1656840&r1=1656839&r2=1656840&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/State.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/State.pm Tue Feb  3 17:26:49 2015
@@ -114,6 +114,49 @@ sub initialize {
 		$self->data->set_reservation_lastcheck_time($reservation_lastcheck);
 	}
 	
+	# If this is a cluster request, wait for all reservations to begin before proceeding
+	if ($reservation_count > 1) {
+		if (!$self->wait_for_all_reservations_to_begin('begin', 90, 5)) {
+			$self->reservation_failed("failed to detect start of processing for all reservation processes", 'available');
+		}
+	}
+	
+	# Parent reservation needs to update the request state to pending
+	if ($is_parent_reservation) {
+		if ($reservation_count > 1) {
+			# Check if any reservations have failed
+			if (my @failed_reservation_ids = $self->does_loadstate_exist_any_reservation('failed')) {
+				notify($ERRORS{'WARNING'}, 0, "reservations failed: " . join(', ', @failed_reservation_ids));
+				$self->state_exit('failed');
+			}
+		}
+		
+		# Update the request state to pending for this reservation
+		if (!update_request_state($request_id, "pending", $request_state_name)) {
+			# Check if request was deleted
+			if (is_request_deleted($request_id)) {
+				exit;
+			}
+			
+			# Check the current state
+			my ($current_request_state, $current_request_laststate) = get_request_current_state_name($request_id);
+			if (!$current_request_state) {
+				# Request probably complete and already removed
+				notify($ERRORS{'DEBUG'}, 0, "current request state could not be retrieved, it was probably completed by another vcld process");
+				exit;
+			}
+			if ($current_request_state =~ /^(deleted|complete)$/ || $current_request_laststate =~ /^(deleted)$/) {
+				notify($ERRORS{'DEBUG'}, 0, "current request state: $current_request_state/$current_request_laststate, exiting");
+				exit;
+			}
+			
+			$self->reservation_failed("failed to update request state to pending");
+		}
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "child reservation, not updating request state to 'pending'");
+	}
+	
 	# Set the PARENTIMAGE and SUBIMAGE keys in the request data hash
 	# These are deprecated, DataStructure's is_parent_reservation function should be used
 	$self->data->get_request_data->{PARENTIMAGE} = ($self->data->is_parent_reservation() + 0);
@@ -183,51 +226,6 @@ sub initialize {
 		}
 	}
 	
-	# If this is a cluster request, wait for all reservations to begin before proceeding
-	if ($reservation_count > 1) {
-		if (!$self->wait_for_all_reservations_to_begin('begin', 90, 5)) {
-			$self->reservation_failed("failed to detect start of processing for all reservation processes");
-		}
-	}
-	
-	# Parent reservation needs to update the request state to pending
-	if ($is_parent_reservation) {
-		if ($reservation_count > 1) {
-			# Check if any reservations have failed
-			if (my @failed_reservation_ids = $self->does_loadstate_exist_any_reservation('failed')) {
-				notify($ERRORS{'WARNING'}, 0, "reservations failed: " . join(', ', @failed_reservation_ids));
-				$self->state_exit('failed');
-			}
-		}
-		
-		# Update the request state to pending for this reservation
-		if (!update_request_state($request_id, "pending", $request_state_name)) {
-			# Check if request was deleted
-			if (is_request_deleted($request_id)) {
-				exit;
-			}
-			
-			# Check the current state
-			my ($current_request_state, $current_request_laststate) = get_request_current_state_name($request_id);
-			if (!$current_request_state) {
-				# Request probably complete and already removed
-				notify($ERRORS{'DEBUG'}, 0, "current request state could not be retrieved, it was probably completed by another vcld process");
-				exit;
-			}
-			if ($current_request_state =~ /^(deleted|complete)$/ || $current_request_laststate =~ /^(deleted)$/) {
-				notify($ERRORS{'DEBUG'}, 0, "current request state: $current_request_state/$current_request_laststate, exiting");
-				exit;
-			}
-			
-			$self->reservation_failed("failed to update request state to pending");
-		}
-	}
-	else {
-		notify($ERRORS{'DEBUG'}, 0, "child reservation, not updating request state to 'pending'");
-	}
-	
-	#notify($ERRORS{'DEBUG'}, 0, "computerloadlog states after state object is initialized:\n" . format_data(get_request_loadstate_names($request_id)));
-	
 	return 1;
 } ## end sub initialize
 
@@ -415,7 +413,8 @@ sub reservation_failed {
 	
 	my $new_request_state_name;
 	my $new_computer_state_name;
-
+	my $request_log_ending;
+	
 	if ($request_state_name eq 'inuse') {
 		# Check if the request end time has not been reached
 		my $request_end_time_epoch = convert_to_epoch_seconds($self->data->get_request_end_time());
@@ -425,6 +424,7 @@ sub reservation_failed {
 			# This was likely caused by this process failing to initialize all of its module objects
 			$new_request_state_name = 'complete';
 			$new_computer_state_name = 'failed';
+			$request_log_ending = 'EOR';
 			notify($ERRORS{'CRITICAL'}, 0, ($initialize_failed ? 'process failed to initialize: ' : '') . "$message, request end time has been reached, setting request state to $new_request_state_name, computer state to $new_computer_state_name");
 		}
 		else {
@@ -455,24 +455,9 @@ sub reservation_failed {
 		}
 	}
 	
-	# Update the request state to failed
-	# Don't check if parent reservation - allow child reservation to change state
-	# Otherwise, multiple failed attempts may be made
-	if (update_request_state($request_id, $new_request_state_name, $request_state_name)) {
-		notify($ERRORS{'OK'}, 0, "set request state to $new_request_state_name/$request_state_name");
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "unable to set request to $new_request_state_name/$request_state_name");
-	}
-	
 	if ($request_state_name =~ /^(new|reserved)/) {
 		# Update log table ending column to failed for this request
-		if (update_log_ending($request_logid, "failed")) {
-			notify($ERRORS{'OK'}, 0, "updated log ending value to 'failed', logid=$request_logid");
-		}
-		else {
-			notify($ERRORS{'WARNING'}, 0, "failed to update log ending value to 'failed', logid=$request_logid");
-		}
+		$request_log_ending = 'failed';
 	}
 	
 	# Insert a row into the computerloadlog table
@@ -483,19 +468,6 @@ sub reservation_failed {
 		notify($ERRORS{'WARNING'}, 0, "failed to insert computerloadlog entry");
 	}
 	
-	# Update the computer state to failed as long as it's not currently maintenance
-	if ($computer_state_name !~ /^(maintenance)/) {
-		if (update_computer_state($computer_id, $new_computer_state_name)) {
-			notify($ERRORS{'OK'}, 0, "computer $computer_short_name ($computer_id) state set to $new_computer_state_name");
-		}
-		else {
-			notify($ERRORS{'WARNING'}, 0, "unable to set computer $computer_short_name ($computer_id) state to $new_computer_state_name");
-		}
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "computer $computer_short_name ($computer_id) state NOT set to $new_computer_state_name because the current state is $computer_state_name");
-	}
-
 	# Check if computer is part of a blockrequest, if so pull out of blockcomputers table
 	if (is_inblockrequest($computer_id)) {
 		notify($ERRORS{'OK'}, 0, "$computer_short_name in blockcomputers table");
@@ -509,9 +481,8 @@ sub reservation_failed {
 	else {
 		notify($ERRORS{'OK'}, 0, "$computer_short_name is NOT in blockcomputers table");
 	}
-
-	notify($ERRORS{'OK'}, 0, "exiting 1");
-	exit 1;
+	
+	$self->state_exit($new_request_state_name, $new_computer_state_name, $request_log_ending);
 } ## end sub reservation_failed
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -558,7 +529,7 @@ sub does_loadstate_exist_all_reservation
 	
 	my @exists;
 	my @does_not_exist;
-	for my $check_reservation_id (keys %$request_loadstate_names) {
+	for my $check_reservation_id (sort {$a <=> $b} keys %$request_loadstate_names) {
 		# Ignore the current reservation
 		if ($ignore_current_reservation && $check_reservation_id eq $reservation_id) {
 			next;
@@ -575,14 +546,19 @@ sub does_loadstate_exist_all_reservation
 	
 	if (@does_not_exist) {
 		notify($ERRORS{'DEBUG'}, 0, "computerloadlog '$loadstate_name' entry does NOT exist for all reservations:\n" .
-			"exists for reservation IDs: " . join(', ', @exists) . "\n" .
-			"does not exist for reservation IDs: " . join(', ', @does_not_exist)
+			"exists for reservation IDs: " . join(', ',  @exists) . "\n" .
+			"does not exist for reservation IDs: " . join(', ',  @does_not_exist)
 		);
-		return 0;
 	}
 	else {
 		notify($ERRORS{'DEBUG'}, 0, "computerloadlog '$loadstate_name' entry exists for all reservations");
-		return 1;
+	}
+	
+	if (wantarray) {
+		return (\@exists, \@does_not_exist);
+	}
+	else {
+		return !scalar(@does_not_exist);
 	}
 }
 
@@ -690,7 +666,7 @@ sub wait_for_all_reservations_to_begin {
 	my $request_id = $self->data->get_request_id();
 	my $request_state_name = $self->data->get_request_state_name();
 	
-	return $self->code_loop_timeout(
+	my $result = $self->code_loop_timeout(
 		sub {
 			if ($request_state_name ne 'deleted' && is_request_deleted($request_id)) {
 				notify($ERRORS{'OK'}, 0, "request has been deleted, exiting");
@@ -702,6 +678,36 @@ sub wait_for_all_reservations_to_begin {
 		[],
 		"waiting for all reservation processes to begin", $total_wait_seconds, $attempt_delay_seconds
 	);
+	
+	if (!$result) {
+		my ($exists, $not_exists) = $self->does_loadstate_exist_all_reservations($loadstate_name, 1);
+		if (!defined($exists) || !defined($not_exists)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to determine if all reservation processes have begun, does_loadstate_exist_all_reservations returned a null value");
+			return;
+		}
+		elsif (!ref($exists) || !ref($not_exists) || ref($exists) ne 'ARRAY' || ref($not_exists) ne 'ARRAY') {
+			notify($ERRORS{'WARNING'}, 0, "failed to determine if all reservation processes have begun, does_loadstate_exist_all_reservations did not return 2 array references:\n1st item returned:\n" . format_data($exists) . "\n2nd item returned:\n" . format_data($not_exists));
+			return;
+		}
+		
+		if (scalar(@$not_exists) == 0) {
+			notify($ERRORS{'DEBUG'}, 0, "detected all reservation processes have begun after loop timed out");
+			return 1;
+		}
+		
+		my $string = '';
+		for my $reservation_id (@$not_exists) {
+			my $management_node_hostname = get_reservation_management_node_hostname($reservation_id) || '<unknown>';
+			$string .= "$reservation_id: $management_node_hostname\n"
+		}
+		$string =~ s/\n$//;
+		
+		notify($ERRORS{'WARNING'}, 0, "failed to determine if processes for the following reservations have begun, computerloadlog '$loadstate_name' entry does not exist:\n$string");
+		return;
+	}
+	
+	
+	return $result;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -711,9 +717,10 @@ sub wait_for_all_reservations_to_begin {
  Parameters  : $total_wait_seconds (optional), $attempt_delay_seconds (optional)
  Returns     : boolean
  Description : Loops until an 'exited' computerloadlog entry exists for all
-               child reservations. Returns false if the loop times out. The
-               default $total_wait_seconds value is 300 seconds. The default
-               $attempt_delay_seconds value is 15 seconds.
+               child reservations which also have a 'begin' entry. Returns false
+               if the loop times out. The default $total_wait_seconds value is
+               300 seconds. The default $attempt_delay_seconds value is 15
+               seconds.
 
 =cut
 
@@ -730,8 +737,35 @@ sub wait_for_child_reservations_to_exit
 	my $request_id = $self->data->get_request_id();
 	my $request_state_name = $self->data->get_request_state_name();
 	
+	my $subroutine_name = get_current_subroutine_name();
+	
 	return $self->code_loop_timeout(
-		\&does_loadstate_exist_all_reservations,
+		sub {
+			if (is_request_deleted($request_id)) {
+				notify($ERRORS{'OK'}, 0, "request has been deleted, exiting");
+				exit;
+			}
+			
+			my ($exited, $not_exited) = $self->does_loadstate_exist_all_reservations('exited', 1);
+			# If no reservations are missing an 'exited' entry return true
+			if (!@$not_exited) {
+				notify($ERRORS{'DEBUG'}, 0, "$subroutine_name: computerloadlog 'exited' entry exists for all reservations");
+				return 1;
+			}
+			
+			# Some reservations are missing an 'exited' entry
+			# Ignore reservations missing both an 'exited' and 'begin' entry
+			my ($began, $not_began) = $self->does_loadstate_exist_all_reservations('begin', 1);
+			my @began_not_exited = get_array_intersection($began, $not_exited);
+			if (@began_not_exited) {
+				notify($ERRORS{'DEBUG'}, 0, "$subroutine_name: reservation exists with a computerloadlog 'begin' entry but no 'exited' entry, returning false\n" . join(', ', @began_not_exited));
+				return 0;
+			}
+			else {
+				notify($ERRORS{'DEBUG'}, 0, "$subroutine_name: no reservations have a computerloadlog 'begin' entry but no 'exited' entry, returning true");
+				return 1;
+			}
+		},
 		[$self, 'exited', 1],
 		"waiting for child reservation processes to exit", $total_wait_seconds, $attempt_delay_seconds
 	);

Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1656840&r1=1656839&r2=1656840&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Tue Feb  3 17:26:49 2015
@@ -173,10 +173,11 @@ our @EXPORT = qw(
 	get_request_current_state_name
 	get_request_end
 	get_request_info
+	get_reservation_accounts
 	get_reservation_computerloadlog_entries
 	get_reservation_computerloadlog_time
+	get_reservation_management_node_hostname
 	get_request_loadstate_names
-	get_reservation_accounts
 	get_resource_groups
 	get_user_group_member_info
 	get_user_info
@@ -5298,21 +5299,27 @@ EOF
 
 =head2 set_reservation_lastcheck
 
- Parameters  : $reservation_id, $lastcheck
+ Parameters  : $lastcheck, @reservation_ids
  Returns     : string
  Description : Updates reservation.lastcheck to the time specified.
 
 =cut
 
 sub set_reservation_lastcheck {
-	my ($reservation_id, $reservation_lastcheck) = @_;
+	my ($reservation_lastcheck, @reservation_ids) = @_;
 	
 	# Check the passed parameter
-	if (!$reservation_id || !$reservation_lastcheck) {
-		notify($ERRORS{'WARNING'}, 0, "reservation ID and last check datetime was not specified");
+	if (!$reservation_lastcheck) {
+		notify($ERRORS{'WARNING'}, 0, "reservation lastcheck argument was not specified");
+		return;
+	}
+	elsif (!@reservation_ids) {
+		notify($ERRORS{'WARNING'}, 0, "reservation ID argument was not specified");
 		return;
 	}
 	
+	my $reservation_id_string = join(", ", @reservation_ids);
+	
 	if ($reservation_lastcheck !~ /:/) {
 		$reservation_lastcheck = convert_to_datetime($reservation_lastcheck);
 	}
@@ -5322,11 +5329,11 @@ sub set_reservation_lastcheck {
 	my $current_time_epoch = time;
 	my $duration_seconds = ($reservation_lastcheck_epoch-$current_time_epoch);
 	if ($duration_seconds < 0) {
-		notify($ERRORS{'WARNING'}, 0, "reservation.lastcheck not set to $reservation_lastcheck for reservation ID $reservation_id, time is in the past");
+		notify($ERRORS{'WARNING'}, 0, "reservation.lastcheck not set to $reservation_lastcheck for reservation IDs: $reservation_id_string, time is in the past");
 		return;
 	}
 	elsif ($duration_seconds < (20*60)) {
-		notify($ERRORS{'WARNING'}, 0, "reservation.lastcheck not set to $reservation_lastcheck for reservation ID $reservation_id, time is too close to the current time");
+		notify($ERRORS{'WARNING'}, 0, "reservation.lastcheck not set to $reservation_lastcheck for reservation IDs: $reservation_id_string, time is too close to the current time");
 		return;
 	}
 	
@@ -5337,16 +5344,16 @@ reservation
 SET
 reservation.lastcheck = '$reservation_lastcheck'
 WHERE
-reservation.id = '$reservation_id'
+reservation.id IN ($reservation_id_string)
 EOF
 
 	# Call the database execute subroutine
 	if (database_execute($update_statement)) {
-		notify($ERRORS{'DEBUG'}, 0, "reservation.lastcheck set to '$reservation_lastcheck' for reservation ID $reservation_id");
+		notify($ERRORS{'DEBUG'}, 0, "reservation.lastcheck set to '$reservation_lastcheck' for reservation IDs: $reservation_id_string");
 		return 1;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to set reservation.lastcheck to '$reservation_lastcheck' for reservation ID $reservation_id");
+		notify($ERRORS{'WARNING'}, 0, "failed to set reservation.lastcheck to '$reservation_lastcheck' for reservation IDs: $reservation_id_string");
 		return;
 	}
 }
@@ -7555,6 +7562,53 @@ EOF
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 get_reservation_management_node_hostname
+
+ Parameters  : $reservation_id, $no_cache (optional)
+ Returns     : string
+ Description : Returns the hostname of the management node assigned to the
+               reservation specified by the argument.
+
+=cut
+
+
+sub get_reservation_management_node_hostname {
+	my ($reservation_id, $no_cache) = @_;
+	if (!defined($reservation_id)) {
+		notify($ERRORS{'WARNING'}, 0, "reservation ID argument was not supplied");
+		return;
+	}
+	
+	if (!$no_cache && defined($ENV{reservation_management_node_hostname}{$reservation_id})) {
+		return $ENV{reservation_management_node_hostname}{$reservation_id};
+	}
+	
+	my $select_statement = <<EOF;
+SELECT
+managementnode.hostname
+FROM
+reservation,
+managementnode
+WHERE
+reservation.id = '$reservation_id'
+AND reservation.managementnodeid = managementnode.id
+EOF
+
+	my @selected_rows = database_select($select_statement);
+	if (!@selected_rows) {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve management node hostname for reservation $reservation_id");
+		return;
+	}
+	
+	my $row = $selected_rows[0];
+	my $hostname = $row->{hostname};
+	notify($ERRORS{'DEBUG'}, 0, "retrieved management node hostname for reservation $reservation_id: hostname");
+	$ENV{reservation_management_node_hostname}{$reservation_id} = $hostname;
+	return $hostname;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 get_reservation_request_id
 
  Parameters  : $reservation_id, $no_cache (optional)
@@ -8538,56 +8592,96 @@ sub reservation_being_processed {
 		return;
 	}
 	
-	my $select_statement = "
-	SELECT
-	computerloadlog.*
-	
-	FROM
-	computerloadlog,
-	computerloadstate
-	
-	WHERE
-	computerloadlog.reservationid = $reservation_id
-	AND computerloadlog.loadstateid = computerloadstate.id
-	AND computerloadstate.loadstatename = \'begin\'
-	";
+	my $select_statement = <<EOF;
+SELECT
+reservation.id AS reservation_id,
+computerloadlog.id AS reservation_computerloadlog_id,
+MIN(parentreservation.id) AS parent_reservation_id,
+parentcomputerloadlog.id AS parent_computerloadlog_id
+FROM
+reservation
+LEFT JOIN (computerloadlog, computerloadstate) ON (
+   computerloadlog.reservationid = reservation.id
+   AND computerloadlog.loadstateid = computerloadstate.id
+   AND computerloadstate.loadstatename = 'begin'
+),
+request,
+reservation parentreservation
+LEFT JOIN (computerloadlog parentcomputerloadlog, computerloadstate parentcomputerloadstate) ON (
+   parentcomputerloadlog.reservationid = parentreservation.id
+   AND parentcomputerloadstate.loadstatename = 'begin'
+)
+WHERE
+reservation.id = $reservation_id
+AND reservation.requestid = request.id
+AND parentreservation.requestid = request.id
+GROUP BY reservation.id
+EOF
 
 	# Call the database select subroutine
 	# This will return an array of one or more rows based on the select statement
 	my @computerloadlog_rows = database_select($select_statement);
 
 	# Check if at least 1 row was returned
-	my $computerloadlog_exists;
-	if (scalar @computerloadlog_rows == 1) {
-		notify($ERRORS{'DEBUG'}, 0, "computerloadlog 'begin' entry exists for reservation $reservation_id");
-		$computerloadlog_exists = 1;
-	}
-	elsif (scalar @computerloadlog_rows > 1) {
-		notify($ERRORS{'WARNING'}, 0, "multiple computerloadlog 'begin' entries exist for reservation $reservation_id");
-		$computerloadlog_exists = 1;
-	}
-	else {
-		notify($ERRORS{'DEBUG'}, 0, "computerloadlog 'begin' entry does NOT exist for reservation $reservation_id");
-		$computerloadlog_exists = 0;
+	my $computerloadlog_exists = 0;
+	
+	my $parent_reservation_id;
+	my $parent_computerloadlog_exists = 0;
+	
+	
+	for my $row (@computerloadlog_rows) {
+		$parent_reservation_id = $row->{parent_reservation_id} if !defined($parent_reservation_id);
+		
+		my $reservation_computerloadlog_id = $row->{reservation_computerloadlog_id};
+		my $parent_computerloadlog_id = $row->{parent_computerloadlog_id};
+		
+		if ($reservation_computerloadlog_id) {
+			$computerloadlog_exists = 1 if (!$computerloadlog_exists);
+		}
+		if ($parent_computerloadlog_id) {
+			$parent_computerloadlog_exists = 1 if (!$parent_computerloadlog_exists);
+		}
 	}
 	
 	# Check if a vcld process is running matching for this reservation
 	my @processes_running = is_management_node_process_running("$PROCESSNAME .\\|[0-9]+\\|[0-9]+\\|$reservation_id\\|");
 	
+	my $info_string = "reservation ID: $reservation_id\n";
+	$info_string .= "parent reservation ID: $parent_reservation_id\n";
+	$info_string .= "reservation computerloadlog 'begin' entry exists: " . ($computerloadlog_exists ? 'yes' : 'no') . "\n";
+	$info_string .= "parent reservation computerloadlog 'begin' entry exists: " . ($parent_computerloadlog_exists ? 'yes' : 'no') . "\n";
+	$info_string .= "reservation process running: " . (@processes_running ? (join(", ", @processes_running)) : 'no');
+	
 	# Check the results and return
 	if ($computerloadlog_exists && @processes_running) {
-		notify($ERRORS{'DEBUG'}, 0, "reservation $reservation_id is currently being processed, computerloadlog 'begin' entry exists and running process was found: @processes_running");
+		#notify($ERRORS{'DEBUG'}, 0, "reservation $reservation_id is currently being processed, computerloadlog 'begin' entry exists and running process was found:\n$info_string");
+		return 1;
 	}
 	elsif (!$computerloadlog_exists && @processes_running) {
-		notify($ERRORS{'DEBUG'}, 0, "computerloadlog 'begin' entry does NOT exist but running process was found: @processes_running, assuming reservation $reservation_id is currently being processed");
+		notify($ERRORS{'DEBUG'}, 0, "computerloadlog 'begin' entry does NOT exist but running process was found: @processes_running, assuming reservation $reservation_id is currently being processed\n$info_string");
+		return 1;
 	}
 	elsif ($computerloadlog_exists && !@processes_running) {
-		notify($ERRORS{'WARNING'}, 0, "computerloadlog 'begin' entry exists but running process was NOT found, assuming reservation $reservation_id is NOT currently being processed");
+		if ($reservation_id eq $parent_reservation_id) {
+			#notify($ERRORS{'WARNING'}, 0, "$reservation_id is the parent reservation, computerloadlog 'begin' entry exists but running process was NOT found, assuming reservation $reservation_id is NOT currently being processed\n$info_string");
+			return 0;
+		}
+		else {
+			# This is a child reservation, computerloadlog exists, no process running for this reservation
+			if ($parent_computerloadlog_exists) {
+				notify($ERRORS{'DEBUG'}, 0, "child reservation: $reservation_id, computerloadlog 'begin' entry exists but running process was NOT found, parent reservation $parent_reservation_id computerloadlog entry exists, assuming a process for this reservation already ran and parent reservation process is still running, returning true\n$info_string");
+				return 1;
+			}
+			else {
+				notify($ERRORS{'DEBUG'}, 0, "child reservation: $reservation_id, computerloadlog 'begin' entry exists but running process was NOT found, parent reservation $parent_reservation_id computerloadlog entry does not exist, assuming this reservation is NOT currently being processed and has not been processed yet, returning false\n$info_string");
+				return 0;
+			}
+		}
 	}
 	else {
-		notify($ERRORS{'DEBUG'}, 0, "reservation $reservation_id is NOT currently being processed");
+		notify($ERRORS{'DEBUG'}, 0, "reservation $reservation_id is NOT currently being processed\n$info_string");
+		return 0;
 	}
-	return wantarray ? @processes_running : scalar(@processes_running);
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -12930,7 +13024,14 @@ sub ip_address_to_network_address {
 
 sub determine_remote_connection_target {
 	my ($argument, $no_cache) = @_;
-	$no_cache = 0 unless defined($no_cache);
+	if (!defined($argument)) {
+		notify($ERRORS{'WARNING'}, 0, "remote connection argument was not supplied");
+		return;
+	}
+	
+	# Check if argument contains an '@' character: root@x.x.x.x
+	# Remove anything preceeding it
+	$argument =~ s/.*@([^@]+)$/$1/g;
 	
 	if (!$no_cache && defined($ENV{remote_connection_target}{$argument})) {
 		return $ENV{remote_connection_target}{$argument};
@@ -12974,7 +13075,10 @@ sub determine_remote_connection_target {
 	
 	if ($resolved_ip_address) {
 		# Attempt to set the private IP address in the database
-		update_computer_private_ip_address($argument, $resolved_ip_address);
+		# Only do this if a private IP was retrieved earlier to avoid additional warnings
+		if ($database_private_ip_address) {
+			update_computer_private_ip_address($argument, $resolved_ip_address);
+		}
 		
 		$ENV{remote_connection_target}{$argument} = $resolved_ip_address;
 		notify($ERRORS{'DEBUG'}, 0, "$argument resolves to IP address $resolved_ip_address, it will be used as the remote connection target");