| #!/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::reclaim - Perl module for the VCL reclaim state |
| |
| =head1 SYNOPSIS |
| |
| use VCL::reclaim; |
| 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::reclaim object based on the request information |
| my $reclaim = VCL::reclaim->new($request_info); |
| |
| =head1 DESCRIPTION |
| |
| This module supports the VCL "reclaim" state. |
| |
| =cut |
| |
| ############################################################################## |
| package VCL::reclaim; |
| |
| # 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.4.1'; |
| |
| # 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 state object |
| Returns : Nothing, process always exits |
| Description : Processes a reservation in the timeout and deleted states. |
| |
| =cut |
| |
| sub process { |
| my $self = shift; |
| |
| # Get required data |
| my $request_data = $self->data->get_request_data(); |
| my $reservation_id = $self->data->get_reservation_id(); |
| my $request_state_name = $self->data->get_request_state_name(); |
| my $request_laststate_name = $self->data->get_request_laststate_name(); |
| my $computer_id = $self->data->get_computer_id(); |
| my $computer_type = $self->data->get_computer_type(); |
| my $computer_shortname = $self->data->get_computer_short_name(); |
| my $computer_state_name = $self->data->get_computer_state_name(); |
| my $computer_currentimage_name = $self->data->get_computer_currentimage_name(0); |
| my $server_request_id = $self->data->get_server_request_id(); |
| my $public_ip_configuration = $self->data->get_management_node_public_ip_configuration() || return; |
| my @reservation_ids = $self->data->get_reservation_ids(); |
| |
| # Remove related fixedIPsr variable, if it exists |
| if ($server_request_id) { |
| my $variable_name = "fixedIPsr" . $server_request_id; |
| if (is_variable_set($variable_name)) { |
| #Delete from variable table. |
| my $delete_sql_statement = "DELETE variable FROM variable WHERE name = '$variable_name' "; |
| if (database_execute($delete_sql_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "Deleted server reservation entry for $variable_name from variable table"); |
| } |
| } |
| |
| if ($public_ip_configuration =~ /static/i) { |
| my $original_IPvalue = "originalIPaddr_" . $server_request_id; |
| if (is_variable_set($original_IPvalue)) { |
| my $original_Public_IP = get_variable($original_IPvalue); |
| if (update_computer_public_ip_address($computer_id, $original_Public_IP)) { |
| notify($ERRORS{'DEBUG'}, 0, "restored original IP address: $original_Public_IP for $computer_state_name "); |
| } |
| my $delete_sql_statement = "DELETE variable FROM variable WHERE name = '$original_IPvalue' "; |
| if (database_execute($delete_sql_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "Deleted server reservation entry for $original_IPvalue from variable table"); |
| } |
| } |
| } |
| } |
| |
| # Clean up rules on the NAT host if NAT is used |
| if ($self->nathost_os(0)) { |
| my $nathost_hostname = $self->data->get_nathost_hostname(); |
| if ($self->nathost_os->firewall()) { |
| if ($self->nathost_os->firewall->can('sanitize_reservation')) { |
| if (!$self->nathost_os->firewall->sanitize_reservation()) { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to sanitize firewall for reservation on NAT host $nathost_hostname"); |
| } |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to sanitize firewall for reservation on NAT host $nathost_hostname, " . ref($self->nathost_os->firewall) . " does not implement a 'sanitize_reservation' subroutine"); |
| } |
| |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to sanitize firewall for reservation on NAT host $nathost_hostname, NAT host OS firewall object is not available"); |
| } |
| } |
| |
| # Insert into computerloadlog if request state = timeout |
| if ($request_state_name =~ /timeout|deleted/) { |
| insertloadlog($reservation_id, $computer_id, $request_state_name, "reclaim: starting $request_state_name process"); |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "beginning to reclaim $computer_shortname:\nrequest state: $request_state_name\nrequest laststate: $request_laststate_name\ncomputer state: $computer_state_name\ncomputer type: $computer_type"); |
| |
| # Don't attempt to do anything to machines that are currently reloading |
| if ($computer_state_name =~ /maintenance|reloading/) { |
| notify($ERRORS{'OK'}, 0, "computer in $computer_state_name state, nothing needs to be done to the computer"); |
| } |
| # If request laststate = new, nothing needs to be done |
| elsif ($request_laststate_name =~ /new/) { |
| notify($ERRORS{'OK'}, 0, "request laststate is $request_laststate_name, nothing needs to be done to the computer"); |
| } |
| # Lab computers only need to be sanitized (have sshd disabled) |
| elsif ($computer_type =~ /lab/) { |
| notify($ERRORS{'OK'}, 0, "computer type is $computer_type, computer will be sanitized"); |
| $self->call_os_sanitize(); |
| } |
| # If request laststate = reserved, user did not log in |
| # Make sure image loaded on computer (currentimage.txt) matches what's set in computer.currentimageid |
| elsif ($request_laststate_name =~ /reserved/) { |
| notify($ERRORS{'DEBUG'}, 0, "request laststate is $request_laststate_name, checking if computer table current image matches image currently loaded on $computer_shortname"); |
| |
| ## If server reservations and in reserved - reload |
| #if ($server_request_id) { |
| # notify($ERRORS{'DEBUG'}, 0, "Detected server reservations, computer will be reloaded"); |
| # $self->insert_reload_and_exit(); |
| #} |
| |
| # Make sure computer current image name was retrieved from the database |
| if (!$computer_currentimage_name) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve computer current image name from the database, computer will be reloaded"); |
| $self->insert_reload_and_exit(); |
| } |
| |
| # Reload the computer if unable to retrieve the current image name |
| my $os_current_image_name = $self->os->get_current_image_info("current_image_name"); |
| if (!$os_current_image_name) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve name of image currently loaded on $computer_shortname, computer will be reloaded"); |
| $self->insert_reload_and_exit(); |
| } |
| |
| # Compare the database current image value with what's on the computer |
| if ($computer_currentimage_name eq $os_current_image_name) { |
| notify($ERRORS{'DEBUG'}, 0, "computer table current image name ($computer_currentimage_name) matches image name on computer ($os_current_image_name), computer will be sanitized"); |
| $self->call_os_sanitize(); |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "computer table current image name ($computer_currentimage_name) does NOT match image name on computer ($os_current_image_name), computer will be reloaded"); |
| $self->insert_reload_and_exit(); |
| } |
| } |
| # Request laststate is not reserved, user logged in |
| else { |
| notify($ERRORS{'OK'}, 0, "request laststate is $request_laststate_name, computer will be reloaded"); |
| $self->insert_reload_and_exit(); |
| } |
| |
| |
| |
| # Update the request state to complete and exit |
| # Set the computer state to available if it isn't in the maintenance or reloading state |
| if ($computer_state_name =~ /maintenance|reloading/) { |
| notify($ERRORS{'OK'}, 0, "$computer_shortname in $computer_state_name state, skipping state update to available"); |
| switch_state($request_data, 'complete', '', '', '1'); |
| } |
| else { |
| switch_state($request_data, 'complete', 'available', '', '1'); |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "exiting"); |
| exit; |
| |
| } ## end sub process |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 insert_reload_and_exit |
| |
| Parameters : Reference to state object |
| Returns : Nothing, process always exits |
| Description : -Retrieves the next image to be loaded on the computer based on |
| a predictive loading algorithm |
| -Inserts a new reload request |
| -Sets the state of the request being processed to complete |
| -Sets the state of the computer to reload |
| |
| =cut |
| |
| sub insert_reload_and_exit { |
| my $self = shift; |
| my $request_data = $self->data->get_request_data; |
| my $computer_id = $self->data->get_computer_id(); |
| my $computer_shortname = $self->data->get_computer_short_name(); |
| |
| # Run any vcl_post_reservation scripts (if exists) |
| if ($self->os->can("post_reservation")) { |
| if ($self->os->post_reservation()) { |
| notify($ERRORS{'OK'}, 0, "post_reservation script has been executed on $computer_shortname prior to reloading"); |
| } |
| } |
| |
| # Retrieve next image |
| my ($action, $next_image_name, $next_image_id, $next_imagerevision_id) = $self->data->get_next_image_data_structure(); |
| |
| if ($action =~ /unload/i) { |
| if ($self->provisioner->can("unload")) { |
| if ($self->provisioner->unload()) { |
| if (update_computer_imagename($computer_id, 'noimage')) { |
| notify($ERRORS{'DEBUG'}, 0, "set computer $computer_shortname current image to 'noimage'"); |
| } |
| |
| switch_state($request_data, 'complete', 'available', '', '1'); |
| } |
| } |
| |
| } |
| else { |
| #elsif ( $action =~ /reload/i ) { |
| if (!$next_image_name || !$next_image_id || !$next_imagerevision_id) { |
| notify($ERRORS{'WARNING'}, 0, "predictor module did not return required information, calling get_next_image_default from utils"); |
| ($next_image_name, $next_image_id, $next_imagerevision_id) = get_next_image_default($computer_id); |
| } |
| |
| # Update the DataStructure object with the next image values |
| # These will be used by insert_reload_request() |
| $self->data->set_image_name($next_image_name); |
| $self->data->set_image_id($next_image_id); |
| $self->data->set_imagerevision_id($next_imagerevision_id); |
| |
| notify($ERRORS{'OK'}, 0, "next image: $next_image_name, image id=$next_image_id, imagerevision id=$next_imagerevision_id"); |
| |
| # Insert reload request data into the database |
| if (insert_reload_request($request_data)) { |
| notify($ERRORS{'OK'}, 0, "inserted reload request into database for computer id=$computer_id, image=$next_image_name"); |
| |
| # Switch the request state to complete, the computer state to reload |
| switch_state($request_data, 'complete', 'reload', '', '1'); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to insert reload request into database for computer id=$computer_id image=$next_image_name"); |
| |
| # Switch the request and computer states to failed |
| switch_state($request_data, 'failed', 'failed', '', '1'); |
| } |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "exiting"); |
| exit; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 call_os_sanitize |
| |
| Parameters : Reference to state object |
| Returns : If successful: true |
| If failed: exits |
| Description : Calls the OS module's sanitize subroutine. If sanitize() fails, |
| a reload request will be inserted into the database and this |
| process will exit. |
| |
| =cut |
| |
| sub call_os_sanitize { |
| my $self = shift; |
| |
| # Make sure sanitize() has been implemented by the OS module |
| if (!$self->os->can("sanitize")) { |
| notify($ERRORS{'WARNING'}, 0, "sanitize subroutine has not been implemented by the " . ref($self->os) . " OS module, computer will be reloaded"); |
| $self->insert_reload_and_exit(); |
| } |
| |
| my $computer_shortname = $self->data->get_computer_short_name(); |
| |
| # Attempt to call OS module's sanitize() subroutine |
| # This subroutine should perform all the tasks necessary to sanitize the OS if it was reserved and not logged in to |
| notify($ERRORS{'DEBUG'}, 0, "calling " . ref($self->os) . "::sanitize() subroutine"); |
| if ($self->os->sanitize()) { |
| notify($ERRORS{'OK'}, 0, "$computer_shortname has been sanitized"); |
| } |
| else { |
| # OS module's sanitize() subroutine returned false, meaning reload is necessary |
| notify($ERRORS{'WARNING'}, 0, "failed to sanitize $computer_shortname, computer will be reloaded"); |
| $self->insert_reload_and_exit(); |
| } |
| |
| return 1; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |