in managementnode/lib/VCL/reserved.pm [93:303]
sub process {
my $self = shift;
my $request_id = $self->data->get_request_id();
my $request_logid = $self->data->get_request_log_id();
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 $computer_id = $self->data->get_computer_id();
my $computer_short_name = $self->data->get_computer_short_name();
my $is_parent_reservation = $self->data->is_parent_reservation();
my $parent_reservation_id = $self->data->get_parent_reservation_id();
my $is_server_request = $self->data->is_server_request();
my $imagemeta_checkuser = $self->data->get_imagemeta_checkuser();
my $acknowledge_timeout_seconds = $self->os->get_timings('acknowledgetimeout');
my $initial_connect_timeout_seconds = $self->os->get_timings('initialconnecttimeout');
# Update the log loaded time to now for this request
update_log_loaded_time($request_logid);
# Make sure firewall object is initialized early to reduce time it takes to configure things after user clicks Connect
$self->os->firewall() if ($self->os->can('firewall'));
# Update the computer state to reserved
# This causes pending to change to the Connect button on the Current Reservations page
update_computer_state($computer_id, 'reserved');
insertloadlog($reservation_id, $computer_id, "reserved", "$computer_short_name successfully reserved");
if ($is_parent_reservation) {
# Send an email and/or IM to the user
# Do this after updating the computer state to reserved because this is when the Connect button appears
$self->notify_user_ready();
# Insert acknowledgetimeout immediately before beginning to check user clicked Connect
# 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, "acknowledgetimeout", "begin acknowledge timeout ($acknowledge_timeout_seconds seconds)");
}
my $acknowledge_check_start_epoch_seconds = $self->wait_for_reservation_loadstate($parent_reservation_id, "acknowledgetimeout", $acknowledge_timeout_seconds, 5);
if (!$acknowledge_check_start_epoch_seconds) {
notify($ERRORS{'WARNING'}, 0, "failed to retrieve timestamp of parent reservation $parent_reservation_id 'acknowledgetimeout' computerloadlog entry");
return;
}
# Get the current time
my $now_epoch_seconds = time;
# Calculate the exact time when connection checking should end
my $acknowledge_check_end_epoch_seconds = ($acknowledge_check_start_epoch_seconds + $acknowledge_timeout_seconds);
my $acknowledge_timeout_remaining_seconds = ($acknowledge_check_end_epoch_seconds - $now_epoch_seconds);
my $now_string = strftime('%H:%M:%S', localtime($now_epoch_seconds));
my $acknowledge_check_start_string = strftime('%H:%M:%S', localtime($acknowledge_check_start_epoch_seconds));
my $acknowledge_check_end_string = strftime('%H:%M:%S', localtime($acknowledge_check_end_epoch_seconds));
my $acknowledge_timeout_string = strftime('%H:%M:%S', gmtime($acknowledge_timeout_seconds));
my $acknowledge_timeout_remaining_string = strftime('%H:%M:%S', gmtime($acknowledge_timeout_remaining_seconds));
notify($ERRORS{'DEBUG'}, 0, "beginning to check for user acknowledgement:\n" .
"acknowledge check start : $acknowledge_check_start_string\n" .
"acknowledge timeout total : + $acknowledge_timeout_string\n" .
"--------------------------------------\n" .
"acknowledge check end : = $acknowledge_check_end_string\n" .
"current time : - $now_string\n" .
"--------------------------------------\n" .
"acknowledge timeout remaining : = $acknowledge_timeout_remaining_string ($acknowledge_timeout_remaining_seconds seconds)\n"
);
# Wait for the user to acknowledge the request by clicking Connect button or from API
# Note: for server requests, this will always return true because the frontend inserts reservation.remoteIP when the reservation is made
my $user_acknowledged = $self->code_loop_timeout(sub{$self->user_acknowledged()}, [], 'waiting for user acknowledgement', $acknowledge_timeout_remaining_seconds, 1, 10);
if (!$user_acknowledged) {
$self->notify_user_timeout_no_acknowledgement();
$self->state_exit('timeout', 'available', 'noack');
}
# Add noinitialconnection and then delete acknowledgetimeout
insertloadlog($reservation_id, $computer_id, "noinitialconnection", "user clicked Connect");
delete_computerloadlog_reservation($reservation_id, 'acknowledgetimeout');
# For non-server requests, the frontend should have inserted an 'initialconnecttimeout' computerloadlog entry for the parent reservation when the user clicks Connect
# Web uses timestamp of this to determine when next to refresh the page
# The timestamp of this computerloadlog entry will be used to determine when to timeout the connection checking during the inuse state
my $connection_check_start_epoch_seconds;
if ($is_server_request) {
$connection_check_start_epoch_seconds = time;
insertloadlog($parent_reservation_id, $computer_id, "initialconnecttimeout", "begin initial connection timeout ($initial_connect_timeout_seconds seconds)");
}
else {
$connection_check_start_epoch_seconds = get_reservation_computerloadlog_time($parent_reservation_id, 'initialconnecttimeout');
if ($connection_check_start_epoch_seconds) {
notify($ERRORS{'DEBUG'}, 0, "retrieved timestamp of computerloadlog 'initialconnecttimeout' entry inserted by web frontend: $connection_check_start_epoch_seconds");
}
else {
notify($ERRORS{'DEBUG'}, 0, "could not retrieve timestamp of computerloadlog 'initialconnecttimeout' entry, web frontend should have inserted this, inserting new entry");
$connection_check_start_epoch_seconds = time;
insertloadlog($reservation_id, $computer_id, "initialconnecttimeout", "begin initial connection timeout ($initial_connect_timeout_seconds seconds)");
}
}
# Call OS module's grant_access() subroutine which adds user accounts to computer
if ($self->os->can("grant_access") && !$self->os->grant_access()) {
$self->reservation_failed("OS module grant_access failed");
}
# User acknowledged request
# Add the cluster information to the loaded computers if this is a cluster reservation
if ($reservation_count > 1 && !$self->os->update_cluster()) {
$self->reservation_failed("update_cluster failed");
}
# Create a JSON file containing the reservation info
my $enable_experimental_features = get_variable('enable_experimental_features', 0);
if ($enable_experimental_features) {
$self->os->create_reservation_info_json_file();
}
# Check if OS module's post_reserve() subroutine exists
if ($self->os->can("post_reserve") && !$self->os->post_reserve()) {
$self->reservation_failed("OS module post_reserve failed");
}
# Add a 'postreserve' computerloadlog entry
# Do this last - important for cluster reservation timing
# Parent's reserved process will loop until this exists for all child reservations
insertloadlog($reservation_id, $computer_id, "postreserve", "$computer_short_name post reserve successful");
# Get the current time
$now_epoch_seconds = time;
# Calculate the exact time when connection checking should end
my $connection_check_end_epoch_seconds = ($connection_check_start_epoch_seconds + $initial_connect_timeout_seconds);
my $connect_timeout_remaining_seconds = ($connection_check_end_epoch_seconds - $now_epoch_seconds);
$now_string = strftime('%H:%M:%S', localtime($now_epoch_seconds));
my $connection_check_start_string = strftime('%H:%M:%S', localtime($connection_check_start_epoch_seconds));
my $connection_check_end_string = strftime('%H:%M:%S', localtime($connection_check_end_epoch_seconds));
my $connect_timeout_string = strftime('%H:%M:%S', gmtime($initial_connect_timeout_seconds));
my $connect_timeout_remaining_string = strftime('%H:%M:%S', gmtime($connect_timeout_remaining_seconds));
notify($ERRORS{'DEBUG'}, 0, "beginning to check for initial user connection:\n" .
"connection check start : $connection_check_start_string\n" .
"connect timeout total : + $connect_timeout_string\n" .
"--------------------------------------\n" .
"connection check end : = $connection_check_end_string\n" .
"current time : - $now_string\n" .
"--------------------------------------\n" .
"connect timeout remaining : = $connect_timeout_remaining_string ($connect_timeout_remaining_seconds seconds)\n"
);
# 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 initial user connection to $computer_short_name", $connect_timeout_remaining_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 ($is_server_request) {
notify($ERRORS{'OK'}, 0, "never detected user connection, skipping timeout, server reservation");
}
elsif (is_request_deleted($request_id) || $self->request_state_changed()) {
$self->state_exit();
}
else {
$self->notify_user_timeout_no_initial_connection();
$self->state_exit('timeout', 'reserved', 'nologin');
}
}
# Add a line to currentimage.txt indicating it's possible a user logged on to the computer
$self->os->set_tainted_status('user may have logged in');
# Update reservation lastcheck, otherwise inuse request will be processed immediately again
update_reservation_lastcheck($reservation_id);
# Tighten up the firewall
# Process the connect methods again, lock the firewall down to the address the user connected from
my $remote_ip = $self->data->get_reservation_remote_ip();
if ($self->os->can('firewall') && $self->os->firewall->can('process_inuse')) {
$self->os->firewall->process_inuse($remote_ip);
}
else {
if (!$self->os->process_connect_methods($remote_ip, 1)) {
notify($ERRORS{'CRITICAL'}, 0, "failed to process connect methods after user connected to computer");
}
}
# Perform steps after a user makes an initial connection
$self->os->post_initial_connection();
# For cluster reservations, the parent must wait until all child reserved processes have exited
# Otherwise, the state will change to inuse while the child processes are still finishing up the reserved state
# vcld will then fail to fork inuse processes for the child reservations
if ($reservation_count > 1 && $is_parent_reservation) {
if (!$self->code_loop_timeout(sub{$self->wait_for_child_reservations()}, [], "waiting for child reservation reserved processes to complete", 360, 5)) {
$self->reservation_failed('all child reservation reserved processes did not complete');
}
# Parent can't tell if reserved processes on other management nodes have terminated
# Wait a short time in case processes on other management nodes are terminating
sleep 3;
}
# Change the request and computer state to inuse then exit
$self->state_exit('inuse', 'inuse');
} ## end sub process