blob: d7a8a33291ea2d8df123c1a353750de18632cebf [file] [log] [blame]
#!/usr/bin/perl -w
# 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.
##############################################################################
# $Id: State.pm 1953 2008-12-12 14:23:17Z arkurth $
##############################################################################
=head1 NAME
VCL::Core::State - VCL state base module
=head1 SYNOPSIS
use base qw(VCL::Module::State);
=head1 DESCRIPTION
Needs to be written.
=cut
##############################################################################
package VCL::Module::State;
# Specify the lib path using FindBin
use FindBin;
use lib "$FindBin::Bin/../..";
# Configure inheritance
use base qw(VCL::Module);
# 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 English '-no_match_vars';
use VCL::utils;
use VCL::DataStructure;
##############################################################################
=head1 OBJECT METHODS
=cut
#/////////////////////////////////////////////////////////////////////////////
=head2 initialize
Parameters : Reference to current inuse object is automatically passed when
invoked as a class method.
Returns : 1 if successful, 0 otherwise
Description : Prepares the delete object to process a reservation. Renames the
process.
=cut
sub initialize {
my $self = shift;
my ($package, $filename, $line, $sub) = caller(0);
# Initialize the database handle count
$ENV{dbh_count} = 0;
# Attempt to get a database handle
if ($ENV{dbh} = getnewdbh()) {
notify($ERRORS{'OK'}, 0, "obtained a database handle for this state process, stored as \$ENV{dbh}");
}
else {
notify($ERRORS{'WARNING'}, 0, "unable to obtain a database handle for this state process");
}
# Check the image OS before creating OS object
if (!$self->check_image_os()) {
notify($ERRORS{'WARNING'}, 0, "failed to check if image OS is correct");
$self->reservation_failed();
}
# Store some hash variables into local variables
my $request_id = $self->data->get_request_id();
my $reservation_id = $self->data->get_reservation_id();
my $provisioning_perl_package = $self->data->get_computer_provisioning_module_perl_package();
my $os_perl_package = $self->data->get_image_os_module_perl_package();
my $predictive_perl_package = $self->data->get_management_node_predictive_module_perl_package();
# Store the name of this class in an environment variable
$ENV{class_name} = ref($self);
# Rename this process to include some request info
rename_vcld_process($self->data);
# 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);
$self->data->get_request_data->{SUBIMAGE} = (!$self->data->is_parent_reservation() + 0);
# Set the parent PID and this process's PID in the hash
set_hash_process_id($self->data->get_request_data);
# Attempt to load the computer provisioning module
if ($provisioning_perl_package) {
notify($ERRORS{'OK'}, 0, "attempting to load provisioning module: $provisioning_perl_package");
eval "use $provisioning_perl_package";
if ($EVAL_ERROR) {
notify($ERRORS{'WARNING'}, 0, "$provisioning_perl_package module could not be loaded");
notify($ERRORS{'OK'}, 0, "returning 0");
return 0;
}
notify($ERRORS{'OK'}, 0, "$provisioning_perl_package module successfully loaded");
# Create provisioner object
if (my $provisioner = ($provisioning_perl_package)->new({data_structure => $self->data})) {
notify($ERRORS{'OK'}, 0, ref($provisioner) . " provisioner object successfully created");
$self->{provisioner} = $provisioner;
}
else {
notify($ERRORS{'OK'}, 0, "provisioning object could not be created, returning 0");
return 0;
}
} ## end if ($provisioning_perl_package)
else {
notify($ERRORS{'OK'}, 0, "provisioning module not loaded, Perl package is not defined");
}
# Attempt to load the OS module
if ($os_perl_package) {
notify($ERRORS{'OK'}, 0, "attempting to load OS module: $os_perl_package");
eval "use $os_perl_package";
if ($EVAL_ERROR) {
notify($ERRORS{'WARNING'}, 0, "$os_perl_package module could not be loaded");
notify($ERRORS{'OK'}, 0, "returning 0");
return 0;
}
if (my $os = ($os_perl_package)->new({data_structure => $self->data})) {
notify($ERRORS{'OK'}, 0, ref($os) . " OS object successfully created");
$self->{os} = $os;
}
else {
notify($ERRORS{'WARNING'}, 0, "OS object could not be created, returning 0");
return 0;
}
} ## end if ($os_perl_package)
else {
notify($ERRORS{'OK'}, 0, "OS module not loaded, Perl package is not defined");
}
# Attempt to load the predictive loading module
if ($predictive_perl_package) {
notify($ERRORS{'OK'}, 0, "attempting to load predictive loading module: $predictive_perl_package");
eval "use $predictive_perl_package";
if ($EVAL_ERROR) {
notify($ERRORS{'WARNING'}, 0, "$predictive_perl_package module could not be loaded");
notify($ERRORS{'OK'}, 0, "returning 0");
return 0;
}
if (my $predictor = ($predictive_perl_package)->new({data_structure => $self->data})) {
notify($ERRORS{'OK'}, 0, ref($predictor) . " predictive loading object successfully created");
$self->{predictor} = $predictor;
}
else {
notify($ERRORS{'WARNING'}, 0, "predictive loading object could not be created, returning 0");
return 0;
}
} ## end if ($predictive_perl_package)
else {
notify($ERRORS{'OK'}, 0, "predictive loading module not loaded, Perl package is not defined");
}
notify($ERRORS{'OK'}, 0, "returning 1");
return 1;
} ## end sub initialize
#/////////////////////////////////////////////////////////////////////////////
=head2 provisioner
Parameters : None
Returns : Object's provisioner object
Description : Returns this objects provisioner object, which is stored in
$self->{provisioner}. This method allows it to accessed using
$self->provisioner.
=cut
sub provisioner {
my $self = shift;
return $self->{provisioner};
}
#/////////////////////////////////////////////////////////////////////////////
=head2 os
Parameters : None
Returns : Object's OS object
Description : Returns this objects OS object, which is stored in
$self->{os}. This method allows it to accessed using
$self->os.
=cut
sub os {
my $self = shift;
return $self->{os};
}
#/////////////////////////////////////////////////////////////////////////////
=head2 predictor
Parameters : None
Returns : Object's predictive loading object
Description : Returns this objects predictive loading object, which is stored
in $self->{predictor}. This method allows it to accessed using
$self->predictor.
=cut
sub predictor {
my $self = shift;
return $self->{predictor};
}
#/////////////////////////////////////////////////////////////////////////////
=head2 reservation_failed
Parameters : Message string
Returns : Nothing, process exits
Description : Performs the steps required when a reservation fails:
-if request was deleted
-sets computer state to available
-exits with status 0
-inserts 'failed' computerloadlog table entry
-updates ending field in the log table to 'failed'
-updates the computer state to 'failed'
-updates the request state to 'failed', laststate to request's previous state
-removes computer from blockcomputers table if this is a block request
-exits with status 1
=cut
sub reservation_failed {
my $self = shift;
if (ref($self) !~ /VCL::/) {
notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method, reservation failure tasks not attempted, process exiting");
exit 1;
}
# Check if a message was passed as an argument
my $message = shift;
if (!$message) {
$message = 'reservation failed';
}
# Get the required 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 $computer_id = $self->data->get_computer_id();
my $computer_short_name = $self->data->get_computer_short_name();
my $request_state_name = $self->data->get_request_state_name();
my $request_laststate_name = $self->data->get_request_laststate_name();
# Check if the request has been deleted
if (is_request_deleted($request_id)) {
notify($ERRORS{'OK'}, 0, "request has been deleted, setting computer state to available and exiting");
# Update the computer state to available
if (update_computer_state($computer_id, "available")) {
notify($ERRORS{'OK'}, 0, "$computer_short_name ($computer_id) state set to 'available'");
}
else {
notify($ERRORS{'OK'}, 0, "failed to set $computer_short_name ($computer_id) state to 'available'");
}
notify($ERRORS{'OK'}, 0, "exiting 0");
exit 0;
} ## end if (is_request_deleted($request_id))
# Display the message
notify($ERRORS{'CRITICAL'}, 0, "reservation failed on $computer_short_name: $message");
# Insert a row into the computerloadlog table
if (insertloadlog($reservation_id, $computer_id, "failed", $message)) {
notify($ERRORS{'OK'}, 0, "inserted computerloadlog entry");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to insert computerloadlog entry");
}
# 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");
}
# Update the computer state to failed
if (update_computer_state($computer_id, "failed")) {
notify($ERRORS{'OK'}, 0, "computer $computer_short_name ($computer_id) state set to failed");
}
else {
notify($ERRORS{'WARNING'}, 0, "unable to set computer $computer_short_name ($computer_id) state to failed");
}
# Update the request state to failed
if (update_request_state($request_id, "failed", $request_laststate_name)) {
notify($ERRORS{'OK'}, 0, "set request state to 'failed'/'$request_laststate_name'");
}
else {
notify($ERRORS{'WARNING'}, 0, "unable to set request to 'failed'/'$request_laststate_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");
if (clearfromblockrequest($computer_id)) {
notify($ERRORS{'OK'}, 0, "removed $computer_short_name from blockcomputers table");
}
else {
notify($ERRORS{'CRITICAL'}, 0, "failed to remove $computer_short_name from blockcomputers table");
}
}
else {
notify($ERRORS{'OK'}, 0, "$computer_short_name is NOT in blockcomputers table");
}
notify($ERRORS{'OK'}, 0, "exiting 1");
exit 1;
} ## end sub reservation_failed
#/////////////////////////////////////////////////////////////////////////////
=head2 check_image_os
Parameters :
Returns :
Description :
=cut
sub check_image_os {
my $self = shift;
my $request_state_name = $self->data->get_request_state_name();
my $image_id = $self->data->get_image_id();
my $image_name = $self->data->get_image_name();
my $image_os_name = $self->data->get_image_os_name();
my $imagerevision_id = $self->data->get_imagerevision_id();
# Only make corrections if state is image
if ($request_state_name ne 'image') {
notify($ERRORS{'OK'}, 0, "no corrections need to be made, not an imaging request, returning 1");
return 1;
}
my $image_os_name_new;
if ($image_os_name =~ /^(rh)el([0-9])/ || $image_os_name =~ /^rh(fc)([0-9])/) {
# Change rhelX --> rhXimage, rhfcX --> fcXimage
$image_os_name_new = "$1$2image";
}
else {
notify($ERRORS{'OK'}, 0, "no corrections need to be made to image OS: $image_os_name");
return 1;
}
# Change the image name
$image_name =~ /^[^-]+-(.*)/;
my $image_name_new = "$image_os_name_new-$1";
notify($ERRORS{'OK'}, 0, "Kickstart image OS needs to be changed: $image_os_name -> $image_os_name_new, image name: $image_name -> $image_name_new");
# Update the image table, change the OS for this image
my $sql_statement = "
UPDATE
OS,
image,
imagerevision
SET
image.OSid = OS.id,
image.name = \'$image_name_new\',
imagerevision.imagename = \'$image_name_new\'
WHERE
image.id = $image_id
AND imagerevision.id = $imagerevision_id
AND OS.name = \'$image_os_name_new\'
";
# Update the image and imagerevision tables
if (database_execute($sql_statement)) {
notify($ERRORS{'OK'}, 0, "image and imagerevision tables updated: $image_name -> $image_name_new");
}
else {
notify($ERRORS{'OK'}, 0, "failed to update image and imagerevision tables: $image_name -> $image_name_new, returning 0");
return 0;
}
if ($self->data->refresh()) {
notify($ERRORS{'OK'}, 0, "DataStructure refreshed after correcting image OS");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to update DataStructure updated correcting image OS, returning 0");
return 0;
}
notify($ERRORS{'OK'}, 0, "returning 1");
return 1;
} ## end sub check_image_os
#/////////////////////////////////////////////////////////////////////////////
=head2 DESTROY
Parameters : None
Returns : Nothing
Description : Performs module cleanup actions:
-closes the database connection
If child classes of VCL::Module need to implement their own
DESTROY method, they should call this method from their own
DESTROY method using:
$self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
=cut
sub DESTROY {
my $self = shift;
my $reservation_id = $self->data->get_reservation_id();
notify($ERRORS{'DEBUG'}, 0, "destructor called, ref(\$self)=" . ref($self));
# Delete all computerloadlog rows with loadstatename = 'begin' for thie reservation
if (delete_computerloadlog_reservation($reservation_id, 'begin')) {
notify($ERRORS{'OK'}, 0, "removed computerloadlog rows with loadstate=begin for reservation");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to remove computerloadlog rows with loadstate=begin for reservation");
}
# Print the number of database handles this process created for testing/development
if (defined $ENV{dbh_count}) {
notify($ERRORS{'DEBUG'}, 0, "number of database handles state process created: $ENV{dbh_count}");
}
else {
notify($ERRORS{'DEBUG'}, 0, "state process created unknown number of database handles, \$ENV{dbh_count} is undefined");
}
# Close the database handle
if (defined $ENV{dbh}) {
notify($ERRORS{'DEBUG'}, 0, "process has a database handle stored in \$ENV{dbh}, attempting disconnect");
if ($ENV{dbh}->disconnect) {
notify($ERRORS{'OK'}, 0, "\$ENV{dbh}: database disconnect successful");
}
else {
notify($ERRORS{'WARNING'}, 0, "\$ENV{dbh}: database disconnect failed, " . DBI::errstr());
}
} ## end if (defined $ENV{dbh})
else {
notify($ERRORS{'OK'}, 0, "process does not have a database handle stored in \$ENV{dbh}");
}
# Check for an overridden destructor
$self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
} ## end sub DESTROY
#/////////////////////////////////////////////////////////////////////////////
1;
__END__
=head1 BUGS and LIMITATIONS
There are no known bugs in this module.
Please report problems to the VCL team (vcl_help@ncsu.edu).
=head1 AUTHOR
Aaron Peeler, aaron_peeler@ncsu.edu
Andy Kurth, andy_kurth@ncsu.edu
=head1 SEE ALSO
L<http://vcl.ncsu.edu>
=cut