blob: 8e763001fd3e670773e6df3d1899d7b173e3953f [file] [log] [blame]
#!/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::utils
=head1 SYNOPSIS
use VCL::utils;
=head1 DESCRIPTION
This module contains general VCL utility subroutines.
=cut
##############################################################################
package VCL::utils;
# Specify the lib path using FindBin
use FindBin;
use lib "$FindBin::Bin/..";
# Configure inheritance
use base qw();
# 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 Mail::Mailer;
use Shell qw(mkdir);
use File::Find;
use Time::Local;
use DBI;
use DBI::Const::GetInfoType;
use diagnostics;
use Net::Ping;
use Fcntl qw(:DEFAULT :flock);
use FindBin;
use Getopt::Long;
use Carp;
use Text::Wrap;
use English;
use List::Util qw(min max);
use HTTP::Headers;
use RPC::XML::Client;
use Scalar::Util 'blessed';
use Data::Dumper;
#use Date::Calc qw(Delta_DHMS Time_to_Date Date_to_Time);
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(
_checknstartservice
_getcurrentimage
_machine_os
_pingnode
_sshd_status
changelinuxpassword
check_blockrequest_time
check_connection
check_endtimenotice_interval
check_ssh
check_time
check_uptime
checkonprocess
clearfromblockrequest
collectsshkeys
construct_image_name
controlVM
convert_to_datetime
convert_to_epoch_seconds
create_management_node_directory
database_execute
database_select
delete_computerloadlog_reservation
delete_request
disablesshd
escape_file_path
firewall_compare_update
format_data
format_number
get_affiliation_info
get_block_request_image_info
get_caller_trace
get_computer_current_state_name
get_computer_grp_members
get_computer_ids
get_computer_info
get_computers_controlled_by_MN
get_current_file_name
get_current_package_name
get_current_subroutine_name
get_group_name
get_highest_imagerevision_info
get_image_info
get_imagemeta_info
get_imagerevision_info
get_management_node_blockrequests
get_management_node_id
get_management_node_info
get_management_node_requests
get_management_predictive_info
get_module_info
get_next_image_default
get_os_info
get_production_imagerevision_info
get_request_by_computerid
get_request_end
get_request_info
get_resource_groups
get_managable_resource_groups
get_user_info
get_vmhost_info
getdynamicaddress
getimagesize
getnewdbh
getpw
getusergroupmembers
help
hostname
insert_reload_request
insert_request
insertloadlog
is_management_node_process_running
is_inblockrequest
is_public_ip_address
is_request_deleted
is_request_imaging
is_valid_dns_host_name
is_valid_ip_address
isconnected
isfilelocked
kill_reservation_process
known_hosts
lockfile
mail
makedatestring
monitorloading
nmap_port
normalize_file_path
notify
notify_via_IM
notify_via_msg
notify_via_wall
parent_directory_path
preplogfile
read_file_to_array
rename_vcld_process
reservation_being_processed
reservations_ready
restoresshkeys
round
run_command
run_scp_command
run_ssh_command
set_hash_process_id
set_logfile_path
set_managementnode_state
setimageid
setnextimage
setstaticaddress
setup_confirm
setup_get_array_choice
setup_get_hash_choice
setup_get_input_string
setup_print_wrap
string_to_ascii
switch_state
switch_vmhost_id
time_exceeded
timefloor15interval
unlockfile
update_blockrequest_processing
update_cluster_info
update_computer_address
update_computer_state
update_computer_lastcheck
update_currentimage
update_computer_imagename
update_image_name
update_lastcheckin
update_log_ending
update_log_loaded_time
update_preload_flag
update_request_password
update_request_state
update_reservation_lastcheck
update_sublog_ipaddress
write_currentimage_txt
xmlrpc_call
$CONF_FILE_PATH
$DAEMON_MODE
$DATABASE
$DEFAULTHELPEMAIL
$GATEWAY
$FQDN
$jabPass
$jabPort
$jabResource
$jabServer
$jabUser
$LOGFILE
$MYSQL_SSL
$MYSQL_SSL_CERT
$PIDFILE
$PROCESSNAME
$WINDOWS_ROOT_PASSWORD
$SERVER
$SETUP_MODE
$TOOLS
$VERBOSE
$WRTPASS
$WRTUSER
$XMLRPC_USER
$XMLRPC_PASS
$XMLRPC_URL
%ERRORS
%OPTIONS
);
#our %ERRORS=('DEPENDENT'=>4,'UNKNOWN'=>3,'OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'MAILMASTERS'=>5);
INIT {
# Parse config file and set globals
our ($JABBER, $jabServer, $jabUser, $jabPass, $jabResource, $jabPort) = 0;
our ($LOGFILE, $PIDFILE, $PROCESSNAME);
our ($DATABASE, $SERVER, $WRTUSER, $WRTPASS, $LockerRdUser, $rdPass) = 0;
our ($DEFAULTHELPEMAIL, $RETURNPATH) = 0;
our ($XCATROOT) = 0;
our ($FQDN) = 0;
our ($MYSQL_SSL, $MYSQL_SSL_CERT);
our ($WINDOWS_ROOT_PASSWORD);
our ($XMLRPC_USER, $XMLRPC_PASS, $XMLRPC_URL);
# Set Getopt pass_through so this module doesn't erase parameters that other modules may use
Getopt::Long::Configure('pass_through');
# Set the VERBOSE flag to 0 by default
our $VERBOSE = 0;
# Set the SETUP_MODE flag to 0 by default
our $SETUP_MODE = 0;
# Set the SETUP_MODE flag to 1 by default
our $DAEMON_MODE = 1;
# Use the default configuration file path if -conf isn't specified on the command line
our $BIN_PATH = $FindBin::Bin;
# Set a default config file path
our ($CONF_FILE_PATH) = 'C:/vcldev.conf';
if (!-f $CONF_FILE_PATH) {
if ($BIN_PATH =~ /dev/) {
$CONF_FILE_PATH = "/etc/vcl/vcldev.conf";
}
else {
$CONF_FILE_PATH = "/etc/vcl/vcld.conf";
}
}
# Store the command line options in hash
our %OPTIONS;
GetOptions(\%OPTIONS,
'config=s' => \$CONF_FILE_PATH,
'daemon!' => \$DAEMON_MODE,
'logfile=s' => \$LOGFILE,
'help' => \&help,
'setup!' => \$SETUP_MODE,
'verbose!' => \$VERBOSE,
);
# Make sure the config file exists
if (!-f $CONF_FILE_PATH) {
print STDOUT "ERROR: config file being does not exist: $CONF_FILE_PATH\n";
help();
}
if (open(CONF, $CONF_FILE_PATH)) {
my @conf = <CONF>;
close(CONF);
foreach my $l (@conf) {
# Remove all new line and carriage return characters from the end of the line
# Chomp doesn't always remove carriage returns
$l =~ s/[\r\n]*$//;
#logfile
if ($l =~ /^log=(.*)/ && (!defined($LOGFILE))) {
chomp($l);
$LOGFILE = $1;
}
#pidfile
if ($l =~ /^pidfile=(.*)/) {
chomp($l);
$PIDFILE = $1;
}
#FQDN - to many issues trying to figure out my FQDN so just tell me
if ($l =~ /^FQDN=([-.a-zA-Z0-9]*)/) {
$FQDN = $1;
}
#mysql settings
#name of db
if ($l =~ /^database=(.*)/) {
$DATABASE = $1;
}
#name of database server
if ($l =~ /^server=([-.a-zA-Z0-9]*)/) {
$SERVER = $1;
}
#write user name
if ($l =~ /^LockerWrtUser=(.*)/) {
$WRTUSER = $1;
}
#write user password
if ($l =~ /^wrtPass=(.*)/) {
$WRTPASS = $1;
}
#read user name
if ($l =~ /^LockerRdUser=(.*)/) {
$LockerRdUser = $1;
}
#read user password
if ($l =~ /^rdPass=(.*)/) {
$rdPass = $1;
}
#xmlrpc_username
if ($l =~ /^xmlrpc_username=(.*)/) {
$XMLRPC_USER = $1;
}
#xmlrpc_username password
if ($l =~ /^xmlrpc_pass=(.*)/) {
$XMLRPC_PASS = $1;
}
#xmlrpc_url
if ($l =~ /^xmlrpc_url=(.*)/) {
$XMLRPC_URL = $1;
}
#is mysql ssl option enabled
if ($l =~ /^enable_mysql_ssl=(yes)/) {
$MYSQL_SSL = 1;
}
elsif ($l =~ /^enable_mysql_ssl=(no)/) {
$MYSQL_SSL = 0;
}
#collect path to cert -- only valid if $MYSQL_SSL is true
if ($l =~ /^mysql_ssl_cert=(.*)/) {
$MYSQL_SSL_CERT = $1;
}
#Sendmail Envelope Sender
if ($l =~ /^RETURNPATH=([,-.\@a-zA-Z0-9_]*)/) {
$RETURNPATH = $1;
}
#jabber - stuff
if ($l =~ /^jabber=(yes)/) {
$JABBER = 1;
}
if ($l =~ /^jabber=(no)/) {
$JABBER = 0;
}
#collect remaining pieces of the jabber settings
#$jabServer,$jabUser,$jabPass,$jabResource,$jabPort
if ($l =~ /^jabServer=([.a-zA-Z0-9]*)/) {
$jabServer = $1;
}
if ($l =~ /^jabPort=([0-9]*)/) {
$jabPort = $1;
}
if ($l =~ /^jabUser=(.*)/) {
$jabUser = $1;
}
if ($l =~ /^jabPass=(.*)/) {
$jabPass = $1;
}
if ($l =~ /^jabResource=(.*)/) {
$jabResource = $1;
}
#process name
if ($l =~ /^processname=([-_a-zA-Z0-9]*)/) {
$PROCESSNAME = $1;
}
if ($l =~ /^windows_root_password=(.*)/i) {
$WINDOWS_ROOT_PASSWORD = $1;
}
if ($l =~ /^verbose=(.*)/i && !$VERBOSE) {
$VERBOSE = $1;
}
} # Close foreach line in conf file
} # Close open conf file
else {
die "VCLD : $CONF_FILE_PATH does not exist, exiting -- $! \n";
}
if (!$PROCESSNAME) {
$PROCESSNAME = "vcld";
}
if (!($LOGFILE) && $LOGFILE ne '0') {
#set default
$LOGFILE = "/var/log/$PROCESSNAME.log";
}
if (!$WINDOWS_ROOT_PASSWORD) {
$WINDOWS_ROOT_PASSWORD = "clOudy";
}
if (!($FQDN)) {
print STDOUT "FQDN is not listed\n";
}
if (!($PIDFILE)) {
#set default
$PIDFILE = "/var/run/$PROCESSNAME.pid";
}
if (!($RETURNPATH)){
$RETURNPATH="";
}
if ($JABBER) {
#jabber is enabled - import required jabber module
# todo - check if Jabber module is installed
# i.e. perl -MNet::Jabber -e1
# check version -- perl -MNet::Jabber -e'print $Net::Jabber::VERSION\n";'
require "Net/Jabber.pm";
import Net::Jabber qw(client);
}
# Can't be both daemon mode and setup mode, use setup if both are set
$DAEMON_MODE = 0 if ($DAEMON_MODE && $SETUP_MODE);
} ## end INIT
our ($JABBER, $PROCESSNAME);
our %ERRORS = ('DEPENDENT' => 4, 'UNKNOWN' => 3, 'OK' => 0, 'WARNING' => 1, 'CRITICAL' => 2, 'MAILMASTERS' => 5, 'DEBUG' => 6);
our ($LockerWrtUser, $wrtPass, $database, $server);
our ($jabServer, $jabUser, $jabPass, $jabResource, $jabPort);
our ($vcldquerykey, $RETURNPATH);
our ($LOGFILE, $PIDFILE, $VCLDRPCQUERYKEY);
our ($SERVER, $DATABASE, $WRTUSER, $WRTPASS);
our ($MYSQL_SSL, $MYSQL_SSL_CERT);
our ($FQDN);
our $XCATROOT = "/opt/xcat";
our $TOOLS = "$FindBin::Bin/../tools";
our $VMWARE_MAC_GENERATED;
our $VERBOSE;
our $CONF_FILE_PATH;
our $WINDOWS_ROOT_PASSWORD;
our ($XMLRPC_USER, $XMLRPC_PASS, $XMLRPC_URL);
our $DAEMON_MODE;
our $SETUP_MODE;
our $BIN_PATH;
our $DEFAULTHELPEMAIL = "vcl_help\@example.org"; # default value if affiliation helpaddress is not set
sub makedatestring;
#/////////////////////////////////////////////////////////////////////////////
=head2 help
Parameters : None
Returns : Nothing, terminates program
Description : Displays a help message and exits.
=cut
sub help {
my $message = <<"END";
============================================================================
Please read the README and INSTALLATION files in the source directory.
Documentation is available at http://cwiki.apache.org/VCL.
Command line options:
-setup | Run management node setup
-conf=<path> | Specify vcld configuration file
-verbose | Run vcld in verbose mode
-debug | Run vcld in non-daemon mode
-help | Display this help information
============================================================================
END
print $message;
exit;
} ## end sub help
#/////////////////////////////////////////////////////////////////////////////
=head2 preplogfile
Parameters : nothing
Returns : nothing
Description : writes header to global log file
=cut
sub preplogfile {
my $currenttime = makedatestring();
#Print the vcld process info
my $process_info = <<EOF;
============================================================================
VCL Management Node Daemon (vcld) | $currenttime
============================================================================
bin path: $BIN_PATH
config file: $CONF_FILE_PATH
log file: $LOGFILE
pid file: $PIDFILE
daemon mode: $DAEMON_MODE
setup mode: $SETUP_MODE
verbose mode: $VERBOSE
============================================================================
EOF
if ($LOGFILE) {
if (!open(LOGFILE, ">>$LOGFILE")) {
die "Failed to open log file: $LOGFILE";
}
print LOGFILE $process_info;
close(LOGFILE);
}
print STDOUT $process_info;
}
#/////////////////////////////////////////////////////////////////////////////
=head2 notify
Parameters : $error, $LOG, $string, $data
Returns : nothing
Description : based on error value write string and/or data to
provide or default log file
=cut
sub notify {
my $error = shift;
my $log = shift;
my $string = shift;
my @data = @_;
# Just return if DEBUG and verbose isn't enabled
return if ($error == 6 && !$VERBOSE);
# Confirm sysadmin address exists
my $sysadmin = 0;
if(defined($ENV{management_node_info}{SYSADMIN_EMAIL}) && $ENV{management_node_info}{SYSADMIN_EMAIL}){
$sysadmin = $ENV{management_node_info}{SYSADMIN_EMAIL};
}
# Confirm shared mail box exists
my $shared_mail_box = 0;
if(defined($ENV{management_node_info}{SHARED_EMAIL_BOX}) && $ENV{management_node_info}{SHARED_EMAIL_BOX}){
my $shared_mail_box = $ENV{management_node_info}{SHARED_EMAIL_BOX};
}
# Get the current time
my $currenttime = makedatestring();
# Open the log file for writing if passed as an argument or set globally
# If not, print to STDOUT
$log = $LOGFILE if (!$log);
# Get info about the subroutine which called this subroutine
my ($package, $filename, $line, $sub) = caller(0);
# Remove leading path from filename
$filename =~ s/.*\///;
# Remove the leading package path from the sub name (VC::...)
$sub =~ s/.*:://;
# Assemble the caller information
my $caller_info;
if (caller(1)) {
my ($caller_previous_package, $caller_previous_filename, $caller_previous_line, $caller_previous_sub) = caller(1);
$caller_previous_filename =~ s/.*\///;
$caller_previous_sub =~ s/.*:://;
$caller_info = "$filename:$caller_previous_sub($line)";
}
else {
$caller_info = "$filename:$sub($line)";
}
# Get the caller trace information
my $caller_trace = get_caller_trace(6);
# Format the message string
# Remove Windows carriage returns from the message string for consistency
$string =~ s/\r//gs;
## Remove newlines from the beginning and end of the message string
#$string =~ s/^\n+//;
#$string =~ s/\n+$//;
# Remove any spaces from the beginning or end of the string
$string =~ s/(^\s+)|(\s+$)//gs;
# Remove any spaces from the beginning or end of the each line
$string =~ s/\s*\n\s*/\n/gs;
# Replace consecutive spaces with a single space to keep log file concise as long as string doesn't contain a quote
if ($string !~ /[\'\"]/gs) {
$string =~ s/[ \t]+/ /gs;
}
# Assemble the process identifier string
my $process_identifier = $PID;
$process_identifier .= "|$ENV{request_id}:$ENV{reservation_id}" if (defined $ENV{request_id} && defined $ENV{reservation_id});
$process_identifier .= "|$ENV{state}" if (defined $ENV{state});
# Assemble the log message
my $log_message = "$currenttime|$process_identifier|$caller_info|$string";
# Format the data if WARNING or CRITICAL, and @data was passed
my $formatted_data;
if (@data && ($error == 1 || $error == 2)) {
# Add the data to the message body if it was passed
$formatted_data = "DATA:\n" . format_data(\@data, 'DATA');
chomp $formatted_data;
}
# Assemble an email message body if CRITICAL
my $body;
if ($error == 2) {
# Get the previous several log file entries for this process
my $log_history_count = 100;
my $log_history = "RECENT LOG ENTRIES FOR THIS PROCESS:\n";
$log_history .= `grep "|$PID|" $log | tail -n $log_history_count` if $log;
chomp $log_history;
# Assemble the e-mail message body
$body = <<"END";
$string
Time: $currenttime
PID: $PID
Caller: $caller_info
$caller_trace
$log_history
END
# Add the formatted data to the message body if data was passed
$body .= "\n\n$formatted_data\n" if $formatted_data;
} ## end if ($error == 2)
# OK, VERBOSE
if (!$error || ($error == 6 && $VERBOSE)) {
}
# WARNING
if ($error == 1) {
$log_message = "\n---- WARNING ---- \n$log_message\n$caller_trace\n\n";
}
# CRITICAL
elsif ($error == 2) {
$log_message = "\n---- CRITICAL ---- \n$log_message\n$caller_trace\n";
$log_message .= "$formatted_data\n" if $formatted_data;
$log_message .= "\n";
my $from = "root\@$FQDN";
my $to = $sysadmin;
my $subject = "PROBLEM -- $filename";
mail($to, $subject, $body, $from);
} ## end elsif ($error == 2) [ if ($error == 1)
# MAILMASTERS - only for email notifications
elsif ($error == 5 && $shared_mail_box) {
my $to = $shared_mail_box;
my $from = "root\@$FQDN";
my $subject = "Informational -- $filename";
# Assemble the e-mail message body
$body = <<"END";
$string
Time: $currenttime
PID: $PID
Caller: $caller_info
END
mail($to, $subject, $body, $from);
}
# Add the process identifier to every line of the log message
chomp $log_message;
$log_message =~ s/\n([^\n])/\n|$process_identifier| $1/g;
# Check if the logfile path has been set and not running in daemon mode and redirect output to log file
# No need to redirect in daemon mode because STDOUT is redirected by vcld
if (!$DAEMON_MODE && $log) {
open(OUTPUT, ">>$log");
print OUTPUT "$log_message\n";
close OUTPUT;
}
else {
open(STDOUT, ">>$log");
print STDOUT "$log_message\n";
}
} ## end sub notify
#/////////////////////////////////////////////////////////////////////////////
=head2 makedatestring
Parameters : empty
Returns : current time in date_time format
Description :
=cut
sub makedatestring {
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
$year += 1900;
$mon++;
my $datestring = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
return $datestring;
}
#/////////////////////////////////////////////////////////////////////////////
=head2 convert_to_datetime
Parameters : time in epoch format
Returns : date in datetime format
Description : accepts time in epoch format (10 digit) and
returns time in datetime format
=cut
sub convert_to_datetime {
my ($epochtime) = shift;
if (!defined($epochtime) || $epochtime == 0) {
$epochtime = time();
}
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($epochtime);
$year += 1900;
$mon++;
my $datestring = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
return $datestring;
} ## end sub convert_to_datetime
#/////////////////////////////////////////////////////////////////////////////
=head2 convert_to_epoch_seconds
Parameters : datetime
Returns : time in epoch format
Description : takes input(optional) and returns epoch 10 digit string of
the supplied date_time or the current time
=cut
sub convert_to_epoch_seconds {
my ($date_time) = shift;
if (!defined($date_time)) {
return time();
}
#somehow we got a null timestamp, set it to current time
if ($date_time =~ /0000-00-00 00:00:00/) {
$date_time = makedatestring;
}
#format received: year-mon-mday hr:min:sec
my ($vardate, $vartime) = split(/ /, $date_time);
my ($yr, $mon, $mday) = split(/-/, $vardate);
my ($hr, $min, $sec) = split(/:/, $vartime);
$mon = $mon - 1; #time uses 0-11 for months :(
my $epoch_time = timelocal($sec, $min, $hr, $mday, $mon, $yr);
return $epoch_time;
} ## end sub convert_to_epoch_seconds
#/////////////////////////////////////////////////////////////////////////////
=head2 check_endtimenotice_interval
Parameters : endtime
Returns : scalar: 2week, 1week, 2day, 1day, 30min, or 0
Description : used to send a notice to owner regarding how far out the end of
their reservation is
=cut
sub check_endtimenotice_interval {
my $end = $_[0];
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "endtime not set") if (!defined($end));
my $now = time();
my $epochend = convert_to_epoch_seconds($end);
#flag on: 2 & 1 week; 2,1 day, 1 hour, 30,15,10,5 minutes
#2 week: between 14 days and a 14 day -15 minutes window
if ($epochend <= (14 * 60 * 60 * 24) && $epochend >= (14 * 60 * 60 * 24 - 15 * 60)) {
return (1, "2week");
}
#1 week: between 7 days and a 14 day -15 minute window
elsif ($epochend <= (7 * 60 * 60 * 24) && $epochend >= (7 * 60 * 60 * 24 - 15 * 60)) {
return (1, "1week");
}
#2 day: between 2 days and a 2 day -15 minute window
if ($epochend <= (2 * 60 * 60 * 24) && $epochend >= (2 * 60 * 60 * 24 - 15 * 60)) {
return (1, "2day");
}
#1 day: between 1 days and a 1 day -15 minute window
if ($epochend <= (1 * 60 * 60 * 24) && $epochend >= (1 * 60 * 60 * 24 - 15 * 60)) {
return (1, "1day");
}
#30-25 minutes
if ($epochend <= (30 * 60) && $epochend >= (25 * 60)) {
return (1, "30min");
}
} ## end sub check_endtimenotice_interval
#sub new_check_endtimenotice_interval {
# my ($request_end, $base_time) = @_;
# my ($package, $filename, $line, $sub) = caller(0);
#
# # Check the parameter
# if (!defined($request_end)) {
# notify($ERRORS{'WARNING'}, 0, "request end time was not specified"");
# return 0;
# }
# elsif (!$request_end) {
# notify($ERRORS{'WARNING'}, 0, "request end time was specified but is blank"");
# return 0;
# }
#
# # Convert the request end time to epoch seconds
# my $end_epoch_seconds = convert_to_epoch_seconds($request_end);
#
# # This is only used for testing
# my @now;
# if ($base_time) {
# my $base_epoch_seconds = convert_to_epoch_seconds($base_time);
# @now = Time_to_Date($base_epoch_seconds);
# }
# else {
# @now = Time_to_Date();
# }
#
# # Get arrays from the Date::Calc::Time_to_Date functions for now and the end time
# my @end = Time_to_Date($end_epoch_seconds);
#
# # Calculate the difference
# my ($days, $hours, $minutes, $seconds) = Delta_DHMS(@now, @end);
#
# # Return a value on: 2 & 1 week; 2,1 day, 1 hour, 30,15,10,5 minutes
# my $return_value = 0;
#
# # Ignore: over 14 days away
# if ($days >= 14){
# $return_value = 0;
# }
# # 2 week notice: between 14 days and a 14 day - 15 minute window
# elsif ($days >= 13 && $hours >= 23 && $minutes >= 45){
# $return_value = "2 weeks";
# }
# # Ignore: between 7 days and 14 day - 15 minute window
# elsif ($days >= 7) {
# $return_value = 0;
# }
# # 1 week notice: between 7 days and a 7 day -15 minute window
# elsif ($days >= 6 && $hours >= 23 && $minutes >= 45) {
# $return_value = "1 week";
# }
# # Ignore: between 2 days and 7 day - 15 minute window
# elsif ($days >= 2) {
# $return_value = 0;
# }
# # 2 day notice: between 2 days and a 2 day -15 minute window
# elsif($days >= 1 && $hours >= 23 && $minutes >= 45) {
# $return_value = "2 days";
# }
# # Ignore: between 1 days and 2 day - 15 minute window
# elsif ($days >= 1) {
# $return_value = 0;
# }
# # 1 day notice: between 1 days and a 1 day -15 minute window
# elsif($days >= 0 && $hours >= 23 && $minutes >= 45) {
# $return_value = "1 day";
# }
# #30-25 minutes
# elsif ($minutes >= 25 && $minutes <= 30) {
# $return_value = "30 minutes";
# }
# notify($ERRORS{'OK'}, 0, "days: time difference is days:$days hours:$hours minutes:$minutes, returning $return_value");
# return $return_value;
#}
#/////////////////////////////////////////////////////////////////////////////
=head2 check_blockrequest_time
Parameters : start, end, and expire times
Returns : 0 or 1 and task
Description : check current time against all three tasks
expire time overides end, end overrides start
=cut
sub check_blockrequest_time {
my ($start_datetime, $end_datetime, $expire_datetime) = @_;
# Check the arguments
if (!$start_datetime) {
notify($ERRORS{'WARNING'}, 0, "start time argument was not passed correctly");
return;
}
if (!$end_datetime) {
notify($ERRORS{'WARNING'}, 0, "end time argument was not passed correctly");
return;
}
if (!$expire_datetime) {
notify($ERRORS{'WARNING'}, 0, "expire time argument was not passed correctly");
return;
}
# Get the current time in epoch seconds
my $current_time_epoch_seconds = time();
my $expire_time_epoch_seconds = convert_to_epoch_seconds($expire_datetime);
my $expire_delta_minutes = int(($expire_time_epoch_seconds - $current_time_epoch_seconds) / 60);
#notify($ERRORS{'DEBUG'}, 0, "expire: $expire_datetime, epoch: $expire_time_epoch_seconds, delta: $expire_delta_minutes minutes");
# If expire time is in the past, remove it
if ($expire_delta_minutes < 0) {
# Block request has expired
notify($ERRORS{'OK'}, 0, "block request expired " . abs($expire_delta_minutes) . " minutes ago, returning 'expire'");
return "expire";
}
if ($start_datetime =~ /^-?\d*$/ || $end_datetime =~ /^-?\d*$/) {
notify($ERRORS{'DEBUG'}, 0, "block request is not expired but has no block times assigned to it, returning 0");
return 0;
}
# Convert the argument datetimes to epoch seconds for easy calculation
my $start_time_epoch_seconds = convert_to_epoch_seconds($start_datetime);
my $end_time_epoch_seconds = convert_to_epoch_seconds($end_datetime);
# Calculate # of seconds away start, end, and expire times are from now
# Positive value means time is in the future
my $start_delta_minutes = int(($start_time_epoch_seconds - $current_time_epoch_seconds) / 60);
my $end_delta_minutes = int(($end_time_epoch_seconds - $current_time_epoch_seconds) / 60);
#notify($ERRORS{'DEBUG'}, 0, "start: $start_datetime, epoch: $start_time_epoch_seconds, delta: $start_delta_minutes minutes");
#notify($ERRORS{'DEBUG'}, 0, "end: $end_datetime, epoch: $end_time_epoch_seconds, delta: $end_delta_minutes minutes");
# if 30min to 6 hrs in advance: start assigning resources
if ($start_delta_minutes >= (30) && $start_delta_minutes <= (6 * 60)) {
# Block request within start window
notify($ERRORS{'OK'}, 0, "block request start time is within start window ($start_delta_minutes minutes from now), returning 'start'");
return "start";
}
# End time it is less than 1 minute
if ($end_delta_minutes < 0) {
# Block request end time is near
notify($ERRORS{'OK'}, 0, "block request end time has been reached ($end_delta_minutes minutes from now), returning 'end'");
return "end";
}
#notify($ERRORS{'DEBUG'}, 0, "block request does not need to be processed now, returning 0");
return 0;
} ## end sub check_blockrequest_time
#/////////////////////////////////////////////////////////////////////////////
=head2 check_time
Parameters : $request_start, $request_end, $reservation_lastcheck, $request_state_name, $request_laststate_name
Returns : start, preload, end, poll, old, remove, or 0
Description : based on the input return a value used by vcld
=cut
sub check_time {
my ($request_start, $request_end, $reservation_lastcheck, $request_state_name, $request_laststate_name) = @_;
my ($package, $filename, $line, $sub) = caller(0);
# Check the arguments
if (!defined($request_state_name)) {
notify($ERRORS{'WARNING'}, 0, "\$request_state_name argument is not defined");
return 0;
}
if (!defined($request_laststate_name)) {
notify($ERRORS{'WARNING'}, 0, "\$request_laststate_name argument is not defined");
return 0;
}
# If lastcheck isn't set, set it to now
if (!defined($reservation_lastcheck) || !$reservation_lastcheck) {
$reservation_lastcheck = makedatestring();
}
# First convert to datetime in case epoch seconds was passed
if ($reservation_lastcheck !~ /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/) {
$reservation_lastcheck = convert_to_datetime($reservation_lastcheck);
}
if ($request_end !~ /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/) {
$request_end = convert_to_datetime($request_end);
}
if ($request_start !~ /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/) {
$request_start = convert_to_datetime($request_start);
}
# Convert times to epoch seconds
my $lastcheck_epoch_seconds = convert_to_epoch_seconds($reservation_lastcheck);
my $start_time_epoch_seconds = convert_to_epoch_seconds($request_start);
my $end_time_epoch_seconds = convert_to_epoch_seconds($request_end);
# Get the current time epoch seconds
my $current_time_epoch_seconds = time();
# Calculate time differences from now in seconds
# These will be positive if in the future, negative if in the past
my $lastcheck_diff_seconds = $lastcheck_epoch_seconds - $current_time_epoch_seconds;
my $start_diff_seconds = $start_time_epoch_seconds - $current_time_epoch_seconds;
my $end_diff_seconds = $end_time_epoch_seconds - $current_time_epoch_seconds;
# Calculate the time differences from now in minutes
# These will be positive if in the future, negative if in the past
my $lastcheck_diff_minutes = round($lastcheck_diff_seconds / 60);
my $start_diff_minutes = round($start_diff_seconds / 60);
my $end_diff_minutes = round($end_diff_seconds / 60);
# Print the time differences
#notify($ERRORS{'OK'}, 0, "reservation lastcheck difference: $lastcheck_diff_minutes minutes");
#notify($ERRORS{'OK'}, 0, "request start time difference: $start_diff_minutes minutes");
#notify($ERRORS{'OK'}, 0, "request end time difference: $end_diff_minutes minutes");
# Check the state, and then figure out the return code
if ($request_state_name =~ /new|imageprep|reload|tomaintenance|tovmhostinuse/) {
if ($start_diff_minutes > 0) {
# Start time is either now or in future, $start_diff_minutes is positive
if ($start_diff_minutes > 35) {
#notify($ERRORS{'DEBUG'}, 0, "reservation will start in more than 35 minutes ($start_diff_minutes)");
return "0";
}
elsif ($start_diff_minutes >= 25 && $start_diff_minutes <= 35) {
notify($ERRORS{'DEBUG'}, 0, "reservation will start in 25-35 minutes ($start_diff_minutes)");
return "preload";
}
else {
#notify($ERRORS{'DEBUG'}, 0, "reservation will start less than 25 minutes ($start_diff_minutes)");
return "0";
}
} ## end if ($start_diff_minutes > 0)
else {
# Start time is in past, $start_diff_minutes is negative
#Start time is fairly old - something is off
#send warning to log for tracking purposes
if ($start_diff_minutes < -17) {
notify($ERRORS{'WARNING'}, 0, "reservation start time was in the past 17 minutes ($start_diff_minutes)");
}
return "start";
} ## end else [ if ($start_diff_minutes > 0)
} ## end if ($request_state_name =~ /new|imageprep|reload|tomaintenance|tovmhostinuse/)
elsif ($request_state_name =~ /inuse|imageinuse/) {
if ($end_diff_minutes <= 10) {
#notify($ERRORS{'DEBUG'}, 0, "reservation will end in 10 minutes or less ($end_diff_minutes)");
return "end";
}
else {
# End time is more than 10 minutes in the future
#notify($ERRORS{'DEBUG'}, 0, "reservation will end in more than 10 minutes ($end_diff_minutes)");
if ($lastcheck_diff_minutes <= -5) {
#notify($ERRORS{'DEBUG'}, 0, "reservation was last checked more than 5 minutes ago ($lastcheck_diff_minutes)");
return "poll";
}
else {
#notify($ERRORS{'DEBUG'}, 0, "reservation has been checked within the past 5 minutes ($lastcheck_diff_minutes)");
return 0;
}
} ## end else [ if ($end_diff_minutes <= 10)
} ## end elsif ($request_state_name =~ /inuse|imageinuse/) [ if ($request_state_name =~ /new|imageprep|reload|tomaintenance|tovmhostinuse/)
elsif ($request_state_name =~ /complete|failed/) {
# Don't need to keep requests in database if laststate was...
if ($request_laststate_name =~ /image|deleted|makeproduction|reload|tomaintenance|tovmhostinuse/) {
return "remove";
}
if ($end_diff_minutes < 0) {
notify($ERRORS{'DEBUG'}, 0, "reservation end time was in the past ($end_diff_minutes)");
return "remove";
}
else {
# End time is now or in the future
#notify($ERRORS{'DEBUG'}, 0, "reservation end time is either right now or in the future ($end_diff_minutes)");
return "0";
}
} # Close if state is complete or failed
# Just return start for all other states
else {
return "start";
}
} ## end sub check_time
#/////////////////////////////////////////////////////////////////////////////
=head2 time_exceeded
Parameters : $time_slice, $limit
Returns : 1(success) or 0(failure)
Description : preform a difference check,
if delta of now and input $time_slice
is less than input $limit return 1(true)
=cut
sub time_exceeded {
my ($time_slice, $limit) = @_;
my ($package, $filename, $line, $sub) = caller(0);
my $now = time();
my $diff = $now - $time_slice;
if ($diff > ($limit * 60)) {
#time exceeded
return 1;
}
else {
return 0;
}
} ## end sub time_exceeded
#/////////////////////////////////////////////////////////////////////////////
=head2 mail
Parameters : $to, $subject, $mailstring, $from
Returns : 1(success) or 0(failure)
Description : send an email
=cut
sub mail {
my ($to, $subject, $mailstring, $from) = @_;
my ($package, $filename, $line, $sub) = caller(0);
# Mail::Mailer relies on sendmail as written, this causes a "die" on Windows
# TODO: Reqork this subroutine to not rely on sendmail
my $osname = lc($^O);
if ($osname =~ /win/i) {
notify($ERRORS{'OK'}, 0, "sending mail from Windows not yet supported\n-----\nTo: $to\nSubject: $subject\nFrom: $from\n$mailstring\n-----");
return;
}
# Wrap text for lines longer than 72 characters
#$Text::Wrap::columns = 72;
#$mailstring = wrap('', '', $mailstring);
# compare requestor and owner, if same only mail one
if (!(defined($from))) {
$from = $DEFAULTHELPEMAIL;
}
my $localreturnpath = "-f $RETURNPATH";
my $mailer = Mail::Mailer->new("sendmail", $localreturnpath);
my $shared_mail_box = 0;
if(defined($ENV{management_node_info}{SHARED_EMAIL_BOX}) && $ENV{management_node_info}{SHARED_EMAIL_BOX}){
$shared_mail_box = $ENV{management_node_info}{SHARED_EMAIL_BOX};
}
if ($shared_mail_box) {
my $bcc = $shared_mail_box;
if ($mailer->open({From => $from,
To => $to,
Bcc => $bcc,
Subject => $subject,}))
{
print $mailer $mailstring;
$mailer->close();
notify($ERRORS{'OK'}, 0, "SUCCESS -- Sending mail To: $to, $subject");
}
else {
notify($ERRORS{'WARNING'}, 0, "NOTICE -- Problem sending mail to: $to From");
}
} ## end if ($shared_mail_box)
else {
if ($mailer->open({From => $from,
To => $to,
Subject => $subject,}))
{
print $mailer $mailstring;
$mailer->close();
notify($ERRORS{'OK'}, 0, "SUCCESS -- Sending mail To: $to, $subject");
}
else {
notify($ERRORS{'WARNING'}, 0, "NOTICE -- Problem sending mail to: $to From");
}
} ## end else [ if ($shared_mail_box)
} ## end sub mail
#/////////////////////////////////////////////////////////////////////////////
=head2 setstaticaddress
Parameters : $node, $osname, $IPaddress
Returns : 1,0 -- success failure
Description : assigns statically assigned IPaddress
=cut
sub setstaticaddress {
my ($node, $osname, $IPaddress, $image_os_type) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'OK'}, 0, "nodename not set") if (!defined($node));
notify($ERRORS{'OK'}, 0, "osname not set") if (!defined($osname));
notify($ERRORS{'CRITICAL'}, 0, "IPaddress not set") if (!defined($IPaddress));
my $subnetmask = $ENV{management_node_info}{PUBLIC_SUBNET_MASK};
my $default_gateway = $ENV{management_node_info}{PUBLIC_DEFAULT_GATEWAY};
my $dns_server = $ENV{management_node_info}{PUBLIC_DNS_SERVER};
#collect private address -- read hosts file only useful if running
# xcat setup and private addresses are listsed in the local
# /etc/hosts file
#should also store/pull private address from the database
my $privateIP;
if (open(HOSTS, "/etc/hosts")) {
my @hosts = <HOSTS>;
close(HOSTS);
foreach my $line (@hosts) {
if ($line =~ /([0-9]*.[0-9]*.[0-9]*.[0-9]*)\s+($node)/) {
$privateIP = $1;
notify($ERRORS{'OK'}, 0, "PrivateIP address for $node collected $privateIP");
last;
}
}
} ## end if (open(HOSTS, "/etc/hosts"))
if (!defined($privateIP)) {
notify($ERRORS{'WARNING'}, 0, "private IP address not found for $node, possible issue with regex");
}
my $identity = $ENV{management_node_info}{keys};
my @sshcmd;
if ($image_os_type =~ /linux/i) {
#create local tmp file
# down interface
#copy tmpfile to /etc/sysconfig/network-scripts/ifcfg-eth1
# up interface
#set route for correct gateway
my @eth1file;
my $tmpfile = "/tmp/ifcfg-eth_device-$node";
push(@eth1file, "DEVICE=eth1\n");
push(@eth1file, "BOOTPROTO=static\n");
push(@eth1file, "IPADDR=$IPaddress\n");
push(@eth1file, "NETMASK=$subnetmask\n");
push(@eth1file, "STARTMODE=onboot\n");
push(@eth1file, "ONBOOT=yes\n");
#write to tmpfile
if (open(TMP, ">$tmpfile")) {
print TMP @eth1file;
close(TMP);
}
else {
#print "could not write $tmpfile $!\n";
}
@sshcmd = run_ssh_command($node, $identity, "/etc/sysconfig/network-scripts/ifdown eth1", "root");
foreach my $l (@{$sshcmd[1]}) {
if ($l) {
#potential problem
notify($ERRORS{'OK'}, 0, "sshcmd outpuer ifdown $node $l");
}
}
#copy new ifcfg-Device
if (run_scp_command($tmpfile, "$node:/etc/sysconfig/network-scripts/ifcfg-eth1", $identity)) {
#confirm it got there
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity, "cat /etc/sysconfig/network-scripts/ifcfg-eth1", "root");
my $success = 0;
foreach my $i (@{$sshcmd[1]}) {
if ($i =~ /$IPaddress/) {
notify($ERRORS{'OK'}, 0, "SUCCESS - copied ifcfg_eth1\n");
$success = 1;
}
}
if (unlink($tmpfile)) {
notify($ERRORS{'OK'}, 0, "unlinking $tmpfile");
}
if (!$success) {
notify($ERRORS{'WARNING'}, 0, "unable to copy $tmpfile to $node file ifcfg-eth1 did get updated with $IPaddress ");
return 0;
}
} ## end if (run_scp_command($tmpfile, "$node:/etc/sysconfig/network-scripts/ifcfg-eth1"...
#bring device up
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity, "/etc/sysconfig/network-scripts/ifup eth1", "root");
#should be empty
foreach my $l (@{$sshcmd[1]}) {
if ($l) {
#potential problem
notify($ERRORS{'OK'}, 0, "possible problem with ifup eth1 $l");
}
}
#correct route table - delete old default and add new in same line
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity, "/sbin/route del default", "root");
#should be empty
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /Usage:/) {
#potential problem
notify($ERRORS{'OK'}, 0, "possible problem with route del default $l");
}
if ($l =~ /No such process/) {
notify($ERRORS{'OK'}, 0, "$l - ok just no default route since we downed eth device");
}
}
notify($ERRORS{'OK'}, 0, "Setting default route");
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity, "/sbin/route add default gw $default_gateway metric 0 eth1", "root");
#should be empty
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /Usage:/) {
#potential problem
notify($ERRORS{'OK'}, 0, "possible problem with route add default gw $default_gateway metric 0 eth1");
}
if ($l =~ /No such process/) {
notify($ERRORS{'CRITICAL'}, 0, "problem with $node $l add default gw $default_gateway metric 0 eth1 ");
return 0;
}
} ## end foreach my $l (@{$sshcmd[1]})
#correct external sshd file
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity, "cat /etc/ssh/external_sshd_config", "root");
foreach my $i (@{$sshcmd[1]}) {
if ($i =~ /No such file or directory/) {
notify($ERRORS{'OK'}, 0, "possible problem $i could not read $node /etc/ssh/external_sshd_config");
#problem
}
if ($i =~ s/ListenAddress (.*)/ListenAddress $IPaddress/) {
notify($ERRORS{'OK'}, 0, "changed Listen Address on $node");
}
} ## end foreach my $i (@{$sshcmd[1]})
#Write contents to tmp file
my $extsshtmpfile = "/tmp/extsshtmpfile$node";
if (open(TMPFILE, ">$extsshtmpfile")) {
print TMPFILE @{$sshcmd[1]};
close(TMPFILE);
}
else {
notify($ERRORS{'OK'}, 0, "could not write tmpfile $extsshtmpfile $!");
}
#copy back to host
if (run_scp_command($extsshtmpfile, "$node:/etc/ssh/external_sshd_config", $identity)) {
notify($ERRORS{'OK'}, 0, "success copied $extsshtmpfile to $node");
}
else {
notify($ERRORS{'WARNING'}, 0, "could not write copy $extsshtmpfile to $node");
}
if (unlink($extsshtmpfile)) {
notify($ERRORS{'OK'}, 0, "unlinking $extsshtmpfile");
}
#modify /etc/resolve.conf
my $search;
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity, "cat /etc/resolv.conf", "root");
foreach my $l (@{$sshcmd[1]}) {
chomp($l);
if ($l =~ /search/) {
$search = $l;
}
}
if (defined($search)) {
my @resolvconf;
push(@resolvconf, "$search\n");
my ($s1, $s2, $s3);
if ( $dns_server =~ /,/) {
($s1, $s2, $s3) = split(/,/, $dns_server);
}
else {
$s1 = $dns_server;
}
push(@resolvconf, "nameserver $s1\n");
push(@resolvconf, "nameserver $s2\n") if (defined($s2));
push(@resolvconf, "nameserver $s3\n") if (defined($s3));
my $rtmpfile = "/tmp/resolvconf$node";
if (open(RES, ">$rtmpfile")) {
print RES @resolvconf;
close(RES);
}
else {
notify($ERRORS{'OK'}, 0, "could not write to $rtmpfile $!");
}
#put resolve.conf file back on node
notify($ERRORS{'OK'}, 0, "copying in new resolv.conf");
if (run_scp_command($rtmpfile, "$node:/etc/resolv.conf", $identity)) {
notify($ERRORS{'OK'}, 0, "SUCCESS copied new resolv.conf to $node");
}
else {
notify($ERRORS{'OK'}, 0, "FALIED to copied new resolv.conf to $node");
return 0;
}
if (unlink($rtmpfile)) {
notify($ERRORS{'OK'}, 0, "unlinking $rtmpfile");
}
} ## end if (defined($search))
else {
notify($ERRORS{'WARNING'}, 0, "pulling resolve.conf from $node failed output= @{ $sshcmd[1] }");
}
} ## end if
} ## end sub setstaticaddress
#/////////////////////////////////////////////////////////////////////////////
=head2 getdynamicaddress
Parameters : $node, $osname
Returns : assigned ipaddress
Description : collects the dynamically assigned ipaddress
=cut
sub getdynamicaddress {
my ($node, $osname, $image_os_type) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'OK'}, 0, "nodename not set") if (!defined($node));
notify($ERRORS{'OK'}, 0, "osname not set") if (!defined($osname));
notify($ERRORS{'OK'}, 0, "image_os_type not set") if (!defined($image_os_type));
#collect private address -- read hosts file only useful if running
# xcat setup and private addresses are listsed in the local
# /etc/hosts file
#should also store/pull private address from the database
my $privateIP;
my @sshcmd;
if (open(HOSTS, "/etc/hosts")) {
my @hosts = <HOSTS>;
close(HOSTS);
foreach my $line (@hosts) {
if ($line =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+($node)/) {
$privateIP = $1;
notify($ERRORS{'OK'}, 0, "PrivateIP address for $node collected $privateIP");
last;
}
}
} ## end if (open(HOSTS, "/etc/hosts"))
if (!defined($privateIP)) {
notify($ERRORS{'WARNING'}, 0, "private IP address not found for $node, possible issue with regex");
}
my $identity_keys = $ENV{management_node_info}{keys};
my $dynaIPaddress = 0;
my $ip_address;
if ($image_os_type =~ /windows/i) {
@sshcmd = run_ssh_command($node, $identity_keys, "ipconfig", "root");
for my $l (@{$sshcmd[1]}) {
# skip class a,b,c private addresses
next if ($l !~ /IP(.*)?Address[\s\.:]*([\d\.]*)/);
my $ip_address_found = $2;
next if ($ip_address_found =~ /^(10|127|192\.168|172\.(1[6-9]|2[0-9]|3[0-1]))\./);
next if ($ip_address_found =~ /$privateIP/);
$ip_address = $ip_address_found;
}
$dynaIPaddress = $ip_address;
} ## end if ($osname =~ /windows/)
elsif ($image_os_type =~ /linux/) {
#$identity = $ENV{management_node_info}{keys};
undef @sshcmd;
@sshcmd = run_ssh_command($node, $identity_keys, "/sbin/ifconfig \|grep inet", "root");
for my $l (@{$sshcmd[1]}) {
# skip class a,b,c private addresses
next if ($l !~ /inet addr:([\d\.]*)/);
my $ip_address_found = $1;
next if ($ip_address_found =~ /^(10|127|192\.168|172\.(1[6-9]|2[0-9]|3[0-1]))\./);
next if ($ip_address_found =~ /$privateIP/);
$ip_address = $ip_address_found;
}
$dynaIPaddress = $ip_address;
} ## end elsif ($image_os_type =~ /linux/) [ if ($image_os_type =~ /windows/)
else {
notify($ERRORS{'WARNING'}, 0, "OStype $image_os_type not supported ");
return 0;
}
notify($ERRORS{'OK'}, 0, "dynamic IP address for $node collected: $dynaIPaddress");
return $dynaIPaddress;
} ## end sub getdynamicaddress
#/////////////////////////////////////////////////////////////////////////////
=head2 _checknstartservice
Parameters : $service name
Returns : 1 or 0
Description : checks for running local service attempts to restart
xCAT specific
=cut
sub _checknstartservice {
my $service = $_[0];
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'OK'}, 0, "service not set") if (!defined($service));
my $status = 0;
if (open(SERVICE, "/sbin/service $service status |")) {
while (<SERVICE>) {
chomp($_);
#notify($ERRORS{'OK'},0,"_checknstartservice: $_");
if ($_ =~ /running/) {
$status = 1;
notify($ERRORS{'OK'}, 0, "_checknstartservice: $service is running");
}
}
close(SERVICE);
if ($status == 1) {
return 1;
}
else {
notify($ERRORS{'OK'}, 0, "_checknstartservice: $service is not running will try to start");
# try to start service
if (open(SERVICE, "/sbin/service $service start |")) {
while (<SERVICE>) {
chomp($_);
notify($ERRORS{'WARNING'}, 0, "_checknstartservice: $_");
if ($_ =~ /started/) {
$status = 1;
last;
}
}
close(SERVICE);
if ($status == 1) {
return 1;
}
else {
notify($ERRORS{'WARNING'}, 0, "_checknstartservice: $service could not start");
return 0;
}
} ## end if (open(SERVICE, "/sbin/service $service start |"...
else {
notify($ERRORS{'WARNING'}, 0, "_checknstartservice: WARNING -- could not run service command for $service start. $! ");
return 0;
}
} ## end else [ if ($status == 1)
} ## end if (open(SERVICE, "/sbin/service $service status |"...
else {
notify($ERRORS{'WARNING'}, 0, "_checknstartservice: WARNING -- could not run service command for $service check. $! ");
return 0;
}
} ## end sub _checknstartservice
#/////////////////////////////////////////////////////////////////////////////
=head2 check_connection
Parameters : $nodename, $ipaddress, $type, $remoteIP, $time_limit, $osname, $dbh, $requestid, $user
Returns : value - deleted failed timeout connected conn_wrong_ip
Description : uses ssh to log into remote node and preform checks on user connection
=cut
sub check_connection {
my ($nodename, $ipaddress, $type, $remoteIP, $time_limit, $osname, $dbh, $requestid, $user,$image_os_type) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'OK'}, 0, "nodename not set") if (!defined($nodename));
notify($ERRORS{'OK'}, 0, "ipaddress not set") if (!defined($ipaddress));
notify($ERRORS{'OK'}, 0, "type not set") if (!defined($type));
notify($ERRORS{'OK'}, 0, "remoteIP not set") if (!defined($remoteIP));
notify($ERRORS{'OK'}, 0, "time_limit not set") if (!defined($time_limit));
notify($ERRORS{'OK'}, 0, "osname not set") if (!defined($osname));
notify($ERRORS{'OK'}, 0, "dbh not set") if (!defined($dbh));
notify($ERRORS{'OK'}, 0, "requestid not set") if (!defined($requestid));
notify($ERRORS{'OK'}, 0, "user not set") if (!defined($user));
notify($ERRORS{'OK'}, 0, "image_os_type not set") if (!defined($image_os_type));
my $start_time = time();
my $time_exceeded = 0;
my $break = 0;
my $ret_val = "no";
$dbh = getnewdbh() if !$dbh;
my $identity_keys = $ENV{management_node_info}{keys};
# Figure out number of loops for log messates
my $maximum_loops = $time_limit * 2;
my $loop_count = 0;
my @SSHCMD;
while (!$break) {
$loop_count++;
notify($ERRORS{'OK'}, 0, "checking for connection by $user on $nodename, attempt $loop_count ");
# confirm we still have an active db handle
if (!$dbh || !($dbh->ping)) {
notify($ERRORS{'WARNING'}, 0, "database handle died, trying to create another one");
$dbh = getnewdbh();
notify($ERRORS{'OK'}, 0, "database handle re-set") if ($dbh->ping);
notify($ERRORS{'WARNING'}, 0, "inuse process: database handle NOT re-set") if (!($dbh->ping));
}
if (is_request_deleted($requestid)) {
notify($ERRORS{'OK'}, 0, "user has deleted request");
$break = 1;
$ret_val = "deleted";
return $ret_val;
}
#notify($ERRORS{'OK'},0,"comparing wait time for connection");
$time_exceeded = time_exceeded($start_time, $time_limit);
if ($time_exceeded) {
notify($ERRORS{'OK'}, 0, "$time_limit minute time limit exceeded begin cleanup process");
#time_exceeded, begin cleanup process
$break = 1;
if ($package =~ /reserved/) {
notify($ERRORS{'OK'}, 0, "user never logged in returning nologin");
$ret_val = "nologin";
}
else {
$ret_val = "timeout";
}
return $ret_val;
} ## end if ($time_exceeded)
else { #time not exceeded check for connection
if ($type =~ /blade|virtualmachine/) {
my $shortnodename = $nodename;
$shortnodename = $1 if ($nodename =~ /([-_a-zA-Z0-9]*)\./);
if ($image_os_type =~ /windows/i) {
undef @SSHCMD;
@SSHCMD = run_ssh_command($shortnodename, $identity_keys, "netstat -an", "root", 22, 1);
foreach my $line (@{$SSHCMD[1]}) {
#check for rdp and ssh connections
# rdp:3389,ssh:22
#check for connection refused, if ssh is gone something
#has happenned put in timeout state
if ($line =~ /Connection refused|Permission denied/) {
chomp($line);
notify($ERRORS{'WARNING'}, 0, "$line");
if ($package =~ /reserved/) {
$ret_val = "failed";
}
else {
$ret_val = "timeout";
}
return $ret_val;
} ## end if ($line =~ /Connection refused|Permission denied/)
if ($line =~ /\s+($ipaddress:3389)\s+([.0-9]*):([0-9]*)\s+(ESTABLISHED)/) {
if ($2 eq $remoteIP) {
$break = 1;
$ret_val = "connected";
return $ret_val;
}
else {
#this isn't the remoteIP
$ret_val = "conn_wrong_ip";
return $ret_val;
}
} ## end if ($line =~ /\s+($ipaddress:3389)\s+([.0-9]*):([0-9]*)\s+(ESTABLISHED)/)
} #foreach
} ## end if ($osname =~ /win|vmwarewin/)
elsif ($image_os_type =~ /linux/i) {
#run two checks
# 1:check connected IP address
# 2:simply check who ouput
my @lines;
undef @SSHCMD;
@SSHCMD = run_ssh_command($shortnodename, $identity_keys, "netstat -an", "root", 22, 1);
foreach my $line (@{$SSHCMD[1]}) {
if ($line =~ /Connection refused|Permission denied/) {
chomp($line);
notify($ERRORS{'WARNING'}, 0, "$line");
if ($package =~ /reserved/) {
$ret_val = "failed";
}
else {
$ret_val = "timeout";
}
return $ret_val;
} ## end if ($line =~ /Connection refused|Permission denied/)
if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s($ipaddress:22)\s+([.0-9]*):([0-9]*)(.*)(ESTABLISHED)/) {
if ($4 eq $remoteIP) {
$break = 1;
$ret_val = "connected";
return $ret_val;
}
else {
#this isn't the remoteIP
$ret_val = "conn_wrong_ip";
return $ret_val;
}
} # tcp check
} #foreach
#who; too make sure we didn't miss it through netstat
undef @SSHCMD;
@SSHCMD = run_ssh_command($shortnodename, $identity_keys, "who", "root");
foreach my $w (@{$SSHCMD[1]}) {
if ($w =~ /$user/) {
$break = 1;
notify($ERRORS{'CRITICAL'}, 0, "found user connected through who command on node $nodename , strange that netstat missed it\nnetstat output:\n @lines");
$ret_val = "connected";
return $ret_val;
}
}
} ## end elsif ($image_os_type =~ /linux/) [ if ($osname =~ /windows/)
} ## end if ($type =~ /blade|virtualmachine/)
elsif ($type eq "lab") {
undef @SSHCMD;
@SSHCMD = run_ssh_command($nodename, $identity_keys, "netstat -an", "vclstaff", 24, 1);
foreach my $line (@{$SSHCMD[1]}) {
chomp($line);
if ($line =~ /Connection refused|Permission denied/) {
notify($ERRORS{'WARNING'}, 0, "$line");
if ($package =~ /reserved/) {
$ret_val = "failed";
}
else {
$ret_val = "timeout";
}
return $ret_val;
} ## end if ($line =~ /Connection refused|Permission denied/)
if ($osname =~ /sun4x_/) {
if ($line =~ /\s*($ipaddress\.22)\s+([.0-9]*)\.([0-9]*)(.*)(ESTABLISHED)/) {
if ($2 eq $remoteIP) {
$break = 1;
$ret_val = "connected";
return $ret_val;
}
else {
#this isn't the remoteIP
$ret_val = "conn_wrong_ip";
return $ret_val;
}
} ## end if ($line =~ /\s*($ipaddress\.22)\s+([.0-9]*)\.([0-9]*)(.*)(ESTABLISHED)/)
} ## end if ($osname =~ /sun4x_/)
elsif ($osname =~ /rhel/) {
if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s($ipaddress:22)\s+([.0-9]*):([0-9]*)(.*)(ESTABLISHED)/) {
if ($4 eq $remoteIP) {
$break = 1;
$ret_val = "connected";
return $ret_val;
}
else {
#this isn't the remoteIP
$ret_val = "conn_wrong_ip";
return $ret_val;
}
} ## end if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s($ipaddress:22)\s+([.0-9]*):([0-9]*)(.*)(ESTABLISHED)/)
if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s::ffff:($ipaddress:22)\s+::ffff:([.0-9]*):([0-9]*)(.*)(ESTABLISHED) /) {
if ($4 eq $remoteIP) {
$break = 1;
$ret_val = "connected";
return $ret_val;
}
else {
#this isn't the remoteIP
$ret_val = "conn_wrong_ip";
return $ret_val;
}
} ## end if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s::ffff:($ipaddress:22)\s+::ffff:([.0-9]*):([0-9]*)(.*)(ESTABLISHED) /)
} ## end elsif ($osname =~ /rhel/) [ if ($osname =~ /sun4x_/)
} #foreach
} #if lab
} #else
#sleep 30;
sleep 20;
} #while
return $ret_val;
} ## end sub check_connection
#/////////////////////////////////////////////////////////////////////////////
=head2 isconnected
Parameters : $nodename, $type, $remoteIP, $osname, $ipaddress
Returns : 1 connected 0 not connected
Description : confirms user is connected to node
assumes port 3389 for windows and port 22 for linux/solaris
=cut
sub isconnected {
my ($nodename, $type, $remoteIP, $osname, $ipaddress, $image_os_type) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'OK'}, 0, "nodename not set") if (!defined($nodename));
notify($ERRORS{'OK'}, 0, "type not set") if (!defined($type));
notify($ERRORS{'OK'}, 0, "remoteIP not set") if (!defined($remoteIP));
notify($ERRORS{'OK'}, 0, "osname not set") if (!defined($osname));
notify($ERRORS{'OK'}, 0, "image_os_type not set") if (!defined($image_os_type));
notify($ERRORS{'OK'}, 0, "ipaddress not set") if (!defined($ipaddress));
my $identity= $ENV{management_node_info}{keys};
my @netstat;
my @SSHCMD;
if ($type =~ /blade|virtualmachine/) {
my $shortname = 0;
$shortname = $1 if ($nodename =~ /([-_a-zA-Z0-9]*)\./);
if ($shortname) {
#convert shortname
$nodename = $shortname;
}
if ($image_os_type =~ /windows/i) {
#notify($ERRORS{'OK'},0,"checking $nodename $ipaddress");
undef @SSHCMD;
@SSHCMD = run_ssh_command($shortname, $identity, "netstat -an", "root", 22, 1);
foreach my $line (@{$SSHCMD[1]}) {
chomp($line);
if ($line =~ /Connection refused/) {
notify($ERRORS{'WARNING'}, 0, "$line");
return 0;
}
#if($line =~ /\s+($ipaddress:3389)\s+([.0-9]*):([0-9]*)\s+(ESTABLISHED)/){
if ($line =~ /\s+(TCP\s+[.0-9]*:3389)\s+([.0-9]*):([0-9]*)\s+(ESTABLISHED)/) {
#notify($ERRORS{'WARNING'},0,"$line");
return 1 if ($2 eq $remoteIP);
if ($2 ne $remoteIP) {
notify($ERRORS{'WARNING'}, 0, "not correct remote IP is connected");
return 1;
}
}
} ## end foreach my $line (@{$SSHCMD[1]})
} ## end if ($osname =~ /win|vmwarewin/)
elsif ($image_os_type =~ /linux/i) {
undef @SSHCMD;
@SSHCMD = run_ssh_command($nodename, $identity, "netstat -an", "root", 22, 1);
foreach my $line (@{$SSHCMD[1]}) {
chomp($line);
if ($line =~ /Warning/) {
if (known_hosts($nodename, "linux", $ipaddress)) {
#good
}
next;
}
if ($line =~ /Connection refused/) {
notify($ERRORS{'WARNING'}, 0, "$line");
return 0;
}
if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s($ipaddress:22)\s+([.0-9]*):([0-9]*)(.*)(ESTABLISHED)/) {
return 1 if ($4 eq $remoteIP);
if ($4 ne $remoteIP) {
notify($ERRORS{'WARNING'}, 0, "not correct remote IP connected: $line");
return 1;
}
}
} ## end foreach my $line (@{$SSHCMD[1]})
}
return 0;
} ## end if ($type =~ /blade|virtualmachine/)
elsif ($type eq "lab") {
undef @SSHCMD;
@SSHCMD = run_ssh_command($nodename, $identity, "netstat -an", "vclstaff", 24, 1);
foreach my $line (@{$SSHCMD[1]}) {
chomp($line);
if ($line =~ /Connection refused/) {
notify($ERRORS{'WARNING'}, 0, "$nodename $line");
return 0;
}
if ($osname =~ /sun4x_/) {
if ($line =~ /\s*($ipaddress\.22)\s+([.0-9]*)\.([0-9]*)(.*)(ESTABLISHED)/) {
return 1 if ($2 eq $remoteIP);
if ($2 ne $remoteIP) {
notify($ERRORS{'WARNING'}, 0, "not correct remote IP connected $4");
return 1;
}
}
}
elsif ($osname =~ /realmrhel3/) {
if ($line =~ /tcp\s+([0-9]*)\s+([0-9]*)\s($ipaddress:22)\s+([.0-9]*):([0-9]*)(.*)(ESTABLISHED)/) {
return 1 if ($4 eq $remoteIP);
if ($4 ne $remoteIP) {
notify($ERRORS{'WARNING'}, 0, "not correct remote IP connected $4");
return 1;
}
}
}
} ## end foreach my $line (@{$SSHCMD[1]})
return 0;
} ## end elsif ($type eq "lab") [ if ($type =~ /blade|virtualmachine/)
} ## end sub isconnected
#/////////////////////////////////////////////////////////////////////////////
=head2 update_preload_fla
Parameters : request id, flag 1,0
Returns : 1 success 0 failure
Description : update preload flag
=cut
sub update_preload_flag {
my ($request_id, $flag) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "request id is not defined") unless (defined($request_id));
notify($ERRORS{'WARNING'}, 0, "preload flag is not defined") unless (defined($flag));
my $update_statement = "
UPDATE
request
SET
preload = $flag
WHERE
id = $request_id
";
# Call the database execute subroutine
if (database_execute($update_statement)) {
# Update successful
notify($ERRORS{'OK'}, $LOGFILE, "preload flag updated for request_id $request_id ");
return 1;
}
else {
notify($ERRORS{'CRITICAL'}, 0, "unable to update preload flag updated for request_id $request_id");
return 0;
}
} ## end sub update_preload_flag
#/////////////////////////////////////////////////////////////////////////////
=head2 update_request_state
Parameters : request id, state name, last state(optional), log(optional)
Returns : 1 success 0 failure
Description : update states
=cut
sub update_request_state {
my ($request_id, $state_name, $laststate_name, $log) = @_;
my ($package, $filename, $line, $sub) = caller(0);
# Check the passed parameters
if (!defined($request_id)) {
notify($ERRORS{'WARNING'}, $log, "unable to update request state, request id is not defined");
return 0;
}
if (!defined($state_name)) {
notify($ERRORS{'WARNING'}, $log, "unable to update request $request_id state, state name not defined");
return 0;
}
my $update_statement;
# Determine whether or not to update laststate, construct the SQL statement
if (defined $laststate_name && $laststate_name ne "") {
$update_statement = "
UPDATE
request,
state state,
state laststate
SET
request.stateid = state.id,
request.laststateid = laststate.id
WHERE
state.name = \'$state_name\'
AND laststate.name = \'$laststate_name\'
AND request.id = $request_id
";
} ## end if (defined $laststate_name && $laststate_name...
else {
$update_statement = "
UPDATE
request,
state state
SET
request.stateid = state.id
WHERE
state.name = \'$state_name\'
AND request.id = $request_id
";
$laststate_name = 'unchanged';
} ## end else [ if (defined $laststate_name && $laststate_name...
# Call the database execute subroutine
if (database_execute($update_statement)) {
# Update successful
notify($ERRORS{'OK'}, $LOGFILE, "request $request_id state updated to: $state_name, laststate to: $laststate_name");
return 1;
}
else {
notify($ERRORS{'CRITICAL'}, 0, "unable to update states for request $request_id");
return 0;
}
} ## end sub update_request_state
#/////////////////////////////////////////////////////////////////////////////
=head2 update_computer_state
Parameters : $computer_id, $state_name, $log
Returns : 1 success 0 failure
Description : update computer state
=cut
sub update_computer_state {
my ($computer_id, $state_name, $log) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, $log, "computer id is not defined") unless (defined($computer_id));
notify($ERRORS{'WARNING'}, $log, "statename is not defined") unless (defined($state_name));
return 0 unless (defined $computer_id && defined $state_name);
my $update_statement = "
UPDATE
computer,
state
SET
computer.stateid = state.id
WHERE
state.name = \'$state_name\'
AND computer.id = $computer_id
";
# Call the database execute subroutine
if (database_execute($update_statement)) {
# Update successful
notify($ERRORS{'OK'}, $LOGFILE, "computer $computer_id state updated to: $state_name");
return 1;
}
else {
notify($ERRORS{'CRITICAL'}, 0, "unable to update states for computer $computer_id");
return 0;
}
} ## end sub update_computer_state
#/////////////////////////////////////////////////////////////////////////////
=head2 update_computer_lastcheck
Parameters : $computer_id, $datestring, $log
Returns : 1 success 0 failure
Description : update computer state
=cut
sub update_computer_lastcheck {
my ($computer_id, $datestring, $log) = @_;
my ($package, $filename, $line, $sub) = caller(0);
$log = 0 unless (defined $log);
notify($ERRORS{'WARNING'}, $log, "computer id is not defined") unless (defined($computer_id));
notify($ERRORS{'WARNING'}, $log, "$datestring is not defined") unless (defined($datestring));
return 0 unless (defined $computer_id);
unless (defined($datestring) ) {
$datestring = makedatestring;
}
my $update_statement = "
UPDATE
computer
SET
computer.lastcheck = '$datestring'
WHERE
computer.id = $computer_id
";
# Call the database execute subroutine
if (database_execute($update_statement)) {
# Update successful
notify($ERRORS{'DEBUG'}, $log, "computer $computer_id lastcheck updated to: $datestring");
return 1;
}
else {
notify($ERRORS{'CRITICAL'}, $log, "unable to update datestring for computer $computer_id");
return 0;
}
} ## end
#/////////////////////////////////////////////////////////////////////////////
=head2 update_request_password
Parameters : $reservation_id, $password
Returns : 1 success 0 failure
Description : updates password field for reservation id
=cut
sub update_request_password {
my ($reservation_id, $password) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "reservation id is not defined") unless (defined($reservation_id));
notify($ERRORS{'WARNING'}, 0, "password is not defined") unless (defined($password));
my $update_statement = "
UPDATE
reservation
SET
pw = \'$password\'
WHERE
id = $reservation_id
";
# Call the database execute subroutine
if (database_execute($update_statement)) {
# Update successful
notify($ERRORS{'OK'}, $LOGFILE, "password updated for reservation_id $reservation_id ");
return 1;
}
else {
notify($ERRORS{'CRITICAL'}, 0, "unable to update password for reservation $reservation_id");
return 0;
}
} ## end sub update_request_password
#/////////////////////////////////////////////////////////////////////////////
=head2 is_request_deleted
Parameters : $request_id
Returns : return 1 if request state or laststate is set to deleted or if request does not exist
return 0 if request exists and neither request state nor laststate is set to deleted1 success 0 failure
Description : checks if request has been deleted
=cut
sub is_request_deleted {
my ($request_id) = @_;
my ($package, $filename, $line, $sub) = caller(0);
# Check the passed parameter
if (!(defined($request_id))) {
notify($ERRORS{'WARNING'}, 0, "request ID was not specified");
return 0;
}
# Create the select statement
my $select_statement = "
SELECT
request.stateid AS currentstate_id,
request.laststateid AS laststate_id,
currentstate.name AS currentstate_name,
laststate.name AS laststate_name
FROM
request, state currentstate, state laststate
WHERE
request.id = $request_id
AND request.stateid = currentstate.id
AND request.laststateid = laststate.id
";
# Call the database select subroutine
# This will return an array of one or more rows based on the select statement
my @selected_rows = database_select($select_statement);
# Check to make sure 1 row was returned
if (scalar @selected_rows == 0) {
return 1;
}
elsif (scalar @selected_rows > 1) {
notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select");
return 0;
}
my $state_name = $selected_rows[0]{currentstate_name};
my $laststate_name = $selected_rows[0]{laststate_name};
#notify($ERRORS{'DEBUG'}, 0,"state=$state_name, laststate=$laststate_name");
if ($state_name eq 'deleted' || $laststate_name eq 'deleted') {
return 1;
}
return 0;
} ## end sub is_request_deleted
#/////////////////////////////////////////////////////////////////////////////
=head2 is_reservation_deleted
Parameters : $reservation_id
Returns : return 1 if reservation's request state or laststate is set to deleted or if reservation does not exist
return 0 if reservation exists and neither request state nor laststate is set to deleted: 1 success, 0 failure
Description : checks if reservation has been deleted
=cut
sub is_reservation_deleted {
my ($reservation_id) = @_;
# Check the passed parameter
if (!(defined($reservation_id))) {
notify($ERRORS{'WARNING'}, 0, "reservation ID was not specified");
return 0;
}
# Create the select statement
my $select_statement = "
SELECT
reservation.id AS reservation_id,
request.stateid AS currentstate_id,
request.laststateid AS laststate_id,
currentstate.name AS currentstate_name,
laststate.name AS laststate_name
FROM
reservation, request, state currentstate, state laststate
WHERE
reservation.id = $reservation_id
AND reservation.requestid = request.id
AND request.stateid = currentstate.id
AND request.laststateid = laststate.id
";
# Call the database select subroutine
# This will return an array of one or more rows based on the select statement
my @selected_rows = database_select($select_statement);
# Check to make sure 1 row was returned
if (scalar @selected_rows == 0) {
return 1;
}
elsif (scalar @selected_rows > 1) {
notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select");
return 0;
}
my $state_name = $selected_rows[0]{currentstate_name};
my $laststate_name = $selected_rows[0]{laststate_name};
#notify($ERRORS{'DEBUG'}, 0,"state=$state_name, laststate=$laststate_name");
if ($state_name eq 'deleted' || $laststate_name eq 'deleted') {
return 1;
}
return 0;
} ## end sub is_reservation_deleted
#/////////////////////////////////////////////////////////////////////////////
=head2 is_request_imaging
Parameters : $request_id
Returns : return 'image' if request state or laststate is set to image
return 'forimaging' if forimaging is set to 1, and neither request state nor laststate is set to image
return 0 if forimaging is set to 0, and neither request state nor laststate is set to image
return undefined if an error occurred
Description : checks if request is in imaging mode and if forimaging has been set
=cut
sub is_request_imaging {
my ($request_id) = @_;
# Check the passed parameter
if (!(defined($request_id))) {
notify($ERRORS{'WARNING'}, 0, "request ID was not specified");
return;
}
# Create the select statement
my $select_statement = "
SELECT
request.forimaging AS forimaging,
request.stateid AS currentstate_id,
request.laststateid AS laststate_id,
currentstate.name AS currentstate_name,
laststate.name AS laststate_name
FROM
request, state currentstate, state laststate
WHERE
request.id = $request_id
AND request.stateid = currentstate.id
AND request.laststateid = laststate.id
";
# Call the database select subroutine
# This will return an array of one or more rows based on the select statement
my @selected_rows = database_select($select_statement);
# Check to make sure 1 row was returned
if (scalar @selected_rows != 1) {
notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select");
return;
}
my $forimaging = $selected_rows[0]{forimaging};
my $state_name = $selected_rows[0]{currentstate_name};
my $laststate_name = $selected_rows[0]{laststate_name};
notify($ERRORS{'DEBUG'}, 0, "forimaging=$forimaging, currentstate=$state_name, laststate=$laststate_name");
# If request state or laststate has been changed to image, return 1
# If forimaging is set, return 0
# If neither state is image and forimaging is not set, return undefined
if ($state_name eq 'image' || $laststate_name eq 'image') {
return 'image';
}
elsif ($forimaging) {
return 'forimaging';
}
else {
return 0;
}
} ## end sub is_request_imaging
#/////////////////////////////////////////////////////////////////////////////
=head2 get_next_image_default
Parameters : $computerid
Returns : imageid,imagerevisionid,imagename
Description : Looks for any upcoming reservations
for supplied computerid, if starttime is
within 50 minutes return that imageid. Else
fetch and return next image
=cut
sub get_next_image_default {
my ($computerid) = @_;
my ($calling_package, $calling_filename, $calling_line, $calling_sub) = caller(0);
if (!defined($computerid)) {
notify($ERRORS{'WARNING'}, 0, "$calling_sub $calling_package missing mandatory variable: computerid ");
return 0;
}
my $select_statement = "
SELECT DISTINCT
req.start AS starttime,
ir.imagename AS imagename,
res.imagerevisionid AS imagerevisionid,
res.imageid AS imageid
FROM
reservation res,
request req,
image i,
state s,
imagerevision ir
WHERE
res.requestid = req.id
AND req.stateid = s.id
AND i.id = res.imageid
AND ir.id = res.imagerevisionid
AND res.computerid = $computerid
AND (s.name = \'new\' OR s.name = \'reload\' OR s.name = \'imageprep\')
";
# Call the database select subroutine
# This will return an array of one or more rows based on the select statement
my @selected_rows = database_select($select_statement);
my @ret_array;
# Check to make sure 1 or more rows were returned
if (scalar @selected_rows > 0) {
# Loop through list of upcoming reservations
# Based on the start time load the next one
my $now = time();
# It contains a hash
for (@selected_rows) {
my %reservation_row = %{$_};
# $reservation_row{starttime}
# $reservation_row{imagename}
# $reservation_row{imagerevisionid}
# $reservation_row{imageid}
my $epoch_start = convert_to_epoch_seconds($reservation_row{starttime});
my $diff = $epoch_start - $now;
# If start time is less than 50 minutes from now return this image
notify($ERRORS{'OK'}, 0, "get_next_image_default : diff= $diff image= $reservation_row{imagename} imageid=$reservation_row{imageid}");
if ($diff < (50 * 60)) {
notify($ERRORS{'OK'}, 0, "get_next_image_default : future reservation detected diff= $diff image= $reservation_row{imagename} imageid=$reservation_row{imageid}");
push(@ret_array, $reservation_row{imagename}, $reservation_row{imageid}, $reservation_row{imagerevisionid});
return @ret_array;
}
} ## end for (@selected_rows)
} ## end if (scalar @selected_rows > 0)
# No upcoming reservations - fetch next image information
my $select_nextimage = "
SELECT DISTINCT
imagerevision.imagename AS imagename,
imagerevision.id AS imagerevisionid,
image.id AS imageid
FROM
image,
computer,
imagerevision
WHERE
imagerevision.imageid = computer.nextimageid
AND imagerevision.production = 1
AND computer.nextimageid = image.id
AND computer.id = $computerid
";
# Call the database select subroutine
# This will return an array of one or more rows based on the select statement
my @next_selected_rows = database_select($select_nextimage);
# Check to make sure at least 1 row were returned
if (scalar @next_selected_rows == 0) {
notify($ERRORS{'WARNING'}, 0, "get_next_image_default failed to fetch next image for computerid $computerid");
return 0;
}
elsif (scalar @next_selected_rows > 1) {
notify($ERRORS{'WARNING'}, 0, "" . scalar @next_selected_rows . " rows were returned from database select");
return 0;
}
notify($ERRORS{'OK'}, 0, "get_next_image_default : returning next image=$next_selected_rows[0]{imagename} imageid=$next_selected_rows[0]{imageid}");
push(@ret_array, $next_selected_rows[0]{imagename}, $next_selected_rows[0]{imageid}, $next_selected_rows[0]{imagerevisionid});
return @ret_array;
} ## end sub get_next_image
#/////////////////////////////////////////////////////////////////////////////
=head2 setnextimage
Parameters : $computerid, $image
Returns : 1 success, 0 failed
Description : updates nextimageid on provided computerid
=cut
sub setnextimage {
my ($computerid, $imageid) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "computerid: node is not defined") if (!(defined($computerid)));
notify($ERRORS{'WARNING'}, 0, "imageid: node is not defined") if (!(defined($imageid)));
my $update_statement = " UPDATE computer SET nextimageid = $imageid WHERE id = $computerid ";
# Call the database execute subroutine
if (database_execute($update_statement)) {
return 1;
}
else {
notify($ERRORS{'WARNING'}, 0, "unable to update nextimageid");
return 0;
}
} ## end sub setnextimage
#/////////////////////////////////////////////////////////////////////////////
=head2 _getcurrentimage
Parameters : $node
Returns : retrieve the currentimage from currentimage.txt file on the node
Description :
=cut
sub _getcurrentimage {
my $node = $_[0];
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "node is not defined") if (!(defined($node)));
# TODO - loop through the available ssh keys to figure out which one works
my $identity = $ENV{management_node_info}{keys};
my @sshcmd = run_ssh_command($node, $identity, "cat currentimage.txt");
foreach my $s (@{$sshcmd[1]}) {
if ($s =~ /Warning: /) {
#need to run makesshgkh
#if (VCL::Module::Provisioning::xCAT::makesshgkh($node)) {
#success
#not worth output here
#}
#else {
#}
}
if ($s =~ /^(rh|win|fc|vmware|cent)/) {
chomp($s);
if ($s =~ s/\x0d//) {
notify($ERRORS{'OK'}, 0, "stripped dos newline $s");
}
return $s;
}
} ## end foreach my $s (@{$sshcmd[1]})
return 0;
} ## end sub _getcurrentimage
#/////////////////////////////////////////////////////////////////////////////
=head2 check_ssh
Parameters : $node, $port, $log
Returns : 1(active) or 0(inactive)
Description : uses check_ssh binary from tools dir to check
the sshd statuse on the remote node
=cut
sub check_ssh {
my ($node, $port, $log) = @_;
my ($package, $filename, $line, $sub) = caller(0);
$log = 0 if (!(defined($log)));
notify($ERRORS{'WARNING'}, $log, "node is not defined") if (!(defined($node)));
notify($ERRORS{'WARNING'}, $log, "port is not defined") if (!(defined($port)));
if (!defined($node)) {
return 0;
}
if (!defined($port)) {
$port = 22;
}
if(nmap_port($node,$port)){
notify($ERRORS{'OK'}, $log, " $node ssh port $port open");
return 1;
}
else{
notify($ERRORS{'OK'}, $log, " $node ssh port $port closed");
return 0;
}
return 0;
} ## end sub check_ssh
#/////////////////////////////////////////////////////////////////////////////
=head2 _sshd_status
Parameters : $node, $imagename, $log
Returns : on or off
Description : actually logs into remote node
=cut
sub _sshd_status {
my ($node, $imagename,$image_os_type, $log) = @_;
my ($package, $filename, $line, $sub) = caller(0);
$log = 0 if (!defined($log));
notify($ERRORS{'WARNING'}, $log, "node is not defined") if (!(defined($node)));
if (!nmap_port($node, 22)) {
return "off";
}
my $identity = $ENV{management_node_info}{keys};
my @sshcmd = run_ssh_command($node, $identity, "uname -s", "root");
return "off" if (!defined($sshcmd[0]) || !defined($sshcmd[1]) || $sshcmd[0] == 1);
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /^Warning:/) {
#if (VCL::Module::Provisioning::xCAT::makesshgkh($node)) {
#}
}
return "off" if ($l =~ /noping/);
return "off" if ($l =~ /No route to host/);
return "off" if ($l =~ /Connection refused/);
return "off" if ($l =~ /Permission denied/);
} ## end foreach my $l (@{$sshcmd[1]})
return "on";
} ## end sub _sshd_status
#/////////////////////////////////////////////////////////////////////////////
=head2 _machine_os
Parameters : $node, $imagename, $log
Returns : 0 or system type name
Description : actually logs into remote node
=cut
sub _machine_os {
my ($node, $imagename) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "node is not defined") if (!(defined($node)));
if (!nmap_port($node, 22)) {
notify($ERRORS{'OK'}, 0, "ssh port not open cannot check $node OS");
return 0;
}
my $identity = $ENV{management_node_info}{keys};
my @sshcmd = run_ssh_command($node, $identity, "uname -s", "root");
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /CYGWIN_NT-5\.1/) {
return "WinXp";
}
elsif ($l =~ "CYGWIN_NT-5\.2") {
return "win2003";
}
elsif ($l =~ /Linux/) {
return "Linux";
}
elsif ($l =~ /Connection refused/) {
return 0;
}
elsif ($l =~ /No route to host/) {
return 0;
}
elsif ($l =~ /Permission denied/) {
return 0;
}
else {
return 0;
}
} ## end foreach my $l (@{$sshcmd[1]})
} ## end sub _machine_os
#/////////////////////////////////////////////////////////////////////////////
=head2 nmap_port
Parameters : $hostname, $port
Returns : 1 open 0 closed
Description : use nmap port scanning tool to determine if port is open
=cut
sub nmap_port {
my ($hostname, $port) = @_;
if (!$hostname) {
notify($ERRORS{'WARNING'}, 0, "hostname argument was not specified");
return;
}
if (!defined($port)) {
notify($ERRORS{'WARNING'}, 0, "port argument was not specified");
return;
}
my $command = "/usr/bin/nmap $hostname -P0 -p $port -T Aggressive";
my ($exit_status, $output) = run_command($command, 1);
if (!defined($output)) {
notify($ERRORS{'WARNING'}, 0, "failed to run nmap command on management node: '$command'");
return;
}
if (grep(/open/i, @$output)) {
notify($ERRORS{'DEBUG'}, 0, "port $port is open on $hostname");
return 1;
}
elsif (grep(/(nmap:|warning)/i, @$output)) {
notify($ERRORS{'WARNING'}, 0, "error occurred running nmap command: '$command', output:\n" . join("\n", @$output));
return;
}
else {
notify($ERRORS{'DEBUG'}, 0, "port $port is closed on $hostname");
return 0;
}
} ## end sub nmap_port
#/////////////////////////////////////////////////////////////////////////////
=head2 _pingnode
Parameters : $hostname
Returns : 1 pingable 0 not-pingable
Description : using Net::Ping to check if node is pingable
assumes icmp echo is allowed
=cut
sub _pingnode {
my ($hostname) = $_[0];
if (!$hostname) {
notify($ERRORS{'WARNING'}, 0, "hostname argument was not supplied");
return;
}
my $p = Net::Ping->new("icmp");
my $result = $p->ping($hostname, 1);
$p->close();
if (!$result) {
return 0;
}
else {
return 1;
}
} ## end sub _pingnode
#/////////////////////////////////////////////////////////////////////////////
=head2 getnewdbh
Parameters : none
Returns : 0 failed or database handle
Description : gets a databasehandle
=cut
sub getnewdbh {
#my $caller_trace = get_caller_trace(7, 1);
#notify($ERRORS{'DEBUG'}, 0, "called from: $caller_trace");
my ($database) = @_;
$database = $DATABASE if !$database;
my $dbh;
# Try to use the existing database handle
if ($ENV{dbh} && $ENV{dbh}->ping && $ENV{dbh}->{Name} =~ /^$database:/) {
#notify($ERRORS{'DEBUG'}, 0, "using database handle stored in \$ENV{dbh}");
return $ENV{dbh};
}
elsif ($ENV{dbh} && $ENV{dbh}->ping) {
my ($stored_database_name) = $ENV{dbh}->{Name} =~ /^([^:]*)/;
notify($ERRORS{'DEBUG'}, 0, "database requested ($database) does not match handle stored in \$ENV{dbh} (" . $ENV{dbh}->{Name} . ")");
}
elsif (defined $ENV{dbh}) {
notify($ERRORS{'DEBUG'}, 0, "unable to use database handle stored in \$ENV{dbh}");
}
else {
#notify($ERRORS{'DEBUG'}, 0, "\$ENV{dbh} is not defined, creating new database handle");
}
my $attempt = 0;
my $max_attempts = 5;
my $retry_delay = 2;
# Assemble the data source string
my $data_source;
if ($MYSQL_SSL) {
$data_source = "$database:$SERVER;mysql_ssl=1;mysql_ssl_ca_file=$MYSQL_SSL_CERT";
}
else {
$data_source = "$database:$SERVER";
}
# Attempt to connect to the data source and get a database handle object
my $dbi_result;
while (!$dbh && $attempt < $max_attempts) {
$attempt++;
# Attempt to connect
#notify($ERRORS{'DEBUG'}, 0, "attempting to connect to data source: $data_source, user: " . string_to_ascii($WRTUSER) . ", pass: " . string_to_ascii($WRTPASS));
$dbh = DBI->connect(qq{dbi:mysql:$data_source}, $WRTUSER, $WRTPASS, {PrintError => 0});
# Check if connect was successful
if ($dbh && $dbh->ping) {
# Set InactiveDestroy = 1 for all dbh's belonging to child processes
# Set InactiveDestroy = 0 for all dbh's belonging to vcld
if (!defined $ENV{vcld} || !$ENV{vcld}) {
$dbh->{InactiveDestroy} = 1;
}
else {
$dbh->{InactiveDestroy} = 0;
}
# Increment the dbh count environment variable if it is defined
# This is only for development and testing to see how many handles a process creates
$ENV{dbh_count}++ if defined($ENV{dbh_count});
# Store the newly created database handle in an environment variable
# Only store it if $ENV{dbh} is already defined
# It's up to other modules to determine if $ENV{dbh} is defined, they must initialize it
if (defined $ENV{dbh}) {
$ENV{dbh} = $dbh;
notify($ERRORS{'DEBUG'}, 0, "database handle stored in \$ENV{dbh}");
}
return $dbh;
} ## end if ($dbh && $dbh->ping)
# Something went wrong, construct a DBI result string
$dbi_result = "DBI result: ";
if (defined(DBI::err())) {
$dbi_result = "(" . DBI::err() . ")";
}
if (defined(DBI::errstr())) {
$dbi_result .= " " . DBI::errstr();
}
# Check for access denied
if (DBI::err() == 1045 || DBI::errstr() =~ /access denied/i) {
notify($ERRORS{'WARNING'}, 0, "unable to connect to database, $dbi_result");
return 0;
}
# Either connect or ping failed
if ($dbh && !$dbh->ping) {
notify($ERRORS{'DEBUG'}, 0, "database connect succeeded but ping failed, attempt $attempt/$max_attempts, $dbi_result");
$dbh->disconnect;
}
else {
notify($ERRORS{'DEBUG'}, 0, "database connect failed, attempt $attempt/$max_attempts, $dbi_result");
}
notify($ERRORS{'DEBUG'}, 0, "sleeping for $retry_delay seconds");
sleep $retry_delay;
next;
} ## end while (!$dbh && $attempt < $max_attempts)
# Maximum number of attempts was reached
notify($ERRORS{'WARNING'}, 0, "failed to connect to database, attempts made: $attempt/$max_attempts, $dbi_result");
return 0;
} ## end sub getnewdbh
#/////////////////////////////////////////////////////////////////////////////
=head2 notify_via_wall
Parameters : empty
Returns : 0 or 1
Description : talks to user at the console using wall
=cut
sub notify_via_wall {
my ($hostname, $username, $string, $OSname, $type) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "hostname is not defined") if (!(defined($hostname)));
notify($ERRORS{'WARNING'}, 0, "username is not defined") if (!(defined($username)));
notify($ERRORS{'WARNING'}, 0, "string is not defined") if (!(defined($string)));
notify($ERRORS{'WARNING'}, 0, "OSname is not defined") if (!(defined($OSname)));
notify($ERRORS{'WARNING'}, 0, "type is not defined") if (!(defined($type)));
my @ssh;
my $n;
my $identity;
#create file, copy to remote host, then run wall
if (open(TMP, ">/tmp/wall.$hostname")) {
print TMP $string;
close TMP;
}
else {
notify($ERRORS{'WARNING'}, 0, "could not open tmp file $!");
}
my $identity_keys = $ENV{management_node_info}{keys};
if ($type eq "blade") {
#this is only going to be rhel
if (run_scp_command("/tmp/wall.$hostname", "$hostname:/root/wall.txt", $identity_keys)) {
unlink "/tmp/wall.$hostname";
if (run_ssh_command($hostname, $identity_keys, " cat /root/wall.txt \| wall; /bin/rm -v /root/wall.txt", "root")) {
notify($ERRORS{'OK'}, 0, "successfully sent wall notification to $hostname");
return 1;
}
}
} ## end if ($type eq "blade")
elsif ($type eq "lab") {
if (run_scp_command("/tmp/wall.$hostname", "vclstaff\@$hostname:/home/vclstaff/wall.txt", $identity_keys, 24)) {
unlink "/tmp/wall.$hostname";
}
else {
notify($ERRORS{'WARNING'}, 0, "could not scp tmp file for wall notification$!");
}
if ($OSname =~ /sun4x_/) {
if (run_ssh_command($hostname, $identity_keys, "wall -a /home/vclstaff/wall.txt; /bin/rm -v /home/vclstaff/wall.txt", "vclstaff", "24")) {
notify($ERRORS{'OK'}, 0, "successfully sent wall notification to $hostname");
return 1;
}
else {
notify($ERRORS{'OK'}, 0, "wall notification $hostname failed ");
}
}
elsif ($OSname =~ /rhel/) {
if (run_ssh_command($hostname, $identity_keys, "cat /home/vclstaff/wall.txt \| wall ; /bin/rm -v /home/vclstaff/wall.txt", "vclstaff", "24")) {
notify($ERRORS{'OK'}, 0, "successfully sent wall notification to $hostname");
return 1;
}
else {
notify($ERRORS{'WARNING'}, 0, "wall notification $hostname failed ");
}
}
else {
notify($ERRORS{'WARNING'}, 0, "not an OS I can handle, os is $OSname");
}
return 1;
} ## end elsif ($type eq "lab") [ if ($type eq "blade")
} ## end sub notify_via_wall
#/////////////////////////////////////////////////////////////////////////////
=head2 isfilelocked
Parameters : $file - file path
Returns : 0 no or 1 yes
Description : looks for supplied file
appends .lock to the end of supplied file
=cut
sub isfilelocked {
my ($file) = $_[0];
my $lockfile = $file . ".lock";
if (-r $lockfile) {
return 1;
}
else {
return 0;
}
} ## end sub isfilelocked
#/////////////////////////////////////////////////////////////////////////////
=head2 lockfile
Parameters : $file
Returns : 0 failed or 1 success
Description : creates $file.lock
=cut
sub lockfile {
my ($file) = $_[0];
my $lockfile = $file . ".lock";
while (!(-r $lockfile)) {
if (open(LOCK, ">$lockfile")) {
print LOCK "1";
close LOCK;
}
else {
notify($ERRORS{'WARNING'}, 0, "could not create $lockfile $!");
return 0;
}
return 1;
} ## end while (!(-r $lockfile))
} ## end sub lockfile
#/////////////////////////////////////////////////////////////////////////////
=head2 unlockfile
Parameters : $file
Returns : 0 or 1
Description : removes file if exists
=cut
sub unlockfile {
my ($file) = $_[0];
my $lockfile = $file . ".lock";
if (-r $lockfile) {
unlink $lockfile;
}
else {
# no lock file exists
}
return 1;
} ## end sub unlockfile
#/////////////////////////////////////////////////////////////////////////////
=head2 notify_via_msg
Parameters : $node, $user, $message
Returns : 0 or 1
Description : using windows msg.exe cmd writes supplied $message
to windows user console
=cut
sub notify_via_msg {
my ($node, $user, $message) = @_;
my ($package, $filename, $line, $sub) = caller(0);
my $osname = lc($^O);
if ($osname =~ /win/i) {
notify($ERRORS{'OK'}, 0, "notifying from Windows not yet supported\n-----\nTo: $user\nNode: $node\n$message\n-----");
return;
}
notify($ERRORS{'WARNING'}, 0, "node is not defined") if (!(defined($node)));
notify($ERRORS{'WARNING'}, 0, "message is not defined") if (!(defined($message)));
notify($ERRORS{'WARNING'}, 0, "user is not defined") if (!(defined($user)));
# Escape new lines
$message =~ s/\n/ /gs;
$message =~ s/\'/\\\\\\\'/gs;
notify($ERRORS{'DEBUG'}, 0, "message:\n$message");
my $command = "msg $user /TIME:180 '$message'";
if (run_ssh_command($node, $ENV{management_node_info}{keys}, $command)) {
notify($ERRORS{'OK'}, 0, "successfully sent message to Windows user $user on $node");
return 1;
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to send message to Windows user $user on $node");
return 0;
}
} ## end sub notify_via_msg
#/////////////////////////////////////////////////////////////////////////////
=head2 getpw
Parameters : length(optional) - if not defined sets to 6
Returns : randomized password
Description : called for standalone accounts and used in randomizing
privileged account passwords
=cut
sub getpw {
my $length = $_[0];
$length = 6 if (!(defined($length)));
my @a = ("A" .. "H", "J" .. "N", "P" .. "Z", "a" .. "k", "m" .. "z", "2" .. "9");
my $b;
srand;
for (1 .. $length) {
$b .= $a[rand(57)];
}
return $b;
} ## end sub getpw
#/////////////////////////////////////////////////////////////////////////////
=head2 hostname
Parameters : NA
Returns : hostname of this machine
Description : attempts to check local hostname using hostname cmd
if global FQDN is set the routine returns this instead
=cut
sub hostname {
my ($package, $filename, $line, $sub) = caller(0);
my @host;
my $h;
#hack
my $osname = lc($^O);
if ($osname eq 'linux') {
if ($FQDN) {
@host = ($FQDN, "linux");
return @host;
}
if (open(HOST, "/bin/hostname -f 2>&1 |")) {
@host = <HOST>;
close(HOST);
foreach $h (@host) {
if ($h =~ /([-a-z0-9]*)([.a-z]*)/) {
chomp($h);
@host = ($h, "linux");
return @host;
}
} ## end foreach $h (@host)
} ## end if (open(HOST, "/bin/hostname -f 2>&1 |"))
else {
notify($ERRORS{'CRITICAL'}, 0, "can't $!");
return 0;
}
} ## end if ($osname eq 'linux')
elsif ($osname eq 'solaris') {
if ($FQDN) {
@host = ($FQDN, "linux");
return @host;
}
if (open(NODENAME, "< /etc/nodename")) {
@host = <NODENAME>;
close(NODENAME);
foreach $h (@host) {
if ($h =~ /([-a-z0-9]*)([.a-z]*)/) {
chomp($h);
my @host = ($h, "solaris");
return @host;
}
}
} ## end if (open(NODENAME, "< /etc/nodename"))
else {
notify($ERRORS{'CRITICAL'}, 0, "can't open /etc/nodename $!");
return 0;
}
} ## end elsif ($osname eq 'solaris') [ if ($osname eq 'linux')
elsif ($osname eq 'mswin32') {
if ($FQDN) {
@host = ($FQDN, "windows");
return @host;
}
}
else {
notify($ERRORS{'CRITICAL'}, 0, "unknown OS type: $osname");
return 0;
}
return 0;
} ## end sub hostname
#/////////////////////////////////////////////////////////////////////////////
=head2 known_hosts
Parameters : $node , management OS, $ipaddress
Returns : 0 or 1
Description : check for or add nodenames public rsa key to local known_hosts file
=cut
sub known_hosts {
my ($node, $mnOS, $ipaddress) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'CRITICAL'}, 0, "node is not defined") if (!(defined($node)));
notify($ERRORS{'CRITICAL'}, 0, "mnOS is not defined") if (!(defined($mnOS)));
notify($ERRORS{'CRITICAL'}, 0, "ipaddress is not defined") if (!(defined($ipaddress)));
my ($known_hosts, $existed, $ssh_keyscan, $port);
#set up dependiences
if ($mnOS eq "solaris") {
$known_hosts = "/.ssh/known_hosts";
$ssh_keyscan = "/local/openssh/bin/ssh-keyscan";
$port = 24;
}
elsif ($mnOS eq "linux") {
$known_hosts = "/root/.ssh/known_hosts";
$ssh_keyscan = "/usr/bin/ssh-keyscan";
$port = 24;
}
else {
notify($ERRORS{'WARNING'}, 0, "unsupported management node OS: $mnOS");
return 0;
}
#remove key
my @known_hosts_file;
if (open(KNOWNHOSTS, "< $known_hosts")) {
@known_hosts_file = <KNOWNHOSTS>;
close(KNOWNHOSTS);
}
else {
notify($ERRORS{'WARNING'}, 0, "could not read $known_hosts $!");
}
foreach my $l (@known_hosts_file) {
if ($l =~ /$node|$ipaddress/) {
$l = "";
$existed = 1;
}
}
#write back
if ($existed) {
if (open(KNOWNHOSTS, ">$known_hosts")) {
print KNOWNHOSTS @known_hosts_file;
close(KNOWNHOSTS);
}
else {
notify($ERRORS{'WARNING'}, 0, "could not write to $known_hosts file $!");
}
}
#proceed to get public rsa key
#notify($ERRORS{'OK'},0,"executing $ssh_keyscan -t rsa -p $port $node >> $known_hosts");
if (open(KEYSCAN, "$ssh_keyscan -t rsa -p $port $node >> $known_hosts 2>&1|")) {
my @ret = <KEYSCAN>;
close(KEYSCAN);
foreach my $r (@ret) {
notify($ERRORS{'OK'}, 0, "$r");
return 0 if ($r =~ /Name or service not known/);
}
return 1;
} ## end if (open(KEYSCAN, "$ssh_keyscan -t rsa -p $port $node >> $known_hosts 2>&1|"...
else {
notify($ERRORS{'WARNING'}, 0, "could not execute append of $node public key to $known_hosts file $!");
return 0;
}
} ## end sub known_hosts
#/////////////////////////////////////////////////////////////////////////////
=head2 getusergroupmembers
Parameters : usergroupid
Returns : array of user group memebers
Description : queries database and collects user members of supplied usergroupid
=cut
sub getusergroupmembers {
my $usergroupid = $_[0];
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "usergroupid is not defined") if (!(defined($usergroupid)));
if (!(defined($usergroupid))) {
return ();
}
my $select_statement = "
SELECT
user.unityid,
user.uid
FROM
user,
usergroupmembers
WHERE
user.id = usergroupmembers.userid
AND usergroupmembers.usergroupid = '$usergroupid'
";
# Call the database select subroutine
# This will return an array of one or more rows based on the select statement
my @selected_rows = database_select($select_statement);
if (scalar @selected_rows == 0) {
notify($ERRORS{'OK'}, 0, "no data returned for usergroupid $usergroupid returning empty lists");
return ();
}
my %hash;
my (@retarray);
for (@selected_rows) {
my %hash = %{$_};
push(@retarray, "$hash{unityid}:$hash{uid}");
}
return @retarray;
} ## end sub getusergroupmembers
#/////////////////////////////////////////////////////////////////////////////
=head2 collectsshkeys
Parameters : node
Returns : 0 or 1
Description : collects ssh keys from client
=cut
sub collectsshkeys {
my $node = $_[0];
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "node is not defined") if (!(defined($node)));
if (!(defined($node))) {
return 0;
}
my ($id, $ipaddress, $type, $hostname, $currentimage, $osname);
#if lab client and OS is linux or solaris fetch ssh keys
#store repective key into computer table for the node
my $dbh = getnewdbh;
#collect a little information about the node.
my $sel = $dbh->prepare("SELECT c.id,c.IPaddress,c.hostname,c.type,o.name,i.name FROM computer c, OS o, image i WHERE c.currentimageid=i.id AND i.OSid=o.id AND c.hostname REGEXP ?") or notify($ERRORS{'WARNING'}, 0, "could not prepare collect computer detail statement" . $dbh->errstr());
$sel->execute($node) or notify($ERRORS{'WARNING'}, 0, "Problem could not execute on computer detail : " . $dbh->errstr);
my $rows = $sel->rows;
$sel->bind_columns(\($id, $ipaddress, $hostname, $type, $osname, $currentimage));
if ($rows) {
if ($sel->fetch) {
print "$id,$ipaddress,$hostname,$type,$osname,$currentimage\n";
}
}
else {
notify($ERRORS{'CRITICAL'}, 0, "no information found in computer table for $node ");
$dbh->disconnect if !defined $ENV{dbh};
return 0;
}
#what identity do we use
my $key = $ENV{management_node_info}{keys};
#send fetch keys flag to node
my @sshcmd = run_ssh_command($ipaddress, $key, "echo fetch > /home/vclstaff/clientdata; echo 1 > /home/vclstaff/flag", "vclstaff", "24");
foreach my $l (@{$sshcmd[1]}) {
if ($l =~ /Warning|denied|No such/) {
notify($ERRORS{'CRITICAL'}, 0, "node $node ouput @{ $sshcmd[1] }");
}
}
#retrieve the keys
#sleep 6, node flag check is every 5 sec
sleep 6;
my ($loop, $ct) = 0;
undef @sshcmd;
@sshcmd = run_ssh_command($ipaddress, $key, "ls -1", "vclstaff", "24");
foreach my $l (@{$sshcmd[1]}) {
chomp($l);
if ($l =~ /ssh_host/) {
#print "$l\n";
if (!(-d "/tmp/$id")) {
notify($ERRORS{'OK'}, 0, "creating /tmp/$id") if (mkdir("/tmp/$id"));
}
if (run_scp_command("vclstaff\@$ipaddress:/home/vclstaff/$l", "/tmp/$id/$l", $key, "24")) {
$ct++;
}
}
} #foreach
if ($ct < 6) {
notify($ERRORS{'OK'}, 0, "count copied, is less than 6 trying again");
if ($loop > 2) {
notify($ERRORS{'CRITICAL'}, 0, "could not copy 6 ssh keys from $node");
$dbh->disconnect if !defined $ENV{dbh};
return 0;
}
$loop++;
goto COLLECT;
} ## end if ($ct < 6)
#read in key files
my $dsa;
my $dsapub;
my $rsa;
my $rsapub;
my $hostbuffer = "";
my $hostpub;
if (open(DSA, "/tmp/$id/ssh_host_dsa_key")) {
print "slurping dsa_key\n";
#@dsa=<DSA>;
read(DSA, $dsa, 1024);
close(DSA);
}
else {
notify($ERRORS{'CRITICAL'}, 0, "could not open dsa file $!");
}
if (open(DSAPUB, "/tmp/$id/ssh_host_dsa_key.pub")) {
print "slurping dsa_pub_key\n";
#@dsapub=<DSAPUB>;
read(DSAPUB, $dsapub, 1024);
close(DSAPUB);
}
else {
notify($ERRORS{'CRITICAL'}, 0, "could not open dsa.pub file $!");
}
if (open(RSA, "/tmp/$id/ssh_host_rsa_key")) {
print "slurping rsa_key\n";
#@rsa=<RSA>;
read(RSA, $rsa, 1024);
close(RSA);
}
else {
notify($ERRORS{'CRITICAL'}, 0, "could not open rsa file $!");
}
if (open(RSAPUB, "/tmp/$id/ssh_host_rsa_key.pub")) {
print "slurping rsa_pub_key\n";
#@rsapub=<RSAPUB>;
read(RSAPUB, $rsapub, 1024);
close(RSAPUB);
}
else {
notify($ERRORS{'CRITICAL'}, 0, "could not open rsa.pub file $!");
}
if (open(HOSTPUB, "/tmp/$id/ssh_host_key.pub")) {
print "slurping host_pub_key\n";
#@hostpub=<HOSTPUB>;
read(HOSTPUB, $hostpub, 1024);
close(HOSTPUB);
}
else {
notify($ERRORS{'CRITICAL'}, 0, "could not open host.pub file $!");
}
#binary file
if (open(HOST, "/tmp/$id/ssh_host_key.pub")) {
print "slurping host_key\n";
binmode(HOST);
my $r = read(HOST, $hostbuffer, 1024);
#@hostbuffer=<HOST>;
close(HOST);
if (defined($r)) {
#print "read $r k chunks on binary file ssh_host_key.pub\n";
#notify($ERRORS{'OK'},0,"read $r k chunks on binary file ssh_host_key.pub");
#print "uploading: $dsa\n $dsapub\n$rsa\n$rsapub\n$hostbuffer\n$hostpub \n $id\n";
}
else {
#print "could not read binary file ssh_host_key.pub\n";
notify($ERRORS{'CRITICAL'}, 0, "could not read binary file ssh_host_key.pub");
}
} ## end if (open(HOST, "/tmp/$id/ssh_host_key.pub"...
else {
notify($ERRORS{'CRITICAL'}, 0, "could not open host binary file $!");
}
#print "uploading: @dsa\n @dsapub\n @rsa\n @rsapub\n $hostbuffer \n,@hostpub \n $id\n";
#upload keys to db
my $update = $dbh->prepare("UPDATE computer SET dsa=?,dsapub=?,rsa=?,rsapub=?,hostpub=? WHERE id=?") or print "could not prepare update key statement node= $node id= $id" . $dbh->errstr();
$update->execute($dsa, $dsapub, $rsa, $rsapub, $hostpub, $id) or print "Problem could not execute on update key statement node= $node id= $id: " . $dbh->errstr;
$dbh->disconnect if !defined $ENV{dbh};
} ## end sub collectsshkeys
#/////////////////////////////////////////////////////////////////////////////
=head2 restoresshkeys
Parameters : node
Returns : 0 or 1
Description : NOT COMPLETED
connects to node and replaces ssh keys with keys stored in db
=cut
sub restoresshkeys {
my $node = $_[0];
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "node is not defined") if (!(defined($node)));
if (!(defined($node))) {
return 0;
}
}
#/////////////////////////////////////////////////////////////////////////////
=head2 notifyviaIM
Parameters : IM type, IM user ID, message string
Returns : 0 or 1
Description : if Jabber enabled - send IM to user
currently only supports jabber
=cut
sub notify_via_IM {
my ($im_type, $im_id, $im_message) = @_;
notify($ERRORS{'WARNING'}, 0, "IM type is not defined") if (!(defined($im_type)));
notify($ERRORS{'WARNING'}, 0, "IM id is not defined") if (!(defined($im_id)));
notify($ERRORS{'WARNING'}, 0, "IM message is not defined") if (!(defined($im_message)));
if ($im_type eq "jabber") {
# Check if jabber functions are disabled on this management node
if ($JABBER) {
notify($ERRORS{'DEBUG'}, 0, "jabber functions are enabled on this management node");
}
else {
notify($ERRORS{'DEBUG'}, 0, "jabber functions are disabled on this management node");
return 1;
}
# Create a jabber client object
my $jabber_client = new Net::Jabber::Client();
if ($jabber_client) {
notify($ERRORS{'DEBUG'}, 0, "jabber client object created");
}
else {
notify($ERRORS{'DEBUG'}, 0, "failed to created jabber client object");
return;
}
# Attempt to connect to the jabber server
my $jabber_connect_result = $jabber_client->Connect(hostname => $jabServer, port => $jabPort);
if (!$jabber_connect_result) {
notify($ERRORS{'DEBUG'}, 0, "connected to jabber server: $jabServer, port: $jabPort, result: $jabber_connect_result");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to connect to jabber server: $jabServer, port: $jabPort, result: $jabber_connect_result");
return;
}
# Attempt to authenticate to jabber
my @jabber_auth_result = $jabber_client->AuthSend(
username => $jabUser,
password => $jabPass,
resource => $jabResource
);
# Check the jabber authentication result
if ($jabber_auth_result[0] && $jabber_auth_result[0] eq "ok") {
notify($ERRORS{'DEBUG'}, 0, "authenticated to jabber server: $jabServer, user: $jabUser, resource: $jabResource");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to authenticate to jabber server: $jabServer, user: $jabUser, resource: $jabResource");
return;
}
# Create jabber message
my $jabber_message = Net::Jabber::Message->new();
$jabber_message->SetMessage(
to => $im_id,
subject => "Notification",
type => "chat",
body => $im_message
);
# Attempt to send the jabber message
my $jabber_send_result = $jabber_client->Send($jabber_message);
if ($jabber_send_result) {
notify($ERRORS{'OK'}, 0, "jabber message sent to $im_id");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to send jabber message to $jabUser");
return;
}
} ## end if ($im_type eq "jabber" && defined $jabberready)
else {
notify($ERRORS{'WARNING'}, 0, "IM type is not supported: $im_type");
return 0;
}
return 1;
} ## end sub notify_via_IM
#/////////////////////////////////////////////////////////////////////////////
=head2 check_uptime
Parameters : $node, $IPaddress, $OSname, $type, $log
Returns : value or 0(failed) + failure message
Description : fetchs uptime of remote node
=cut
sub check_uptime {
my ($node, $IPaddress, $OSname, $type, $log) = @_;
my ($package, $filename, $line, $sub) = caller(0);
$log = 0 if (!(defined($log)));
notify($ERRORS{'WARNING'}, $log, "node is not defined") if (!(defined($node)));
notify($ERRORS{'WARNING'}, $log, "IPaddress is not defined") if (!(defined($IPaddress)));
notify($ERRORS{'WARNING'}, $log, "OSname is not defined") if (!(defined($OSname)));
notify($ERRORS{'WARNING'}, $log, "type is not defined") if (!(defined($type)));
if ($type eq "lab") {
my $identity = $ENV{management_node_info}{keys};
my @sshcmd = run_ssh_command($node, $identity, "uptime", "vclstaff", "24");
my $l;
foreach $l (@{$sshcmd[1]}) {
if ($l =~ /(\s*\d*:\d*:\d*\s*up\s*)(\d*)(\s*days,)/) {
return $2;
}
if ($l =~ /^(\s*\d*:\d*)(am|pm)(\s*up\s*)(\d*)(\s*day)/) {
return $4;
}
if ($l =~ /(\s*\d*:\d*:\d*\s*up)/) {
return 1;
}
if ($l =~ /password/) {
notify($ERRORS{'WARNING'}, $log, "@{ $sshcmd[1] }");
return (0, $l);
}
} ## end foreach $l (@{$sshcmd[1]})
} ## end if ($type eq "lab")
elsif ($type eq "blade") {
return 0;
}
elsif ($type eq "virtualmachine") {
return 0;
}
} ## end sub check_uptime
#/////////////////////////////////////////////////////////////////////////////
=head2 timefloor15interval
Parameters : time string(optional)
Returns : the nearest 15 minute interval 0,15,30,45 less than the current time and zero seconds
Description :
=cut
sub timefloor15interval {
my $time = $_[0];
# we got nothing set to current time
if (!defined($time)) {
$time = makedatestring;
}
#we got a null timestamp, set it to current time
if ($time =~ /0000-00-00 00:00:00/) {
$time = makedatestring;
}
#format received: year-mon-mday hr:min:sec
my ($vardate, $vartime) = split(/ /, $time);
my ($yr, $mon, $mday) = split(/-/, $vardate);
my ($hr, $min, $sec) = split(/:/, $vartime);
$sec = "0";
if ($min < 15) {
$min = 0;
}
elsif ($min < 30) {
$min = 15;
}
elsif ($min < 45) {
$min = 30;
}
elsif ($min < 60) {
$min = 45;
}
my $datestring = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $yr, $mon, $mday, $hr, $min, $sec);
return $datestring;
} ## end sub timefloor15interval
#/////////////////////////////////////////////////////////////////////////////
=head2 monitorloading
Parameters : $reservationid, $requestedimagename, $computerid, $nodename, $ert
Returns : 0 or 1
Description : using database loadlog table,
monitor given node for available state
=cut
sub monitorloading {
my ($reservationid, $requestedimagename, $computerid, $nodename, $ert) = @_;
my ($package, $filename, $line, $sub) = caller(0);
notify($ERRORS{'WARNING'}, 0, "reservationid is not defined") if (!(defined($reservationid)));
notify($ERRORS{'WARNING'}, 0, "requestedimagename is not defined") if (!(defined($requestedimagename)));
notify($ERRORS{'WARNING'}, 0, "computerid is not defined") if (!(defined($computerid)));
notify($ERRORS{'WARNING'}, 0, "nodename is not defined") if (!(defined($nodename)));
notify($ERRORS{'WARNING'}, 0, "ert is not defined") if (!(defined($ert)));
#get start time of this wait period
my $mystarttime = convert_to_epoch_seconds;
my $currentime = 0;
my $mydbhandle = getnewdbh();
my $selhdl = $mydbhandle->prepare(
"SELECT s.loadstatename,c.additionalinfo,c.timestamp
FROM computerloadlog c, computerloadstate s
WHERE s.id = c.loadstateid AND c.loadstateid=s.id AND c.reservationid =? AND c.computerid=?") or notify($ERRORS{'WARNING'}, 0, "could not prepare statement to monitor for available stat" . $mydbhandle->errstr());
my $selhdl2 = $mydbhandle->prepare("SELECT s.name FROM computer c,state s WHERE c.stateid=s.id AND c.id =?") or notify($ERRORS{'WARNING'}, 0, "could not prepare statement check node for available state" . $mydbhandle->errstr());
#get est reload time of image
my $available = 0;
my $stillrunnning = 1;
my $state;
my $s1 = 0;
my $s2 = 0;
my $s3 = 0;
my $s4 = 0;
my $s5 = 0;
my $s6 = 0;
my $s7 = 0;
my $s8 = 0;
my $s1time = 0;
my $s2time = 0;
my $s3time = 0;
my $s4time = 0;
my $s5time = 0;
my $s6time = 0;
my $s7time = 0;
MONITORLOADCHECKS:
$selhdl->execute($reservationid, $computerid) or notify($ERRORS{'WARNING'}, 0, "could not execute statement to monitor for available stat" . $mydbhandle->errstr());
my $rows = $selhdl->rows;
#check state of machine
$selhdl2->execute($computerid) or notify($ERRORS{'WARNING'}, 0, "could not execute statement to check state of blade " . $mydbhandle->errstr());
my $irows = $selhdl2->rows;
notify($ERRORS{'OK'}, 0, "checking if $nodename is available");
if ($irows) {
if (my @irow = $selhdl2->fetchrow_array) {
if ($irow[0] =~ /available/) {
#good machine is available
notify($ERRORS{'OK'}, 0, "good $nodename is now available");
return 1;
}
elsif ($irow[0] =~ /failed/) {
notify($ERRORS{'WARNING'}, 0, "$nodename reported failure");
return 0;
}
} ## end if (my @irow = $selhdl2->fetchrow_array)
} ## end if ($irows)
else {
notify($ERRORS{'WARNING'}, 0, "strange no records found for computerid $computerid $nodename - possible issue with query");
return 0;
}
my @row;
while (@row = $selhdl->fetchrow_array) {
if (!$s1) {
if ($row[0] =~ /loadimage|loadimagevmware/) {
notify($ERRORS{'OK'}, 0, "detected s1");
$s1 = 1;
$s1time = convert_to_epoch_seconds($row[2]);
}
}
if (!$s2) {
if ($row[0] =~ /startload/) {
notify($ERRORS{'OK'}, 0, "detected startload state");
if ($row[1] =~ /$requestedimagename/) {
notify($ERRORS{'OK'}, 0, "good $nodename is loading $requestedimagename");
$s2 = 1;
$s2time = convert_to_epoch_seconds($row[2]);
}
else {
notify($ERRORS{'WARNING'}, 0, "$nodename is not loading desired image");
return 0;
}
} ## end if ($row[0] =~ /startload/)
} ## end if (!$s2)
if (!$s3) {
if ($row[0] =~ /rinstall|transfervm/) {
notify($ERRORS{'OK'}, 0, "detected $row[0] for $nodename");
$s3 = 1;
$s3time = convert_to_epoch_seconds($row[2]);
}
}
if (!$s4) {
if ($row[0] =~ /xcatstage5|startvm/) {
notify($ERRORS{'OK'}, 0, "detected $row[0] for $nodename");
$s4 = 1;
$s4time = convert_to_epoch_seconds($row[2]);
}
}
if (!$s5) {
if ($row[0] =~ /bootstate|vmstage1/) {
notify($ERRORS{'OK'}, 0, "detected $row[0] for $nodename");
$s5 = 1;
$s5time = convert_to_epoch_seconds($row[2]);
}
}
if (!$s6) {
if ($row[0] =~ /xcatround3|vmstage5/) {
notify($ERRORS{'OK'}, 0, "detected $row[0] for $nodename");
$s6 = 1;
$s6time = convert_to_epoch_seconds($row[2]);
}
}
if (!$s7) {
if ($row[0] =~ /xcatREADY|vmwareready/) {
notify($ERRORS{'OK'}, 0, "detected $row[0] for $nodename");
$s7 = 1;
$s7time = convert_to_epoch_seconds($row[2]);
}
}
if (!$s8) {
if ($row[0] =~ /nodeready/) {
notify($ERRORS{'OK'}, 0, "detected $row[0] for $nodename, returning to calling process");
$s8 = 1;
#ready to return
return 1;
}
}
if ($row[0] =~ /failed/) {
return 0;
}
} ## end while (@row = $selhdl->fetchrow_array)
notify($ERRORS{'OK'}, 0, "current stages passed s1='$s1' s2='$s2' s3='$s3' s4='$s4' s5='$s5' s6='$s6' s7='$s7' going to sleep 15");
sleep 15;
#prevent infinite loop - check how long we've waited
$currentime = convert_to_epoch_seconds;
my $delta = $currentime - $mystarttime;
#check some state times
#if($s5){
# if(!$s6){
# #how long has it been since $s5 was set
# my $s5diff = ($currentime-$s5time);
# if($s5diff > 5*60){
# #greater than 5 minutes
# notify($ERRORS{'OK'},0,"waited over 5 minutes - $s5diff seconds for stage 6 to complete, returning");
# return 0;
# }
# }
# if(!$s7){
# #how long has it been since $s6 was set
# my $s6diff = $currentime-$s6time;
# if($s6diff > 6*60){
# #greater than 4 minutes
# notify($ERRORS{'OK'},0,"waited over 6 minutes - $s6diff seconds for stage 7 to be reached, returning");
# return 0;
# }
#
# }
# }
if ($delta > ($ert * 60)) {
notify($ERRORS{'OK'}, 0, "waited $delta seconds and we have exceeded our ert of $ert, returning");
#just return at this point - it should have been completed by now
return 0;
}
goto MONITORLOADCHECKS;
} ## end sub monitorloading
#/////////////////////////////////////////////////////////////////////////////
=head2 insertloadlog
Parameters : $resid, $computerid, $loadstatename, $additionalinfo
Returns : 0 or 1
Description : accepts info from processes to update the loadlog table
=cut
sub insertloadlog {
my ($resid, $computerid, $loadstatename, $additionalinfo) = @_;
my ($package, $filename, $line, $sub) = caller(0);
# Check the parameters
if (!(defined($resid))) {
notify($ERRORS{'CRITICAL'}, 0, "unable to insert into computerloadlog, reservation id is not defined");
return 0;
}
elsif (!($resid)) {
notify($ERRORS{'CRITICAL'}, 0, "unable to insert into computerloadlog, reservation id is 0");
return 0;
}
if (!(defined($computerid))) {
notify($ERRORS{'CRITICAL'}, 0, "unable to insert into computerloadlog, computer id is not defined");
return 0;
}
elsif (!($computerid)) {
notify($ERRORS{'CRITICAL'}, 0, "unable to insert into computerloadlog, computer id is 0");
return 0;
}
if (!(defined($additionalinfo)) || !$additionalinfo) {
notify($ERRORS{'WARNING'}, 0, "additionalinfo is either not defined or 0, using 'no additional info'");
$additionalinfo = 'no additional info';
}
my $loadstateid;
if (!(defined($loadstatename)) || !$loadstatename) {
notify($ERRORS{'WARNING'}, 0, "loadstatename is either not defined or 0, using NULL");
$loadstatename = 'NULL';
$loadstateid = 'NULL';
}
else {
# loadstatename was specified as a parameter
# Check if the loadstatename exists in the computerloadstate table
my $select_statement = "
SELECT DISTINCT
computerloadstate.id
FROM
computerloadstate
WHERE
computerloadstate.loadstatename = '$loadstatename'
";
my @selected_rows = database_select($select_statement);
if ((scalar @selected_rows) == 0) {
notify($ERRORS{'WARNING'}, 0, "computerloadstate table entry does not exist: $loadstatename, using NULL");
$loadstateid = 'NULL';
$loadstatename = 'NULL';
}
else {
$loadstateid = $selected_rows[0]{id};
#notify($ERRORS{'DEBUG'}, 0, "computerloadstate id found: id=$loadstateid, name=$loadstatename");
}
} ## end else [ if (!(defined($loadstatename)) || !$loadstatename)
# Escape any single quotes in additionalinfo
$additionalinfo =~ s/\'/\\\'/g;
# Check to make sure the reservation has not been deleted
# The INSERT statement will fail if it has been deleted because of the key constraint on reservationid
if (is_reservation_deleted($resid)) {
notify($ERRORS{'OK'}, 0, "computerloadlog entry not inserted, reservation has been deleted");
return 1;
}
# Assemble the SQL statement
my $insert_loadlog_statement = "
INSERT INTO
computerloadlog
(
reservationid,
computerid,
loadstateid,
timestamp,
additionalinfo
)
VALUES
(
'$resid',
'$computerid',
'$loadstateid',
NOW(),
'$additionalinfo'
)
";
# Execute the insert statement, the return value should be the id of the computerloadlog row that was inserted
my $loadlog_id = database_execute($insert_loadlog_statement);
if ($loadlog_id) {
notify($ERRORS{'OK'}, 0, "inserted computer=$computerid, $loadstatename, $additionalinfo");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to insert entry into computerloadlog table");
return 0;
}
return 1;
} ## end sub insertloadlog
#/////////////////////////////////////////////////////////////////////////////
=head2 checkonprocess
Parameters : $state, $requestid
Returns : 0 or 1
Description : checks the process list to confirm the process for given request is actually running
in case the process dies for some reason
=cut
sub checkonprocess {
my ($request_state_name, $request_id) = @_;
notify($ERRORS{'WARNING'}, 0, "state is not defined") if (!(defined($request_state_name)));
notify($ERRORS{'WARNING'}, 0, "requestid is not defined") if (!(defined($request_id)));
return if (!(defined($request_state_name)));
return if (!(defined($request_id)));
# Use the pgrep utility to find processes matching the state and request ID
if (open(PGREP, "/bin/pgrep -fl '$request_state_name $request_id' 2>&1 |")) {
my @process_list = <PGREP>;
close(PGREP);
notify($ERRORS{'DEBUG'}, 0, "pgrep found " . scalar @process_list . " processes matching state=$request_state_name and request=$request_id");
return scalar @process_list;
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to open handle for pgrep process");
return;
}
} ## end sub checkonprocess
#/////////////////////////////////////////////////////////////////////////////
=head2 kill_reservation_process
Parameters : $request_state_name, $reservation_id
Returns : 0 or 1
Description :
=cut
sub kill_reservation_process {
my ($reservation_id) = @_;
# Sanity check, make sure reservation id is valid
if (!$reservation_id) {
notify($ERRORS{'WARNING'}, 0, "reservation id is not defined");
return;
}
if ($reservation_id !~ /^\d+$/) {
notify($ERRORS{'WARNING'}, 0, "reservation id is not valid: $reservation_id");
return;
}
notify($ERRORS{'OK'}, 0, "attempting to kill process for reservation $reservation_id");
# Use the pkill utility to find processes matching the reservation ID
# Do not use -9 or else DESTROY won't run
my $pkill_command = "pkill -f ':$reservation_id ' 2>&1";
notify($ERRORS{'DEBUG'}, 0, "executing pkill command: $pkill_command");
my $pkill_output = `$pkill_command`;
my $pkill_exit_status = $? >> 8;
# Check the pgrep exit status
if ($pkill_exit_status == 0) {
notify($ERRORS{'OK'}, 0, "reservation $reservation_id process was killed, returning 1");
return 1;
}
elsif ($? == -1) {
notify($ERRORS{'OK'}, 0, "\$? is set to -1, Perl bug likely encountered, assuming reservation $reservation_id process was killed, returning 1");
return 1;
}
elsif ($pkill_exit_status == 1) {
notify($ERRORS{'OK'}, 0, "process was not found for reservation $reservation_id, returning 1");
return 1;
}
else {
notify($ERRORS{'WARNING'}, 0, "pkill error occurred, returning undefined, output:\n$pkill_output");
return;
}
} ## end sub kill_reservation_process
#/////////////////////////////////////////////////////////////////////////////
=head2 database_select
Parameters : SQL select statement
Returns : array containing hash references to rows returned
Description : gets information from the database
=cut
sub database_select {
my ($select_statement, $database) = @_;
my ($package, $filename, $line, $sub) = caller(0);
my $dbh;
if (!($dbh = getnewdbh($database))) {
# Try again if first attempt failed
if (!($dbh = getnewdbh($database))) {
notify($ERRORS{'WARNING'}, 0, "unable to obtain database handle, " . DBI::errstr());
return ();
}
}
# Prepare the select statement handle
my $select_handle;
$select_handle = $dbh->prepare($select_statement);
# Check the select statement handle
if (!$select_handle) {
notify($ERRORS{'WARNING'}, 0, "could not prepare select statement, $select_statement, " . $dbh->errstr());
$dbh->disconnect if !defined $ENV{dbh};
return ();
}
# Execute the statement handle
if (!($select_handle->execute())) {
notify($ERRORS{'WARNING'}, 0, "could not execute statement, $select_statement, " . $dbh->errstr());
$select_handle->finish;
$dbh->disconnect if !defined $ENV{dbh};
return ();
}
# Fetch all the rows returned by the select query
# An array reference is created containing hash refs because {} is passed to fetchall_arrayref
my @return_rows = @{$select_handle->fetchall_arrayref({})};
$select_handle->finish;
$dbh->disconnect if !defined $ENV{dbh};
return @return_rows;
} ## end sub database_select
#/////////////////////////////////////////////////////////////////////////////
=head2 database_execute
Parameters : SQL statement
Returns : 1 if successful, 0 if failed
Description : Executes an SQL statement
=cut
sub database_execute {
my ($sql_statement, $database) = @_;
my ($package, $filename, $line, $sub) = caller(0);
my $dbh;
if (!($dbh = getnewdbh($database))) {
# Try again if first attempt failed
if (!($dbh = getnewdbh($database))) {
notify($ERRORS{'WARNING'}, 0, "unable to obtain database handle, " . DBI::errstr());
return;
}
}
# Prepare the statement handle
my $statement_handle = $dbh->prepare($sql_statement);
# Check the statement handle
if (!$statement_handle) {
notify($ERRORS{'WARNING'}, 0, "could not prepare SQL statement, $sql_statement, " . $dbh->errstr());
$dbh->disconnect if !defined $ENV{dbh};
return;
}
# Execute the statement handle
if (!($statement_handle->execute())) {
notify($ERRORS{'WARNING'}, 0, "could not execute SQL statement, $sql_statement, " . $dbh->errstr());
$statement_handle->finish;
$dbh->disconnect if !defined $ENV{dbh};
return;
}
# Get the id of the last inserted record if this is an INSERT statement
if ($sql_statement =~ /insert/i) {
my $sql_insertid = $statement_handle->{'mysql_insertid'};
$statement_handle->finish;
$dbh->disconnect if !defined $ENV{dbh};
return $sql_insertid;
}
else {
$statement_handle->finish;
$dbh->disconnect if !defined $ENV{dbh};
return 1;
}
} ## end sub database_execute
#/////////////////////////////////////////////////////////////////////////////
=head2 get_request_info
Parameters : databasehandle, management node id
Returns : hash0 or 1
Description : gets all reservation related information
=cut
sub get_request_info {
my ($request_id) = @_;
my ($package, $filename, $line, $sub) = caller(0);
if (!(defined($request_id))) {
notify($ERRORS{'WARNING'}, 0, "request ID was not specified");
return 0;
}
my $select_statement = "
SELECT DISTINCT
request.id AS request_id,
request.stateid AS request_stateid,
request.userid AS request_userid,
request.laststateid AS request_laststateid,
request.logid AS request_logid,
request.forimaging AS request_forimaging,
request.test AS request_test,
request.preload AS request_preload,
request.start AS request_start,
request.end AS request_end,
request.daterequested AS request_daterequested,
request.datemodified AS request_datemodified,
request.checkuser AS request_checkuser,
requeststate.name AS requeststate_name,
requestlaststate.name AS requestlaststate_name,
reservation.id AS reservation_id,
reservation.requestid AS reservation_requestid,
reservation.computerid AS reservation_computerid,
reservation.imageid AS reservation_imageid,
reservation.imagerevisionid AS reservation_imagerevisionid,
reservation.managementnodeid AS reservation_managementnodeid,
reservation.remoteIP AS reservation_remoteIP,
reservation.lastcheck AS reservation_lastcheck,
reservation.pw AS reservation_pw,
image.id AS image_id,
image.name AS image_name,
image.prettyname AS image_prettyname,
image.ownerid AS image_ownerid,
image.platformid AS image_platformid,
image.OSid AS image_OSid,
image.imagemetaid AS image_imagemetaid,
image.minram AS image_minram,
image.minprocnumber AS image_minprocnumber,
image.minprocspeed AS image_minprocspeed,
image.minnetwork AS image_minnetwork,
image.maxconcurrent AS image_maxconcurrent,
image.reloadtime AS image_reloadtime,
image.deleted AS image_deleted,
image.test AS image_test,
image.lastupdate AS image_lastupdate,
image.forcheckout AS image_forcheckout,
image.maxinitialtime AS image_maxinitialtime,
image.project AS image_project,
image.size AS image_size,
image.architecture AS image_architecture,
imagerevision.id AS imagerevision_id,
imagerevision.imageid AS imagerevision_imageid,
imagerevision.revision AS imagerevision_revision,
imagerevision.userid AS imagerevision_userid,
imagerevision.datecreated AS imagerevision_datecreated,
imagerevision.deleted AS imagerevision_deleted,
imagerevision.production AS imagerevision_production,
imagerevision.comments AS imagerevision_comments,
imagerevision.imagename AS imagerevision_imagename,
imageplatform.name AS imageplatform_name,
OS.name AS OS_name,
OS.prettyname AS OS_prettyname,
OS.type AS OS_type,
OS.installtype AS OS_installtype,
OS.sourcepath AS OS_sourcepath,
OS.moduleid AS OS_moduleid,
imageOSmodule.name AS imageOSmodule_name,
imageOSmodule.prettyname AS imageOSmodule_prettyname,
imageOSmodule.description AS imageOSmodule_description,
imageOSmodule.perlpackage AS imageOSmodule_perlpackage,
user.id AS user_id,
user.uid AS user_uid,
user.unityid AS user_unityid,
user.affiliationid AS user_affiliationid,
user.firstname AS user_firstname,
user.lastname AS user_lastname,
user.preferredname AS user_preferredname,
user.email AS user_email,
user.emailnotices AS user_emailnotices,
user.IMtypeid AS user_IMtypeid,
user.IMid AS user_IMid,
user.adminlevelid AS user_adminlevelid,
user.width AS user_width,
user.height AS user_height,
user.bpp AS user_bpp,
user.audiomode AS user_audiomode,
user.mapdrives AS user_mapdrives,
user.mapprinters AS user_mapprinters,
user.mapserial AS user_mapserial,
user.showallgroups AS user_showallgroups,
user.lastupdated AS user_lastupdated,
adminlevel.name AS adminlevel_name,
affiliation.name AS affiliation_name,
affiliation.dataUpdateText AS affiliation_dataUpdateText,
affiliation.sitewwwaddress AS affiliation_sitewwwaddress,
affiliation.helpaddress AS affiliation_helpaddress,
IMtype.name AS IMtype_name,
computer.id AS computer_id,
computer.stateid AS computer_stateid,
computer.ownerid AS computer_ownerid,
computer.platformid AS computer_platformid,
computer.scheduleid AS computer_scheduleid,
computer.currentimageid AS computer_currentimageid,
computer.nextimageid AS computer_nextimageid,
computer.imagerevisionid AS computer_imagerevisionid,
computer.RAM AS computer_RAM,
computer.procnumber AS computer_procnumber,
computer.procspeed AS computer_procspeed,
computer.network AS computer_network,
computer.hostname AS computer_hostname,
computer.IPaddress AS computer_IPaddress,
computer.privateIPaddress AS computer_privateIPaddress,
computer.eth0macaddress AS computer_eth0macaddress,
computer.eth1macaddress AS computer_eth1macaddress,
computer.type AS computer_type,
computer.provisioningid AS computer_provisioningid,
computer.drivetype AS computer_drivetype,
computer.deleted AS computer_deleted,
computer.notes AS computer_notes,
computer.lastcheck AS computer_lastcheck,
computer.location AS computer_location,
computer.dsa AS computer_dsa,
computer.dsapub AS computer_dsapub,
computer.rsa AS computer_rsa,
computer.rsapub AS computer_rsapub,
computer.host AS computer_host,
computer.hostpub AS computer_hostpub,
computer.vmhostid AS computer_vmhostid,
computerplatform.name AS computerplatform_name,
computerstate.name AS computerstate_name,
computerschedule.name AS computerschedule_name,
computerprovisioning.name AS computerprovisioning_name,
computerprovisioning.prettyname AS computerprovisioning_prettyname,
computerprovisioning.moduleid AS computerprovisioning_moduleid,
computerprovisioningmodule.name AS computerprovisioningmodule_name,
computerprovisioningmodule.prettyname AS computerprovisioningmodule_prettyname,
computerprovisioningmodule.description AS computerprovisioningmodule_description,
computerprovisioningmodule.perlpackage AS computerprovisioningmodule_perlpackage
FROM
request,
user,
adminlevel,
affiliation,
IMtype,
reservation,
image,
platform imageplatform,
imagerevision,
OS,
module imageOSmodule,
computer,
provisioning computerprovisioning,
module computerprovisioningmodule,
platform computerplatform,
schedule computerschedule,
state requeststate,
state requestlaststate,
state computerstate