| #!/usr/bin/perl -w |
| ############################################################################### |
| # $Id$ |
| ############################################################################### |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to You under the Apache License, Version 2.0 |
| # (the "License"); you may not use this file except in compliance with |
| # the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| ############################################################################### |
| |
| =head1 NAME |
| |
| VCL::reserved - Perl module for the VCL reserved state |
| |
| =head1 SYNOPSIS |
| |
| use VCL::reserved; |
| use VCL::utils; |
| |
| # Set variables containing the IDs of the request and reservation |
| my $request_id = 5; |
| my $reservation_id = 6; |
| |
| # Call the VCL::utils::get_request_info subroutine to populate a hash |
| my %request_info = get_request_info($request_id); |
| |
| # Set the reservation ID in the hash |
| $request_info{RESERVATIONID} = $reservation_id; |
| |
| # Create a new VCL::reserved object based on the request information |
| my $reserved = VCL::reserved->new(%request_info); |
| |
| =head1 DESCRIPTION |
| |
| This module supports the VCL "reserved" state. The reserved state is reached |
| after a user creates a reservation from the VCL web page. This module checks |
| whether or not the user has acknowledged the reservation and connected to |
| the machine. Once connected, the reservation will be put into the "inuse" |
| state and the reserved process exits. |
| |
| =cut |
| |
| ############################################################################## |
| package VCL::reserved; |
| |
| # Specify the lib path using FindBin |
| use FindBin; |
| use lib "$FindBin::Bin/.."; |
| |
| # Configure inheritance |
| use base qw(VCL::Module::State); |
| |
| # Specify the version of this module |
| our $VERSION = '2.00'; |
| |
| # Specify the version of Perl to use |
| use 5.008000; |
| |
| use strict; |
| use warnings; |
| use diagnostics; |
| |
| use VCL::utils; |
| |
| ############################################################################## |
| |
| =head1 OBJECT METHODS |
| |
| =cut |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 process |
| |
| Parameters : Reference to current reserved object is automatically passed |
| when invoked as a class method. |
| Returns : 1 if successful, 0 otherwise |
| Description : Processes a reservation in the reserved state. Waits for user |
| acknowledgement and connection. |
| |
| =cut |
| |
| sub process { |
| my $self = shift; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Store hash variables into local variables |
| my $request_data = $self->data->get_request_data; |
| |
| my $request_id = $self->data->get_request_id(); |
| my $request_logid = $self->data->get_request_log_id(); |
| my $reservation_id = $self->data->get_reservation_id(); |
| my $reservation_password = $request_data->{reservation}{$reservation_id}{pw}; |
| my $computer_id = $self->data->get_computer_id(); |
| my $computer_hostname = $self->data->get_computer_host_name(); |
| my $computer_short_name = $self->data->get_computer_short_name(); |
| my $computer_type = $self->data->get_computer_type(); |
| my $computer_ip_address = $self->data->get_computer_ip_address(); |
| my $image_os_name = $self->data->get_image_os_name(); |
| my $image_os_type = $self->data->get_image_os_type(); |
| my $request_forimaging = $self->data->get_request_forimaging; |
| my $image_name = $self->data->get_image_name(); |
| my $user_unityid = $self->data->get_user_login_id(); |
| my $user_standalone = $self->data->get_user_standalone(); |
| my $imagemeta_checkuser = $self->data->get_imagemeta_checkuser(); |
| my $reservation_count = $self->data->get_reservation_count(); |
| |
| |
| # Update the log table, set the loaded time to now for this request |
| if (update_log_loaded_time($request_logid)) { |
| notify($ERRORS{'OK'}, 0, "updated log table, set loaded time to now for id:$request_logid"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update log table while attempting to set id:$request_logid loaded time to now"); |
| } |
| |
| # Figure out if image has usergroupid set in meta data and how many members it has |
| my $imagemeta_usergroupid = ''; |
| my @user_group_members; |
| my $user_group_member_count = 0; |
| if (defined $request_data->{reservation}{$reservation_id}{image}{imagemeta}{usergroupid}) { |
| notify($ERRORS{'OK'}, 0, "imagemeta user group defined $request_data->{reservation}{$reservation_id}{image}{imagemeta}{usergroupid}"); |
| $imagemeta_usergroupid = $request_data->{reservation}{$reservation_id}{image}{imagemeta}{usergroupid}; |
| @user_group_members = getusergroupmembers($imagemeta_usergroupid); |
| $user_group_member_count = scalar @user_group_members; |
| } |
| notify($ERRORS{'OK'}, 0, "imagemeta user group membership count = $user_group_member_count"); |
| |
| my $nodename; |
| my $retval_conn; |
| |
| # Figure out the node name based on the type of computer |
| if ($computer_type eq "blade") { |
| $nodename = $computer_short_name; |
| } |
| elsif ($computer_type eq "lab") { |
| $nodename = $computer_hostname; |
| } |
| elsif ($computer_type eq "virtualmachine") { |
| $nodename = $computer_short_name; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "computer id=$computer_id did not have a type, exiting"); |
| exit; |
| } |
| |
| notify($ERRORS{'OK'}, 0, "computer info: id=$computer_id, type=$computer_type, hostname=$nodename"); |
| notify($ERRORS{'OK'}, 0, "user info: login_id id=$user_unityid, standalone=$user_standalone"); |
| notify($ERRORS{'OK'}, 0, "imagemeta checkuser set to: $imagemeta_checkuser"); |
| notify($ERRORS{'OK'}, 0, "formimaging set to: $request_forimaging"); |
| |
| my $connected = 0; |
| my $curr_time = time(); |
| my $time_limit = 15; |
| my $time_exceeded = 0; |
| my $break = 0; |
| |
| # Create limit to keep our magic under control |
| my $acknowledge_attempts = 0; |
| |
| notify($ERRORS{'OK'}, 0, "begin checking for user acknowledgement"); |
| insertloadlog($reservation_id, $computer_id, "info", "reserved: waiting for user acknowledgement"); |
| |
| ACKNOWLEDGE: |
| $acknowledge_attempts++; |
| |
| # Try to get the remote IP again and update the data hash |
| my $remote_ip = $self->data->get_reservation_remote_ip(); |
| |
| # 0 should be returned if remoteIP isn't set, undefined if an error occurred |
| if (!defined $remote_ip) { |
| notify($ERRORS{'WARNING'}, 0, "failed to determine remote IP"); |
| return; |
| } |
| |
| # Check if remoteIP is defined yet (user has acknowledged) |
| elsif ($remote_ip ne '0') { |
| # User has acknowledged |
| notify($ERRORS{'OK'}, 0, "user acknowledged, remote IP: $remote_ip"); |
| |
| #if cluster reservation - populate parent node with child node information |
| if ($reservation_count > 1) { |
| notify($ERRORS{'OK'}, 0, "cluster reservation, attempting to populate nodes with cluster_info data"); |
| if (update_cluster_info($request_data)) { |
| notify($ERRORS{'OK'}, 0, "updated cluster nodes with cluster infomation"); |
| } |
| } |
| # Attempt to call modularized OS module's grant_access() subroutine |
| if ($self->os->can("grant_access")) { |
| # If grant_access() has been implemented by OS module, |
| # don't check for remote IP and open RDP firewall port directly in this module |
| # OS module's grant_access() subroutine to perform the same tasks as below |
| notify($ERRORS{'OK'}, 0, "calling " . ref($self->os) . "::grant_access() subroutine"); |
| if ($self->os->grant_access()) { |
| notify($ERRORS{'OK'}, 0, "OS access has been granted on $nodename"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to grant OS access on $nodename"); |
| } |
| } |
| else{ |
| notify($ERRORS{'CRITICAL'}, 0,"failed to grant access" . ref($self->os) . "::grant_access() subroutine not implemented"); |
| insertloadlog($reservation_id, $computer_id, "failed", "failed to grant access, grant_access "); |
| return; |
| } |
| |
| # Check if OS module's post_reserve() subroutine exists |
| if ($self->os->can("post_reserve")) { |
| notify($ERRORS{'DEBUG'}, 0, ref($self->os) . "->post_reserve() subroutine exists"); |
| |
| # Call OS module's post_reserve() subroutine |
| notify($ERRORS{'DEBUG'}, 0, "calling " . ref($self->os) . "->post_reserve() subroutine"); |
| insertloadlog($reservation_id, $computer_id, "info", "calling " . ref($self->os) . "->post_reserve() subroutine"); |
| if ($self->os->post_reserve()) { |
| notify($ERRORS{'OK'}, 0, "performed OS post_reserve tasks for $image_name on $computer_short_name"); |
| insertloadlog($reservation_id, $computer_id, "info", "performed OS post_reserve tasks for $image_name on $computer_short_name"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to perform OS post_reserve tasks for $image_name on $computer_short_name"); |
| insertloadlog($reservation_id, $computer_id, "info", "failed to perform OS post_reserve tasks for $image_name on $computer_short_name"); |
| } |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, ref($self->os) . "->post_reserve() not implemented by " . ref($self->os)); |
| } |
| |
| } # close if defined remoteIP |
| |
| elsif ($acknowledge_attempts < 180) { |
| # User has approximately 15 minutes to acknowledge (5 seconds * 180 attempts) |
| |
| # Print a status message every tenth attempt |
| if (($acknowledge_attempts % 10) == 0) { |
| # Print message every tenth attempt |
| notify($ERRORS{'OK'}, 0, "attempt $acknowledge_attempts of 180, user has not acknowleged"); |
| } |
| |
| sleep 5; |
| |
| # Check if user deleted the request |
| if (is_request_deleted($request_id)) { |
| notify($ERRORS{'OK'}, 0, "user has deleted the request, exiting"); |
| exit; |
| } |
| |
| # Going back to check for user acknowledgment again |
| goto ACKNOWLEDGE; |
| |
| } # Close acknowledge attempts < 120 |
| |
| |
| else { |
| # Acknowledge attemtps >= 120 |
| # User never acknowledged reques, return noack |
| notify($ERRORS{'OK'}, 0, "user never acknowleged request, proceed to timeout"); |
| |
| # Check if user deleted the request |
| if (is_request_deleted($request_id)) { |
| notify($ERRORS{'OK'}, 0, "user has deleted the request, exiting"); |
| exit; |
| } |
| |
| $retval_conn = "noack"; |
| |
| # Skipping check_connection code |
| goto RETVALCONN; |
| } ## end else [ if (defined $remote_ip && $remote_ip eq '0') [... [elsif ($acknowledge_attempts < 180) |
| |
| |
| # Determine if connection needs to be checked based on imagemeta checkuser flag |
| if (!$imagemeta_checkuser) { |
| # If checkuser = 1, check for a user connection |
| # If checkuser = 0, set as inuse and return |
| notify($ERRORS{'OK'}, 0, "checkuser flag set to 0, skipping user connection"); |
| $retval_conn = "connected"; |
| goto RETVALCONN; |
| } |
| # Check if cluster request |
| elsif ($reservation_count > 1) { |
| notify($ERRORS{'OK'}, 0, "reservation count is $reservation_count, skipping user connection check"); |
| $retval_conn = "connected"; |
| goto RETVALCONN; |
| } |
| # Check if forimaging |
| elsif ($request_forimaging){ |
| notify($ERRORS{'OK'}, 0, "reservation is for image creation skipping user connection check"); |
| $retval_conn = "connected"; |
| goto RETVALCONN; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "checkuser flag=1 imageosname is $image_os_name, checking for user connection by $user_unityid"); |
| $retval_conn = check_connection($nodename, $computer_ip_address, $computer_type, $remote_ip, $time_limit, $image_os_name, 0, $request_id, $user_unityid,$image_os_type); |
| } ## end else [ if (!$imagemeta_checkuser) |
| |
| RETVALCONN: |
| notify($ERRORS{'OK'}, 0, "retval_conn = $retval_conn"); |
| |
| # Check the return value and perform some actions |
| if ($retval_conn eq "deleted") { |
| notify($ERRORS{'OK'}, 0, "user deleted request, exiting"); |
| exit; |
| } |
| |
| elsif ($retval_conn eq "connected") { |
| # User is connected, update state of the request, computer and set lastcheck time to current time |
| notify($ERRORS{'OK'}, 0, "$remote_ip connected to $nodename"); |
| |
| insertloadlog($reservation_id, $computer_id, "connected", "reserved: user connected to remote machine"); |
| |
| # Update the request state to either inuse or imageinuse |
| if (update_request_state($request_id, "inuse", "reserved")) { |
| notify($ERRORS{'OK'}, 0, "setting request into inuse state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set request into inuse state"); |
| } |
| |
| # Update the computer state to inuse |
| if (update_computer_state($computer_id, "inuse")) { |
| notify($ERRORS{'OK'}, 0, "setting computerid $computer_id into inuse state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set computerid $computer_id into inuse state"); |
| } |
| |
| # Update the lastcheck value for this reservation to now |
| if (update_reservation_lastcheck($reservation_id)) { |
| notify($ERRORS{'OK'}, 0, "updated lastcheck time for reservation $reservation_id"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to update lastcheck time for reservation $reservation_id"); |
| } |
| |
| notify($ERRORS{'OK'}, 0, "exiting"); |
| exit; |
| } ## end elsif ($retval_conn eq "connected") [ if ($retval_conn eq "deleted") |
| |
| elsif ($retval_conn eq "conn_wrong_ip") { |
| # does the same as above, until we make a firm decision as to how to handle this |
| |
| # Update the request state to inuse |
| if (update_request_state($request_id, "inuse", "reserved")) { |
| notify($ERRORS{'OK'}, 0, "setting request into inuse state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set request into inuse state"); |
| } |
| |
| # Update the computer state to inuse |
| if (update_computer_state($computer_id, "inuse")) { |
| notify($ERRORS{'OK'}, 0, "setting computerid $computer_id into inuse state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set computerid $computer_id into inuse state"); |
| } |
| |
| # Update the lastcheck value for this reservation to now |
| if (update_reservation_lastcheck($reservation_id)) { |
| notify($ERRORS{'OK'}, 0, "updated lastcheck time for reservation $reservation_id"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to update lastcheck time for reservation $reservation_id"); |
| } |
| |
| notify($ERRORS{'OK'}, 0, "exiting"); |
| exit; |
| } ## end elsif ($retval_conn eq "conn_wrong_ip") [ if ($retval_conn eq "deleted") |
| |
| elsif ($retval_conn eq "nologin") { |
| #user ack'd but did not login |
| notify($ERRORS{'OK'}, 0, "user acknowledged but did not log in"); |
| |
| # Update the request state to timeout |
| if (update_request_state($request_id, "timeout", "reserved")) { |
| notify($ERRORS{'OK'}, 0, "setting request into timeout state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set request into timeout state"); |
| } |
| |
| # Update the computer state to timeout |
| if (update_computer_state($computer_id, "timeout")) { |
| notify($ERRORS{'OK'}, 0, "setting computerid $computer_id into timeout state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set computerid $computer_id into timeout state"); |
| } |
| |
| $self->_notify_user_timeout($request_data); |
| |
| # Update the entry in the log table with the current finalend time and ending set to nologin |
| if (update_log_ending($request_logid, "nologin")) { |
| notify($ERRORS{'OK'}, 0, "log id $request_logid was updated and ending set to nologin"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "log id $request_logid could not be updated and ending set to nologin"); |
| } |
| |
| insertloadlog($reservation_id, $computer_id, "info", "reserved: timing out user not connected to remote machine"); |
| |
| notify($ERRORS{'OK'}, 0, "exiting"); |
| exit; |
| } ## end elsif ($retval_conn eq "nologin") [ if ($retval_conn eq "deleted") |
| elsif ($retval_conn eq "noack") { |
| # set to timeout state |
| #user never ack'd |
| notify($ERRORS{'OK'}, 0, "user never acknowledged"); |
| |
| # Update the request state to timeout |
| if (update_request_state($request_id, "timeout", "reserved")) { |
| notify($ERRORS{'OK'}, 0, "setting request into timeout state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set request into timeout state"); |
| } |
| |
| # Update the computer state to timeout |
| if (update_computer_state($computer_id, "timeout")) { |
| notify($ERRORS{'OK'}, 0, "setting computerid $computer_id into timeout state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set computerid $computer_id into timeout state"); |
| } |
| |
| $self->_notify_user_timeout($request_data); |
| |
| # Update the entry in the log table with the current finalend time and ending set to noack |
| if (update_log_ending($request_logid, "noack")) { |
| notify($ERRORS{'OK'}, 0, "log id $request_logid was updated and ending set to noack"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "log id $request_logid could not be updated and ending set to noack"); |
| } |
| |
| insertloadlog($reservation_id, $computer_id, "info", "reserved: timing out user not acknowledged reservation"); |
| |
| notify($ERRORS{'OK'}, 0, "exiting"); |
| exit; |
| } ## end elsif ($retval_conn eq "noack") [ if ($retval_conn eq "deleted") |
| elsif ($retval_conn eq "failed") { |
| # Update the request state to failed |
| notify($ERRORS{'OK'}, 0, "failed to reserve machine"); |
| |
| if (update_request_state($request_id, "failed", "reserved")) { |
| notify($ERRORS{'OK'}, 0, "setting request into failed state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set request into failed state"); |
| } |
| |
| # Update the computer state to failed |
| if (update_computer_state($computer_id, "failed")) { |
| notify($ERRORS{'OK'}, 0, "setting computerid $computer_id into failed state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set computerid $computer_id into failed state"); |
| } |
| |
| # Update the entry in the log table with the current finalend time and ending set to noack |
| if (update_log_ending($request_logid, "failed")) { |
| notify($ERRORS{'OK'}, 0, "log id $request_logid was updated and ending set to failed"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "log id $request_logid could not be updated and ending set to failed"); |
| } |
| |
| notify($ERRORS{'OK'}, 0, "exiting"); |
| exit; |
| } ## end elsif ($retval_conn eq "failed") [ if ($retval_conn eq "deleted") |
| |
| elsif ($retval_conn eq "timeout") { |
| # Update the request state to timeout |
| notify($ERRORS{'OK'}, 0, "reservation timed out"); |
| |
| if (update_request_state($request_id, "timeout", "reserved")) { |
| notify($ERRORS{'OK'}, 0, "setting request into timeout state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set request into timeout state"); |
| } |
| |
| # Update the computer state to timeout |
| if (update_computer_state($computer_id, "timeout")) { |
| notify($ERRORS{'OK'}, 0, "setting computerid $computer_id into timeout state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to set computerid $computer_id into timeout state"); |
| } |
| |
| # Update the entry in the log table with the current finalend time and ending set to timeout |
| if (update_log_ending($request_logid, "timeout")) { |
| notify($ERRORS{'OK'}, 0, "log id $request_logid was updated and ending set to timeout"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "log id $request_logid could not be updated and ending set to timeout"); |
| } |
| |
| notify($ERRORS{'OK'}, 0, "exiting"); |
| exit; |
| } ## end elsif ($retval_conn eq "timeout") [ if ($retval_conn eq "deleted") |
| } ## end sub process |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 _notify_user_timeout |
| |
| Parameters : $request_data_hash_reference |
| Returns : 1 if successful, 0 otherwise |
| Description : Notifies the user that the request has timed out becuase no |
| initial connection was made. An e-mail and/or IM message will |
| be sent to the user. |
| |
| =cut |
| |
| sub _notify_user_timeout { |
| my $self = shift; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| my $request_id = $self->data->get_request_id(); |
| my $reservation_id = $self->data->get_reservation_id(); |
| my $user_email = $self->data->get_user_email(); |
| my $user_emailnotices = $self->data->get_user_emailnotices(); |
| my $user_im_name = $self->data->get_user_imtype_name(); |
| my $user_im_id = $self->data->get_user_im_id(); |
| my $affiliation_sitewwwaddress = $self->data->get_user_affiliation_sitewwwaddress(); |
| my $affiliation_helpaddress = $self->data->get_user_affiliation_helpaddress(); |
| my $image_prettyname = $self->data->get_image_prettyname(); |
| my $computer_ip_address = $self->data->get_computer_ip_address(); |
| |
| my $message = <<"EOF"; |
| |
| Your reservation has timed out for image $image_prettyname at address $computer_ip_address because no initial connection was made. |
| |
| To make another reservation, please revisit $affiliation_sitewwwaddress. |
| |
| Thank You, |
| VCL Team |
| |
| |
| ****************************************************************** |
| This is an automated notice. If you need assistance please respond |
| with detailed information on the issue and a help ticket will be |
| generated. |
| |
| To disable email notices |
| -Visit $affiliation_sitewwwaddress |
| -Select User Preferences |
| -Select General Preferences |
| |
| ****************************************************************** |
| EOF |
| |
| my $subject = "VCL -- Reservation Timeout"; |
| |
| if ($user_emailnotices) { |
| #if "0" user does not care to get additional notices |
| mail($user_email, $subject, $message, $affiliation_helpaddress); |
| notify($ERRORS{'OK'}, 0, "sent reservation timeout e-mail to $user_email"); |
| } |
| if ($user_im_name ne "none") { |
| notify_via_IM($user_im_name, $user_im_id, $message); |
| notify($ERRORS{'OK'}, 0, "sent reservation timeout IM to $user_im_name"); |
| } |
| return 1; |
| } ## end sub _notify_user_timeout |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |