sub process()

in managementnode/lib/VCL/inuse.pm [102:340]


sub process {
	my $self = shift;
	
	my $request_id            = $self->data->get_request_id();
	my $request_state_name    = $self->data->get_request_state_name();
	my $request_start         = $self->data->get_request_start_time();
	my $request_end           = $self->data->get_request_end_time();
	my $request_forimaging    = $self->data->get_request_forimaging();
	my $request_checkuser     = $self->data->get_request_checkuser();
	my $reservation_id        = $self->data->get_reservation_id();
	my $reservation_count     = $self->data->get_reservation_count();
	my $server_request_id     = $self->data->get_server_request_id();
	my $imagemeta_checkuser   = $self->data->get_imagemeta_checkuser();
	my $is_parent_reservation = $self->data->is_parent_reservation();
	my $computer_id           = $self->data->get_computer_id();
	my $computer_short_name   = $self->data->get_computer_short_name();
	
	my $connect_timeout_seconds = $self->os->get_timings('reconnecttimeout');
	
	# Check if reboot operation was requested
	if ($request_state_name =~ /reboot/) {
		if ($self->os->can('reboot')) {
			if (!$self->os->reboot()) {
				notify($ERRORS{'CRITICAL'}, 0, "user requested reboot of $computer_short_name failed");
			}
		}
		else {
			notify($ERRORS{'CRITICAL'}, 0, "'$request_state_name' operation requested, " . ref($self->os) . " does not implement a 'reboot' subroutine");
		}
		
		$self->state_exit('inuse', 'inuse');
	}
	
	# Check if server reservation has been modified
	if ($request_state_name =~ /servermodified/) {
		if (!$self->os->add_user_accounts()) {
			notify($ERRORS{'CRITICAL'}, 0, "failed to update server access");
      }
		$self->state_exit('inuse', 'inuse');
	}
	
	# Make sure connect timeout is long enough
	# It has to be a bit longer than the ~5 minute period between inuse checks due to cluster reservations
	# If too short, a user may be connected to one computer in a cluster and another inuse process times out before the connected computer is checked
	my $connect_timeout_minutes = ceil($connect_timeout_seconds / 60);
	
	# Connect timeout must be in whole minutes
	$connect_timeout_seconds = ($connect_timeout_minutes * 60);
	
	my $now_epoch_seconds = time;
	
	my $request_start_epoch_seconds = convert_to_epoch_seconds($request_start);
	my $request_end_epoch_seconds = convert_to_epoch_seconds($request_end);
	
	my $request_remaining_seconds = ($request_end_epoch_seconds - $now_epoch_seconds);
	my $request_remaining_minutes = floor($request_remaining_seconds / 60);
	
	my $request_duration_seconds = ($request_end_epoch_seconds - $request_start_epoch_seconds);
	my $request_duration_hours = floor($request_duration_seconds / 60 / 60);
	
	my $end_time_notify_seconds = $self->os->get_timings('general_end_notice_first');
	my $end_time_notify_minutes = floor($end_time_notify_seconds / 60);
	my $second_end_time_notify_seconds = $self->os->get_timings('general_end_notice_second');
	my $second_end_time_notify_minutes = floor($second_end_time_notify_seconds / 60);
	
	my $now_string               = strftime('%H:%M:%S', localtime($now_epoch_seconds));
	my $request_end_string       = strftime('%H:%M:%S', localtime($request_end_epoch_seconds));
	my $request_remaining_string = strftime('%H:%M:%S', gmtime($request_remaining_seconds));
	my $end_time_notify_string   = strftime('%H:%M:%S', gmtime($end_time_notify_seconds));
	my $connect_timeout_string   = strftime('%H:%M:%S', gmtime($connect_timeout_seconds));
	
	# Check if near the end time
	# Compare remaining minutes to connect timeout minutes in case this is > 15 minutes
	if ($request_remaining_minutes <= ($end_time_notify_minutes + 6)) {
		# Only 1 reservation needs to handle the end time countdown
		if (!$is_parent_reservation) {
			notify($ERRORS{'OK'}, 0, "request end time countdown handled by parent reservation, exiting");
			$self->state_exit();
		}
		
		my $sleep_seconds = ($request_remaining_seconds - $end_time_notify_seconds);
		if ($sleep_seconds > 0) {
			my $sleep_string = strftime('%H:%M:%S', gmtime($sleep_seconds));
			notify($ERRORS{'OK'}, 0, "request end time is near, sleeping for $sleep_seconds seconds:\n" .
				"current time     : $now_string\n" .
				"request end time : $request_end_string\n" .
				"remaining time   : $request_remaining_string\n" .
				"notify time      : $end_time_notify_string\n" .
				"sleep time       : $sleep_string"
			);
			sleep $sleep_seconds;
		}
		else {
			notify($ERRORS{'WARNING'}, 0, "request notify end time has passed:\n" .
				"current time           : $now_string\n" .
				"request end time       : $request_end_string\n" .
				"remaining time         : $request_remaining_string\n" .
				"notify time            : $end_time_notify_string"
			);
		}
		
		# Loop for $end_time_notify_minutes regardless of how much time is actually left
		# The time until request.end may be short if vcld wasn't running
		# This gives the user notice that the request is ending
		for (my $iteration = 0; $iteration <= $end_time_notify_minutes; ++$iteration) {
			$request_remaining_minutes = ($end_time_notify_minutes - $iteration);
			notify($ERRORS{'OK'}, 0, "minutes until end of end of request: $request_remaining_minutes");
			
			# Check if the request state changed for any reason
			# This will occur if the user deletes the request, makeproduction is initiated, reboot is initiated, image capture is started
			if ($self->request_state_changed()) {
				$self->state_exit();
			}
			
			# Get the current request end time from the database
			my $current_request_end = get_request_end($request_id);
			my $current_request_end_epoch_seconds = convert_to_epoch_seconds($current_request_end);
			
			# Check if the user extended the request
			if ($current_request_end_epoch_seconds > $request_end_epoch_seconds) {
				notify($ERRORS{'OK'}, 0, "user extended request, end time: $request_end --> $current_request_end, returning request to inuse state");
				$self->state_exit('inuse', 'inuse');
			}
			
			# Notify user when 5 or 10 minutes remain
			if ($request_remaining_minutes == $second_end_time_notify_minutes || $request_remaining_minutes == $end_time_notify_minutes) {
				$self->notify_user_endtime_imminent("$request_remaining_minutes minutes");
			}
			
			if ($iteration < $end_time_notify_minutes) {
				notify($ERRORS{'OK'}, 0, "sleeping for 60 seconds");
				sleep 60;
			}
		}
		
		# Notify user - endtime and image capture has started
		$self->notify_user_endtime_reached();
		
		# Initiate auto-capture process if this is an imaging request and not a cluster reservation
		if ($request_forimaging && $reservation_count == 1) {
			notify($ERRORS{'OK'}, 0, "initiating image auto-capture process");
			if (!$self->start_imaging_request()) {
				notify($ERRORS{'CRITICAL'}, 0, "failed to initiate image auto-capture process, changing request and computer state to maintenance");
				$self->state_exit('maintenance', 'maintenance');
			}
			#Successful, cleanly exit with no state change
			$self->state_exit()
		}
		
		$self->state_exit('timeout', 'timeout', 'EOR');
	}
	
	# If duration is greater than 24 hours perform end time notice checks
	if ($is_parent_reservation && $request_duration_hours >= 24) {
		notify($ERRORS{'DEBUG'}, 0, "checking end time notice interval, request duration: $request_duration_hours hours, parent reservation: $is_parent_reservation");
		# Check end time for a notice interval - returns 0 if no notice is to be given
		my $notice_interval = check_endtimenotice_interval($request_end);
		if ($notice_interval) {
			$self->notify_user_future_endtime($notice_interval);
		}
	}
	else {
		notify($ERRORS{'DEBUG'}, 0, "skipping end time notice interval check, request duration: $request_duration_hours hours, parent reservation: $is_parent_reservation");
	}
	
	# Check if the computer is responding to SSH
	# Skip connection checks if the computer is not responding to SSH
	# This prevents a reservatino from timing out if the user is actually connected but SSH from the management node isn't working
	if (!$self->os->is_ssh_responding()) {
		notify($ERRORS{'OK'}, 0, "$computer_short_name is not responding to SSH, skipping user connection check");
		$self->state_exit('inuse', 'inuse');
	}
	
	# Update the firewall if necessary - this is what allows a user to click Connect from different locations
	if ($self->os->can('firewall_compare_update')) {
		$self->os->firewall_compare_update();
	}
	
	# Compare remaining minutes to connect timeout
	# Connect timeout may be longer than 15 minutes
	# Make sure connect timeout doesn't run into the end time notice
	if ($request_remaining_minutes < ($connect_timeout_minutes + $end_time_notify_minutes)) {
		notify($ERRORS{'DEBUG'}, 0, "skipping user connection check, connect timeout would run into the end time notice stage:\n" .
			"current time     : $now_string\n" .
			"request end time : $request_end_string\n" .
			"remaining time   : $request_remaining_string\n" .
			"notify time      : $end_time_notify_string\n" . 
			"connect timeout  : $connect_timeout_string"
		);
		$self->state_exit('inuse', 'inuse');
	}
	
	# TODO: fix user connection checking for cluster requests
	if ($reservation_count > 1) {
		notify($ERRORS{'OK'}, 0, "skipping user connection check for cluster request");
		$self->state_exit('inuse', 'inuse');
	}
	
	# Insert reconnecttimeout immediately before beginning to check for user connection
	# Web uses timestamp of this to determine when next to refresh the page
	# Important because page should refresh as soon as possible to reservation timing out
	insertloadlog($reservation_id, $computer_id, "reconnecttimeout", "begin reconnection timeout ($connect_timeout_seconds seconds)");
	
	# Check to see if user is connected. user_connected will true(1) for servers and requests > 24 hours
	my $user_connected = $self->code_loop_timeout(sub{$self->user_connected()}, [], "waiting for user to connect to $computer_short_name", $connect_timeout_seconds, 15);
	
	# Delete the connecttimeout immediately after acknowledgement loop ends
	delete_computerloadlog_reservation($reservation_id, 'connecttimeout');
	
	if (!$user_connected) {
		if (!$imagemeta_checkuser || !$request_checkuser) {
			notify($ERRORS{'OK'}, 0, "never detected user connection, skipping timeout, imagemeta checkuser: $imagemeta_checkuser, request checkuser: $request_checkuser");
		}
		elsif ($server_request_id) {
			notify($ERRORS{'OK'}, 0, "never detected user connection, skipping timeout, server reservation");
		}
		elsif ($request_forimaging) {
			notify($ERRORS{'OK'}, 0, "never detected user connection, skipping timeout, imaging reservation");
		}
		elsif ($reservation_count > 1) {
			notify($ERRORS{'OK'}, 0, "never detected user connection, skipping timeout, cluster reservation");
		}
		elsif ($request_duration_hours > 24) {
			notify($ERRORS{'OK'}, 0, "never detected user connection, skipping timeout, request duration: $request_duration_hours hours");
		}
		elsif (is_request_deleted($request_id) || $self->request_state_changed()) {
			$self->state_exit();
		}
		else {
			# Update reservation lastcheck, otherwise request will be processed immediately again
			update_reservation_lastcheck($reservation_id);
			
			$self->notify_user_timeout_inactivity();
			$self->state_exit('timeout', 'inuse', 'timeout');
		}
	}
	
	$self->state_exit('inuse', 'inuse');
}