| #!/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.3.2'; |
| |
| # 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 Cwd; |
| use Sys::Hostname; |
| use XML::Simple; |
| |
| #use Date::Calc qw(Delta_DHMS Time_to_Date Date_to_Time); |
| |
| require Exporter; |
| our @ISA = qw(Exporter); |
| |
| our @EXPORT = qw( |
| _checknstartservice |
| _machine_os |
| _pingnode |
| _sshd_status |
| changelinuxpassword |
| check_blockrequest_time |
| 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 |
| format_data |
| format_number |
| get_affiliation_info |
| get_array_summary_string |
| 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_connect_method_info |
| get_copy_speed_info_string |
| get_current_file_name |
| get_current_package_name |
| get_current_subroutine_name |
| get_file_size_info_string |
| get_group_name |
| get_highest_imagerevision_info |
| get_image_info |
| get_imagemeta_info |
| get_imagerevision_info |
| get_current_image_contents_noDS |
| get_local_user_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_random_mac_address |
| get_request_by_computerid |
| get_request_end |
| get_request_info |
| get_reservation_accounts |
| get_resource_groups |
| get_managable_resource_groups |
| get_user_affiliation |
| get_user_info |
| get_vmhost_info |
| getimagesize |
| getnewdbh |
| getpw |
| getusergroupmembers |
| get_user_group_member_info |
| hash_to_xml_string |
| 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_child_processes |
| kill_reservation_process |
| known_hosts |
| lockfile |
| mail |
| makedatestring |
| monitorloading |
| nmap_port |
| normalize_file_path |
| notify |
| notify_via_IM |
| notify_via_msg |
| notify_via_wall |
| notify_via_oascript |
| 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 |
| setup_confirm |
| setup_get_array_choice |
| setup_get_hash_choice |
| setup_get_input_string |
| setup_print_wrap |
| sort_by_file_name |
| 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_computer_procnumber |
| update_computer_procspeed |
| update_computer_ram |
| update_currentimage |
| update_computer_imagename |
| update_image_name |
| update_image_type |
| update_lastcheckin |
| update_log_ending |
| update_log_loaded_time |
| update_preload_flag |
| update_request_password |
| update_request_state |
| update_reservation_accounts |
| update_reservation_lastcheck |
| update_sublog_ipaddress |
| write_currentimage_txt |
| xmlrpc_call |
| xml_string_to_hash |
| |
| $CONF_FILE_PATH |
| $DAEMON_MODE |
| $DATABASE |
| $DEFAULTHELPEMAIL |
| $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 |
| my $hostname = hostname(); |
| $hostname =~ s/\..*//g; |
| my $cwd = getcwd(); |
| our $CONF_FILE_PATH = "$cwd/$hostname.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) { |
| if (!$SETUP_MODE) { |
| print STDOUT "ERROR: config file does not exist: $CONF_FILE_PATH\n"; |
| help(); |
| } |
| } |
| elsif (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; |
| } |
| |
| if ($l =~ /^defaulthelpemail=(.*)/i){ |
| $DEFAULTHELPEMAIL = $1; |
| } |
| |
| } # Close foreach line in conf file |
| } # Close open conf file |
| |
| else { |
| die "failed to open vcld configuration file: $CONF_FILE_PATH, 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 1; |
| } ## 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); |
| } else { |
| 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); |
| |
| # 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); |
| |
| # Assemble the caller information |
| my $caller_info; |
| if (caller(1)) { |
| $sub = (caller(1))[3]; |
| } |
| |
| # Remove leading path from filename |
| $filename =~ s/.*\///; |
| |
| # Remove the leading package path from the sub name (VC::...) |
| $sub =~ s/.*:://; |
| |
| $caller_info = "$filename:$sub($line)"; |
| |
| # 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; |
| my $body_separator = '-' x 72; |
| |
| my $sysadmin = ''; |
| my $shared_mail_box = ''; |
| |
| if ($error == 2 || $error == 5) { |
| my $caller_trace = get_caller_trace(999); |
| if ($caller_trace !~ /get_management_node_info/) { |
| my $management_node_info = get_management_node_info(); |
| if ($management_node_info) { |
| $sysadmin = $management_node_info->{SYSADMIN_EMAIL} if $management_node_info->{SYSADMIN_EMAIL}; |
| $shared_mail_box = $management_node_info->{SHARED_EMAIL_BOX} if $management_node_info->{SHARED_EMAIL_BOX}; |
| } |
| } |
| } |
| |
| # WARNING |
| if ($error == 1) { |
| my $caller_trace = get_caller_trace(6); |
| $log_message = "\n---- WARNING ---- \n$log_message\n$caller_trace\n\n"; |
| } |
| |
| # CRITICAL |
| elsif ($error == 2) { |
| my $caller_trace = get_caller_trace(15); |
| $log_message = "\n---- CRITICAL ---- \n$log_message\n$caller_trace\n\n"; |
| if($sysadmin) { |
| |
| # Assemble the e-mail message body |
| $body = <<"END"; |
| $string |
| $body_separator |
| time: $currenttime |
| caller: $caller_info |
| $caller_trace |
| $body_separator |
| END |
| |
| # Add the reservation info to the message if the DataStructure object is defined in %ENV |
| if ($ENV{data}) { |
| my $reservation_info_string = $ENV{data}->get_reservation_info_string(); |
| if ($reservation_info_string) { |
| $reservation_info_string =~ s/\s+$//; |
| $body .= "$reservation_info_string\n"; |
| $body .= "$body_separator\n"; |
| } |
| } |
| |
| # 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; |
| $body .= $log_history; |
| |
| # Add the formatted data to the message body if data was passed |
| $body .= "\n\nDATA:\n$formatted_data\n" if $formatted_data; |
| |
| my ($management_node_short_name) = $FQDN =~ /^([^.]+)/; |
| my $subject = "PROBLEM -- $management_node_short_name|"; |
| |
| # Assemble the process identifier string |
| if (defined $ENV{request_id} && defined $ENV{reservation_id} && defined $ENV{state}) { |
| $subject .= "$ENV{request_id}:$ENV{reservation_id}|$ENV{state}|$filename"; |
| } |
| else { |
| $subject .= "$caller_info"; |
| } |
| |
| if (defined($ENV{data})) { |
| my $blockrequest_name = $ENV{data}->get_blockrequest_name(0); |
| $subject .= "|$blockrequest_name" if (defined $blockrequest_name); |
| |
| my $computer_name = $ENV{data}->get_computer_short_name(0); |
| $subject .= "|$computer_name" if (defined $computer_name); |
| |
| my $vmhost_hostname = $ENV{data}->get_vmhost_hostname(0); |
| $subject .= ">$vmhost_hostname" if (defined $vmhost_hostname); |
| |
| my $image_name = $ENV{data}->get_image_name(0); |
| $subject .= "|$image_name" if (defined $image_name); |
| |
| my $user_name = $ENV{data}->get_user_login_id(0); |
| $subject .= "|$user_name" if (defined $user_name); |
| } |
| |
| my $from = "root\@$FQDN"; |
| my $to = $sysadmin; |
| |
| 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 = convert_to_epoch_seconds(); |
| my $epochend = convert_to_epoch_seconds($end); |
| my $epoch_until_end = $epochend - $now; |
| |
| notify($ERRORS{'OK'}, 0, "endtime= $end epoch_until_end= $epoch_until_end"); |
| |
| my $diff_seconds = $epoch_until_end; |
| |
| my $diff_weeks = int($epoch_until_end/604800); |
| $diff_seconds -= $diff_weeks * 604800; |
| |
| my $diff_days = int($diff_seconds/86400); |
| my $Total_days = int($epoch_until_end/86400); |
| $diff_seconds -= $diff_days * 86400; |
| |
| my $diff_hours = int($diff_seconds/3600); |
| $diff_seconds -= $diff_hours * 3600; |
| |
| my $diff_minutes = int($diff_seconds/60); |
| $diff_seconds -= $diff_minutes * 60; |
| |
| notify($ERRORS{'OK'}, 0, "End Time is in: $diff_weeks week\(s\) $diff_days day\(s\) $diff_hours hour\(s\) $diff_minutes min\(s\) and $diff_seconds sec\(s\)"); |
| |
| #flag on: 2 & 1 week; 2,1 day, 1 hour, 30,15,10,5 minutes |
| #ignore over 2weeks away |
| if($diff_weeks >= 2){ |
| return 0; |
| } |
| #2 week: between 14 days and a 14 day -6 minutes window |
| elsif($Total_days >= 13 && $diff_hours >= 23 && $diff_minutes >= 55){ |
| return "2 weeks"; |
| } |
| #Ignore: between 7 days and 14 day - 6 minute window |
| elsif($Total_days >=7) { |
| return 0; |
| } |
| # 1 week notice: between 7 days and a 7 day -6 minute window |
| elsif ($Total_days >= 6 && $diff_hours >= 23 && $diff_minutes >= 55) { |
| return "1 week"; |
| } |
| # Ignore: between 2 days and 7 day - 15 minute window |
| elsif ($Total_days >= 2) { |
| return 0; |
| } |
| # 2 day notice: between 2 days and a 2 day -6 minute window |
| elsif($Total_days >= 1 && $diff_hours >= 23 && $diff_minutes >= 55) { |
| return "2 days"; |
| } |
| # 1 day notice: between 1 days and a 1 day -6 minute window |
| elsif($Total_days >= 0 && $diff_hours >= 23 && $diff_minutes >= 55) { |
| return "24 hours"; |
| } |
| |
| return 0; |
| |
| |
| } ## end sub check_endtimenotice_interval |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =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{'OK'}, 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 < 1) { |
| # 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{'OK'}, 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{'OK'}, 0, "start: $start_datetime, epoch: $start_time_epoch_seconds, delta: $start_delta_minutes minutes"); |
| #notify($ERRORS{'OK'}, 0, "end: $end_datetime, epoch: $end_time_epoch_seconds, delta: $end_delta_minutes minutes"); |
| |
| # End time it is less than 1 minute |
| if ($end_delta_minutes < 1) { |
| notify($ERRORS{'OK'}, 0, "block request end time has been reached ($end_delta_minutes minutes from now), returning 'end'"); |
| return "end"; |
| } |
| |
| # if 30min to 6 hrs in advance: start assigning resources |
| if ($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"; |
| } |
| |
| |
| #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) = @_; |
| |
| # 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|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) |
| } |
| elsif ($request_state_name =~ /tomaintenance/) { |
| if ($start_diff_minutes > 0) { |
| # Start time is either now or in future, $start_diff_minutes is positive |
| notify($ERRORS{'DEBUG'}, 0, "$request_state_name request will be processed in $start_diff_minutes minutes"); |
| return "0"; |
| } |
| else { |
| # Start time is in past, $start_diff_minutes is negative |
| notify($ERRORS{'DEBUG'}, 0, "$request_state_name request will be processed now"); |
| return "start"; |
| } |
| } |
| elsif ($request_state_name =~ /inuse/) { |
| 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 = ''; |
| my $management_node_info = get_management_node_info(); |
| if ($management_node_info) { |
| $shared_mail_box = $management_node_info->{SHARED_EMAIL_BOX} if $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 _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 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_keys = get_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($nodename, $identity_keys, "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_keys, "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_keys, "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)); |
| 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_computer_procnumber |
| |
| Parameters : $computer_id, $cpu_count |
| Returns : boolean |
| Description : Updates the computer.procnumber value for the specified computer. |
| |
| =cut |
| |
| sub update_computer_procnumber { |
| my ($computer_id, $cpu_count) = @_; |
| |
| if (!$computer_id || !$cpu_count) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID and CPU count arguments were not supplied correctly"); |
| return; |
| } |
| |
| my $update_statement = <<EOF; |
| UPDATE |
| computer |
| SET |
| computer.procnumber = '$cpu_count' |
| WHERE |
| computer.id = $computer_id |
| EOF |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "updated the procnumber value to $cpu_count for computer ID $computer_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to update the procnumber value to $cpu_count for computer ID $computer_id"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_computer_procspeed |
| |
| Parameters : $computer_id, $cpu_speed |
| Returns : boolean |
| Description : Updates the computer.procspeed value for the specified computer. |
| The $cpu_speed argument should contain an integer value of the |
| CPU speed in MHz. |
| |
| =cut |
| |
| sub update_computer_procspeed { |
| my ($computer_id, $cpu_speed_mhz) = @_; |
| |
| if (!$computer_id || !$cpu_speed_mhz) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID and CPU speed arguments were not supplied correctly"); |
| return; |
| } |
| |
| my $update_statement = <<EOF; |
| UPDATE |
| computer |
| SET |
| computer.procspeed = '$cpu_speed_mhz' |
| WHERE |
| computer.id = $computer_id |
| EOF |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "updated the procspeed value to $cpu_speed_mhz for computer ID $computer_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to update the procspeed value to $cpu_speed_mhz for computer ID $computer_id"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_computer_ram |
| |
| Parameters : $computer_id, $ram_mb |
| Returns : boolean |
| Description : Updates the computer.ram value for the specified computer. |
| The $ram_mb argument should contain an integer value of the |
| RAM in MB. |
| |
| =cut |
| |
| sub update_computer_ram { |
| my ($computer_id, $ram_mb) = @_; |
| |
| if (!$computer_id || !$ram_mb) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID and RAM arguments were not supplied correctly"); |
| return; |
| } |
| |
| my $update_statement = <<EOF; |
| UPDATE |
| computer |
| SET |
| computer.ram = '$ram_mb' |
| WHERE |
| computer.id = $computer_id |
| EOF |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "updated the RAM value to $ram_mb for computer ID $computer_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to update the RAM value to $ram_mb for computer ID $computer_id"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =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 =~ /(deleted|makeproduction)/ || $laststate_name =~ /(deleted|makeproduction)/) { |
| 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 =~ /(deleted|makeproduction)/ || $laststate_name =~ /(deleted|makeproduction)/) { |
| 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 : $reservationid |
| Returns : userid,password,affiliation |
| Description : Used for server loads, provides list of users for group access |
| |
| =cut |
| |
| sub get_reservation_accounts { |
| my ($reservationid) = @_; |
| my ($calling_package, $calling_filename, $calling_line, $calling_sub) = caller(0); |
| |
| if (!defined($reservationid)) { |
| notify($ERRORS{'WARNING'}, 0, "$calling_sub $calling_package missing mandatory variable: reservationid "); |
| return 0; |
| } |
| |
| my $select_statement = " |
| SELECT DISTINCT |
| reservationaccounts.userid AS reservationaccounts_userid, |
| reservationaccounts.password AS reservationaccounts_password, |
| affiliation.name AS affiliation_name, |
| user.unityid AS user_name |
| FROM |
| reservationaccounts, |
| affiliation, |
| user |
| WHERE |
| user.id = reservationaccounts.userid AND |
| affiliation.id = user.affiliationid AND |
| reservationaccounts.reservationid = $reservationid |
| "; |
| |
| # 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; |
| my %user_info; |
| |
| # Check to make sure 1 or more rows were returned |
| if (scalar @selected_rows > 0) { |
| # It contains a hash |
| for (@selected_rows) { |
| my %reservation_acct= %{$_}; |
| my $userid = $reservation_acct{reservationaccounts_userid}; |
| $user_info{$userid}{"userid"} = $userid; |
| $user_info{$userid}{"password"} = $reservation_acct{reservationaccounts_password}; |
| $user_info{$userid}{"affiliation"} = $reservation_acct{affiliation_name}; |
| $user_info{$userid}{"username"} = $reservation_acct{user_name}; |
| } |
| |
| return %user_info; |
| |
| } |
| |
| return (); |
| |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_reservation_accounts |
| |
| Parameters : $reservation_id, $userid, $password, $mode |
| Returns : boolean |
| Description : |
| |
| =cut |
| |
| sub update_reservation_accounts { |
| my $reservation_id = shift; |
| my $user_id = shift; |
| my $password = shift; |
| my $mode = shift; |
| |
| if (!$mode) { |
| notify($ERRORS{'WARNING'}, 0, "mode argument was not specified, it must be either add or delete"); |
| return; |
| } |
| |
| if (!$reservation_id) { |
| notify($ERRORS{'WARNING'}, 0, "reservation ID argument was not specified"); |
| return; |
| } |
| |
| if (!$user_id) { |
| notify($ERRORS{'WARNING'}, 0, "user ID argument was not specified"); |
| return; |
| } |
| |
| if (!$password) { |
| $password = ''; |
| } |
| |
| my $statement; |
| if ($mode =~ /add/i) { |
| $statement = <<EOF; |
| INSERT IGNORE INTO |
| reservationaccounts |
| ( |
| reservationid, |
| userid, |
| password |
| ) |
| VALUES |
| ( |
| '$reservation_id', |
| '$user_id', |
| '$password' |
| ) |
| ON DUPLICATE KEY UPDATE password = '$password' |
| EOF |
| } |
| elsif ($mode =~ /delete/i) { |
| $statement = <<EOF; |
| DELETE |
| reservationaccounts |
| FROM |
| reservationaccounts |
| WHERE |
| reservationid = '$reservation_id' AND |
| userid = '$user_id' |
| EOF |
| } |
| |
| if (database_execute($statement)) { |
| #notify($ERRORS{'OK'}, 0, "executed $statement $reservation_id $user_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "failed to to execute statement to update reservationaccounts table:\n$statement"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =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 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_keys = get_management_node_info()->{keys}; |
| |
| my @sshcmd = run_ssh_command($node, $identity_keys, "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_keys = get_management_node_info()->{keys}; |
| my @sshcmd = run_ssh_command($node, $identity_keys, "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,n $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_oascript |
| |
| Parameters : $node, $user, $message |
| Returns : 0 or 1 |
| Description : using apple oascript write supplied $message to finder |
| |
| =cut |
| |
| sub notify_via_oascript { |
| my ($node, $user, $message) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| 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 = "/var/root/VCL/oamessage \"$message\""; |
| |
| if (run_ssh_command($node, $ENV{management_node_info}{keys}, $command)) { |
| notify($ERRORS{'OK'}, 0, "successfully sent message to OSX user $user on $node"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to send message to OSX user $user on $node"); |
| return 0; |
| } |
| |
| } ## end sub notify_via_oascript |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =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 = get_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'"; |
| |
| my $identity_keys = get_management_node_info()->{keys}; |
| if (run_ssh_command($node, $identity_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 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, |
| user.id |
| |
| 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 = %{$_}; |
| if(!defined($hash{uid})){ |
| $hash{uid} = 0; |
| } |
| push(@retarray, "$hash{unityid}:$hash{uid}:$hash{id}"); |
| } |
| |
| return @retarray; |
| |
| } ## end sub getusergroupmembers |
| |
| |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_user_group_member_info |
| |
| Parameters : $usergroupid |
| Returns : array of user group memebers |
| Description : queries database and collects user members of supplied usergroupid |
| |
| =cut |
| |
| sub get_user_group_member_info { |
| my ($user_group_id) = @_; |
| |
| if (!defined($user_group_id)) { |
| notify($ERRORS{'WARNING'}, 0, "user group ID argument was not specified"); |
| return; |
| } |
| |
| my $select_statement = <<EOF; |
| SELECT |
| user.* |
| FROM |
| user, |
| usergroupmembers |
| WHERE |
| user.id = usergroupmembers.userid |
| AND usergroupmembers.usergroupid = '$user_group_id' |
| EOF |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| if (!@selected_rows) { |
| notify($ERRORS{'DEBUG'}, 0, "no data was returned for user group ID $user_group_id, returning an empty list"); |
| return {}; |
| } |
| |
| my $user_group_member_info; |
| for my $row (@selected_rows) { |
| my $user_id = $row->{id}; |
| for my $column (keys %$row) { |
| next if $column eq 'id'; |
| $user_group_member_info->{$user_id}{$column} = $row->{$column}; |
| } |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved member info for user group ID $user_group_id:\n" . format_data($user_group_member_info)); |
| return $user_group_member_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =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 = get_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_keys = get_management_node_info()->{keys}; |
| |
| my @sshcmd = run_ssh_command($node, $identity_keys, "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 $calling_sub = (caller(1))[3]; |
| |
| # Initialize the database_select_calls element if not already initialized |
| if (!ref($ENV{database_select_calls})) { |
| $ENV{database_select_calls} = {}; |
| } |
| |
| # For performance tuning - count the number of calls |
| $ENV{database_select_count}++; |
| if (!defined($ENV{database_select_calls}{$calling_sub})) { |
| $ENV{database_select_calls}{$calling_sub} = 1; |
| } |
| else { |
| $ENV{database_select_calls}{$calling_sub}++; |
| } |
| |
| 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); |
| |
| $ENV{database_execute_count}++; |
| |
| 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}; |
| if($sql_insertid) { |
| return $sql_insertid; |
| } |
| else { |
| return 1; |
| } |
| } |
| else { |
| $statement_handle->finish; |
| $dbh->disconnect if !defined $ENV{dbh}; |
| return 1; |
| } |
| |
| } ## end sub database_execute |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_request_info |
| |
| Parameters : $request_id |
| Returns : hash |
| Description : Retrieves all request/reservation information. |
| |
| =cut |
| |
| |
| sub get_request_info { |
| my ($request_id) = @_; |
| if (!(defined($request_id))) { |
| notify($ERRORS{'WARNING'}, 0, "request ID argument was not specified"); |
| return; |
| } |
| |
| # Get a hash ref containing the database column names |
| my $database_table_columns = get_database_table_columns(); |
| |
| my %tables = ( |
| 'request' => 'request', |
| 'serverrequest' => 'serverrequest', |
| 'reservation' => 'reservation', |
| 'state' => 'state', |
| 'laststate' => 'state', |
| ); |
| |
| # Construct the select statement |
| my $select_statement = "SELECT DISTINCT\n"; |
| |
| # Get the column names for each table and add them to the select statement |
| for my $table_alias (keys %tables) { |
| my $table_name = $tables{$table_alias}; |
| my @columns = @{$database_table_columns->{$table_name}}; |
| for my $column (@columns) { |
| $select_statement .= "$table_alias.$column AS '$table_alias-$column',\n"; |
| } |
| } |
| |
| # Remove the comma after the last column line |
| $select_statement =~ s/,$//; |
| |
| # Complete the select statement |
| $select_statement .= <<EOF; |
| |
| FROM |
| request |
| LEFT JOIN (serverrequest) ON (serverrequest.requestid = request.id), |
| reservation, |
| state, |
| state laststate |
| |
| WHERE |
| request.id = $request_id |
| AND reservation.requestid = request.id |
| AND state.id = request.stateid |
| AND laststate.id = request.laststateid |
| |
| GROUP BY |
| reservation.id |
| EOF |
| |
| # 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 or more rows were returned |
| if (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "info for request $request_id could not be retrieved from the database, select statement:\n$select_statement"); |
| return; |
| } |
| |
| # Build the hash |
| my $request_info; |
| |
| for my $reservation_row (@selected_rows) { |
| my $reservation_id = $reservation_row->{'reservation-id'}; |
| if (!$reservation_id) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve request info, row does not contain a reservation-id value:\n" . format_data($reservation_row)); |
| return; |
| } |
| $request_info->{RESERVATIONID} = $reservation_id if (scalar @selected_rows == 1); |
| |
| # Loop through all the columns returned |
| for my $key (keys %$reservation_row) { |
| my $value = $reservation_row->{$key}; |
| |
| # Split the table-column names |
| my ($table, $column) = $key =~ /^([^-]+)-(.+)/; |
| |
| if ($table eq 'request') { |
| $request_info->{$column} = $value; |
| } |
| elsif ($table eq 'reservation') { |
| $request_info->{reservation}{$reservation_id}{$column} = $value; |
| } |
| elsif ($table eq 'serverrequest') { |
| $request_info->{reservation}{$reservation_id}{serverrequest}{$column} = $value; |
| } |
| else { |
| $request_info->{$table}{$column} = $value; |
| } |
| } |
| |
| # Store duration in epoch seconds format |
| my $request_start_epoch = convert_to_epoch_seconds($request_info->{start}); |
| my $request_end_epoch = convert_to_epoch_seconds($request_info->{end}); |
| $request_info->{DURATION} = ($request_end_epoch - $request_start_epoch); |
| |
| # Add the image info to the hash |
| my $image_id = $request_info->{reservation}{$reservation_id}{imageid}; |
| my $image_info = get_image_info($image_id, 1); |
| $request_info->{reservation}{$reservation_id}{image} = $image_info; |
| |
| # Add the imagerevision info to the hash |
| my $imagerevision_id = $request_info->{reservation}{$reservation_id}{imagerevisionid}; |
| my $imagerevision_info = get_imagerevision_info($imagerevision_id, 1); |
| $request_info->{reservation}{$reservation_id}{imagerevision} = $imagerevision_info; |
| |
| # Add the computer info to the hash |
| my $computer_id = $request_info->{reservation}{$reservation_id}{computerid}; |
| my $computer_info = get_computer_info($computer_id, 1); |
| $request_info->{reservation}{$reservation_id}{computer} = $computer_info; |
| |
| # Add the connect method info to the hash |
| my $connect_method_info = get_connect_method_info($imagerevision_id); |
| $request_info->{reservation}{$reservation_id}{connect_methods} = $connect_method_info; |
| |
| # Add the managementnode info to the hash |
| my $management_node_id = $request_info->{reservation}{$reservation_id}{managementnodeid}; |
| my $management_node_info = get_management_node_info($management_node_id); |
| $request_info->{reservation}{$reservation_id}{managementnode} = $management_node_info; |
| |
| # Retrieve the user info and add to the hash |
| my $user_id = $request_info->{userid}; |
| my $user_info = get_user_info($user_id); |
| $request_info->{user} = $user_info; |
| |
| my $imagemeta_root_access = $request_info->{reservation}{$reservation_id}{image}{imagemeta}{rootaccess}; |
| |
| # Add the request user to the hash, set ROOTACCESS to the value configured in imagemeta |
| $request_info->{reservation}{$reservation_id}{users}{$user_id} = get_user_info($user_id); |
| $request_info->{reservation}{$reservation_id}{users}{$user_id}{ROOTACCESS} = $imagemeta_root_access; |
| |
| # If server request and logingroupid is set, add user group members to hash, set ROOTACCESS to 0 |
| if (my $login_group_id = $request_info->{reservation}{$reservation_id}{serverrequest}{logingroupid}) { |
| my $login_group_member_info = get_user_group_member_info($login_group_id); |
| for my $login_user_id (keys %$login_group_member_info) { |
| $request_info->{reservation}{$reservation_id}{users}{$login_user_id} = get_user_info($login_user_id); |
| $request_info->{reservation}{$reservation_id}{users}{$login_user_id}{ROOTACCESS} = 0; |
| } |
| } |
| |
| # If server request and admingroupid is set, add user group members to hash, set ROOTACCESS to 1 |
| if (my $admin_group_id = $request_info->{reservation}{$reservation_id}{serverrequest}{admingroupid}) { |
| my $admin_group_member_info = get_user_group_member_info($admin_group_id); |
| for my $admin_user_id (keys %$admin_group_member_info, $user_id) { |
| $request_info->{reservation}{$reservation_id}{users}{$admin_user_id} = get_user_info($admin_user_id); |
| $request_info->{reservation}{$reservation_id}{users}{$admin_user_id}{ROOTACCESS} = 1; |
| } |
| } |
| |
| # If server request or duration is greater >= 24 hrs disable user checks |
| if ($request_info->{reservation}{$reservation_id}{serverrequest}{id}) { |
| notify($ERRORS{'DEBUG'}, 0, "server sequest - disabling user checks"); |
| $request_info->{checkuser} = 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{ALLOW_USERS} = $request_info->{user}{unityid}; |
| } |
| elsif ($request_info->{DURATION} >= (60 * 60 * 24) ){ |
| notify($ERRORS{'DEBUG'}, 0, "request length > 24 hours, disabling user checks"); |
| $request_info->{checkuser} = 0; |
| } |
| |
| $request_info->{reservation}{$reservation_id}{serverrequest}{id} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{fixedIP} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{fixedMAC} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{router} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{netmask} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{DNSservers} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{admingroupid} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{logingroupid} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{monitored} ||= 0; |
| $request_info->{reservation}{$reservation_id}{serverrequest}{ALLOW_USERS} ||= 0; |
| |
| $request_info->{reservation}{$reservation_id}{READY} = '0'; |
| |
| |
| } |
| |
| # Set some default non-database values for the entire request |
| # All data ever added to the hash should be initialized here |
| $request_info->{PID} = ''; |
| $request_info->{PPID} = ''; |
| $request_info->{PARENTIMAGE} = ''; |
| $request_info->{PRELOADONLY} = '0'; |
| $request_info->{SUBIMAGE} = ''; |
| $request_info->{CHECKTIME} = ''; |
| $request_info->{NOTICEINTERVAL} = ''; |
| $request_info->{RESERVATIONCOUNT} = scalar keys %{$request_info->{reservation}}; |
| $request_info->{UPDATED} = '0'; |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved request info:\n" . format_data($request_info)); |
| return %$request_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 set_managementnode_state |
| |
| Parameters : management node info, state |
| Returns : 1 or 0 |
| Description : sets a given management node to maintenance |
| |
| =cut |
| |
| sub set_managementnode_state { |
| my ($mninfo, $state) = @_; |
| |
| if(!(defined($state))){ |
| notify($ERRORS{'WARNING'}, 0, "state was not specified"); |
| return (); |
| } |
| if(!(defined($mninfo->{hostname}))){ |
| notify($ERRORS{'WARNING'}, 0, "management node hostname was not specified"); |
| return (); |
| } |
| if(!(defined($mninfo->{id}))){ |
| notify($ERRORS{'WARNING'}, 0, "management node ID was not specified"); |
| return (); |
| } |
| |
| my $mn_ID = $mninfo->{id}; |
| my $mn_hostname = $mninfo->{hostname}; |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| managementnode, |
| state |
| SET |
| managementnode.stateid = state.id |
| WHERE |
| state.name = '$state' AND |
| managementnode.id = '$mn_ID' |
| "; |
| |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| # Update successful, return timestamp |
| notify($ERRORS{'OK'}, 0, "Successfully updated management node $mn_hostname state to $state"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to update database, management node $mn_hostname state to $state"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_requests |
| |
| Parameters : management node id |
| Returns : hash |
| Description : gets request information for a particular management node |
| |
| =cut |
| |
| |
| sub get_management_node_requests { |
| my ($management_node_id) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| if (!(defined($management_node_id))) { |
| notify($ERRORS{'WARNING'}, 0, "management node ID was not specified"); |
| return (); |
| } |
| |
| my $select_statement = " |
| SELECT DISTINCT |
| |
| request.id AS request_id, |
| request.stateid AS request_stateid, |
| request.laststateid AS request_laststateid, |
| request.logid AS request_logid, |
| request.start AS request_start, |
| request.end AS request_end, |
| request.daterequested AS request_daterequested, |
| request.datemodified AS request_datemodified, |
| request.preload AS request_preload, |
| |
| requeststate.name AS requeststate_name, |
| |
| requestlaststate.name AS requestlaststate_name, |
| |
| reservation.id AS reservation_id, |
| reservation.requestid AS reservation_requestid, |
| reservation.managementnodeid AS reservation_managementnodeid, |
| reservation.lastcheck AS reservation_lastcheck |
| |
| FROM |
| request, |
| reservation, |
| state requeststate, |
| state requestlaststate |
| |
| WHERE |
| reservation.managementnodeid = $management_node_id |
| AND reservation.requestid = request.id |
| AND requeststate.id = request.stateid |
| AND requestlaststate.id = request.laststateid |
| |
| GROUP BY |
| reservation.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 or more rows were returned |
| if (scalar @selected_rows == 0) { |
| return (); |
| } |
| |
| # Build the hash |
| my %requests; |
| |
| for (@selected_rows) { |
| my %reservation_row = %{$_}; |
| |
| # Grab the request and reservation IDs to make the code a little cleaner |
| my $request_id = $reservation_row{request_id}; |
| my $reservation_id = $reservation_row{reservation_id}; |
| |
| # Loop through all the columns returned for the reservation |
| foreach my $key (keys %reservation_row) { |
| my $value = $reservation_row{$key}; |
| |
| # Create another variable by stripping off the column_ part of each key |
| # This variable stores the original (correct) column name |
| (my $original_key = $key) =~ s/^.+_//; |
| |
| if ($key =~ /request_/) { |
| # Set the top-level key if not already set |
| $requests{$request_id}{$original_key} = $value if (!$requests{$request_id}{$original_key}); |
| } |
| elsif ($key =~ /requeststate_/) { |
| $requests{$request_id}{state}{$original_key} = $value if (!$requests{$request_id}{state}{$original_key}); |
| } |
| elsif ($key =~ /requestlaststate_/) { |
| $requests{$request_id}{laststate}{$original_key} = $value if (!$requests{$request_id}{laststate}{$original_key}); |
| } |
| elsif ($key =~ /reservation_/) { |
| $requests{$request_id}{reservation}{$reservation_id}{$original_key} = $value; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unknown key found in SQL data: $key"); |
| } |
| |
| } # Close foreach key in reservation row |
| } # Close loop through selected rows |
| |
| # Each selected row represents a reservation associated with this request |
| |
| return %requests; |
| } ## end sub get_management_node_requests |
| |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_image_info |
| |
| Parameters : $image_identifier, $no_cache (optional), $ignore_error (optional) |
| Returns : hash reference |
| Description : Retrieves info for the image specified by the argument. The |
| argument can either be the image ID or image name. |
| |
| =cut |
| |
| |
| sub get_image_info { |
| my ($image_identifier, $no_cache, $ignore_error) = @_; |
| if (!defined($image_identifier)) { |
| notify($ERRORS{'WARNING'}, 0, "image identifier argument was not specified"); |
| return; |
| } |
| |
| return $ENV{image_info}{$image_identifier} if (!$no_cache && $ENV{image_info}{$image_identifier}); |
| |
| # Get a hash ref containing the database column names |
| my $database_table_columns = get_database_table_columns(); |
| |
| my @tables = ( |
| 'image', |
| 'platform', |
| 'OS', |
| 'OStype', |
| 'imagetype', |
| 'module', |
| ); |
| |
| # Construct the select statement |
| my $select_statement = "SELECT DISTINCT\n"; |
| |
| # Get the column names for each table and add them to the select statement |
| for my $table (@tables) { |
| my @columns = @{$database_table_columns->{$table}}; |
| for my $column (@columns) { |
| $select_statement .= "$table.$column AS '$table-$column',\n"; |
| } |
| } |
| |
| # Remove the comma after the last column line |
| $select_statement =~ s/,$//; |
| |
| # Complete the select statement |
| $select_statement .= <<EOF; |
| FROM |
| image, |
| platform, |
| OS, |
| OStype, |
| imagetype, |
| module |
| |
| WHERE |
| platform.id = image.platformid |
| AND OS.id = image.OSid |
| AND OS.type = OStype.name |
| AND image.imagetypeid = imagetype.id |
| AND module.id = OS.moduleid |
| AND |
| EOF |
| |
| if ($image_identifier =~ /^\d+$/){ |
| $select_statement .= "image.id = $image_identifier"; |
| } |
| else { |
| $image_identifier =~ s/(-v)\d+$/$1/g; |
| $select_statement .= "image.name LIKE '$image_identifier\%'"; |
| } |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @selected_rows == 0) { |
| if ($ignore_error) { |
| notify($ERRORS{'DEBUG'}, 0, "image does NOT exist in the database: $image_identifier"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select statement:\n$select_statement"); |
| } |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| |
| # Get the single row returned from the select statement |
| my $row = $selected_rows[0]; |
| |
| # Construct a hash with all of the image info |
| my $image_info; |
| |
| # Loop through all the columns returned |
| for my $key (keys %$row) { |
| my $value = $row->{$key}; |
| |
| # Split the table-column names |
| my ($table, $column) = $key =~ /^([^-]+)-(.+)/; |
| |
| # Add the values for the primary table to the hash |
| # Add values for other tables under separate keys |
| if ($table eq $tables[0]) { |
| $image_info->{$column} = $value; |
| } |
| elsif ($table =~ /^(module|OStype)$/) { |
| $image_info->{OS}{$table}{$column} = $value; |
| } |
| else { |
| $image_info->{$table}{$column} = $value; |
| } |
| } |
| |
| # Retrieve the imagemeta info and add it to the hash |
| my $imagemeta_id = $image_info->{imagemetaid}; |
| my $imagemeta_info = get_imagemeta_info($imagemeta_id); |
| $image_info->{imagemeta} = $imagemeta_info; |
| |
| my $image_owner_id = $image_info->{ownerid}; |
| my $image_owner_user_info = get_user_info($image_owner_id); |
| $image_info->{owner} = $image_owner_user_info; |
| |
| $image_info->{IDENTITY} = get_management_node_info()->{keys}; |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved info for image '$image_identifier':\n" . format_data($image_info)); |
| $ENV{image_info}{$image_identifier} = $image_info; |
| return $ENV{image_info}{$image_identifier}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_imagerevision_info |
| |
| Parameters : $imagerevision_identifier, $no_cache (optional) |
| Returns : Hash reference |
| Description : collects data from database on supplied $imagerevision_id |
| |
| =cut |
| |
| sub get_imagerevision_info { |
| my ($imagerevision_identifier, $no_cache) = @_; |
| if (!defined($imagerevision_identifier)) { |
| notify($ERRORS{'WARNING'}, 0, "imagerevision identifier argument was not specified"); |
| return; |
| } |
| |
| return $ENV{imagerevision_info}{$imagerevision_identifier} if (!$no_cache && $ENV{imagerevision_info}{$imagerevision_identifier}); |
| |
| my $select_statement = <<EOF; |
| SELECT |
| imagerevision.* |
| FROM |
| imagerevision |
| WHERE |
| EOF |
| |
| # Check input value - complete select_statement |
| if($imagerevision_identifier =~ /^\d/){ |
| $select_statement .= "imagerevision.id = '$imagerevision_identifier'"; |
| } |
| else{ |
| $select_statement .= "imagerevision.imagename = \'$imagerevision_identifier\'"; |
| } |
| |
| # 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 (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "imagerevision '$imagerevision_identifier' was not found in the database, 0 rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| |
| my $imagerevision_info = $selected_rows[0]; |
| |
| # Retrieve the image info |
| my $imagerevision_image_id = $imagerevision_info->{imageid}; |
| my $imagerevision_image_info = get_image_info($imagerevision_image_id); |
| if (!$imagerevision_image_info) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve imagerevision info, image info could not be retrieved for image ID: $imagerevision_image_id"); |
| return; |
| } |
| $imagerevision_info->{image} = $imagerevision_image_info; |
| |
| # Retrieve the imagerevision user info |
| $imagerevision_info->{user} = get_user_info($imagerevision_info->{userid}); |
| |
| # Add the info to %ENV so it doesn't need to be retrieved from the database again |
| $ENV{imagerevision_info}{$imagerevision_identifier} = $imagerevision_info; |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved info from database for imagerevision '$imagerevision_identifier':\n" . format_data($ENV{imagerevision_info}{$imagerevision_identifier})); |
| return $ENV{imagerevision_info}{$imagerevision_identifier}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_production_imagerevision_info |
| |
| Parameters : $image_id |
| Returns : Hash containing imagerevision data for the production revision of an image |
| Description : |
| |
| =cut |
| |
| |
| sub get_production_imagerevision_info { |
| my ($image_identifier) = @_; |
| |
| # Check the passed parameter |
| if (!defined($image_identifier)) { |
| notify($ERRORS{'WARNING'}, 0, "imagerevision identifier argument was not specified"); |
| return; |
| } |
| |
| my $select_statement = <<EOF; |
| SELECT |
| id |
| FROM |
| imagerevision |
| WHERE |
| imagerevision.production = '1' |
| AND |
| EOF |
| |
| # Check input value - complete select_statement |
| if($image_identifier =~ /^\d/){ |
| $select_statement .= "imagerevision.imageid = '$image_identifier'"; |
| } |
| else{ |
| $select_statement .= "imagerevision.imagename = \'$image_identifier\'"; |
| } |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "production imagerevision for image '$image_identifier' was not found in the database, 0 rows were returned, select statement:\n$select_statement"); |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| |
| my $imagerevision_id = $selected_rows[0]{id}; |
| return get_imagerevision_info($imagerevision_id); |
| } ## end sub get_production_imagerevision_info |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_imagemeta_info |
| |
| Parameters : $imagemeta_id, $no_cache (optional) |
| Returns : Hash reference |
| Description : |
| |
| =cut |
| |
| |
| sub get_imagemeta_info { |
| my ($imagemeta_id, $no_cache) = @_; |
| |
| my $default_imagemeta_info = get_default_imagemeta_info(); |
| |
| # Return defaults if nothing was passed as the imagemeta id |
| if (!$imagemeta_id) { |
| return $default_imagemeta_info; |
| } |
| |
| if (!$no_cache && $ENV{imagemeta_info}{$imagemeta_id}) { |
| return $ENV{imagemeta_info}{$imagemeta_id}; |
| } |
| |
| # If imagemetaid isnt' NULL, perform another query to get the meta info |
| my $select_statement = <<EOF; |
| SELECT |
| imagemeta.* |
| FROM |
| imagemeta |
| WHERE |
| imagemeta.id = '$imagemeta_id' |
| EOF |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (!@selected_rows || scalar @selected_rows > 1) { |
| $ENV{imagemeta_info}{$imagemeta_id} = $default_imagemeta_info; |
| |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve imagemeta ID=$imagemeta_id, returning default imagemeta values"); |
| return $ENV{imagemeta_info}{$imagemeta_id}; |
| } |
| |
| # Get the single row returned from the select statement |
| my $imagemeta_info = $selected_rows[0]; |
| |
| for my $column (keys %$imagemeta_info) { |
| if (!defined($imagemeta_info->{$column})) { |
| $imagemeta_info->{$column} = $default_imagemeta_info->{$column}; |
| } |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved imagemeta info:\n" . format_data($imagemeta_info)); |
| $ENV{imagemeta_info}{$imagemeta_id} = $imagemeta_info; |
| return $ENV{imagemeta_info}{$imagemeta_id}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_default_imagemeta_info |
| |
| Parameters : |
| Returns : Hash reference |
| Description : |
| |
| =cut |
| |
| |
| sub get_default_imagemeta_info { |
| if ($ENV{imagemeta_info}{default}) { |
| # Create a copy to ensure that the correct default data is returned |
| # Other processes may use the same cached copy |
| # If the same reference is returned for multiple processes, one process may alter the data |
| my %default_imagemeta_info = %{$ENV{imagemeta_info}{default}}; |
| return \%default_imagemeta_info; |
| } |
| |
| # Call the database select subroutine to retrieve the imagemeta table structure |
| my $describe_imagemeta_statement = "DESCRIBE imagemeta"; |
| my @describe_imagemeta_rows = database_select($describe_imagemeta_statement); |
| if (!@describe_imagemeta_rows) { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve imagemeta table structure, SQL statement:\n$describe_imagemeta_statement"); |
| return; |
| } |
| |
| my $default_imagemeta_info; |
| for my $describe_imagemeta_row (@describe_imagemeta_rows) { |
| my $field = $describe_imagemeta_row->{Field}; |
| my $default_value = $describe_imagemeta_row->{Default}; |
| if (defined($default_value)) { |
| $default_imagemeta_info->{$field} = $default_value; |
| } |
| else { |
| $default_imagemeta_info->{$field} = ''; |
| } |
| } |
| |
| $ENV{imagemeta_info}{default} = $default_imagemeta_info; |
| |
| my %default_imagemeta_info_copy = %{$ENV{imagemeta_info}{default}}; |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved default imagemeta info:\n" . format_data(\%default_imagemeta_info_copy)); |
| return \%default_imagemeta_info_copy; |
| } |
| |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_vmhost_info |
| |
| Parameters : $vmhost_id |
| Returns : Hash reference |
| Description : Retrieves info from the database for the vmhost, vmprofile, and |
| repository and datastore imagetypes. |
| |
| =cut |
| |
| |
| sub get_vmhost_info { |
| my ($vmhost_id) = @_; |
| |
| # Check the passed parameter |
| if (!defined($vmhost_id)) { |
| notify($ERRORS{'WARNING'}, 0, "vmhost ID argument was not specified"); |
| return; |
| } |
| |
| # Get a hash ref containing the database column names |
| my $database_table_columns = get_database_table_columns(); |
| |
| my %tables = ( |
| 'vmhost' => 'vmhost', |
| 'vmprofile' => 'vmprofile', |
| 'repositoryimagetype' => 'imagetype', |
| 'datastoreimagetype' => 'imagetype', |
| ); |
| |
| # Construct the select statement |
| my $select_statement = "SELECT\n"; |
| |
| # Get the column names for each table and add them to the select statement |
| for my $table_alias (keys %tables) { |
| my $table_name = $tables{$table_alias}; |
| my @columns = @{$database_table_columns->{$table_name}}; |
| for my $column (@columns) { |
| $select_statement .= "$table_alias.$column AS '$table_alias-$column',\n"; |
| } |
| } |
| |
| # Remove the comma after the last column line |
| $select_statement =~ s/,$//; |
| |
| # Complete the select statement |
| $select_statement .= <<EOF; |
| FROM |
| vmhost, |
| vmprofile, |
| imagetype repositoryimagetype, |
| imagetype datastoreimagetype |
| |
| WHERE |
| vmhost.id = '$vmhost_id' |
| AND vmprofile.id = vmhost.vmprofileid |
| AND vmprofile.repositoryimagetypeid = repositoryimagetype.id |
| AND vmprofile.datastoreimagetypeid = datastoreimagetype.id |
| EOF |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @selected_rows == 0) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| |
| # Get the single row returned from the select statement |
| my $row = $selected_rows[0]; |
| |
| # Construct a hash with all of the vmhost info |
| my $vmhost_info; |
| |
| # Loop through all the columns returned |
| for my $key (keys %$row) { |
| my $value = $row->{$key}; |
| |
| # Split the table-column names |
| my ($table, $column) = $key =~ /^([^-]+)-(.+)/; |
| |
| # Add the values for the vmhost table to the hash |
| # Add values for other tables under separate keys |
| if ($table eq 'vmhost') { |
| $vmhost_info->{$column} = $value; |
| } |
| else { |
| $vmhost_info->{$table}{$column} = $value; |
| } |
| } |
| |
| # Get the vmhost computer info and add it to the hash |
| my $computer_id = $vmhost_info->{computerid}; |
| my $computer_info = get_computer_info($computer_id); |
| if ($computer_info) { |
| $vmhost_info->{computer} = $computer_info; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to retrieve vmhost computer info, computer ID: $computer_id"); |
| } |
| |
| # Get the vmprofile image info and add it to the hash |
| my $vmprofile_image_id = $vmhost_info->{vmprofile}{imageid}; |
| my $vmprofile_image_info = get_image_info($vmprofile_image_id); |
| if ($vmprofile_image_info) { |
| $vmhost_info->{vmprofile}{image} = $vmprofile_image_info; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to retrieve vmprofile image info, image ID: $vmprofile_image_id"); |
| } |
| |
| $vmhost_info->{vmprofile}{username} = '' if !$vmhost_info->{vmprofile}{username}; |
| $vmhost_info->{vmprofile}{password} = '' if !$vmhost_info->{vmprofile}{password}; |
| |
| $vmhost_info->{vmprofile}{vmpath} = $vmhost_info->{vmprofile}{datastorepath} if !$vmhost_info->{vmprofile}{vmpath}; |
| $vmhost_info->{vmprofile}{virtualdiskpath} = $vmhost_info->{vmprofile}{vmpath} if !$vmhost_info->{vmprofile}{virtualdiskpath}; |
| |
| return $vmhost_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 run_ssh_command |
| |
| Parameters : $node, $identity_path, $command, $user, $port, $output_level, $timeout_seconds |
| -or- |
| Hash reference with the following keys: |
| node - node name (required) |
| command - command to be executed remotely (required) |
| identity_paths - string containing paths to identity key files separated by commas (optional) |
| user - user to run remote command as (optional, default is 'root') |
| port - SSH port number (optional, default is 22) |
| output_level - allows the amount of output to be controlled: 0, 1, or 2 (optional) |
| max_attempts - maximum number of SSH attempts to make |
| timeout_seconds - maximum number seconds SSH process can run before being terminated |
| Returns : If successful: array: |
| $array[0] = the exit status of the command |
| $array[1] = reference to array containing lines of output |
| If failed: false |
| Description : Runs an SSH command on the specified node. |
| |
| =cut |
| |
| sub run_ssh_command { |
| my ($node, $identity_paths, $command, $user, $port, $output_level, $timeout_seconds) = @_; |
| |
| my $max_attempts = 3; |
| |
| if (ref($_[0]) eq 'HASH') { |
| my $arguments = shift; |
| |
| $node = $arguments->{node}; |
| $command = $arguments->{command}; |
| $identity_paths = $arguments->{identity_paths} || ''; |
| $user = $arguments->{user} || 'root'; |
| $port = $arguments->{port} || '22'; |
| $output_level = $arguments->{output_level}; |
| $max_attempts = $arguments->{max_attempts} || 3; |
| $timeout_seconds = $arguments->{timeout}; |
| } |
| |
| # Determine the output level if it was specified |
| # Set $output_level to 0, 1, or 2 |
| if (!defined($output_level)) { |
| $output_level = 2; |
| } |
| elsif ($output_level =~ /0|none/i) { |
| $output_level = 0; |
| } |
| elsif ($output_level =~ /1|min/i) { |
| $output_level = 1; |
| } |
| else { |
| $output_level = 2; |
| } |
| |
| # Check the arguments |
| if (!defined($node) || !$node) { |
| notify($ERRORS{'WARNING'}, 0, "computer node was not specified"); |
| return 0; |
| } |
| if (!defined($command) || !$command) { |
| notify($ERRORS{'WARNING'}, 0, "command was not specified"); |
| return 0; |
| } |
| |
| # Set default values if not passed as an argument |
| $user = "root" if (!$user); |
| $port = 22 if (!$port); |
| $timeout_seconds = 0 if (!$timeout_seconds); |
| $identity_paths = get_management_node_info()->{keys} if (!defined $identity_paths || length($identity_paths) == 0); |
| |
| #return VCL::Module::OS::execute_new($node, $command, $output_level, $timeout_seconds, $max_attempts, $port, $user, '', $identity_paths); |
| |
| # TODO: Add ssh path to config file and set global variable |
| # Locate the path to the ssh binary |
| my $ssh_path; |
| if (-f '/usr/bin/ssh') { |
| $ssh_path = '/usr/bin/ssh'; |
| } |
| elsif (-f 'C:/cygwin/bin/ssh.exe') { |
| $ssh_path = 'C:/cygwin/bin/ssh.exe'; |
| } |
| elsif (-f 'D:/cygwin/bin/ssh.exe') { |
| $ssh_path = 'D:/cygwin/bin/ssh.exe'; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to locate the SSH executable in the usual places"); |
| return 0; |
| } |
| |
| # Format the identity path string |
| if (defined $identity_paths && length($identity_paths) > 0) { |
| # Add -i to beginning of string |
| $identity_paths = "-i $identity_paths"; |
| |
| # Split string on commas, add -i to each value after a comma |
| $identity_paths =~ s/\s*,\s*/ -i /g; |
| |
| # Add a space to the end of the string |
| $identity_paths .= ' '; |
| } |
| else { |
| $identity_paths = ''; |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "ssh path: $ssh_path"); |
| #notify($ERRORS{'DEBUG'}, 0, "node: $node, identity file paths: $identity_paths, user: $user, port: $port"); |
| #notify($ERRORS{'DEBUG'}, 0, "command: $command"); |
| |
| #if ($command =~ /['\\]/) { |
| # my @octals = map { "0" . sprintf("%o", $_) } unpack("C*", $command); |
| # my $octal_string = '\\' . join("\\", @octals); |
| # $command = "echo -e \"$octal_string\" | \$SHELL"; |
| # notify($ERRORS{'DEBUG'}, 0, "octal command:\n$command"); |
| #} |
| |
| # Assemble the SSH command |
| # -i <identity_file>, Selects the file from which the identity (private key) for RSA authentication is read. |
| # -l <login_name>, Specifies the user to log in as on the remote machine. |
| # -p <port>, Port to connect to on the remote host. |
| # -x, Disables X11 forwarding. |
| # Dont use: -q, Quiet mode. Causes all warning and diagnostic messages to be suppressed. |
| my $ssh_command = "$ssh_path $identity_paths -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectionAttempts=1 -o ConnectTimeout=3 -l $user -p $port -x $node '$command' 2>&1"; |
| |
| # Execute the command |
| my $ssh_output = ''; |
| my $ssh_output_formatted = ''; |
| my $attempts = 0; |
| my $exit_status = 255; |
| |
| # Make multiple attempts if failure occurs |
| while ($attempts < $max_attempts) { |
| $attempts++; |
| |
| # Delay performing next attempt if this isn't the first attempt |
| if ($attempts > 1) { |
| my $delay_seconds = (2 * ($attempts - 1)); |
| notify($ERRORS{'DEBUG'}, 0, "sleeping for $delay_seconds seconds before making next SSH attempt") if $output_level; |
| sleep $delay_seconds; |
| } |
| |
| ## Add -v (verbose) argument to command if this is the 2nd attempt |
| #$ssh_command =~ s/$ssh_path/$ssh_path -v/ if $attempts == 2; |
| |
| # Print the SSH command, only display the attempt # if > 1 |
| if ($attempts == 1) { |
| notify($ERRORS{'DEBUG'}, 0, "executing SSH command on $node:\n$ssh_command") if $output_level; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "attempt $attempts/$max_attempts: executing SSH command on $node:\n$ssh_command") if $output_level; |
| } |
| |
| # Enclose SSH command in an eval block and use alarm to eventually timeout the SSH command if it hangs |
| my $start_time = time; |
| eval { |
| # Override the die and alarm handlers |
| local $SIG{__DIE__} = sub{}; |
| local $SIG{ALRM} = sub { die "alarm\n" }; |
| |
| if ($timeout_seconds) { |
| notify($ERRORS{'DEBUG'}, 0, "waiting up to $timeout_seconds seconds for SSH process to finish"); |
| alarm $timeout_seconds; |
| } |
| |
| # Execute the command |
| $ssh_output = `$ssh_command`; |
| |
| # Save the exit status |
| $exit_status = $? >> 8; |
| |
| # Ignore the returned value of $? if it is -1 |
| # This likely means a Perl bug was encountered |
| # Assume command was successful |
| if ($? == -1) { |
| notify($ERRORS{'DEBUG'}, 0, "exit status changed from $exit_status to 0, Perl bug likely encountered") if $output_level; |
| $exit_status = 0; |
| } |
| |
| if ($timeout_seconds) { |
| # Cancel the timer |
| alarm 0; |
| } |
| }; |
| |
| my $duration = (time - $start_time); |
| |
| # Check if the timeout was reached |
| if ($EVAL_ERROR && $EVAL_ERROR eq "alarm\n") { |
| # Kill the child processes of this reservation process |
| kill_child_processes($PID); |
| |
| if ($max_attempts == 1 || $attempts < $max_attempts) { |
| notify($ERRORS{'WARNING'}, 0, "attempt $attempts/$max_attempts: SSH command timed out after $duration seconds, timeout threshold: $timeout_seconds seconds, command: $node:\n$ssh_command"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "attempt $attempts/$max_attempts: SSH command timed out after $duration seconds, timeout threshold: $timeout_seconds seconds, command: $node:\n$ssh_command"); |
| return; |
| } |
| next; |
| } |
| elsif ($EVAL_ERROR) { |
| notify($ERRORS{'CRITICAL'}, 0, "attempt $attempts/$max_attempts: eval error was generated attempting to run SSH command: $node:\n$ssh_command, error: $EVAL_ERROR"); |
| next; |
| } |
| |
| # Strip out the key warning message from the output |
| $ssh_output =~ s/\@{10,}.*man-in-the-middle attacks\.//igs; |
| |
| # Strip out known SSH warning messages |
| # Warning: Permanently added 'blade1b2-8' (RSA) to the list of known hosts. |
| # |
| # Warning: the RSA host key for 'vi1-62' differs from the key for the IP address '10.25.7.62' |
| # Offending key for IP in /root/.ssh/known_hosts:264 |
| # Matching host key in /root/.ssh/known_hosts:3977 |
| # Address x.x.x.x maps to y.y.org, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT! |
| $ssh_output =~ s/^Warning:.*//ig; |
| $ssh_output =~ s/Offending key.*//ig; |
| $ssh_output =~ s/Matching host key in.*//ig; |
| $ssh_output =~ s/.*POSSIBLE BREAK-IN ATTEMPT.*//ig; |
| |
| # Remove any spaces from the beginning and end of the output |
| $ssh_output =~ s/(^\s+)|(\s+$)//g; |
| |
| # Set the output string to none if no output was produced |
| $ssh_output = '' if !$ssh_output; |
| |
| # Replace line breaks in the output with \n$pid| SSH output: |
| my $pid = $$; |
| $ssh_output_formatted = $ssh_output; |
| |
| # Get a slice of the SSH output if there are many lines |
| my @ssh_output_formatted_lines = split("\n", $ssh_output_formatted); |
| my $ssh_output_formatted_line_count = scalar @ssh_output_formatted_lines; |
| if ($ssh_output_formatted_line_count > 50) { |
| @ssh_output_formatted_lines = @ssh_output_formatted_lines[0 .. 49]; |
| push(@ssh_output_formatted_lines, "displayed first 50 of $ssh_output_formatted_line_count SSH output lines"); |
| $ssh_output_formatted = join("\n", @ssh_output_formatted_lines); |
| } |
| |
| my $command_prefix = substr($command, 0, 10); |
| $command_prefix .= "..." if (length($command) > 10); |
| $ssh_output_formatted =~ s/\r//g; |
| $ssh_output_formatted =~ s/^/ssh output ($command_prefix): /g; |
| $ssh_output_formatted =~ s/\n/\nssh output ($command_prefix): /g; |
| |
| # Check the exit status |
| # ssh exits with the exit status of the remote command or with 255 if an error occurred. |
| # Check for vmware-cmd usage message, it returns 255 if the vmware-cmd usage output is returned |
| if ($ssh_output_formatted =~ /ssh:.*(lost connection|reset by peer|no route to host|connection refused|connection timed out|resource temporarily unavailable|connection reset)/i) { |
| notify($ERRORS{'WARNING'}, 0, "attempt $attempts/$max_attempts: failed to execute SSH command on $node: '$command', exit status: $exit_status, output:\n$ssh_output_formatted"); |
| next; |
| } |
| elsif ($exit_status == 255 && $ssh_command !~ /(vmware-cmd|vim-cmd|vmkfstools|vmrun)/i) { |
| notify($ERRORS{'WARNING'}, 0, "attempt $attempts/$max_attempts: failed to execute SSH command on $node: '$command', exit status: $exit_status, SSH exits with the exit status of the remote command or with 255 if an error occurred, output:\n$ssh_output_formatted") if $output_level; |
| next; |
| } |
| else { |
| # SSH command was executed successfully, actual command on node may have succeeded or failed |
| |
| # Split the output up into an array of lines |
| my @output_lines = split(/[\r\n]+/, $ssh_output); |
| |
| # Print the output unless no_output is set |
| notify($ERRORS{'DEBUG'}, 0, "run_ssh_command output:\n" . join("\n", @output_lines)) if $output_level > 1; |
| |
| # Print the command and exit status |
| (my $ssh_output_summary = $ssh_output) =~ s/\s+/ /gs; |
| if (length($ssh_output_summary) > 30) { |
| $ssh_output_summary = substr($ssh_output_summary, 0, 30); |
| $ssh_output_summary .= "..."; |
| } |
| |
| # Display the full ssh command if the exit status is not 0 |
| if ($exit_status) { |
| notify($ERRORS{'OK'}, 0, "SSH command executed on $node, command:\n$ssh_command\nreturning ($exit_status, \"$ssh_output_summary\")") if $output_level > 1; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "SSH command executed on $node, returning ($exit_status, \"$ssh_output_summary\")") if $output_level > 1; |
| } |
| |
| # Return the exit status and output |
| return ($exit_status, \@output_lines); |
| } |
| } ## end while ($attempts < $max_attempts) |
| |
| # Failure, SSH command did not run at all |
| notify($ERRORS{'WARNING'}, 0, "failed to run SSH command after $attempts attempts, command: $ssh_command, exit status: $exit_status, output:\n$ssh_output_formatted") if $output_level; |
| return (); |
| |
| } ## end sub run_ssh_command |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 run_scp_command |
| |
| Parameters : $path1, $path2, $identity_path, $port, $options |
| Returns : 1 success |
| Description : assumes path1 or path2 contains the src and target |
| example: copy from remote node to local file |
| path1 = $user\@$node:<filename> |
| path2 = <localfilename> |
| |
| example: copy local file to remote node |
| path1 = <localfilename> |
| path2 = $user\@$node:<filename> |
| |
| =cut |
| |
| sub run_scp_command { |
| my ($path1, $path2, $identity_paths, $port, $options) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| if (!defined($path1) || !$path1) { |
| notify($ERRORS{'WARNING'}, 0, "path1 was not specified"); |
| return 0; |
| } |
| if (!defined($path2) || !$path2) { |
| notify($ERRORS{'WARNING'}, 0, "path2 was not specified"); |
| return 0; |
| } |
| |
| # Escape spaces in the paths if they aren't already escaped |
| $path1 =~ s/([^\\]) /$1\\ /g; |
| $path2 =~ s/([^\\]) /$1\\ /g; |
| |
| # Format the identity path string |
| if ($identity_paths) { |
| $identity_paths =~ s/^\s*/-i /; |
| $identity_paths =~ s/\s*,\s*/ -i /g; |
| $identity_paths .= ' '; |
| } |
| else { |
| $identity_paths = ''; |
| } |
| |
| # Set default values if not passed as an argument |
| $port = 22 if (!defined($port)); |
| |
| # TODO: Add SCP path to config file and set global variable |
| # Locate the path to the SCP binary |
| my $scp_path; |
| if (-f '/usr/bin/scp') { |
| $scp_path = '/usr/bin/scp'; |
| } |
| elsif (-f 'C:/cygwin/bin/scp.exe') { |
| $scp_path = 'C:/cygwin/bin/scp.exe'; |
| } |
| elsif (-f 'D:/cygwin/bin/scp.exe') { |
| $scp_path = 'D:/cygwin/bin/scp.exe'; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to locate the SCP executable in the usual places"); |
| return 0; |
| } |
| |
| # could be hazardous not confirming optional input flags |
| if (defined($options)) { |
| $scp_path .= " $options "; |
| } |
| |
| # Print the configuration if $VERBOSE |
| if ($VERBOSE) { |
| #notify($ERRORS{'OK'}, 0, "path1: $path1, path2: $path2 identity file path: $identity_path, port: $port"); |
| #notify($ERRORS{'OK'}, 0, "node: $node, identity file path: $identity_path, user: $user, port: $port"); |
| #notify($ERRORS{'OK'}, 0, "source path: $source_path"); |
| #notify($ERRORS{'OK'}, 0, "destination path: $destination_path"); |
| } |
| |
| # Assemble the SCP command |
| # -B, Selects batch mode (prevents asking for passwords or passphrases). |
| # -i <identity_file>, Selects the file from which the identity (private key) for RSA authentication is read. |
| # -P <port>, Specifies the port to connect to on the remote host. |
| # -p, Preserves modification times, access times, and modes from the original file. |
| # -r, Recursively copy entire directories. |
| # -v, Verbose mode. Causes scp and ssh to print debugging messages about their progress. |
| # Don't use -q, Disables the progress meter. Error messages are more descriptive without it |
| my $scp_command = "$scp_path -B $identity_paths-P $port -p -r $path1 $path2"; |
| |
| # Redirect standard output and error output so all messages are captured |
| $scp_command .= ' 2>&1'; |
| |
| # Execute the command |
| my $scp_output; |
| my $attempts = 0; |
| my $max_attempts = 3; |
| my $scp_exit_status = 0; |
| |
| # Make multiple attempts if failure occurs |
| while ($attempts < $max_attempts) { |
| $attempts++; |
| |
| # Delay performing next attempt if this isn't the first attempt |
| if ($attempts > 1) { |
| my $delay = 2; |
| notify($ERRORS{'DEBUG'}, 0, "sleeping for $delay seconds before making next SCP attempt"); |
| sleep $delay; |
| notify($ERRORS{'DEBUG'}, 0, "attempt $attempts/$max_attempts: executing SCP command: $scp_command"); |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "attempting to copy file via SCP: '$path1' --> '$path2'"); |
| } |
| |
| ## Add -v (verbose) argument to command if this is the 2nd attempt |
| #$scp_command =~ s/$scp_path/$scp_path -v/ if $attempts == 2; |
| |
| $scp_output = `$scp_command`; |
| |
| # Save the exit status |
| $scp_exit_status = $?; |
| |
| # Strip out the key warning message |
| $scp_output =~ s/\@{10,}.*man-in-the-middle attacks\.//igs; |
| $scp_output =~ s/^\s+|\s+$//g; |
| $scp_output =~ s/Warning:.*known hosts.*//ig; |
| |
| if ($scp_output && length($scp_output) > 0) { |
| # Add a newline to the beginning of the output if something was generated |
| # This is to make multi-line output more readable |
| $scp_output = "\n" . $scp_output; |
| } |
| else { |
| # Indicate there was no output if it is blank |
| $scp_output = ''; |
| } |
| |
| # Check the output for known error messages |
| if ($scp_output =~ /permission denied|no such file|ambiguous target|is a directory|not known|no space/i) { |
| notify($ERRORS{'WARNING'}, 0, "failed to copy via SCP: '$path1' --> '$path2'\ncommand: $scp_command\noutput:\n$scp_output"); |
| return 0; |
| } |
| elsif ($scp_output =~ /^(scp|ssh):/i) { |
| notify($ERRORS{'WARNING'}, 0, "attempt $attempts/$max_attempts: error occurred while attempting to copy file via SCP: '$path1' --> '$path2'\ncommand: $scp_command\noutput:\n$scp_output"); |
| next; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "copied file via SCP: '$path1' --> '$path2'"); |
| return 1; |
| } |
| } ## end while ($attempts < $max_attempts) |
| |
| # Failure |
| return 0; |
| } ## end sub run_scp_command |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 write_currentimage_txt |
| |
| Parameters : hash of hashes hash{image} contains image info |
| Returns : 0 failed or 1 successful |
| Description : runs an ssh command on the specified node and returns the output |
| |
| =cut |
| |
| sub write_currentimage_txt { |
| my ($data) = @_; |
| |
| # Store some hash variables into local variables |
| my $computer_node_name = $data->get_computer_node_name(); |
| my $computer_host_name = $data->get_computer_host_name(); |
| my $computer_id = $data->get_computer_id(); |
| my $image_identity = $data->get_image_identity(); |
| my $image_id = $data->get_image_id(); |
| my $image_name = $data->get_image_name(); |
| my $image_prettyname = $data->get_image_prettyname(); |
| my $imagerevision_id = $data->get_imagerevision_id(); |
| my $imagerevision_date_created = $data->get_imagerevision_date_created(); |
| my $image_os_type = $data->get_image_os_type(); |
| |
| my @current_image_lines; |
| push @current_image_lines, "$image_name"; |
| push @current_image_lines, "id=$image_id"; |
| push @current_image_lines, "prettyname=$image_prettyname"; |
| push @current_image_lines, "imagerevision_id=$imagerevision_id"; |
| push @current_image_lines, "imagerevision_datecreated=$imagerevision_date_created"; |
| push @current_image_lines, "computer_id=$computer_id"; |
| push @current_image_lines, "computer_hostname=$computer_host_name"; |
| |
| my $current_image_contents = join('\\r\\n', @current_image_lines); |
| |
| # Remove single quotes - they cause echo command to break |
| $current_image_contents =~ s/'//g; |
| |
| #Make sure currentimage.txt writable |
| my $chown_command = "chown root currentimage.txt; chmod 777 currentimage.txt"; |
| if(run_ssh_command($computer_node_name, $image_identity, $chown_command)){ |
| notify($ERRORS{'OK'}, 0, "updated ownership and permissions on currentimage.txt"); |
| } |
| |
| my $command; |
| if($image_os_type =~ /osx/i) { |
| $command = 'echo "'; |
| } |
| else { |
| $command = 'echo -e "'; |
| } |
| |
| $command .= $current_image_contents . '" > currentimage.txt && cat currentimage.txt'; |
| |
| # Copy the temp file to the node as currentimage.txt |
| my ($ssh_exit_status, $ssh_output) = run_ssh_command($computer_node_name, $image_identity, $command); |
| |
| if (defined($ssh_exit_status) && $ssh_exit_status == 0) { |
| notify($ERRORS{'OK'}, 0, "created currentimage.txt file on $computer_node_name:\n" . join "\n", @{$ssh_output}); |
| return 1; |
| } |
| elsif (defined($ssh_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to create currentimage.txt file on $computer_node_name:\n" . join "\n", @{$ssh_output}); |
| return; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to create currentimage.txt file on $computer_node_name"); |
| return; |
| } |
| |
| } ## end sub write_currentimage_txt |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 vmwareclone |
| |
| Parameters : $hostnode, $identity, $srcDisk, $dstDisk, $dstDir |
| Returns : 1 if successful, 0 if error occurred |
| Description : using vm tools clone srcdisk to dstdisk |
| currently using builtin vmkfstools |
| |
| =cut |
| |
| =pod |
| |
| sub vmwareclone { |
| my ($hostnode, $identity, $srcDisk, $dstDisk, $dstDir) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| #TODO - add checks for VI toolkit - then use vmclone.pl instead |
| #vmclone.pl would need additional parameters |
| |
| my @list = run_ssh_command($hostnode, $identity, "ls -1 $srcDisk", "root"); |
| my $srcDiskexist = 0; |
| |
| foreach my $l (@{ $list[1] }) { |
| $srcDiskexist = 1 if ($l =~ /($srcDisk)$/); |
| $srcDiskexist = 0 if ($l =~ /No such file or directory/); |
| notify($ERRORS{'OK'}, 0, "$l"); |
| } |
| my @ssh; |
| if ($srcDiskexist) { |
| #make dir for dstdisk |
| my @mkdir = run_ssh_command($hostnode, $identity, "mkdir -m 755 $dstDir", "root"); |
| notify($ERRORS{'OK'}, 0, "srcDisk is exists $srcDisk "); |
| notify($ERRORS{'OK'}, 0, "starting clone process vmkfstools -d thin -i $srcDisk $dstDisk"); |
| if (open(SSH, "/usr/bin/ssh -x -q -i $identity -l root $hostnode \"vmkfstools -i $srcDisk -d thin $dstDisk\" 2>&1 |")) { |
| #@ssh=<SSH>; |
| #close(SSH); |
| #foreach my $l (@ssh) { |
| # notify($ERRORS{'OK'},0,"$l"); |
| #} |
| while (<SSH>) { |
| notify($ERRORS{'OK'}, 0, "started $_") if ($_ =~ /Destination/); |
| notify($ERRORS{'OK'}, 0, "started $_") if ($_ =~ /Cloning disk/); |
| notify($ERRORS{'OK'}, 0, "status $_") if ($_ =~ /Clone:/); |
| } |
| close(SSH); |
| } ## end if (open(SSH, "/usr/bin/ssh -x -q -i $identity -l root $hostnode \"vmkfstools -i $srcDisk -d thin $dstDisk\" 2>&1 |"... |
| } ## end if ($srcDiskexist) |
| else { |
| notify($ERRORS{'OK'}, 0, "srcDisk $srcDisk does not exists"); |
| } |
| #confirm |
| @list = 0; |
| @list = run_ssh_command($hostnode, $identity, "ls -1 $dstDisk", "root"); |
| my $dstDiskexist = 0; |
| foreach my $l (@{ $list[1] }) { |
| $dstDiskexist = 1 if ($l =~ /($dstDisk)$/); |
| $dstDiskexist = 0 if ($l =~ /No such file or directory/); |
| } |
| if ($dstDiskexist) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "clone process failed dstDisk $dstDisk does not exist"); |
| return 0; |
| } |
| } ## end sub vmwareclone |
| |
| =cut |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_management_predictive_info |
| |
| Parameters : Either a management node hostname or database ID |
| Returns : Hash containing data contained in the managementnode table |
| Description : |
| |
| =cut |
| |
| sub get_management_predictive_info { |
| my ($management_node_identifier) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($management_node_identifier))) { |
| # If nothing was passed, assume management node is this machine |
| # Try to get the hostname of this machine |
| unless ($management_node_identifier = (hostname())[0]) { |
| notify($ERRORS{'WARNING'}, 0, "management node hostname or ID was not specified and hostname could not be determined"); |
| return (); |
| } |
| } |
| |
| my $select_statement = " |
| SELECT |
| managementnode.*, |
| predictivemodule.name AS predictive_name, |
| predictivemodule.prettyname AS predictive_prettyname, |
| predictivemodule.description AS predictive_description, |
| predictivemodule.perlpackage AS predictive_perlpackage, |
| state.name AS statename |
| FROM |
| managementnode, |
| module predictivemodule, |
| state |
| WHERE |
| managementnode.predictivemoduleid = predictivemodule.id |
| AND managementnode.stateid = state.id |
| AND |
| "; |
| |
| # Figure out if the ID or hostname was passed as the identifier and complete the SQL statement |
| # Check if it only contains digits |
| chomp $management_node_identifier; |
| if ($management_node_identifier =~ /^\d+$/) { |
| $select_statement .= "managementnode.id = $management_node_identifier"; |
| } |
| else { |
| $select_statement .= "managementnode.hostname like \'$management_node_identifier%\'"; |
| } |
| |
| # 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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select"); |
| return (); |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select"); |
| return (); |
| } |
| # Get the single returned row |
| # It contains a hash |
| my $management_node_info = $selected_rows[0]; |
| |
| notify($ERRORS{'DEBUG'}, 0, "management node info retrieved from database "); |
| return $management_node_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_management_node_info |
| |
| Parameters : Either a management node hostname or database ID |
| Returns : Hash containing data contained in the managementnode table |
| Description : |
| |
| =cut |
| |
| sub get_management_node_info { |
| # Get the management node identifier argument |
| # If argument was not passed, assume management node is this machine |
| my $management_node_identifier = shift; |
| |
| # If a management node identifier argument wasn't specified get the hostname of this management node |
| if (!$management_node_identifier) { |
| if ($FQDN) { |
| $management_node_identifier = $FQDN; |
| } |
| else { |
| $management_node_identifier = (hostname())[0]; |
| } |
| } |
| |
| if (!defined($ENV{management_node_info}) || !ref($ENV{management_node_info}) || ref($ENV{management_node_info}) ne 'HASH') { |
| notify($ERRORS{'DEBUG'}, 0, "initializing management node info hash reference"); |
| $ENV{management_node_info} = {}; |
| } |
| |
| if (defined($ENV{management_node_info}{$management_node_identifier})) { |
| my $data_age_seconds = (time - $ENV{management_node_info}{$management_node_identifier}{RETRIEVAL_TIME}); |
| |
| if ($data_age_seconds < 60) { |
| #notify($ERRORS{'DEBUG'}, 0, "returning previously retrieved management node info for '$management_node_identifier'"); |
| return $ENV{management_node_info}{$management_node_identifier}; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "retrieving current management node info for '$management_node_identifier' from database, cached data is stale: $data_age_seconds seconds old"); |
| } |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "management node info for '$management_node_identifier' is not stored in \$ENV{management_node_info}"); |
| } |
| |
| my $select_statement = " |
| SELECT |
| managementnode.*, |
| resource.id AS resource_id, |
| predictivemodule.name AS predictive_name, |
| predictivemodule.prettyname AS predictive_prettyname, |
| predictivemodule.description AS predictive_description, |
| predictivemodule.perlpackage AS predictive_perlpackage, |
| state.name AS statename |
| FROM |
| managementnode, |
| module predictivemodule, |
| resource, |
| resourcetype, |
| state |
| WHERE |
| managementnode.predictivemoduleid = predictivemodule.id |
| AND managementnode.stateid = state.id |
| AND resource.resourcetypeid = resourcetype.id |
| AND resource.subid = managementnode.id |
| AND resourcetype.name = 'managementnode' |
| AND "; |
| |
| # Figure out if the ID or hostname was passed as the identifier and complete the SQL statement |
| chomp $management_node_identifier; |
| if ($management_node_identifier =~ /^\d+$/) { |
| # Identifier only contains digits, assume it's the id |
| $select_statement .= "managementnode.id = $management_node_identifier"; |
| } |
| elsif ($management_node_identifier =~ /^[\d\.]+$/) { |
| # Identifier contains digits and periods, assume it's the IP address |
| $select_statement .= "managementnode.IPAddress like \'$management_node_identifier%\'"; |
| } |
| else { |
| # Assume hostname was specified |
| $select_statement .= "managementnode.hostname REGEXP '^$management_node_identifier(\\\\.|\$)'"; |
| } |
| |
| # 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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select, management node identifier may be invalid: '$management_node_identifier'\n$select_statement"); |
| return (); |
| } |
| elsif (scalar @selected_rows > 1) { |
| my @id_hostnames = map ("management node: id=$_->{id}, hostname=$_->{hostname}, IP=$_->{IPaddress}", @selected_rows); |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select, management node identifier may be ambiguous: '$management_node_identifier'\n" . join("\n", @id_hostnames)); |
| return (); |
| } |
| |
| # Get the single returned row |
| # It contains a hash |
| my $management_node_info = $selected_rows[0]; |
| |
| # Move the state name to a subkey to match get_request_info |
| $management_node_info->{state}{name} = $management_node_info->{statename}; |
| delete $management_node_info->{statename}; |
| |
| $management_node_info->{hostname} =~ /([-_a-zA-Z0-9]*)(\.?)/; |
| my $shortname = $1; |
| $management_node_info->{SHORTNAME} = $shortname; |
| |
| # Get the image library partner info if imagelibenable=1 and imagelibgroupid>0 |
| my $imagelib_enable = $management_node_info->{imagelibenable}; |
| my $imagelib_group_id = $management_node_info->{imagelibgroupid}; |
| my $management_node_id = $management_node_info->{id}; |
| if ($imagelib_enable && defined($imagelib_group_id) && $imagelib_group_id) { |
| my $imagelib_statement = " |
| SELECT DISTINCT |
| managementnode.IPaddress |
| FROM |
| managementnode, |
| resource, |
| resourcegroup, |
| resourcegroupmembers |
| WHERE |
| resourcegroup.id = $imagelib_group_id |
| AND resourcegroupmembers.resourcegroupid = resourcegroup.id |
| AND resource.id = resourcegroupmembers.resourceid |
| AND resource.subid = managementnode.id |
| AND managementnode.id != $management_node_id |
| "; |
| |
| # Call the database select subroutine |
| my @imagelib_rows = database_select($imagelib_statement); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @imagelib_rows == 0) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select, image library functions will be disabled"); |
| $management_node_info->{imagelibenable} = 0; |
| } |
| else { |
| #notify($ERRORS{'DEBUG'}, 0, "imagelib partners found: " . scalar @imagelib_rows); |
| # Loop through the rows, assemble a string separated by commas |
| my $imagelib_ipaddress_string; |
| for my $imagelib_row (@imagelib_rows) { |
| $imagelib_ipaddress_string .= "$imagelib_row->{IPaddress},"; |
| } |
| # Remove the trailing comma |
| $imagelib_ipaddress_string =~ s/,$//; |
| #notify($ERRORS{'DEBUG'}, 0, "image library partner IP address string: $imagelib_ipaddress_string"); |
| $management_node_info->{IMAGELIBPARTNERS} = $imagelib_ipaddress_string; |
| } ## end else [ if (scalar @imagelib_rows == 0) |
| } ## end if ($imagelib_enable && defined($imagelib_group_id... |
| else { |
| $management_node_info->{IMAGELIBPARTNERS} = 0; |
| #notify($ERRORS{'DEBUG'}, 0, "image library sharing functions are disabled"); |
| } |
| |
| # Get the OS name |
| my $os_name = lc($^O); |
| $management_node_info->{OSNAME} = $os_name; |
| |
| # Add the public IP address configuration variables |
| $management_node_info->{PUBLIC_IP_CONFIGURATION} = $management_node_info->{publicIPconfiguration}; |
| $management_node_info->{PUBLIC_SUBNET_MASK} = $management_node_info->{publicSubnetMask}; |
| $management_node_info->{PUBLIC_DEFAULT_GATEWAY} = $management_node_info->{publicDefaultGateway}; |
| $management_node_info->{PUBLIC_DNS_SERVER} = $management_node_info->{publicDNSserver}; |
| |
| # Add sysadmin and sharedMailBox email address values |
| $management_node_info->{SYSADMIN_EMAIL} = $management_node_info->{sysadminEmailAddress}; |
| $management_node_info->{SHARED_EMAIL_BOX} = $management_node_info->{sharedMailBox}; |
| |
| # Add affiliations that are not to use the standalone passwords |
| $management_node_info->{NOT_STANDALONE} = $management_node_info->{NOT_STANDALONE} || ''; |
| |
| # Store the info in $ENV{management_node_info} |
| # Add keys for all of the unique identifiers that may be passed as an argument to this subroutine |
| $ENV{management_node_info}{$management_node_identifier} = $management_node_info; |
| $ENV{management_node_info}{$management_node_info->{hostname}} = $management_node_info; |
| $ENV{management_node_info}{$management_node_info->{SHORTNAME}} = $management_node_info; |
| $ENV{management_node_info}{$management_node_info->{id}} = $management_node_info; |
| $ENV{management_node_info}{$management_node_info->{IPaddress}} = $management_node_info; |
| |
| # Save the time when the data was retrieved |
| $ENV{management_node_info}{$management_node_identifier}{RETRIEVAL_TIME} = time; |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved management node info: '$management_node_identifier' ($management_node_info->{SHORTNAME})"); |
| return $management_node_info; |
| } ## end sub get_management_node_info |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_computer_imagename |
| |
| Parameters : $computerid, $imagename |
| Returns : 0 failed or 1 success |
| Description : Updates currentimage on a node, based on imagename only |
| |
| =cut |
| sub update_computer_imagename { |
| my ($computerid, $imagename, $log) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameters |
| if (!(defined($computerid))) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID was not specified"); |
| return (); |
| } |
| if (!(defined($imagename))) { |
| notify($ERRORS{'WARNING'}, 0, "image name was not specified"); |
| return (); |
| } |
| |
| #get computer infomation based on imagename |
| my $imagerevision_info; |
| if( $imagerevision_info = get_imagerevision_info($imagename)){ |
| notify($ERRORS{'DEBUG'}, 0, "successfully retreived image info for $imagename"); |
| } |
| else{ |
| notify($ERRORS{'WARNING'}, 0, "failed to get_imagerevision_info for $imagename"); |
| return 0; |
| } |
| |
| my $image_id = $imagerevision_info->{imageid}; |
| my $imagerevision_id = $imagerevision_info->{id}; |
| |
| if(update_currentimage($computerid, $image_id, $imagerevision_id)){ |
| notify($ERRORS{'DEBUG'}, 0, "successfully updated computerid= $computerid image_id= $image_id imagerevision_id= $imagerevision_id"); |
| return 1; |
| } |
| else{ |
| notify($ERRORS{'WARNING'}, 0, "failed to update_currentimage imagename= $imagename computerid= $computerid"); |
| return 0; |
| } |
| |
| return 0; |
| |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_currentimage |
| |
| Parameters : $computerid, $imageid, $imagerevisionid, $nextimagid(optional) |
| Returns : 0 failed or 1 success |
| Description : Updates currentimage on a node, nextimageid = optional |
| |
| =cut |
| |
| sub update_currentimage { |
| my ($computerid, $imageid, $imagerevisionid, $nextimagid) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameters |
| if (!(defined($computerid))) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID was not specified"); |
| return (); |
| } |
| if (!(defined($imageid))) { |
| notify($ERRORS{'WARNING'}, 0, "image ID was not specified"); |
| return (); |
| } |
| if (!(defined($imagerevisionid))) { |
| notify($ERRORS{'WARNING'}, 0, "image revision ID was not specified"); |
| return (); |
| } |
| |
| notify($ERRORS{'OK'}, 0, "updating computer $computerid: image=$imageid, imagerevision=$imagerevisionid"); |
| |
| # Construct the update statement |
| # If $nextimageid defined and set build slightly different statement |
| my $update_statement = " |
| UPDATE |
| computer c, image i |
| SET |
| c.currentimageid = $imageid, |
| c.imagerevisionid= $imagerevisionid |
| WHERE |
| c.id = $computerid |
| "; |
| |
| if (defined($nextimagid) && ($nextimagid)) { |
| $update_statement = " |
| UPDATE |
| computer c, image i |
| SET |
| c.currentimageid = $imageid, |
| c.nextimageid = $imageid, |
| c.imagerevisionid= $imagerevisionid |
| WHERE |
| c.id = $computerid |
| "; |
| } ## end if (defined($nextimagid) && ($nextimagid... |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| # Update successful, return timestamp |
| notify($ERRORS{'OK'}, 0, "updated currentimageid and imagerevision id for computer id $computerid"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to update database, computerid $computerid currentimageid $imageid"); |
| return 0; |
| } |
| } ## end sub update_currentimage |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 is_inblockrequest |
| |
| Parameters : Updates currentimage on a node |
| Returns : 1 if successful, 0 otherwise |
| Description : checks blockComputers table for supplied computerid |
| |
| =cut |
| |
| |
| sub is_inblockrequest { |
| my ($computerid) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameters |
| if (!(defined($computerid))) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID was not specified"); |
| return (); |
| } |
| # Construct the select statement |
| my $select_statement = " |
| SELECT |
| b.blockRequestid,c.blockTimeid |
| FROM blockTimes b, blockComputers c |
| WHERE |
| c.blockTimeid=b.id AND c.computerid = $computerid |
| "; |
| |
| # 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 on what we return |
| if (scalar @selected_rows == 0) { |
| notify($ERRORS{'OK'}, 0, "zero rows were returned from database select"); |
| return 0; |
| } |
| elsif (scalar @selected_rows => 1) { |
| notify($ERRORS{'OK'}, 0, "" . scalar @selected_rows . " rows were returned from database select"); |
| return 1; |
| } |
| } ## end sub is_inblockrequest |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_lastcheckin |
| |
| Parameters : $management_node_id |
| Returns : 0 or 1 |
| Description : Updates lastcheckin for a management node |
| |
| =cut |
| |
| |
| sub update_lastcheckin { |
| my ($management_node_id) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($management_node_id))) { |
| notify($ERRORS{'WARNING'}, 0, "management node ID was not specified"); |
| return (); |
| } |
| |
| # Get current timestamp |
| my $timestamp = makedatestring(); |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| managementnode |
| SET |
| lastcheckin = \'$timestamp\' |
| WHERE |
| id = $management_node_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| # Update successful, return timestamp |
| return $timestamp; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to update database, management node id $management_node_id"); |
| return 0; |
| } |
| } ## end sub update_lastcheckin |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_computer_ipaddress |
| |
| Parameters : $computer_id, $IPaddress |
| Returns : 0 failed or 1 success |
| Description : Updates computer's ipaddress - used in dynamic dhcp setup |
| |
| =cut |
| |
| |
| sub update_computer_address { |
| my ($computer_id, $IPaddress) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($computer_id))) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID was not specified"); |
| return (); |
| } |
| # Check the passed parameter |
| if (!(defined($IPaddress))) { |
| notify($ERRORS{'WARNING'}, 0, "IPaddress was not specified"); |
| return (); |
| } |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| computer |
| SET |
| IPaddress = \'$IPaddress\' |
| WHERE |
| id = $computer_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| # Update successful, return timestamp |
| notify($ERRORS{'OK'}, $LOGFILE, "computer $computer_id IP address $IPaddress updated in database"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to update database, computer IPaddress $computer_id,$IPaddress"); |
| return 0; |
| } |
| } ## end sub update_computer_address |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_request_end |
| |
| Parameters : $request_id |
| Returns : Scalar containing end value for given request ID |
| Description : |
| |
| =cut |
| |
| sub get_request_end { |
| 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 (); |
| } |
| |
| # Create the select statement |
| my $select_statement = " |
| SELECT |
| request.end AS end |
| FROM |
| request |
| WHERE |
| request.id = $request_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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select"); |
| return (); |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select"); |
| return (); |
| } |
| |
| # Get the single returned row |
| # It contains a hash |
| my $end; |
| |
| # Make sure we return undef if the column wasn't found |
| if (defined $selected_rows[0]{end}) { |
| $end = $selected_rows[0]{end}; |
| return $end; |
| } |
| else { |
| return undef; |
| } |
| } ## end sub get_request_end |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_request_by_computerid |
| |
| Parameters : $computer_id |
| Returns : hash containing values of assigned request/reservation |
| Description : |
| |
| =cut |
| |
| |
| sub get_request_by_computerid { |
| my ($computer_id) = @_; |
| |
| # Check the passed parameter |
| if (!defined($computer_id)) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID argument was not specified"); |
| return |
| } |
| |
| # Create the select statement |
| my $select_statement = <<EOF; |
| SELECT DISTINCT |
| request.id AS request_id, |
| reservation.id AS reservation_id |
| |
| FROM |
| request, |
| reservation |
| |
| WHERE |
| request.id = reservation.requestid |
| AND reservation.computerid = $computer_id |
| |
| ORDER BY |
| reservation.id |
| EOF |
| |
| # 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) { |
| notify($ERRORS{'OK'}, 0, "$computer_id is not assigned to any reservations"); |
| return (); |
| } |
| |
| my $computer_request_info; |
| |
| # It contains a hash |
| for my $row (@selected_rows) { |
| my $request_id = $row->{request_id}; |
| my $reservation_id = $row->{reservation_id}; |
| |
| my %request_info = get_request_info($request_id); |
| if (!%request_info) { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to retrieve request info, request ID: $request_id"); |
| return; |
| } |
| |
| my $data_structure; |
| eval {$data_structure = new VCL::DataStructure({request_data => \%request_info, reservation_id => $reservation_id});}; |
| if (my $exception = Exception::Class::Base->caught()) { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to create DataStructure object" . $exception->message); |
| return; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved info and DataStructure object for $request_id:$reservation_id"); |
| $computer_request_info->{$request_id}{data} = $data_structure; |
| } |
| |
| return $computer_request_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_computer_current_state_name |
| |
| Parameters : $computer_id |
| Returns : String containing state name for a particular computer |
| Description : |
| |
| =cut |
| |
| |
| sub get_computer_current_state_name { |
| my ($computer_id) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($computer_id))) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID was not specified"); |
| return (); |
| } |
| |
| # Create the select statement |
| my $select_statement = " |
| SELECT DISTINCT |
| state.name AS name |
| FROM |
| state, |
| computer |
| WHERE |
| computer.stateid = state.id |
| AND computer.id = $computer_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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select"); |
| return (); |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select"); |
| return (); |
| } |
| |
| # Make sure we return undef if the column wasn't found |
| if (defined $selected_rows[0]{name}) { |
| return $selected_rows[0]{name}; |
| } |
| else { |
| return undef; |
| } |
| } ## end sub get_computer_current_state_name |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 construct_image_name |
| |
| Parameters : $image_name, $specified_version |
| Returns : String containing a new image name. |
| If no number is specified, version is incremented by 1. |
| If number is specified, version is set to that number |
| Description : |
| |
| =cut |
| |
| |
| sub construct_image_name { |
| my ($image_name, $specified_version, $os_name) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($image_name))) { |
| notify($ERRORS{'WARNING'}, 0, "image name was not specified"); |
| return 0; |
| } |
| |
| # If version was specified, check to make sure it's just numerical digits |
| if (defined($specified_version) && $specified_version !~ /^\d+$/) { |
| notify($ERRORS{'WARNING'}, 0, "specified version is not in the correct format: $specified_version"); |
| return 0; |
| } |
| |
| # Image Name Format: osname-longnameid-v# |
| # Example: winxp-Thisistheimagename23-v0 |
| |
| # Split the image name by dashes |
| my @name_sections = split(/-/, $image_name); |
| my $section_count = scalar @name_sections; |
| |
| # Check to make sure at least 3 sections were found (separated by "-") |
| if ($section_count < 3) { |
| notify($ERRORS{'WARNING'}, 0, "image name is in the wrong format, cannot construct: $image_name"); |
| return 0; |
| } |
| |
| # The OS should be the first section |
| my $os_section = $name_sections[0]; |
| |
| # If an OS name was passed as an argument use it, otherwise use the OS name from the previous image name |
| if (defined $os_name && $os_name ne '') { |
| $os_section = $os_name; |
| } |
| |
| # The version should be the last section |
| my $version_section = $name_sections[$section_count - 1]; |
| |
| # Everything in between should be the image name and version |
| my $name_section = join('-', @name_sections[1 .. ($section_count - 2)]); |
| |
| # Check to make sure the version number is valid |
| if ($version_section =~ /^([v|V])([0-9]+)/) { |
| my $v = $1; |
| my $version_number = $2; |
| |
| # Increment version number or use the specified version if it was passed |
| if (defined($specified_version)) { |
| $version_number = $specified_version; |
| } |
| else { |
| $version_number++; |
| } |
| |
| return $os_section . "-" . "$name_section" . "-" . $v . $version_number; |
| } ## end if ($version_section =~ /^([v|V])([0-9]+)/) |
| |
| else { |
| notify($ERRORS{'WARNING'}, 0, "could not detect version number from image name: $image_name"); |
| return 0; |
| } |
| |
| } ## end sub construct_image_name |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_log_ending |
| |
| Parameters : $log_id, $ending |
| Returns : 0 or 1 |
| Description : Updates the finalend and ending fields |
| in the log table for the specified log ID |
| |
| =cut |
| |
| sub update_log_ending { |
| my ($log_id, $ending) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($log_id))) { |
| notify($ERRORS{'WARNING'}, 0, "$0: log ID was not specified"); |
| return (); |
| } |
| |
| # Check the passed parameter |
| if (!(defined($ending))) { |
| notify($ERRORS{'WARNING'}, 0, "$0: ending string was not specified"); |
| return (); |
| } |
| |
| my $datestring = makedatestring(); |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| log |
| SET |
| finalend = \'$datestring\', |
| ending = \'$ending\' |
| WHERE |
| id = $log_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update database, log id $log_id"); |
| return 0; |
| } |
| } ## end sub update_log_ending |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_reservation_lastcheck |
| |
| Parameters : Updates the finalend and ending fields in the log table for the specified log ID |
| Returns : date string if successful, 0 if failed |
| Description : |
| |
| =cut |
| |
| sub update_reservation_lastcheck { |
| my ($reservation_id) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($reservation_id))) { |
| notify($ERRORS{'WARNING'}, 0, "reservation ID was not specified"); |
| return (); |
| } |
| |
| my $datestring = makedatestring(); |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| reservation |
| SET |
| lastcheck = \'$datestring\' |
| WHERE |
| id = $reservation_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| return $datestring; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update database, reservation id $reservation_id"); |
| return 0; |
| } |
| } ## end sub update_reservation_lastcheck |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_log_loaded_time |
| |
| Parameters : $request_logid |
| Returns : 0 or 1 |
| Description : Updates the finalend and ending fields in the log table for the specified log ID |
| |
| =cut |
| |
| sub update_log_loaded_time { |
| my ($request_logid) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($request_logid))) { |
| notify($ERRORS{'WARNING'}, 0, "request log ID was not specified"); |
| return (); |
| } |
| |
| # Construct the update statement |
| # Use an IF clause to only update log.loaded if it is NULL |
| # It should only be updated once to capture the time the image load was done |
| my $update_statement = " |
| UPDATE |
| log |
| SET |
| log.loaded = IF(log.loaded IS NULL, NOW(), log.loaded) |
| WHERE |
| id = $request_logid |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update database, request log ID $request_logid"); |
| return 0; |
| } |
| } ## end sub update_log_loaded_time |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_image_name |
| |
| Parameters : $image_id, $imagerevision_id, $new_image_name |
| Returns : boolean |
| Description : Updates the image.name and imagerevision.imagename values in the |
| database. |
| |
| =cut |
| |
| sub update_image_name { |
| my ($image_id, $imagerevision_id, $new_image_name) = @_; |
| |
| # Check the passed parameter |
| unless (defined($image_id) && defined($imagerevision_id) && defined($new_image_name)) { |
| notify($ERRORS{'WARNING'}, 0, "image ID, imagerevision ID, and new image name arguments were not specified"); |
| return; |
| } |
| |
| # Construct the update statement |
| my $update_statement = <<EOF; |
| UPDATE |
| image, |
| imagerevision |
| SET |
| image.name = \'$new_image_name\', |
| imagerevision.imagename = \'$new_image_name\' |
| WHERE |
| image.id = $image_id AND |
| imagerevision.id = $imagerevision_id |
| EOF |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "updated image.name and imagerevision.imagename in database to '$new_image_name' for image ID: $image_id, imagerevision ID: $imagerevision_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to update image.name and imagerevision.imagename in database to '$new_image_name' for image ID: $image_id, imagerevision ID: $imagerevision_id"); |
| return 0; |
| } |
| } ## end sub update_image_name |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_image_type |
| |
| Parameters : $image_id, $image_type |
| Returns : boolean |
| Description : Updates the image.imagetypeid value in the database. The |
| $image_type argument may either be an imagetype ID or name. |
| |
| =cut |
| |
| sub update_image_type { |
| my ($image_id, $image_type) = @_; |
| |
| # Check the passed parameter |
| unless (defined($image_id) && defined($image_type)) { |
| notify($ERRORS{'WARNING'}, 0, "image ID and image type arguments were not specified"); |
| return; |
| } |
| |
| # Construct the update statement |
| my $update_statement = <<EOF; |
| UPDATE |
| image |
| SET |
| EOF |
| |
| # Check if the $image_type argument is an integer (imagetype.id) or string (imagetype.name) |
| if ($image_type =~ /^\d+$/) { |
| $update_statement .= "image.imagetypeid = \'$image_type\'"; |
| } |
| else { |
| $update_statement .= "image.imagetypeid = (SELECT imagetype.id FROM imagetype WHERE imagetype.name = \'$image_type\')"; |
| } |
| |
| $update_statement .= <<EOF; |
| WHERE |
| image.id = $image_id |
| EOF |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| notify($ERRORS{'DEBUG'}, 0, "updated image type in database to $image_type for image ID: $image_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to update image type in database to $image_type for image ID: $image_id"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 delete_computerloadlog_reservation |
| |
| Parameters : $reservation_id, optional loadstatename |
| Returns : 0 failed or 1 success |
| Description : Deletes rows from the computerloadlog table. A loadstatename |
| argument can be specified to limit the rows removed to a |
| certain loadstatename. To delete all rows except those |
| matching a certain loadstatename, begin the loadstatename |
| with a !. |
| |
| =cut |
| |
| |
| sub delete_computerloadlog_reservation { |
| my ($reservation_id, $loadstatename) = @_; |
| |
| # Check the passed parameter |
| if (!(defined($reservation_id))) { |
| notify($ERRORS{'WARNING'}, 0, "reservation ID was not specified"); |
| return (); |
| } |
| |
| # Construct the SQL statement |
| my $sql_statement; |
| # Check if loadstateid was specified |
| # If so, only delete rows matching the loadstateid |
| if ($loadstatename && $loadstatename !~ /^!/) { |
| notify($ERRORS{'DEBUG'}, 0, "removing computerloadlog entries matching loadstate = $loadstatename"); |
| |
| $sql_statement = " |
| DELETE |
| computerloadlog |
| FROM |
| computerloadlog, |
| computerloadstate |
| WHERE |
| computerloadlog.reservationid = $reservation_id |
| AND computerloadlog.loadstateid = computerloadstate.id |
| AND computerloadstate.loadstatename = \'$loadstatename\' |
| "; |
| } |
| elsif ($loadstatename) { |
| # Remove the first character of loadstatename, it is ! |
| $loadstatename = substr($loadstatename, 1); |
| notify($ERRORS{'DEBUG'}, 0, "removing computerloadlog entries NOT matching loadstate = $loadstatename"); |
| |
| $sql_statement = " |
| DELETE |
| computerloadlog |
| FROM |
| computerloadlog, |
| computerloadstate |
| WHERE |
| computerloadlog.reservationid = $reservation_id |
| AND computerloadlog.loadstateid = computerloadstate.id |
| AND computerloadstate.loadstatename != \'$loadstatename\' |
| "; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "removing all computerloadlog entries for reservation"); |
| |
| $loadstatename = 'all'; |
| $sql_statement = " |
| DELETE |
| computerloadlog |
| FROM |
| computerloadlog |
| WHERE |
| computerloadlog.reservationid = $reservation_id |
| "; |
| } |
| |
| # Call the database execute subroutine |
| if (database_execute($sql_statement)) { |
| notify($ERRORS{'OK'}, 0, "deleted rows from computerloadlog for reservation id=$reservation_id"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to delete from computerloadlog table for reservation id=$reservation_id"); |
| return 0; |
| } |
| } ## end sub delete_computerloadlog_reservation |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 delete_request |
| |
| Parameters : $request_id |
| Returns : 0 or 1 |
| Description : Deletes request and all associated reservations for a given request |
| ID. This also deletes all computerloadlog rows associated with any |
| of the reservations. |
| |
| =cut |
| |
| sub delete_request { |
| 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; |
| } |
| |
| # Construct the SQL statement |
| my $sql_computerloadlog_delete = " |
| DELETE |
| computerloadlog.* |
| FROM |
| request, |
| reservation, |
| computerloadlog |
| WHERE |
| request.id = $request_id |
| AND reservation.requestid = request.id |
| AND computerloadlog.reservationid = reservation.id |
| "; |
| |
| # Construct the SQL statement |
| my $sql_request_delete = " |
| DELETE |
| request.*, |
| reservation.* |
| FROM |
| request, |
| reservation |
| WHERE |
| request.id = $request_id |
| AND reservation.requestid = request.id |
| "; |
| |
| # Try to delete any associated entries in the computerloadlog table |
| # There may not be any entries, but database_execute should still return 1 |
| if (!database_execute($sql_computerloadlog_delete)) { |
| notify($ERRORS{'WARNING'}, 0, "unable to delete from computerloadlog table where request id=$request_id"); |
| } |
| |
| # Try to delete any associated entries in the request and reservation tables |
| if (database_execute($sql_request_delete)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to delete from request and reservation tables where request id=$request_id"); |
| return 0; |
| } |
| } ## end sub delete_request |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_blockrequest_processing |
| |
| Parameters : $blockrequest_id, $processing |
| Returns : 0 or 1 |
| Description : Updates the processing flag in the blockRequest table |
| |
| =cut |
| |
| sub update_blockrequest_processing { |
| my ($blockrequest_id, $processing) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the arguments |
| if (!defined($blockrequest_id)) { |
| notify($ERRORS{'WARNING'}, 0, "blockrequest ID was not specified"); |
| return 0; |
| } |
| if (!defined($processing)) { |
| notify($ERRORS{'WARNING'}, 0, "processing was not specified"); |
| return 0; |
| } |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| blockRequest |
| SET |
| blockRequest.processing = $processing |
| WHERE |
| blockRequest.id = $blockrequest_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update blockRequest table, id=$blockrequest_id, processing=$processing"); |
| return 0; |
| } |
| } ## end sub update_blockrequest_processing |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 clearfromblockrequest |
| |
| Parameters : $computer_id |
| Returns : 0 or 1 |
| Description : removes provided computerid and blcok request id from blockcomputer table |
| |
| =cut |
| |
| |
| sub clearfromblockrequest { |
| my ($computer_id) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| # Check the passed parameter |
| if (!(defined($computer_id))) { |
| notify($ERRORS{'WARNING'}, 0, "computer_id was not specified"); |
| return 0; |
| } |
| |
| # Construct the SQL statement |
| my $sql_statement = " |
| DELETE |
| blockComputers |
| FROM |
| blockComputers |
| WHERE |
| computerid = $computer_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($sql_statement)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to delete from computerloadlog table where computerid = $computer_id"); |
| return 0; |
| } |
| } ## end sub clearfromblockrequest |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_sublog_ipaddress |
| |
| Parameters : $computer_id |
| Returns : 0 or 1 |
| Description : updates log table with IPaddress of node |
| when dynamic dhcp is enabled there is no way to track which IP was used |
| =cut |
| |
| |
| sub update_sublog_ipaddress { |
| my ($logid, $computer_ip_address) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| # Check the passed parameter |
| if (!(defined($computer_ip_address))) { |
| notify($ERRORS{'WARNING'}, 0, "computer_ip_address was not specified"); |
| return 0; |
| } |
| if (!(defined($logid))) { |
| notify($ERRORS{'WARNING'}, 0, "logid was not specified"); |
| return 0; |
| } |
| |
| # Construct the SQL statement |
| my $sql_statement = "UPDATE sublog SET IPaddress = \'$computer_ip_address\' WHERE logid=$logid"; |
| |
| # Call the database execute subroutine |
| if (database_execute($sql_statement)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update sublog table logid = $logid with ipaddress $computer_ip_address"); |
| return 0; |
| } |
| } ## end sub update_sublog_ipaddress |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 set_hash_process_id |
| |
| Parameters : Reference to a hash |
| Returns : 0 or 1 |
| Description : Sets the process ID of the current process and parent process ID |
| in a hash, to which a reference was passed. |
| $hash{PID} = process ID |
| $hash{PPID} = parent process ID |
| |
| =cut |
| |
| sub set_hash_process_id { |
| my ($hash_ref) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!(defined($hash_ref))) { |
| notify($ERRORS{'WARNING'}, 0, "hash reference was not specified"); |
| return 0; |
| } |
| |
| # Make sure it's a hash reference |
| if (ref($hash_ref) ne "HASH") { |
| notify($ERRORS{'WARNING'}, 0, "passed parameter is not a hash reference"); |
| return 0; |
| } |
| |
| # Get the parent PID and this process's PID |
| # getppid() doesn't work under Windows so just set it to 0 |
| my $ppid = 0; |
| $ppid = getppid() if ($^O !~ /win/i); |
| $hash_ref->{PPID} = $ppid; |
| my $pid = $$; |
| $hash_ref->{PID} = $pid; |
| |
| return 1; |
| } ## end sub set_hash_process_id |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 rename_vcld_process |
| |
| Parameters : hash - Reference to hash containing request data |
| Returns : 0 or 1 |
| Description : Renames running process based on request information. Appends the state |
| name, request ID, and reservation ID to the process name. |
| Sets PARENTIMAGE and SUBIMAGE in the hash depending on whether or |
| reservation ID is the lowest for a request. |
| |
| =cut |
| |
| sub rename_vcld_process { |
| my ($input_data, $process_name) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| $filename =~ s/.*\///; |
| |
| # IMPORTANT: if you change the process name, check the checkonprocess subroutine |
| # It looks for running reservation processes based on the process name |
| |
| # Check the argument |
| my $data_structure; |
| if (defined($input_data) && (ref $input_data) =~ /HASH/) { |
| # Get a new data structure object |
| eval { |
| $data_structure = new VCL::DataStructure({request_data => $input_data, reservation_id => $input_data->{RESERVATIONID}}); |
| notify($ERRORS{'DEBUG'}, 0, "created DataStructure object from passed hash"); |
| }; |
| if (my $e = Exception::Class::Base->caught()) { |
| notify($ERRORS{'WARNING'}, 0, "hash was passed but could not be turned into a DataStructure, " . $e->message); |
| $data_structure = undef; |
| } |
| } ## end if (defined($input_data) && (ref $input_data... |
| elsif (defined($input_data) && (ref $input_data) !~ /DataStructure/) { |
| notify($ERRORS{'WARNING'}, 0, "passed parameter (" . ref($input_data) . ") is not a reference to a hash or DataStructure, it will be ignored"); |
| $data_structure = undef; |
| } |
| else { |
| $data_structure = $input_data; |
| } |
| |
| # Begin assembling a new process name |
| my $new_process_name = "$PROCESSNAME"; |
| |
| # Check if DataStructure, assemble process name with additional information |
| if (defined $data_structure) { |
| my $state_name = $data_structure->get_state_name(); |
| |
| if ($state_name ne 'blockrequest') { |
| my $request_id = $data_structure->get_request_id(); |
| my $reservation_id = $data_structure->get_reservation_id(); |
| my $request_state_name = $data_structure->get_request_state_name(); |
| my $computer_short_name = $data_structure->get_computer_short_name(); |
| my $vmhost_hostname = $data_structure->get_vmhost_hostname(0); |
| my $image_name = $data_structure->get_image_name(); |
| my $user_login_id = $data_structure->get_user_login_id(); |
| my $request_forimaging = $data_structure->get_request_forimaging(); |
| my $reservation_count = $data_structure->get_reservation_count(); |
| my $reservation_is_parent = $data_structure->is_parent_reservation(); |
| |
| # Append the request and reservation IDs if they are set |
| $new_process_name .= " $request_id:$reservation_id"; |
| $new_process_name .= " $request_state_name" if ($request_state_name); |
| $new_process_name .= " $computer_short_name" if ($computer_short_name); |
| $new_process_name .= ">$vmhost_hostname" if ($vmhost_hostname); |
| $new_process_name .= " $image_name" if ($image_name); |
| $new_process_name .= " $user_login_id" if ($user_login_id); |
| $new_process_name .= " (imaging)" if $request_forimaging; |
| |
| # Append cluster if there are multiple reservations for this request |
| notify($ERRORS{'DEBUG'}, 0, "reservation count: $reservation_count"); |
| |
| if ($reservation_count > 1) { |
| if ($reservation_is_parent) { |
| $data_structure->get_request_data->{PARENTIMAGE} = 1; |
| $data_structure->get_request_data->{SUBIMAGE} = 0; |
| $new_process_name .= " (cluster=parent)"; |
| } |
| else { |
| $data_structure->get_request_data->{PARENTIMAGE} = 0; |
| $data_structure->get_request_data->{SUBIMAGE} = 1; |
| $new_process_name .= " (cluster=child)"; |
| } |
| } ## end if ($reservation_count > 1) |
| else { |
| $data_structure->get_request_data->{PARENTIMAGE} = 1; |
| $data_structure->get_request_data->{SUBIMAGE} = 0; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "PARENTIMAGE: " . $data_structure->get_request_data->{PARENTIMAGE}); |
| notify($ERRORS{'DEBUG'}, 0, "SUBIMAGE: " . $data_structure->get_request_data->{SUBIMAGE}); |
| } ## end if ($state_name ne 'blockrequest') |
| else { |
| my $blockrequest_id = $data_structure->get_blockrequest_id(); |
| my $blockrequest_name = $data_structure->get_blockrequest_name(); |
| my $blocktime_id = $data_structure->get_blocktime_id(); |
| |
| # Append the IDs if they are set |
| $new_process_name .= " $blockrequest_id:$blocktime_id"; |
| $new_process_name .= " '$blockrequest_name'"; |
| } |
| } ## end if (defined $data_structure) |
| else { |
| #notify($ERRORS{'DEBUG'}, 0, "DataStructure object is NOT defined"); |
| } |
| |
| # Rename this process |
| $0 = $new_process_name; |
| notify($ERRORS{'OK'}, 0, "renamed process to \'$0\'"); |
| } ## end sub rename_vcld_process |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 round |
| |
| Parameters : number |
| Returns : rounded number |
| Description : rounds to the nearest whole number |
| |
| =cut |
| |
| |
| sub round { |
| my ($number) = @_; |
| if ($number >= 0) { |
| return int($number + .5); |
| } |
| else { |
| return int($number - .5); |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 set_logfile_path |
| |
| Parameters : file path |
| Returns : 0 or 1 |
| Description : This subroutine is for testing purposes. It sets vcld's logfile |
| path to the parameter passed. It is useful when running automated |
| tests to isoloate logfile output. |
| =cut |
| |
| sub set_logfile_path { |
| my ($package, $filename, $line, $sub) = caller(0); |
| ($LOGFILE) = @_; |
| #print STDOUT "log file path changed to \'$LOGFILE\'\n"; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_highest_imagerevision_info |
| |
| Parameters : $image_id |
| Returns : Hash containing image revision data |
| Description : |
| |
| =cut |
| |
| |
| sub get_highest_imagerevision_info { |
| my ($image_id) = @_; |
| |
| # Check the passed parameter |
| if (!defined($image_id)) { |
| notify($ERRORS{'WARNING'}, 0, "image ID argument was not specified"); |
| return; |
| } |
| |
| # Select the highest image revision id for the specified image id |
| my $select_statement = <<EOF; |
| SELECT |
| MAX(imagerevision.id) AS id |
| FROM |
| imagerevision |
| WHERE |
| imagerevision.imageid = '$image_id' |
| EOF |
| |
| # 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 (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "image revision data for image ID $image_id was not found in the database, 0 rows were returned"); |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select statement:\n$select_statement"); |
| return (); |
| } |
| |
| # A single row was returned |
| my $row = $selected_rows[0]; |
| my $imagerevision_id = $row->{id}; |
| |
| return get_imagerevision_info($imagerevision_id); |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 switch_state |
| |
| Parameters : $request_data, $request_state_name_new, $computer_state_name_new, $request_log_ending, $exit |
| Returns : 0 if something goes wrong, exits if successful |
| Description : Changes the state of this request to the state specified |
| terminates. The vcld process will then pick up the switched |
| request. It is important that this process sets the request |
| laststate to the original state and that vcld does not alter it. |
| |
| =cut |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| sub switch_state { |
| my ($request_data, $request_state_name_new, $computer_state_name_new, $request_log_ending, $exit) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| my ($caller_package, $caller_filename, $caller_line, $caller_sub) = caller(1); |
| |
| my $caller_info = "$caller_sub($line)"; |
| |
| my $caller = scalar caller; |
| notify($ERRORS{'OK'}, 0, "called from $caller_info"); |
| |
| # Check the arguments |
| if (!defined($request_data)) { |
| notify($ERRORS{'CRITICAL'}, 0, "request data hash reference is undefined"); |
| return 0; |
| } |
| elsif (!ref($request_data) eq "HASH") { |
| notify($ERRORS{'CRITICAL'}, 0, "1st argument is not a hash reference to the request data"); |
| return 0; |
| } |
| |
| # Set the default value for exit |
| $exit = 0 if (!defined($exit)); |
| |
| # Store some hash variables into local variables |
| my $request_id = $request_data->{id}; |
| my $request_logid = $request_data->{logid}; |
| my $reservation_id = $request_data->{RESERVATIONID}; |
| my $request_state_name_old = $request_data->{state}{name}; |
| my $request_laststate_name_old = $request_data->{laststate}{name}; |
| my $computer_id = $request_data->{reservation}{$reservation_id}{computer}{id}; |
| my $computer_type = $request_data->{reservation}{$reservation_id}{computer}{type}; |
| my $computer_state_name_old = $request_data->{reservation}{$reservation_id}{computer}{state}{name}; |
| my $computer_shortname = $request_data->{reservation}{$reservation_id}{computer}{SHORTNAME}; |
| |
| if($request_state_name_old eq 'reload'){ |
| $request_logid = 0; |
| } |
| |
| # Figure out if this is the parent reservation |
| my @reservation_ids = sort keys %{$request_data->{reservation}}; |
| # The parent reservation has the lowest ID |
| my $parent_reservation_id = min @reservation_ids; |
| my $is_parent_reservation; |
| if ($reservation_id == $parent_reservation_id) { |
| notify($ERRORS{'DEBUG'}, 0, "parent: parent reservation ID for this request: $parent_reservation_id"); |
| $is_parent_reservation = 1; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "child: parent reservation ID for this request: $parent_reservation_id"); |
| $is_parent_reservation = 0; |
| } |
| |
| # Don't set request state to failed if previous state is image or inuse |
| if ($request_state_name_new && $request_state_name_new eq 'failed') { |
| if ($request_state_name_old eq 'image') { |
| notify($ERRORS{'DEBUG'}, 0, "previous request state is $request_state_name_old, not setting request state to $request_state_name_new, setting request state to maintenance"); |
| $request_state_name_new = 'maintenance'; |
| $computer_state_name_new = 'maintenance'; |
| } |
| elsif ($request_state_name_old eq 'inuse') { |
| notify($ERRORS{'DEBUG'}, 0, "previous request state is $request_state_name_old, not setting request state to $request_state_name_new, setting request state back to $request_state_name_old"); |
| $request_state_name_new = 'inuse'; |
| $computer_state_name_new = 'inuse'; |
| } |
| } |
| |
| # Check if new request state was passed |
| if (!$request_state_name_new) { |
| notify($ERRORS{'DEBUG'}, 0, "request state was not specified, state not changed"); |
| } |
| elsif (!$is_parent_reservation) { |
| notify($ERRORS{'DEBUG'}, 0, "child reservation, request state not changed"); |
| } |
| else { |
| # Add an entry to the loadlog |
| insertloadlog($reservation_id, $computer_id, "info", "$caller: switching request state to $request_state_name_new"); |
| |
| # Update the request state to $request_state_name_new and set laststate to current state |
| if (update_request_state($request_id, $request_state_name_new, $request_state_name_old)) { |
| notify($ERRORS{'OK'}, 0, "request state changed: $request_state_name_old->$request_state_name_new, laststate: $request_laststate_name_old->$request_state_name_old"); |
| insertloadlog($reservation_id, $computer_id, "info", "$caller: request state changed to $request_state_name_new, laststate to $request_state_name_old"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "request state could not be changed: $request_state_name_old --> $request_state_name_new, laststate: $request_laststate_name_old->$request_state_name_old"); |
| insertloadlog($reservation_id, $computer_id, "info", "$caller: unable to change request state to $request_state_name_new, laststate to $request_state_name_old"); |
| } |
| } ## end else [ if (!$request_state_name_new) [elsif (!$is_parent_reservation) |
| |
| # Update the computer state |
| if (!$computer_state_name_new) { |
| notify($ERRORS{'DEBUG'}, 0, "computer state not specified, $computer_shortname state not changed"); |
| } |
| else { |
| # Add an entry to the loadlog |
| insertloadlog($reservation_id, $computer_id, "info", "$caller: switching computer state to $computer_state_name_new"); |
| |
| # Update the computer state |
| if (update_computer_state($computer_id, $computer_state_name_new)) { |
| notify($ERRORS{'OK'}, 0, "computer $computer_shortname state changed: $computer_state_name_old->$computer_state_name_new"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to computer $computer_shortname state: $computer_state_name_old->$computer_state_name_new"); |
| } |
| } ## end else [ if (!$computer_state_name_new) |
| |
| # Update log table for this request |
| # Ending can be deleted, released, failed, noack, nologin, timeout, EOR, none |
| if (!$request_log_ending) { |
| notify($ERRORS{'DEBUG'}, 0, "log table id=$request_logid will not be updated"); |
| } |
| elsif (!$is_parent_reservation) { |
| notify($ERRORS{'DEBUG'}, 0, "child reservation, log table id=$request_logid will not be updated"); |
| } |
| elsif (update_log_ending($request_logid, $request_log_ending)) { |
| notify($ERRORS{'OK'}, 0, "log table id=$request_logid, ending set to $request_log_ending"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "unable to set log table id=$request_logid, ending to $request_log_ending"); |
| } |
| |
| # Call exit if the state changed, return otherwise |
| if ($exit) { |
| insertloadlog($reservation_id, $computer_id, "info", "$caller: process exiting"); |
| notify($ERRORS{'OK'}, 0, "process exiting"); |
| exit; |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "returning"); |
| return; |
| } |
| |
| } ## end sub switch_state |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_management_node_blockrequests |
| |
| Parameters : $managementnode_id |
| Returns : hash containing block request info |
| Description : |
| |
| =cut |
| |
| sub get_management_node_blockrequests { |
| my ($managementnode_id) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameter |
| if (!defined($managementnode_id)) { |
| notify($ERRORS{'WARNING'}, 0, "management node ID was not specified"); |
| return; |
| } |
| |
| # Create the select statement |
| my $select_statement = " |
| SELECT |
| blockRequest.id AS blockRequest_id, |
| blockRequest.name AS blockRequest_name, |
| blockRequest.imageid AS blockRequest_imageid, |
| blockRequest.numMachines AS blockRequest_numMachines, |
| blockRequest.groupid AS blockRequest_groupid, |
| blockRequest.repeating AS blockRequest_repeating, |
| blockRequest.ownerid AS blockRequest_ownerid, |
| blockRequest.admingroupid AS blockRequest_admingroupid, |
| blockRequest.managementnodeid AS blockRequest_managementnodeid, |
| blockRequest.expireTime AS blockRequest_expireTime, |
| blockRequest.processing AS blockRequest_processing, |
| blockRequest.status AS blockRequest_status, |
| |
| blockTimes.id AS blockTimes_id, |
| blockTimes.blockRequestid AS blockTimes_blockRequestid, |
| blockTimes.start AS blockTimes_start, |
| blockTimes.end AS blockTimes_end, |
| blockTimes.processed AS blockTimes_processed |
| |
| FROM |
| blockRequest |
| |
| LEFT JOIN |
| blockTimes ON ( |
| blockRequest.id = blockTimes.blockRequestid |
| ) |
| |
| WHERE |
| blockRequest.managementnodeid = $managementnode_id AND |
| blockRequest.status = 'accepted' AND |
| blockTimes.processed = '0' AND |
| (blockTimes.skip = '0' AND blockTimes.start < (NOW() + INTERVAL 360 MINUTE )) OR |
| blockTimes.end < NOW() |
| "; |
| |
| # 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 or more rows were returned |
| if (scalar @selected_rows == 0) { |
| #Lets check to see if we have blockRequests that have expired and don't have any time ids |
| $select_statement = " |
| SELECT |
| blockRequest.id AS blockRequest_id, |
| blockRequest.name AS blockRequest_name, |
| blockRequest.imageid AS blockRequest_imageid, |
| blockRequest.numMachines AS blockRequest_numMachines, |
| blockRequest.groupid AS blockRequest_groupid, |
| blockRequest.repeating AS blockRequest_repeating, |
| blockRequest.ownerid AS blockRequest_ownerid, |
| blockRequest.admingroupid AS blockRequest_admingroupid, |
| blockRequest.managementnodeid AS blockRequest_managementnodeid, |
| blockRequest.expireTime AS blockRequest_expireTime, |
| blockRequest.processing AS blockRequest_processing, |
| blockRequest.status AS blockRequest_status, |
| |
| blockTimes.id AS blockTimes_id, |
| blockTimes.blockRequestid AS blockTimes_blockRequestid, |
| blockTimes.start AS blockTimes_start, |
| blockTimes.end AS blockTimes_end, |
| blockTimes.processed AS blockTimes_processed |
| |
| FROM |
| blockRequest |
| LEFT JOIN |
| blockTimes ON ( |
| blockRequest.id = blockTimes.blockRequestid |
| ) |
| |
| WHERE |
| blockRequest.managementnodeid = $managementnode_id AND |
| blockRequest.status = 'accepted' AND |
| blockRequest.expireTime < NOW() |
| "; |
| |
| @selected_rows = database_select($select_statement); |
| |
| if (scalar @selected_rows == 0) { |
| return 0; |
| } |
| } |
| |
| # Build the hash |
| my %blockrequests; |
| |
| for (@selected_rows) { |
| my %blockrequest_row = %{$_}; |
| |
| # Get the blockRequest id and blockTimes id |
| my $blockrequest_id = $blockrequest_row{blockRequest_id}; |
| my $blocktimes_id = $blockrequest_row{blockTimes_id}; |
| $blocktimes_id = -1 if !$blocktimes_id; |
| |
| # Loop through all the columns returned for the blockrequest |
| foreach my $key (keys %blockrequest_row) { |
| my $value = $blockrequest_row{$key}; |
| |
| # Create another variable by stripping off the column_ part of each key |
| # This variable stores the original (correct) column name |
| (my $original_key = $key) =~ s/^.+_//; |
| |
| $value = '-1' if (!defined($value)); |
| |
| if ($key =~ /blockRequest_/) { |
| $blockrequests{$blockrequest_id}{$original_key} = $value; |
| if($key =~ /_groupid/){ |
| $blockrequests{$blockrequest_id}{groupname} = get_group_name($value); |
| } |
| } |
| elsif ($key =~ /blockTimes_/) { |
| $blockrequests{$blockrequest_id}{blockTimes}{$blocktimes_id}{$original_key} = $value; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unknown key found in SQL data: $key"); |
| } |
| } ## end foreach my $key (keys %blockrequest_row) |
| |
| } ## end for (@selected_rows) |
| |
| return \%blockrequests; |
| |
| } ## end sub get_management_node_blockrequests |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_computers_controlled_by_MN |
| |
| Parameters : $managementnode_id |
| Returns : hash containing computer info |
| Description : |
| |
| =cut |
| |
| sub get_computers_controlled_by_MN { |
| my (%managementnode) = @_; |
| |
| my %info; |
| |
| #set some local variables |
| my $management_node_resourceid = $managementnode{resource_id}; |
| my $management_node_id = $managementnode{id}; |
| my $management_node_hostname = $managementnode{hostname}; |
| |
| # Collect resource group this management node is a member of |
| if($info{managementnode}{resoucegroups} = get_resource_groups($management_node_resourceid)){ |
| notify($ERRORS{'DEBUG'}, $LOGFILE, "retrieved management node resource groups from database"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, $LOGFILE, "unable to retrieve management node resource groups from database"); |
| return 0; |
| } |
| |
| # Collect resource group management node grpcan control |
| foreach my $mresgrp_id (keys %{$info{managementnode}{resoucegroups}} ) { |
| |
| my $grp_id = $info{managementnode}{resoucegroups}{$mresgrp_id}{groupid}; |
| |
| notify($ERRORS{'DEBUG'}, $LOGFILE, "grp_id = $grp_id "); |
| |
| if($info{manageable_resoucegroups}{$mresgrp_id} = get_managable_resource_groups($grp_id)){ |
| notify($ERRORS{'DEBUG'}, $LOGFILE, "retrieved manageable resource groups from database for mresgrp_id= $grp_id groupname= $info{managementnode}{resoucegroups}{$mresgrp_id}{groupname}"); |
| |
| foreach my $id (keys %{ $info{manageable_resoucegroups}{$grp_id} } ) { |
| my $computer_group_id = $info{manageable_resoucegroups}{$grp_id}{$id}{groupid}; |
| if($info{"manageable_computer_grps"}{$id}{"members"} = get_computer_grp_members($computer_group_id) ){ |
| notify($ERRORS{'DEBUG'}, $LOGFILE, "retrieved computers from computer groupname= $info{manageable_resoucegroups}{$grp_id}{$id}{groupname}"); |
| } |
| else{ |
| notify($ERRORS{'DEBUG'}, $LOGFILE, "no computers in computer groupid= $computer_group_id}"); |
| delete $info{manageable_resoucegroups}{$grp_id}{$id}; |
| } |
| } |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, $LOGFILE, "no manageable resource groups associated for resgrp_id= $mresgrp_id groupname= $info{managementnode}{resoucegroups}{$mresgrp_id}{groupname}"); |
| #delete $info{managementnode}{resoucegroups}{$mresgrp_id}; |
| } |
| } |
| |
| #Build master list of computerids |
| my %computer_list; |
| |
| foreach my $computergroup (keys %{ $info{manageable_computer_grps}}){ |
| foreach my $computerid (keys %{ $info{manageable_computer_grps}{$computergroup}{members} }){ |
| if ( !(exists $computer_list{$computerid}) ){ |
| # add to return list |
| $computer_list{$computerid}{"computer_id"}=$computerid; |
| } |
| } |
| } |
| |
| return \%computer_list; |
| |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_resource_groups |
| |
| Parameters : $management_node_resourceid |
| Returns : hash containing list of resource groups id is a part of |
| Description : |
| |
| =cut |
| |
| sub get_resource_groups { |
| my ($resource_id) = @_; |
| |
| if(!defined($resource_id)){ |
| notify($ERRORS{'WARNING'}, $LOGFILE, "resource_id was not supplied"); |
| return 0; |
| } |
| |
| my $select_statement = " |
| SELECT DISTINCT |
| resourcegroupmembers.resourcegroupid AS resource_groupid, |
| resourcegroup.name AS resource_groupname |
| FROM |
| resourcegroupmembers, |
| resourcegroup |
| WHERE |
| resourcegroup.id = resourcegroupmembers.resourcegroupid |
| AND resourceid = $resource_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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select for resource id $resource_id"); |
| return (); |
| } |
| |
| #my %return_hash = %{$selected_rows[0]}; |
| my %return_hash; |
| for (@selected_rows) { |
| my %resgrps = %{$_}; |
| my $resgrpid = $resgrps{resource_groupid}; |
| $return_hash{$resgrpid}{"groupid"} = $resgrpid; |
| $return_hash{$resgrpid}{"groupname"} = $resgrps{resource_groupname}; |
| } |
| |
| return \%return_hash; |
| |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_managable_resource_groups |
| |
| Parameters : $management_node_grp_id |
| Returns : hash containing list of resource groups that can be controlled |
| Description : |
| |
| =cut |
| |
| sub get_managable_resource_groups { |
| my ($managing_resgrp_id) = @_; |
| |
| if(!defined($managing_resgrp_id)){ |
| notify($ERRORS{'WARNING'}, $LOGFILE, "managing_resgrp_id resource_id was not supplied"); |
| return 0; |
| } |
| |
| my $select_statement = " |
| SELECT DISTINCT |
| resourcemap.resourcegroupid2 AS resource_groupid, |
| resourcegroup.name AS resource_groupname |
| FROM |
| resourcemap, |
| resourcegroup, |
| resourcetype |
| WHERE |
| resourcemap.resourcetypeid2 = resourcetype.id |
| AND resourcetype.name = 'computer' |
| AND resourcegroup.id = resourcemap.resourcegroupid2 |
| AND resourcemap.resourcegroupid1 = $managing_resgrp_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) { |
| notify($ERRORS{'DEBUG'}, 0, "zero rows were returned from database select for resource id $managing_resgrp_id"); |
| return (); |
| } |
| my %return_hash; |
| for (@selected_rows) { |
| my %resgrps = %{$_}; |
| my $resgrpid = $resgrps{resource_groupid}; |
| $return_hash{$resgrpid}{"groupid"} = $resgrpid; |
| $return_hash{$resgrpid}{"groupname"} = $resgrps{resource_groupname}; |
| } |
| return \%return_hash; |
| } |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_computer_grp_members |
| |
| Parameters : $computer_grp_id |
| Returns : hash containing list of of computer ids |
| Description : |
| |
| =cut |
| |
| sub get_computer_grp_members { |
| my ($computer_grp_id) = @_; |
| |
| if(!defined($computer_grp_id)){ |
| notify($ERRORS{'WARNING'}, $LOGFILE, "computer_grp_id resource_id was not supplied"); |
| return 0; |
| } |
| |
| my $select_statement = " |
| SELECT DISTINCT |
| resource.subid AS computer_id |
| FROM |
| resourcegroupmembers, |
| resourcetype, |
| resource, |
| computer |
| WHERE |
| resourcegroupmembers.resourceid = resource.id |
| AND resourcetype.id = resource.resourcetypeid |
| AND resourcetype.name = 'computer' |
| AND resourcegroupmembers.resourcegroupid = $computer_grp_id |
| AND computer.deleted != '1' |
| AND computer.id = resource.subid |
| "; |
| |
| # 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) { |
| notify($ERRORS{'DEBUG'}, 0, "zero rows were returned from database select for computer grp id $computer_grp_id"); |
| return (); |
| } |
| my %return_hash; |
| for (@selected_rows) { |
| my %computerids = %{$_}; |
| my $compid = $computerids{computer_id}; |
| $return_hash{$compid}{"computer_id"} = $compid; |
| } |
| return \%return_hash; |
| |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_user_info |
| |
| Parameters : $user_identifier, $affiliation_identifier (optional), $no_cache (optional) |
| Returns : hash reference |
| Description : Retrieves user information from the database. The user identifier |
| argument can either be a user ID or unityid. A hash reference is |
| returned. |
| |
| =cut |
| |
| sub get_user_info { |
| my ($user_identifier, $affiliation_identifier, $no_cache) = @_; |
| |
| if (!defined($user_identifier)) { |
| notify($ERRORS{'WARNING'}, 0, "user identifier argument was not specified"); |
| return; |
| } |
| |
| #return $ENV{user_info}{$user_identifier} if (!$no_cache && $ENV{user_info}{$user_identifier}); |
| |
| # If affiliation identifier argument wasn't supplied, set it to % wildcard |
| $affiliation_identifier = '%' if !$affiliation_identifier; |
| |
| my $select_statement = <<EOF; |
| SELECT DISTINCT |
| user.*, |
| adminlevel.name AS adminlevel_name, |
| affiliation.name AS affiliation_name, |
| affiliation.shibname AS affiliation_shibname, |
| affiliation.dataUpdateText AS affiliation_dataUpdateText, |
| affiliation.sitewwwaddress AS affiliation_sitewwwaddress, |
| affiliation.helpaddress AS affiliation_helpaddress, |
| affiliation.shibonly AS affiliation_shibonly, |
| |
| IMtype.name AS IMtype_name, |
| |
| localauth.passhash AS localauth_passhash, |
| localauth.salt AS localauth_salt, |
| localauth.lastupdated AS localauth_lastupdated, |
| localauth.lockedout AS localauth_lockedout |
| |
| FROM |
| user |
| LEFT JOIN (adminlevel) ON (adminlevel.id = user.adminlevelid) |
| LEFT JOIN (affiliation) ON (affiliation.id = user.affiliationid) |
| LEFT JOIN (IMtype) ON (IMtype.id = user.IMtypeid) |
| LEFT JOIN (localauth) ON (localauth.userid = user.id) |
| WHERE |
| EOF |
| |
| # If the user identifier is all digits match it to user.id |
| # Otherwise, match user.unityid |
| if ($user_identifier =~ /^\d+$/) { |
| $select_statement .= "user.id = $user_identifier"; |
| } |
| else { |
| $select_statement .= "user.unityid LIKE '$user_identifier'"; |
| } |
| |
| # If the affiliation identifier argument was specified add affiliation table clause |
| if (defined($affiliation_identifier)) { |
| if ($affiliation_identifier =~ /^\d+$/) { |
| $select_statement .= "\nAND affiliation.id = $affiliation_identifier"; |
| } |
| else { |
| $select_statement .= "\nAND affiliation.name LIKE '$affiliation_identifier'"; |
| } |
| } |
| |
| # 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 row was returned |
| if (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "user was not found in the database: '$user_identifier'"); |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| my $user_ids; |
| for my $row (@selected_rows) { |
| $user_ids->{$row->{id}} = $row->{unityid} . '@' . $row->{affiliation_name}; |
| } |
| |
| notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select for user: '$user_identifier', affiliation '$affiliation_identifier':\n" . format_data($user_ids) . "\nSQL statement:\n$select_statement"); |
| return; |
| } |
| |
| # Transform the database row into a hash |
| my $row = $selected_rows[0]; |
| my $user_info; |
| |
| # Loop through all the columns returned |
| for my $key (keys %$row) { |
| my $value = $row->{$key}; |
| |
| # Create another variable by stripping off the column_ part of each key |
| # This variable stores the original (correct) column name |
| (my $original_key = $key) =~ s/^.+_//; |
| |
| if ($key =~ /^(.+)_/) { |
| $user_info->{$1}{$original_key} = $value; |
| } |
| else { |
| $user_info->{$original_key} = $value; |
| } |
| } |
| |
| my $user_id = $user_info->{id}; |
| my $user_login_id = $user_info->{unityid}; |
| |
| # Set the user's preferred name to the first name if it isn't defined |
| if (!defined($user_info->{preferredname}) || $user_info->{preferredname} eq '') { |
| $user_info->{preferredname} = $user_info->{firstname}; |
| } |
| |
| # Set the user's IMid to '' if it's NULL |
| if (!defined($user_info->{IMid})) { |
| $user_info->{IMid} = ''; |
| } |
| |
| # Affiliation specific changes |
| # Check if the user's affiliation is listed in the management node's NOT_STANDALONE parameter |
| $user_info->{STANDALONE} = 1; |
| |
| # Set the user's UID to the VCL user ID if it's not configured in the database, set STANDALONE = 1 |
| if (!$user_info->{uid}) { |
| $user_info->{uid} = $user_info->{id}; |
| $user_info->{STANDALONE} = 1; |
| notify($ERRORS{'DEBUG'}, 0, "UID value is not configured for user $user_login_id, setting UID to VCL user ID: $user_login_id, standalone: 1"); |
| } |
| |
| # Fix the unityid if the user's UID is >= 1,000,000 |
| # Remove the domain section if the user's unityid contains @... |
| elsif ($user_info->{uid} >= 1000000) { |
| $user_info->{STANDALONE} = 1; |
| notify($ERRORS{'DEBUG'}, 0, "UID value for user $user_login_id is >= 1000000, standalone: 1"); |
| } |
| |
| # Check if the user's affiliation is listed in the management node's NOT_STANDALONE list |
| elsif (my $not_standalone_list = get_management_node_info()->{NOT_STANDALONE}) { |
| my $user_affiliation_name = $user_info->{affiliation}{name}; |
| if (grep(/^$user_affiliation_name$/i, split(/[,;]/, $not_standalone_list))) { |
| notify($ERRORS{'DEBUG'}, 0, "non-standalone affiliation found for user $user_login_id:\nuser affiliation: $user_affiliation_name\nnot standalone list: $not_standalone_list"); |
| $user_info->{STANDALONE} = 0; |
| } |
| } |
| |
| # If user's unityid is an email address, use only the first part |
| if ($user_login_id =~ /(.+)@/) { |
| my $corrected_unity_id = $1; |
| notify($ERRORS{'DEBUG'}, 0, "user's unityid value contains '\@': $user_login_id, changing to $corrected_unity_id"); |
| $user_info->{unityid} = $corrected_unity_id; |
| } |
| |
| # For test account only |
| if ($user_login_id =~ /vcladmin/) { |
| $user_info->{STANDALONE} = 1; |
| } |
| |
| # Set the user's affiliation sitewwwaddress and help address if not defined or blank |
| if (!$user_info->{affiliation}{sitewwwaddress}) { |
| $user_info->{affiliation}{sitewwwaddress} = 'http://cwiki.apache.org/VCL'; |
| } |
| if (!$user_info->{affiliation}{helpaddress}) { |
| $user_info->{affiliation}{helpaddress} = 'nobody@example.com'; |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved info for user '$user_identifier', affiliation: '$affiliation_identifier':\n" . format_data($user_info)); |
| $ENV{user_info}{$user_identifier} = $user_info; |
| return $ENV{user_info}{$user_identifier}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_user_affiliation |
| |
| Parameters : $user_id |
| Returns : scalar - affiliation name |
| Description : |
| |
| =cut |
| |
| sub get_user_affiliation { |
| my ($user_id) = shift; |
| |
| if(!defined($user_id)){ |
| notify($ERRORS{'WARNING'}, $LOGFILE, "user_id was not supplied"); |
| return 0; |
| } |
| |
| my $select_statement = <<EOF; |
| SELECT DISTINCT |
| affiliation.name |
| FROM |
| user, |
| affiliation |
| WHERE |
| affiliation.id = user.affiliationid AND |
| user.id = $user_id |
| EOF |
| |
| # 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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select"); |
| return (); |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select"); |
| return (); |
| } |
| |
| # Get the single returned row |
| # It contains a hash |
| |
| # Make sure we return undef if the column wasn't found |
| if (defined $selected_rows[0]{name}) { |
| my $name = $selected_rows[0]{name}; |
| return $name; |
| } |
| else { |
| return undef; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_local_user_info |
| |
| Parameters : none |
| Returns : hash reference |
| Description : Retrieves info for all local users and returns a hash reference. |
| The keys of the hash are user IDs. |
| |
| =cut |
| |
| sub get_local_user_info { |
| my $select_statement = <<EOF; |
| SELECT |
| userid |
| FROM |
| localauth |
| EOF |
| |
| # 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) { |
| notify($ERRORS{'OK'}, 0, "local user was not found in the database, 0 rows were returned"); |
| return; |
| } |
| |
| # Transform the array of database rows into a hash |
| my $local_user_info; |
| for my $row (@selected_rows) { |
| my $user_id = $row->{userid}; |
| |
| my $user_info = get_user_info($user_id); |
| $local_user_info->{$user_id} = $user_info; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "retrieved local user info:\n" . format_data($local_user_info)); |
| return $local_user_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_group_name |
| |
| Parameters : $group_id |
| Returns : scalar - group name |
| Description : |
| |
| =cut |
| |
| sub get_group_name { |
| my ($group_id) = @_; |
| |
| |
| if(!defined($group_id)){ |
| notify($ERRORS{'WARNING'}, $LOGFILE, "group_id was not supplied"); |
| return 0; |
| } |
| |
| my $select_statement = <<EOF; |
| SELECT DISTINCT |
| usergroup.name |
| FROM |
| usergroup |
| WHERE |
| usergroup.id = $group_id |
| EOF |
| |
| # 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) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select"); |
| return (); |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "" . scalar @selected_rows . " rows were returned from database select"); |
| return (); |
| } |
| |
| # Get the single returned row |
| # It contains a hash |
| my $end; |
| |
| # Make sure we return undef if the column wasn't found |
| if (defined $selected_rows[0]{name}) { |
| my $groupname = $selected_rows[0]{name}; |
| return $groupname; |
| } |
| else { |
| return undef; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_computer_info |
| |
| Parameters : $computer_identifier, $no_cache (optional) |
| Returns : hash reference |
| Description : |
| |
| =cut |
| |
| sub get_computer_info { |
| my ($computer_identifier, $no_cache) = @_; |
| if (!defined($computer_identifier)){ |
| notify($ERRORS{'WARNING'}, 0, "computer identifier argument was not supplied"); |
| return; |
| } |
| |
| return $ENV{computer_info}{$computer_identifier} if (!$no_cache && $ENV{computer_info}{$computer_identifier}); |
| |
| # Get a hash ref containing the database column names |
| my $database_table_columns = get_database_table_columns(); |
| |
| my @tables = ( |
| 'computer', |
| 'state', |
| 'provisioning', |
| 'module', |
| 'schedule', |
| 'platform', |
| ); |
| |
| # Construct the select statement |
| my $select_statement = "SELECT DISTINCT\n"; |
| |
| # Get the column names for each table and add them to the select statement |
| for my $table (@tables) { |
| my @columns = @{$database_table_columns->{$table}}; |
| for my $column (@columns) { |
| $select_statement .= "$table.$column AS '$table-$column',\n"; |
| } |
| } |
| |
| # Remove the comma after the last column line |
| $select_statement =~ s/,$//; |
| |
| # Complete the select statement |
| $select_statement .= <<EOF; |
| FROM |
| computer |
| |
| LEFT JOIN (state) ON (state.id = computer.stateid) |
| LEFT JOIN (platform) ON (platform.id = computer.platformid) |
| LEFT JOIN ( |
| provisioning, |
| module |
| ) |
| ON ( |
| provisioning.id = computer.provisioningid |
| AND module.id = provisioning.moduleid |
| ) |
| LEFT JOIN (schedule) ON (schedule.id = computer.scheduleid) |
| |
| WHERE |
| computer.deleted != '1' |
| AND |
| EOF |
| |
| # If the computer identifier is all digits match it to computer.id |
| # Otherwise, match computer.hostname |
| if ($computer_identifier =~ /^\d+$/) { |
| $select_statement .= "computer.id = \'$computer_identifier\'"; |
| } |
| else { |
| $select_statement .= "computer.hostname REGEXP '$computer_identifier(\\\\.|\$)'"; |
| } |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @selected_rows == 0) { |
| notify($ERRORS{'WARNING'}, 0, "zero rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| elsif (scalar @selected_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, scalar @selected_rows . " rows were returned from database select statement:\n$select_statement"); |
| return; |
| } |
| |
| # Get the single row returned from the select statement |
| my $row = $selected_rows[0]; |
| |
| # Construct a hash with all of the computer info |
| my $computer_info; |
| |
| # Loop through all the columns returned |
| for my $key (keys %$row) { |
| my $value = $row->{$key}; |
| |
| # Split the table-column names |
| my ($table, $column) = $key =~ /^([^-]+)-(.+)/; |
| |
| # Add the values for the primary table to the hash |
| # Add values for other tables under separate keys |
| if ($table eq $tables[0]) { |
| $computer_info->{$column} = $value; |
| } |
| elsif ($table eq 'module') { |
| $computer_info->{provisioning}{$table}{$column} = $value; |
| } |
| else { |
| $computer_info->{$table}{$column} = $value; |
| } |
| } |
| |
| # Set the short name of the computer based on the hostname |
| my $computer_hostname = $computer_info->{hostname}; |
| my ($computer_shortname) = $computer_hostname =~ /^([^\.]+)/; |
| $computer_info->{SHORTNAME} = $computer_shortname; |
| |
| # Set the NODENAME key based on the type of computer |
| # Use the full hostname if the computer type is lab |
| # Use the short name otherwise |
| my $computer_type = $computer_info->{type}; |
| if ($computer_type eq "lab") { |
| $computer_info->{NODENAME} = $computer_hostname; |
| } |
| else { |
| $computer_info->{NODENAME} = $computer_shortname; |
| } |
| |
| # Get the imagerevision info and add it to the hash |
| my $imagerevision_id = $computer_info->{imagerevisionid}; |
| if ($imagerevision_id && (my $imagerevision_info = get_imagerevision_info($imagerevision_id))) { |
| $computer_info->{currentimagerevision} = $imagerevision_info; |
| $computer_info->{currentimage} = $imagerevision_info->{image}; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "unable to retrieve image revision info for $computer_hostname, imagerevision ID=$imagerevision_id, attempting to retrieve image revision info for the 'noimage' image"); |
| |
| my $imagerevision_info = get_imagerevision_info('noimage'); |
| if ($imagerevision_info) { |
| $computer_info->{currentimagerevision} = $imagerevision_info; |
| $computer_info->{currentimage} = $imagerevision_info->{image}; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve 'noimage' image revision info for $computer_hostname"); |
| } |
| } |
| |
| # |
| my $next_image_id = $computer_info->{nextimageid}; |
| if ($next_image_id && (my $nextimage_info = get_image_info($next_image_id))) { |
| my $next_image_name = $nextimage_info->{name}; |
| #trim trailing white space |
| $next_image_name =~ s/\s+$//; |
| |
| my $next_imagerevision_info = get_imagerevision_info($next_image_name); |
| if ($next_imagerevision_info) { |
| $computer_info->{nextimagerevision} = $next_imagerevision_info; |
| $computer_info->{nextimage} = $next_imagerevision_info->{image}; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve nextimage info for $computer_hostname, nextimageid=$next_image_id next_image_name=$next_image_name"); |
| } |
| |
| } |
| |
| # Check if the computer associated with this reservation has a vmhostid set |
| if (my $vmhost_id = $computer_info->{vmhostid}) { |
| my $vmhost_info = get_vmhost_info($vmhost_id); |
| |
| if ($vmhost_info) { |
| $computer_info->{vmhost} = $vmhost_info; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "vmhostid $vmhost_id is set for $computer_hostname but the vmhost info could not be retrieved"); |
| } |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved info for computer '$computer_identifier':\n" . format_data($computer_info)); |
| $ENV{computer_info}{$computer_identifier} = $computer_info; |
| return $ENV{computer_info}{$computer_identifier}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_computer_ids |
| |
| Parameters : $computer_identifier |
| Returns : array |
| Description : Queries the computer table for computers matching the |
| $computer_identifier argument. The argument may contain either |
| the computer's hostname or IP address. An array containing the |
| computer IDs is returned. |
| |
| =cut |
| |
| sub get_computer_ids { |
| my ($computer_identifier) = @_; |
| |
| if(!defined($computer_identifier)){ |
| notify($ERRORS{'WARNING'}, $LOGFILE, "computer identifier argument was not supplied"); |
| return; |
| } |
| |
| my $select_statement = <<EOF; |
| SELECT |
| * |
| FROM |
| computer |
| WHERE |
| hostname REGEXP '^$computer_identifier(\\\\.|\$)' |
| OR IPaddress = '$computer_identifier' |
| OR privateIPaddress = '$computer_identifier' |
| EOF |
| |
| my @selected_rows = database_select($select_statement); |
| if (!@selected_rows) { |
| notify($ERRORS{'DEBUG'}, 0, "no computers were found matching identifier: $computer_identifier"); |
| return (); |
| } |
| |
| my @computer_ids = map { $_->{id} } @selected_rows; |
| notify($ERRORS{'DEBUG'}, 0, "found computers matching identifier: $computer_identifier, IDs: @computer_ids"); |
| return sort @computer_ids; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 insert_request |
| |
| Parameters : $managementnode_id, $request_state_name, $request_laststate_name, $end_minutes_in_future, $user_unityid, $computer_id, $image_id, $imagerevision_id |
| Returns : 1 if successful, 0 if failed |
| Description : |
| |
| =cut |
| |
| sub insert_request { |
| my ($managementnode_id, $request_state_name, $request_laststate_name, $request_logid, $user_unityid, $computer_id, $image_id, $imagerevision_id, $start_minutes_in_future, $end_minutes_in_future) = @_; |
| |
| |
| if (!$request_state_name) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory request key: state_name"); |
| return 0; |
| } |
| if (!$request_laststate_name) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory request key: laststate_name"); |
| return 0; |
| } |
| if (!$user_unityid) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory request key: user_unityid"); |
| return 0; |
| } |
| |
| if (!$computer_id) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory reservation key: computer_id"); |
| return 0; |
| } |
| if (!$image_id) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory reservation key: image_id"); |
| return 0; |
| } |
| if (!$imagerevision_id) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory reservation key: imagerevision_id"); |
| return 0; |
| } |
| if (!$managementnode_id) { |
| notify($ERRORS{'WARNING'}, 0, "missing mandatory reservation key: managementnode_id"); |
| return 0; |
| } |
| |
| my $insert_request_statment = " |
| INSERT INTO |
| request |
| ( |
| request.stateid, |
| request.laststateid, |
| request.userid, |
| request.logid, |
| request.forimaging, |
| request.test, |
| request.preload, |
| request.start, |
| request.end, |
| request.daterequested |
| ) |
| VALUES |
| ( |
| (SELECT id FROM state WHERE state.name = '$request_state_name'), |
| (SELECT id FROM state WHERE state.name = '$request_laststate_name'), |
| (SELECT id FROM user WHERE user.unityid = '$user_unityid'), |
| '$request_logid', |
| '0', |
| '0', |
| '0', |
| TIMESTAMPADD(MINUTE, $start_minutes_in_future, NOW()), |
| TIMESTAMPADD(MINUTE, $end_minutes_in_future, NOW()), |
| NOW() |
| ) |
| "; |
| |
| # Execute the request insert statement |
| # If successful, the id of the newly inserted row is returned |
| my $request_id = database_execute($insert_request_statment); |
| if ($request_id) { |
| notify($ERRORS{'OK'}, 0, "inserted new reload request into request table, request id=$request_id"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to insert new reload request into request table"); |
| return 0; |
| } |
| |
| my $insert_reservation_statment = " |
| INSERT INTO |
| reservation |
| ( |
| reservation.requestid, |
| reservation.computerid, |
| reservation.imageid, |
| reservation.imagerevisionid, |
| reservation.managementnodeid |
| ) |
| VALUES |
| ( |
| '$request_id', |
| '$computer_id', |
| '$image_id', |
| '$imagerevision_id', |
| '$managementnode_id' |
| ) |
| "; |
| |
| # Execute the reservation insert statement |
| # If successful, the id of the newly inserted row is returned |
| my $reservation_id = database_execute($insert_reservation_statment); |
| if ($reservation_id) { |
| notify($ERRORS{'OK'}, 0, "inserted new reload request into reservation table, reservation id=$reservation_id"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "failed to insert new reload request into reservation table"); |
| return 0; |
| } |
| |
| return ($request_id, $reservation_id); |
| } ## end sub insert_request |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 insert_reload_request |
| |
| Parameters : $request_data_hash_reference |
| Returns : nothing, always calls exit |
| Description : Changes this request to a reload request then |
| terminates. The vcld process will then pick up the reload |
| request. It is important that this process sets the request |
| laststate to 'reclaim' and that vcld does not alter it. |
| |
| =cut |
| |
| sub insert_reload_request { |
| my ($request_data) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| my ($calling_package, $calling_filename, $calling_line, $calling_sub) = caller(0); |
| |
| # Store some hash variables into local variables |
| my $request_id = $request_data->{id}; |
| my $request_state_name = $request_data->{state}{name}; |
| my $request_laststate_name = $request_data->{laststate}{name}; |
| my $reservation_id = $request_data->{RESERVATIONID}; |
| my $computer_id = $request_data->{reservation}{$reservation_id}{computer}{id}; |
| my $computer_type = $request_data->{reservation}{$reservation_id}{computer}{type}; |
| my $managementnode_id = $request_data->{reservation}{$reservation_id}{managementnode}{id}; |
| my $request_logid = $request_data->{logid}; |
| my $user_unityid = $request_data->{user}{unityid}; |
| my $image_id = $request_data->{reservation}{$reservation_id}{image}{id}; |
| my $imagerevision_id = $request_data->{reservation}{$reservation_id}{imagerevision}{id}; |
| |
| # Assemble a consistent prefix for notify messages |
| my $notify_prefix = "req=$request_id:"; |
| |
| # Add an entry to the loadlog |
| if ($computer_type eq "blade") { |
| insertloadlog($reservation_id, $computer_id, "loadimageblade", "$calling_sub: switching request state to reload"); |
| } |
| elsif ($computer_type eq "virtualmachine") { |
| insertloadlog($reservation_id, $computer_id, "loadimagevmware", "$calling_sub: switching request state to reload"); |
| } |
| else { |
| insertloadlog($reservation_id, $computer_id, "info", "$calling_sub: switching request state to reload"); |
| } |
| |
| # Check to make sure computer state is not currently reload or reloading |
| # It's possible for reclaimed cluster reservations to attempt to insert multiple reloads for child reservations |
| # because only the parent can update the request state |
| my $current_computer_state_name = get_computer_current_state_name($computer_id); |
| if ($current_computer_state_name =~ /reload/) { |
| notify($ERRORS{'WARNING'}, 0, "$notify_prefix current computer state is $current_computer_state_name, reload request will not be inserted"); |
| return 0; |
| } |
| |
| # Modify computer state so reload will process |
| if (update_computer_state($computer_id, "reload")) { |
| notify($ERRORS{'OK'}, 0, "$notify_prefix setting computerid $computer_id into reload state"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "$notify_prefix unable to set computerid $computer_id reload state"); |
| } |
| |
| # Attempt to create a new reload request |
| my $request_id_reload; |
| if ($request_id_reload = insert_request($managementnode_id, 'reload', $request_laststate_name, '0', 'vclreload', $computer_id, $image_id, $imagerevision_id, '0', '30')) { |
| notify($ERRORS{'OK'}, 0, "$notify_prefix inserted new reload request, id=$request_id_reload nodeid=$computer_id, imageid=$image_id, imagerevision_id=$imagerevision_id"); |
| insertloadlog($reservation_id, $computer_id, "info", "$calling_sub: created new reload request"); |
| } |
| else { |
| notify($ERRORS{'CRITICAL'}, 0, "$notify_prefix failed to insert new reload request"); |
| return 0; |
| } |
| |
| return 1; |
| } ## end sub insert_reload_request |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 update_cluster_info |
| |
| Parameters :data hash |
| Returns : 0 or 1 |
| Description : |
| |
| =cut |
| |
| sub update_cluster_info { |
| |
| my ($request_data) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| my ($calling_package, $calling_filename, $calling_line, $calling_sub) = caller(0); |
| |
| my $reservation_id = $request_data->{RESERVATIONID}; |
| my $computer_short_name = $request_data->{reservation}{$reservation_id}{computer}{SHORTNAME}; |
| my $image_OS_type = $request_data->{reservation}{$reservation_id}{image}{OS}{type}; |
| my $is_cluster_parent = $request_data->{PARENTIMAGE}; |
| my $is_cluster_child = $request_data->{SUBIMAGE}; |
| |
| my $cluster_info = "/tmp/$computer_short_name.cluster_info"; |
| my @cluster_string = ""; |
| |
| |
| |
| my @reservation_ids = sort keys %{$request_data->{reservation}}; |
| |
| # parent reservation id lowest |
| my $parent_reservation_id = min @reservation_ids; |
| notify($ERRORS{'DEBUG'}, 0, "$computer_short_name is_cluster_parent = $is_cluster_parent "); |
| notify($ERRORS{'DEBUG'}, 0, "$computer_short_name is_cluster_child = $is_cluster_child "); |
| notify($ERRORS{'DEBUG'}, 0, "parent_reservation_id = $parent_reservation_id "); |
| |
| foreach my $rid (keys %{$request_data->{reservation}}) { |
| if ($rid == $parent_reservation_id) { |
| push(@cluster_string, "parent= $request_data->{reservation}{$rid}{computer}{IPaddress}" . "\n"); |
| notify($ERRORS{'DEBUG'}, 0, "writing parent= $request_data->{reservation}{$rid}{computer}{IPaddress}"); |
| } |
| else { |
| push(@cluster_string, "child= $request_data->{reservation}{$rid}{computer}{IPaddress}" . "\n"); |
| notify($ERRORS{'DEBUG'}, 0, "writing child= $request_data->{reservation}{$rid}{computer}{IPaddress}"); |
| } |
| } |
| |
| |
| if (open(CLUSTERFILE, ">$cluster_info")) { |
| print CLUSTERFILE @cluster_string; |
| close(CLUSTERFILE); |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "could not write to $cluster_info"); |
| } |
| |
| my $identity; |
| #scp cluster file to each node |
| my $targetpath; |
| foreach my $rid (keys %{$request_data->{reservation}}) { |
| $identity = $request_data->{reservation}{$rid}{image}{IDENTITY}; |
| my $node_name = $request_data->{reservation}{$rid}{computer}{SHORTNAME}; |
| if ($image_OS_type =~ /linux/i) { |
| $targetpath = "$node_name:/etc/cluster_info"; |
| } |
| elsif ($image_OS_type =~ /windows/i) { |
| $targetpath = "$node_name:C:\/cluster_info"; |
| } |
| else { |
| $targetpath = "$node_name:/etc/cluster_info"; |
| } |
| |
| if (run_scp_command($cluster_info, $targetpath, $identity)) { |
| notify($ERRORS{'OK'}, 0, " successfully copied cluster_info file to $node_name"); |
| } |
| } ## end foreach my $rid (keys %{$request_data->{reservation... |
| |
| unlink $cluster_info; |
| |
| return 1; |
| |
| } ## end sub update_cluster_info |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 format_data |
| |
| Parameters : $data |
| Returns : string |
| Description : Formats the data argument using Data::Dumper. |
| |
| =cut |
| |
| sub format_data { |
| my @data = @_; |
| |
| if (!(@data)) { |
| return '<undefined>'; |
| } |
| |
| # If a string was passed which appears to be XML, convert it to a hash using XML::Simple |
| if (scalar(@data) == 1 && !ref($data[0]) && $data[0] =~ /^</) { |
| my $xml_hashref = xml_string_to_hash($data[0]); |
| return format_data($xml_hashref); |
| } |
| |
| $Data::Dumper::Indent = 1; |
| $Data::Dumper::Purity = 0; |
| $Data::Dumper::Useqq = 1; # Use double quotes for representing string values |
| $Data::Dumper::Terse = 1; |
| $Data::Dumper::Quotekeys = 1; # Quote hash keys |
| $Data::Dumper::Pair = ' => '; # Specifies the separator between hash keys and values |
| $Data::Dumper::Sortkeys = 1; # Hash keys are dumped in sorted order |
| $Data::Dumper::Deparse = 0; |
| |
| my $formatted_string = Dumper(@data); |
| |
| my @formatted_lines = split("\n", $formatted_string); |
| |
| map { $_ = ": $_" } @formatted_lines; |
| |
| return join("\n", @formatted_lines); |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_caller_trace |
| |
| Parameters : $level_limit - number of previous calls to return |
| Returns : multi-line string containing subroutine caller information |
| Description : |
| |
| =cut |
| |
| sub get_caller_trace { |
| my ($level_limit, $brief_output) = @_; |
| $level_limit = 4 if !$level_limit; |
| |
| # Add one to make the argument usage more intuitive |
| # One of the levels is the subroutine which called this |
| $level_limit++; |
| |
| # Check if this subroutine was called from notify |
| my $called_from_notify = ((caller(1))[3] =~ /notify$/)? 1 : 0; |
| |
| my $caller_trace = ""; |
| for (1 .. $level_limit) { |
| my $caller_index = $_; |
| if (caller($caller_index)) { |
| my ($package_last, $filename_last, $line_last, $sub_last) = caller($caller_index - 1); |
| my ($package, $filename, $line, $sub) = caller($caller_index); |
| |
| $filename_last =~ s/.*\///; |
| $sub =~ s/.*:://; |
| |
| if ($called_from_notify) { |
| if ($sub =~ /notify$/) { |
| next; |
| } |
| else { |
| $caller_index--; |
| } |
| } |
| |
| if ($brief_output) { |
| $caller_trace .= (($caller_index - 1) * -1) . ":$filename_last:$sub:$line_last;"; |
| } |
| else { |
| $caller_trace .= "(" . sprintf("% d", (($caller_index - 1) * -1)) . ") $filename_last, $sub (line: $line_last)\n"; |
| } |
| } ## end if (caller($caller_index)) |
| else { |
| last; |
| } |
| } ## end for (1 .. $level_limit) |
| |
| # Remove the trailing semicolon if brief output is used |
| $caller_trace =~ s/;$//; |
| |
| # Chomp the trailing newline if brief output isn't used |
| chomp $caller_trace; |
| |
| return $caller_trace; |
| } ## end sub get_caller_trace |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_management_node_id |
| |
| Parameters : |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub get_management_node_id { |
| my $management_node_id; |
| |
| # Check the management_node_id environment variable |
| if ($ENV{management_node_id}) { |
| notify($ERRORS{'DEBUG'}, 0, "environment variable: $ENV{management_node_id}"); |
| return $ENV{management_node_id}; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "management_node_id environment variable not set"); |
| } |
| |
| # If $management_node_id wasn't set using the env variable, try the subroutine |
| my $management_node_info = get_management_node_info(); |
| if ($management_node_info && ($management_node_id = $management_node_info->{id})) { |
| notify($ERRORS{'DEBUG'}, 0, "get_managementnode_info(): $management_node_id"); |
| $ENV{management_node_id} = $management_node_id; |
| return $management_node_id; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "get_management_node_info() failed"); |
| } |
| |
| notify($ERRORS{'WARNING'}, 0, "management node ID could not be determined"); |
| return 0; |
| } ## end sub get_management_node_id |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| sub get_database_table_columns { |
| return $ENV{database_table_columns} if $ENV{database_table_columns}; |
| |
| my $database = 'information_schema'; |
| |
| my $select_all_table_columns = " |
| SELECT DISTINCT |
| TABLES.TABLE_NAME, |
| COLUMNS.COLUMN_NAME |
| FROM |
| TABLES, |
| COLUMNS |
| WHERE |
| COLUMNS.TABLE_SCHEMA = \'$DATABASE\' |
| AND |
| TABLES.TABLE_NAME = COLUMNS.TABLE_NAME |
| "; |
| |
| # Call the database select subroutine |
| my @rows = database_select($select_all_table_columns, $database); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @rows == 0) { |
| notify($ERRORS{'WARNING'}, 0, "unable to get database table columns, 0 rows were returned from database select"); |
| return 0; |
| } |
| |
| # Use the map function to populate a hash of arrays |
| # The hash keys are the table names |
| # The hash values are arrays of column names |
| my %database_table_columns; |
| map({push @{$database_table_columns{$_->{TABLE_NAME}}}, $_->{COLUMN_NAME}} @rows); |
| |
| $ENV{database_table_columns} = \%database_table_columns; |
| |
| return $ENV{database_table_columns}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| sub switch_vmhost_id { |
| my ($computer_id, $host_id) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| my ($caller_package, $caller_filename, $caller_line, $caller_sub) = caller(1); |
| |
| my $caller_info = "$caller_sub($line)"; |
| |
| my $caller = scalar caller; |
| notify($ERRORS{'OK'}, 0, "called from $caller_info"); |
| |
| # Check the arguments |
| if (!defined($computer_id)) { |
| notify($ERRORS{'CRITICAL'}, 0, "computer_id is undefined"); |
| return 0; |
| } |
| |
| if (!(defined($host_id)) || !$host_id) { |
| notify($ERRORS{'WARNING'}, 0, "$host_id is either not defined or 0, using NULL"); |
| $host_id = 'NULL'; |
| } |
| |
| # Construct the update statement |
| my $update_statement = " |
| UPDATE |
| computer |
| SET |
| vmhostid = $host_id |
| WHERE |
| computer.id = $computer_id |
| "; |
| |
| # Call the database execute subroutine |
| if (database_execute($update_statement)) { |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unable to update computer table, id=$computer_id, vmhostid=$host_id"); |
| return 0; |
| } |
| } ## end sub switch_vmhost_id |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 reservations_ready |
| |
| Parameters : request ID |
| Returns : 1 if all reservations are ready, 0 if any are not ready, undefined if any failed |
| Description : |
| |
| =cut |
| |
| sub reservations_ready { |
| my ($request_id) = @_; |
| |
| # Make sure request ID was passed |
| if (!$request_id) { |
| notify($ERRORS{'WARNING'}, 0, "request ID argument was not passed"); |
| return; |
| } |
| |
| my $select_statement = " |
| SELECT |
| reservation.id AS reservation_id, |
| computerloadstate.loadstatename |
| |
| FROM |
| request, |
| reservation |
| |
| LEFT JOIN |
| computerloadlog |
| ON ( |
| computerloadlog.reservationid = reservation.id |
| ) |
| |
| LEFT JOIN |
| computerloadstate |
| ON ( |
| computerloadstate.id = computerloadlog.loadstateid |
| ) |
| |
| WHERE |
| request.id = $request_id |
| AND reservation.requestid = request.id |
| "; |
| |
| # Call the database select subroutine |
| # This will return an array of one or more rows based on the select statement |
| my @computerloadlog_rows = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @computerloadlog_rows == 0) { |
| notify($ERRORS{'WARNING'}, 0, "reservations associated with request $request_id could not be retrieved from the database, 0 rows were returned"); |
| return; |
| } |
| |
| my %reservation_status; |
| |
| # Loop through the rows, check the loadstate |
| for my $computerloadlog_row (@computerloadlog_rows) { |
| my $reservation_id = $computerloadlog_row->{reservation_id}; |
| my $computerloadstate_name = $computerloadlog_row->{loadstatename}; |
| |
| # Initialize the hash key for the reservation if it isn't defined |
| if (!defined($reservation_status{$reservation_id})) { |
| $reservation_status{$reservation_id} = 'not ready'; |
| } |
| |
| # Skip if loadstatename is undefined, means no computerloadlog rows exist for the reservation |
| if (!defined($computerloadstate_name)) { |
| next; |
| } |
| |
| # Only populate hash keys with loadstatnames we care about |
| # Ignore 'info' and other entries |
| if ($computerloadstate_name =~ /loadimagecomplete|nodeready|failed/i) { |
| |
| # Update the reservation hash key, don't overwrite 'failed' |
| if ($reservation_status{$reservation_id} !~ /failed/) { |
| $reservation_status{$reservation_id} = $computerloadstate_name; |
| } |
| } |
| } ## end for my $computerloadlog_row (@computerloadlog_rows) |
| |
| # Assemble a string of all of the statuses |
| my $status_string = ''; |
| my $failed = 0; |
| my $ready = 1; |
| foreach my $reservation_check_id (sort keys(%reservation_status)) { |
| my $reservation_check_status = $reservation_status{$reservation_check_id}; |
| $status_string .= "reservation $request_id:$reservation_check_id: $reservation_check_status\n"; |
| |
| # Set the failed flag to 1 if any reservations failed |
| if ($reservation_check_status =~ /failed/i) { |
| $failed = 1; |
| } |
| |
| # Set the ready flag to 0 if any reservations are set to 0 (matching state wasn't found) |
| if ($reservation_check_status =~ /not ready/) { |
| $ready = 0; |
| } |
| } ## end foreach my $reservation_check_id (sort keys(%reservation_status... |
| |
| if ($failed) { |
| notify($ERRORS{'WARNING'}, 0, "request $request_id has failed reservations, returning undefined:\n$status_string"); |
| return; |
| } |
| |
| if ($ready) { |
| notify($ERRORS{'OK'}, 0, "all reservations for request $request_id are ready, returning $ready:\n$status_string"); |
| } |
| else { |
| notify($ERRORS{'OK'}, 0, "not all reservations for request $request_id are ready, returning $ready:\n$status_string"); |
| } |
| |
| return $ready; |
| |
| } ## end sub reservations_ready |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 reservation_being_processed |
| |
| Parameters : reservation ID |
| Returns : true if reservation is avtively being processed, false otherwise |
| Description : Checks the computerloadlog table for rows matching the |
| reservation ID and loadstate = begin. Returns true if any |
| matching rows exist, false otherwise. |
| |
| =cut |
| |
| sub reservation_being_processed { |
| my ($reservation_id) = @_; |
| |
| # Make sure reservation ID was passed |
| if (!$reservation_id) { |
| notify($ERRORS{'WARNING'}, 0, "reservation ID argument was not passed"); |
| return; |
| } |
| |
| my $select_statement = " |
| SELECT |
| computerloadlog.* |
| |
| FROM |
| computerloadlog, |
| computerloadstate |
| |
| WHERE |
| computerloadlog.reservationid = $reservation_id |
| AND computerloadlog.loadstateid = computerloadstate.id |
| AND computerloadstate.loadstatename = \'begin\' |
| "; |
| |
| # Call the database select subroutine |
| # This will return an array of one or more rows based on the select statement |
| my @computerloadlog_rows = database_select($select_statement); |
| |
| # Check if at least 1 row was returned |
| my $computerloadlog_exists; |
| if (scalar @computerloadlog_rows == 1) { |
| notify($ERRORS{'DEBUG'}, 0, "computerloadlog 'begin' entry exists for reservation"); |
| $computerloadlog_exists = 1; |
| } |
| elsif (scalar @computerloadlog_rows > 1) { |
| notify($ERRORS{'WARNING'}, 0, "multiple computerloadlog 'begin' entries exist for reservation"); |
| $computerloadlog_exists = 1; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "computerloadlog 'begin' entry does NOT exist for reservation $reservation_id"); |
| $computerloadlog_exists = 0; |
| } |
| |
| # Check if a vcld process is running matching for this reservation |
| my @processes_running = is_management_node_process_running("$PROCESSNAME [0-9]+:$reservation_id "); |
| |
| # Check the results and return |
| if ($computerloadlog_exists && @processes_running) { |
| notify($ERRORS{'DEBUG'}, 0, "reservation is currently being processed, computerloadlog 'begin' entry exists and running process was found: @processes_running"); |
| return 1; |
| } |
| elsif (!$computerloadlog_exists && @processes_running) { |
| notify($ERRORS{'WARNING'}, 0, "computerloadlog 'begin' entry does NOT exist but running process was found: @processes_running, assuming reservation is currently being processed"); |
| return 1; |
| } |
| elsif ($computerloadlog_exists && !@processes_running) { |
| notify($ERRORS{'WARNING'}, 0, "computerloadlog 'begin' entry exists but running process was NOT found, assuming reservation is NOT currently being processed"); |
| return 0; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "reservation is NOT currently being processed"); |
| return 0; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 run_command |
| |
| Parameters : string |
| Returns : array if command run, undefined if it didn't |
| Description : Runs a command locally on the management node. |
| If command completed successfully, an array containing |
| the exit status and a reference to an array containing the |
| lines of output of the command specified is returned. |
| $array[0] = the exit status of the command |
| $array[1] = reference to array containing lines of output |
| generated by the command |
| If the command fails, an empty array is returned. |
| |
| =cut |
| |
| sub run_command { |
| my ($command, $no_output) = @_; |
| |
| my $pid; |
| my @output = (); |
| my $exit_status; |
| |
| # Pipe the command output to a file handle |
| # The open function returns the pid of the process |
| if ($pid = open(COMMAND, "$command 2>&1 |")) { |
| # Capture the output of the command |
| @output = <COMMAND>; |
| |
| # Save the exit status |
| $exit_status = $? >> 8; |
| |
| if ($? == -1) { |
| notify($ERRORS{'OK'}, 0, "\$? is set to $?, setting exit status to 0, Perl bug likely encountered"); |
| $exit_status = 0; |
| } |
| |
| # Close the command handle |
| close(COMMAND); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command: $command, error: $!"); |
| return; |
| } |
| |
| if (!$no_output) { |
| notify($ERRORS{'DEBUG'}, 0, "executed command: $command, pid: $pid, exit status: $exit_status, output:\n@output"); |
| } |
| |
| # Remove newlines from output lines |
| map { chomp $_ } @output; |
| |
| return ($exit_status, \@output); |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 string_to_ascii |
| |
| Parameters : string |
| Returns : string with special ASCII characters replaced with character |
| names |
| Description : Takes the string passed, checks each character, and replaces |
| special ASCII characters with the character name. For |
| example, "This is a\r\nstring." would return |
| "This[SP]is[SP]a[CR][LF]string." |
| |
| =cut |
| |
| sub string_to_ascii { |
| my $string = shift; |
| |
| my %ascii_codes = ( |
| 0 => 'NUL', |
| 1 => 'SOH', |
| 2 => 'STX', |
| 3 => 'ETX', |
| 4 => 'EOT', |
| 5 => 'ENQ', |
| 6 => 'ACK', |
| 7 => 'BEL', |
| 8 => 'BS', |
| 9 => 'HT', |
| 10 => 'LF', |
| 11 => 'VT', |
| 12 => 'FF', |
| 13 => 'CR', |
| 14 => 'SO', |
| 15 => 'SI', |
| 16 => 'DLE', |
| 17 => 'DC1', |
| 18 => 'DC2', |
| 19 => 'DC3', |
| 20 => 'DC4', |
| 21 => 'NAK', |
| 22 => 'SYN', |
| 23 => 'ETB', |
| 24 => 'CAN', |
| 25 => 'EM', |
| 26 => 'SUB', |
| 27 => 'ESC', |
| 28 => 'FS', |
| 29 => 'GS', |
| 30 => 'RS', |
| 31 => 'US', |
| 32 => 'SP', |
| 127 => 'DEL', |
| ); |
| |
| my $ascii_value_string; |
| foreach my $ascii_code (unpack("C*", $string)) { |
| if (defined($ascii_codes{$ascii_code})) { |
| $ascii_value_string .= "[$ascii_codes{$ascii_code}]"; |
| $ascii_value_string .= "\n" if $ascii_code == 10; |
| } |
| else { |
| $ascii_value_string .= pack("C*", $ascii_code); |
| } |
| } |
| |
| if (defined($ascii_value_string)) { |
| return $ascii_value_string; |
| } |
| else { |
| return ''; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 xmlrpc_call |
| |
| Parameters : @arguments |
| Returns : RPC::XML::Client response value |
| Description : Calls the RPC::XML function defined in the arguments |
| |
| =cut |
| sub xmlrpc_call { |
| my @arguments = @_; |
| if (!@arguments) { |
| notify($ERRORS{'WARNING'}, 0, "no arguments were passed to subroutine"); |
| return; |
| } |
| |
| if (!$XMLRPC_URL || !$XMLRPC_USER || !$XMLRPC_PASS) { |
| notify($ERRORS{'WARNING'}, 0, "unable to call " . join(", ", @arguments) . " function - RPC::XML values have not been set in the vcld.ini file"); |
| return; |
| } |
| |
| # Create a Client object |
| my $client; |
| |
| if (LWP::UserAgent->new->can('ssl_opts')) { |
| notify($ERRORS{'DEBUG'}, 0, "RPC::XML version supports useragent options, setting verify_hostname to 0"); |
| $client = RPC::XML::Client->new($XMLRPC_URL, useragent => ['ssl_opts' => {verify_hostname => 0}]); |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "RPC::XML version does not support useragent options"); |
| $client = RPC::XML::Client->new($XMLRPC_URL); |
| } |
| |
| $client->request->header('X-User' => $XMLRPC_USER); |
| $client->request->header('X-Pass' => $XMLRPC_PASS); |
| $client->request->header('X-APIVERSION' => 2); |
| |
| if (defined($client)) { |
| notify($ERRORS{'DEBUG'}, 0, "created RPC::XML client object:\n" . |
| "URL: $XMLRPC_URL\n" . |
| "username: $XMLRPC_USER" |
| ); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to create a new RPC::XML client object, error: " . ($RPC::XML::ERROR || '<none>')); |
| return; |
| } |
| |
| # Call send_request |
| my $response = $client->send_request(@arguments); |
| $ENV{rpc_xml_response} = $response; |
| |
| if (!ref($response)) { |
| notify($ERRORS{'WARNING'}, 0, "RPC::XML::Client::send_request failed\n" . |
| "URL: $XMLRPC_URL\n" . |
| "username: $XMLRPC_USER\n" . |
| "password: $XMLRPC_PASS\n" ."error: " . ($RPC::XML::ERROR || '<none>') . "\n" . |
| "arguments: " . join(", ", @arguments) . "\n" . |
| "response: '$response'\n" . format_data($response) |
| #"client: '$client'\n" . format_data($client) |
| ); |
| |
| $ENV{rpc_xml_error} = $response; |
| $ENV{rpc_xml_error} =~ s/^RPC::XML::Client::send_request:\s*//; |
| return; |
| } |
| |
| # Check if fault occurred |
| if ($response->is_fault) { |
| notify($ERRORS{'WARNING'}, 0, "RPC::XML::Client::send_request fault occurred\n" . |
| "URL: $XMLRPC_URL\n" . |
| "username: $XMLRPC_USER\n" . |
| "password: $XMLRPC_PASS\n" . |
| "arguments: " . join(", ", @arguments) . "\n" . |
| "fault code: " . $response->code . "\n" . |
| "fault string: " . $response->string |
| ); |
| $ENV{rpc_xml_error} = $response->string; |
| return; |
| } |
| |
| # Display the response details |
| notify($ERRORS{'OK'}, 0, "called RPC::XML::Client::send_request:\n" . |
| "arguments: " . join(", ", @arguments) . "\n" . |
| "response value:\n" . format_data($response->value) |
| ); |
| |
| # Return the response |
| return $response; |
| |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 is_management_node_process_running |
| |
| Parameters : PID or process name |
| Returns : 0 or 1 |
| Description : |
| |
| =cut |
| |
| sub is_management_node_process_running { |
| my ($process_identifier) = @_; |
| |
| # Check the arguments |
| unless ($process_identifier) { |
| notify($ERRORS{'WARNING'}, 0, "process PID or name argument was not specified"); |
| return; |
| } |
| |
| my $command = "pgrep -fl '$process_identifier'"; |
| my ($exit_status, $output) = run_command($command, 0); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to run command to determine if process is running: $command"); |
| return; |
| } |
| |
| my @processes_running; |
| for my $line (@$output) { |
| my ($pid) = $line =~ /^(\d+)/; |
| |
| if (!defined($pid)) { |
| notify($ERRORS{'DEBUG'}, 0, "ignoring pgrep output line, it does not begin with a number: $line"); |
| next; |
| } |
| elsif ($pid eq $PID) { |
| notify($ERRORS{'DEBUG'}, 0, "ignoring pgrep output line for the currently running process: $line"); |
| next; |
| } |
| elsif ($line =~ /pgrep -fl/) { |
| notify($ERRORS{'DEBUG'}, 0, "ignoring pgrep output line containing for pgrep command: $line"); |
| next; |
| } |
| elsif ($line =~ /sh -c/) { |
| # Ignore lines containing 'sh -c', probably indicating a duplicate process of a command run remotely |
| notify($ERRORS{'DEBUG'}, 0, "ignoring pgrep output line containing 'sh -c': $line"); |
| next; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "found matching process: $line"); |
| push @processes_running, $pid; |
| } |
| } |
| |
| if (@processes_running) { |
| notify($ERRORS{'DEBUG'}, 0, "process is running, identifier: '$process_identifier', returning array containing PIDs: @processes_running"); |
| return @processes_running; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "process is NOT running, identifier: '$process_identifier'"); |
| return; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 read_file_to_array |
| |
| Parameters : File path |
| Returns : If successful: array containing lines in file |
| If failed: false |
| Description : Returns the contents of the file specified by the argument in an |
| array. Each array element contains a line from the file. |
| |
| =cut |
| |
| sub read_file_to_array { |
| my $file_path = shift; |
| if (!$file_path) { |
| notify($ERRORS{'WARNING'}, 0, "file path argument was not specified"); |
| return; |
| } |
| |
| unless (open(FILE, $file_path)) { |
| notify($ERRORS{'WARNING'}, 0, "unable to open file: $file_path, reason: $!"); |
| return; |
| } |
| |
| my @file_contents = <FILE>; |
| close FILE; |
| |
| return @file_contents; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 is_valid_ip_address |
| |
| Parameters : IP address string |
| Returns : If valid: true |
| If not valid: false |
| Description : Determines if the argument is a valid IP address. |
| |
| =cut |
| |
| sub is_valid_ip_address { |
| my $ip_address = shift; |
| if (!$ip_address) { |
| notify($ERRORS{'WARNING'}, 0, "IP address argument was not specified"); |
| return; |
| } |
| |
| # Split up the IP address being checked into its octets |
| my @octets = split(/\./, $ip_address); |
| |
| # Make sure 4 octets were found |
| if (scalar @octets != 4) { |
| notify($ERRORS{'DEBUG'}, 0, "IP address does not contain 4 octets: $ip_address, octets:\n" . join("\n", @octets)); |
| return 0; |
| } |
| |
| # Make sure address only contains digits |
| if (grep(/\D/, @octets)) { |
| notify($ERRORS{'DEBUG'}, 0, "IP address contains a non-digit: $ip_address"); |
| return 0; |
| } |
| |
| # Make sure none of the octets is > 255 |
| if ($octets[0] > 255 || $octets[0] > 255 || $octets[0] > 255 || $octets[0] > 255) { |
| notify($ERRORS{'DEBUG'}, 0, "IP address contains an octet > 255: $ip_address"); |
| return 0; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "IP address is valid: $ip_address"); |
| return 1; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 is_valid_dns_host_name |
| |
| Parameters : $dns_host_name |
| Returns : If valid: true |
| If not valid: false |
| Description : Determines if the argument is a valid DNS host name. |
| |
| =cut |
| |
| sub is_valid_dns_host_name { |
| my $dns_host_name = shift; |
| if (!$dns_host_name) { |
| notify($ERRORS{'WARNING'}, 0, "DNS host name argument was not specified"); |
| return; |
| } |
| |
| if (!$dns_host_name) { |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name argument was not specified"); |
| return 0; |
| } |
| |
| if ((my $length = length($dns_host_name)) > 255) { |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name is too long ($length characters): $dns_host_name"); |
| return 0; |
| } |
| |
| if (my @illegal_characters = $dns_host_name =~ /([^\da-z\.\-])/i) { |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name contains illegal characters: " . join(" ", @illegal_characters)); |
| return 0; |
| } |
| |
| if ($dns_host_name =~ /\.\./i) { |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name contains contiguous periods: $dns_host_name"); |
| return 0; |
| } |
| |
| if ($dns_host_name !~ /^[\da-z]/i) { |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name does not begin with a letter or digit: " . string_to_ascii($dns_host_name)); |
| return 0; |
| } |
| |
| if (grep(/^\d+$/, split(/\./, $dns_host_name))) { |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name contains a section comprised only of digits: $dns_host_name"); |
| return 0; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "DNS host name is valid: $dns_host_name"); |
| return 1; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 is_public_ip_address |
| |
| Parameters : IP address string |
| Returns : If public: true |
| If not public: false |
| Description : Determines if the argument is a valid public IP address. It will |
| return true if the IP address is valid and not in any of the |
| following ranges: |
| Private: |
| 10.0.0.0 - 10.255.255.255 |
| 172.16.0.0 - 172.16.31.255.255 |
| 192.168.0.0 - 192.168.255.255 |
| Loopback: |
| 127.0.0.0 - 127.255.255.255 |
| Reserved: |
| 0.0.0.0 |
| 169.254.0.1 - 169.254.255.254 |
| 191.255.0.0 |
| 223.255.255.0 |
| 240.0.0.0 - 255.255.255.254 |
| Multicast: |
| 224.0.0.0 - 239.255.255.255 |
| Broadcast: |
| 255.255.255.255 |
| =cut |
| |
| sub is_public_ip_address { |
| my $ip_address = shift; |
| if (!$ip_address) { |
| notify($ERRORS{'WARNING'}, 0, "IP address argument was not specified"); |
| return; |
| } |
| |
| # Split up the IP address being checked into its octets |
| my @octets = split(/\./, $ip_address); |
| |
| # Make sure the address is valid |
| unless (is_valid_ip_address($ip_address)) { |
| notify($ERRORS{'WARNING'}, 0, "unable to determine if IP address is private, the address is not valid: $ip_address"); |
| return 0; |
| } |
| |
| # Determine the type of address address |
| # Private: |
| # 10.0.0.0 - 10.255.255.255 |
| # 172.16.0.0 - 172.16.31.255.255 |
| # 192.168.0.0 - 192.168.255.255 |
| if (($octets[0] == 10) || |
| ($octets[0] == 172 && ($octets[1] >= 16 && $octets[1] <= 31)) || |
| ($octets[0] == 192 && $octets[1] == 168) |
| ) { |
| notify($ERRORS{'DEBUG'}, 0, "private IP address: $ip_address, returning 0"); |
| return 0; |
| } |
| # Loopback: |
| # 127.0.0.0 - 127.255.255.255 |
| elsif ($ip_address =~ /^127/) { |
| notify($ERRORS{'DEBUG'}, 0, "loopback IP address: $ip_address, returning 0"); |
| return 0; |
| } |
| # Reserved: |
| # 0.0.0.0 |
| # 169.254.0.1 - 169.254.255.254 |
| # 191.255.0.0 |
| # 223.255.255.0 |
| # 240.0.0.0 - 255.255.255.254 |
| elsif (($ip_address eq '0.0.0.0') || |
| ($ip_address =~ /^169\.254/) || |
| ($ip_address eq '191.255.0.0') || |
| ($ip_address eq '223.255.255.0') || |
| ($octets[0] >= 240 && $octets[0] <= 255) |
| ) { |
| notify($ERRORS{'DEBUG'}, 0, "reserved IP address: $ip_address, returning 0"); |
| return 0; |
| } |
| # Multicast: |
| # 224.0.0.0 - 239.255.255.255 |
| elsif ($octets[0] >= 224 && $octets[0] <= 239) { |
| notify($ERRORS{'DEBUG'}, 0, "multicast IP address: $ip_address, returning 0"); |
| return 0; |
| } |
| # Broadcast: |
| # 255.255.255.255 |
| elsif ($ip_address eq '255.255.255.255') { |
| notify($ERRORS{'DEBUG'}, 0, "broadcast IP address: $ip_address, returning 0"); |
| return 0; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "public IP address: $ip_address, returning 1"); |
| return 1; |
| } |
| } |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_block_request_image_info |
| |
| Parameters : computer ID |
| Returns : imagename imageid imagerevisionid |
| Description : Checks the blockcomputers table matching computer id |
| |
| =cut |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| sub get_block_request_image_info { |
| my ($computerid) = @_; |
| |
| my ($package, $filename, $line, $sub) = caller(0); |
| |
| # Check the passed parameters |
| if (!(defined($computerid))) { |
| notify($ERRORS{'WARNING'}, 0, "computer ID was not specified"); |
| return (); |
| } |
| # Construct the select statement |
| my $select_statement = " |
| SELECT DISTINCT |
| image.name AS image_name, |
| image.id AS image_id, |
| imagerevision.id AS imagerevision_id, |
| blockTimes.start AS starttime |
| FROM |
| image, |
| imagerevision, |
| blockComputers, |
| blockTimes |
| WHERE |
| blockComputers.imageid = image.id |
| AND imagerevision.imageid = image.id |
| AND imagerevision.production = 1 |
| AND blockTimes.id = blockComputers.blockTimeid |
| AND blockComputers.computerid = $computerid |
| ORDER BY blockTimes.start LIMIT 1 |
| "; |
| |
| # Call the database select subroutine |
| # This will return an array of one or more rows based on the select statement |
| my @block_image_info = database_select($select_statement); |
| |
| # Check to make sure 1 row was returned |
| if (scalar @block_image_info == 0) { |
| notify($ERRORS{'WARNING'}, 0, "no block reservation image information existed for $computerid, 0 rows were returned"); |
| return; |
| } |
| |
| if (scalar @block_image_info == 1) { |
| my @ret_array; |
| push (@ret_array, $block_image_info[0]{image_name},$block_image_info[0]{image_id},$block_image_info[0]{imagerevision_id}); |
| return @ret_array; |
| } |
| |
| return; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_module_info |
| |
| Parameters : Module ID (optional) |
| Returns : Array |
| Description : Returns a hash reference containing information from the module |
| table. |
| |
| An optional module ID argument can be supplied. If supplied, only |
| the information for the specified module is returned. |
| |
| A hash reference is returned. The keys of the hash are the module IDs. |
| Example showing the format of the data structure returned: |
| |
| my $module_info = get_module_info(); |
| $module_info->{15} |
| |--{description} = 'Provides OS support for standalone Unix lab machines' |
| |--{name} = 'os_unix_lab' |
| |--{perlpackage} = 'VCL::Module::OS::Linux::UnixLab' |
| |--{prettyname} = 'Unix Lab OS Module' |
| $module_info->{15} |
| |--{description} = '' |
| |--{name} = 'os_win2008' |
| |--{perlpackage} = 'VCL::Module::OS::Windows::Version_6::2008' |
| |--{prettyname} = 'Windows Server 2008 OS Module' |
| |
| =cut |
| |
| sub get_module_info { |
| # Create the select statement |
| my $select_statement = " |
| SELECT |
| * |
| FROM |
| module |
| "; |
| |
| # Append a WHERE clause if a module ID argument was supplied |
| my $module_id = shift; |
| if ($module_id) { |
| $select_statement .= "WHERE id = $module_id"; |
| } |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure rows were returned |
| if (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "unable to retrieve rows from module table"); |
| return; |
| } |
| |
| # Transform the array of database rows into a hash |
| my %module_info_hash; |
| for my $row (@selected_rows) { |
| my $module_id = $row->{id}; |
| |
| for my $key (keys %$row) { |
| next if $key eq 'id'; |
| my $value = $row->{$key}; |
| $module_info_hash{$module_id}{$key} = $value; |
| } |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved module info:\n" . format_data(\%module_info_hash)); |
| return \%module_info_hash; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_current_package_name |
| |
| Parameters : None |
| Returns : String |
| Description : Returns a string containing the name of the Perl package which |
| called get_current_package_name. |
| |
| =cut |
| |
| sub get_current_package_name { |
| my $package_name = (caller(0))[0]; |
| return $package_name; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_current_file_name |
| |
| Parameters : None |
| Returns : String |
| Description : Returns a string containing the name of the file which |
| called get_current_file_name. |
| |
| =cut |
| |
| sub get_current_file_name { |
| my $file_name = (caller(0))[1]; |
| |
| # Remove path leaving only file name |
| $file_name =~ s/.*\///g; |
| |
| return $file_name; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_current_subroutine_name |
| |
| Parameters : None |
| Returns : String |
| Description : Returns a string containing the name of the subroutine which |
| called get_current_subroutine_name. |
| |
| =cut |
| |
| sub get_current_subroutine_name { |
| my $subroutine_name = (caller(1))[3]; |
| |
| # Remove path leaving only sub name |
| $subroutine_name =~ s/.*:://g; |
| |
| return $subroutine_name; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 disablesshd |
| |
| Parameters : $hostname, $unityname, $remoteIP, $state, $osname, $log |
| Returns : 1 success 0 failure |
| Description : using ssh identity key log into remote lab machine |
| and set flag for vclclientd to disable sshd for remote user |
| |
| =cut |
| |
| sub disablesshd { |
| my ($hostname, $unityname, $remoteIP, $state, $osname, $log) = @_; |
| my ($package, $filename, $line, $sub) = caller(0); |
| $log = 0 if (!(defined($log))); |
| notify($ERRORS{'WARNING'}, $log, "hostname is not defined") if (!(defined($hostname))); |
| notify($ERRORS{'WARNING'}, $log, "unityname is not defined") if (!(defined($unityname))); |
| notify($ERRORS{'WARNING'}, $log, "remoteIP is not defined") if (!(defined($remoteIP))); |
| notify($ERRORS{'WARNING'}, $log, "state is not defined") if (!(defined($state))); |
| notify($ERRORS{'WARNING'}, $log, "osname is not defined") if (!(defined($osname))); |
| |
| if (!(defined($remoteIP))) { |
| $remoteIP = "127.0.0.1"; |
| } |
| my @lines; |
| my $l; |
| my $identity_keys = get_management_node_info()->{keys}; |
| # create clientdata file |
| my $clientdata = "/tmp/clientdata.$hostname"; |
| if (open(CLIENTDATA, ">$clientdata")) { |
| print CLIENTDATA "$state\n"; |
| print CLIENTDATA "$unityname\n"; |
| print CLIENTDATA "$remoteIP\n"; |
| close CLIENTDATA; |
| |
| # scp to hostname |
| my $target = "vclstaff\@$hostname:/home/vclstaff/clientdata"; |
| if (run_scp_command($clientdata, $target, $identity_keys, "24")) { |
| notify($ERRORS{'OK'}, $log, "Success copied $clientdata to $target"); |
| unlink($clientdata); |
| |
| # send flag to activate changes |
| my @sshcmd = run_ssh_command($hostname, $identity_keys, "echo 1 > /home/vclstaff/flag", "vclstaff", "24"); |
| notify($ERRORS{'OK'}, $log, "setting flag to 1 on $hostname"); |
| |
| my $nmapchecks = 0; |
| # return nmap check |
| |
| NMAPPORT: |
| if (!(nmap_port($hostname, 22))) { |
| return 1; |
| } |
| else { |
| if ($nmapchecks < 5) { |
| $nmapchecks++; |
| sleep 1; |
| notify($ERRORS{'OK'}, $log, "port 22 not closed yet calling NMAPPORT code block"); |
| goto NMAPPORT; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, $log, "port 22 never closed on client $hostname"); |
| return 0; |
| } |
| } ## end else [ if (!(nmap_port($hostname, 22))) |
| } ## end if (run_scp_command($clientdata, $target, ... |
| else { |
| notify($ERRORS{'OK'}, $log, "could not copy src=$clientdata to target=$target"); |
| return 0; |
| } |
| } ## end if (open(CLIENTDATA, ">$clientdata")) |
| else { |
| notify($ERRORS{'WARNING'}, $log, "could not open /tmp/clientdata.$hostname $! "); |
| return 0; |
| } |
| } ## end sub disablesshd |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_affiliation_info |
| |
| Parameters : Affiliation ID (optional) |
| Returns : Array |
| Description : Returns a hash reference containing information from the affiliation |
| table. |
| |
| An optional affiliation ID argument can be supplied. If supplied, only |
| the information for the specified affiliation is returned. |
| |
| A hash reference is returned. The keys of the hash are the affiliation IDs. |
| Example showing the format of the data structure returned: |
| |
| my $affiliation_info = get_affiliation_info(); |
| $affiliation_info->{0} |
| |--{dataUpdateText} = '' |
| |--{helpaddress} = NULL |
| |--{name} = 'Global' |
| |--{shibname} = NULL |
| |--{shibonly} = '0' |
| |--{sitewwwaddress} = NULL |
| $affiliation_info->{1} |
| |--{dataUpdateText} = '<font size="-2">* To update any of these fields, follow the appropriate<br>link under <strong>Related Tools</strong> at the Campus Directory</font>' |
| |--{helpaddress} = 'vcl_help@blah.edu' |
| |--{name} = 'University of Blah' |
| |--{shibname} = 'blah.edu' |
| |--{shibonly} = '0' |
| |--{sitewwwaddress} = 'http://vcl.blah.edu' |
| |
| |
| =cut |
| |
| sub get_affiliation_info { |
| # Create the select statement |
| my $select_statement = " |
| SELECT |
| * |
| FROM |
| affiliation |
| "; |
| |
| # Append a WHERE clause if a affiliation ID argument was supplied |
| my $affiliation_id = shift; |
| if ($affiliation_id) { |
| $select_statement .= "WHERE id = $affiliation_id"; |
| } |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Check to make sure rows were returned |
| if (!@selected_rows) { |
| notify($ERRORS{'WARNING'}, 0, "unable to retrieve rows from affiliation table"); |
| return; |
| } |
| |
| # Transform the array of database rows into a hash |
| my %affiliation_info_hash; |
| for my $row (@selected_rows) { |
| my $affiliation_id = $row->{id}; |
| |
| for my $key (keys %$row) { |
| next if $key eq 'id'; |
| my $value = $row->{$key}; |
| $affiliation_info_hash{$affiliation_id}{$key} = $value; |
| } |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved affiliation info:\n" . format_data(\%affiliation_info_hash)); |
| return \%affiliation_info_hash; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 format_number |
| |
| Parameters : $number, $decimal_places (optional) |
| Returns : string |
| Description : Formats a number with commas and rounds it to a number of |
| decimal places. The default number of decimal places is 0 so |
| that numbers are rounded to the nearest integer. |
| |
| =cut |
| |
| sub format_number { |
| my ($number, $decimal_places) = @_; |
| $decimal_places = 0 if !$decimal_places; |
| $number = sprintf("%." . $decimal_places . "f", $number); |
| |
| $number = reverse($number); |
| $number =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; |
| |
| return scalar reverse $number; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_file_size_info_string |
| |
| Parameters : $bytes |
| Returns : string |
| Description : |
| |
| =cut |
| |
| sub get_file_size_info_string { |
| my ($size_bytes, $separator) = @_; |
| $separator = " - " if !$separator; |
| |
| my $size_kb = format_number(($size_bytes / 1024), 1); |
| my $size_mb = format_number(($size_bytes / 1024 ** 2), 1); |
| my $size_gb = format_number(($size_bytes / 1024 ** 3), 2); |
| my $size_tb = format_number(($size_bytes / 1024 ** 4), 2); |
| |
| my $size_info; |
| $size_info .= format_number($size_bytes) . " bytes$separator"; |
| $size_info .= "$size_kb KB$separator"; |
| $size_info .= "$size_mb MB$separator"; |
| $size_info .= "$size_gb GB"; |
| $size_info .= "$separator$size_tb TB" if ($size_tb >= 1); |
| return $size_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_copy_speed_info_string |
| |
| Parameters : $copied_bytes, $duration_seconds |
| Returns : string |
| Description : Calculates various copy rates based on the amount of data copied |
| and time the copy took. |
| |
| =cut |
| |
| sub get_copy_speed_info_string { |
| my ($copied_bytes, $duration_seconds) = @_; |
| |
| my $minutes = ($duration_seconds / 60); |
| $minutes =~ s/\..*//g; |
| my $seconds = ($duration_seconds - ($minutes * 60)); |
| if (length($seconds) == 0) { |
| $seconds = "00"; |
| } |
| elsif (length($seconds) == 1) { |
| $seconds = "0$seconds"; |
| } |
| |
| my $copied_bits = ($copied_bytes * 8); |
| |
| my $copied_kb = ($copied_bytes / 1024); |
| my $copied_mb = ($copied_bytes / 1024 / 1024); |
| my $copied_gb = ($copied_bytes / 1024 / 1024 / 1024); |
| |
| my $copied_kbit = ($copied_bits / 1024); |
| my $copied_mbit = ($copied_bits / 1024 / 1024); |
| my $copied_gbit = ($copied_bits / 1024 / 1024 / 1024); |
| |
| my $bytes_per_second = ($copied_bytes / $duration_seconds); |
| my $kb_per_second = ($copied_kb / $duration_seconds); |
| my $mb_per_second = ($copied_mb / $duration_seconds); |
| my $gb_per_second = ($copied_gb / $duration_seconds); |
| |
| my $bits_per_second = ($copied_bits / $duration_seconds); |
| my $kbit_per_second = ($copied_kbit / $duration_seconds); |
| my $mbit_per_second = ($copied_mbit / $duration_seconds); |
| my $gbit_per_second = ($copied_gbit / $duration_seconds); |
| |
| my $bytes_per_minute = ($copied_bytes / $duration_seconds * 60); |
| my $kb_per_minute = ($copied_kb / $duration_seconds * 60); |
| my $mb_per_minute = ($copied_mb / $duration_seconds * 60); |
| my $gb_per_minute = ($copied_gb / $duration_seconds * 60); |
| |
| my $info_string; |
| |
| $info_string .= "data copied: " . get_file_size_info_string($copied_bytes) . "\n"; |
| $info_string .= "time to copy: $minutes:$seconds (" . format_number($duration_seconds) . " seconds)\n"; |
| $info_string .= "---\n"; |
| $info_string .= "bits copied: " . format_number($copied_bits) . " ($copied_bits)\n"; |
| $info_string .= "bytes copied: " . format_number($copied_bytes) . " ($copied_bytes)\n"; |
| $info_string .= "MB copied: " . format_number($copied_mb, 1) . "\n"; |
| $info_string .= "GB copied: " . format_number($copied_gb, 2) . "\n"; |
| $info_string .= "---\n"; |
| $info_string .= "B/m: " . format_number($bytes_per_minute) . "\n"; |
| $info_string .= "MB/m: " . format_number($mb_per_minute, 1) . "\n"; |
| $info_string .= "GB/m: " . format_number($gb_per_minute, 2) . "\n"; |
| $info_string .= "---\n"; |
| $info_string .= "B/s: " . format_number($bytes_per_second) . "\n"; |
| $info_string .= "MB/s: " . format_number($mb_per_second, 1) . "\n"; |
| $info_string .= "GB/s: " . format_number($gb_per_second, 2) . "\n"; |
| $info_string .= "---\n"; |
| $info_string .= "Mbit/s: " . format_number($mbit_per_second, 1) . "\n"; |
| $info_string .= "Gbit/s: " . format_number($gbit_per_second, 2); |
| |
| return $info_string; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 create_management_node_directory |
| |
| Parameters : $directory_path |
| Returns : boolean |
| Description : Creates a directory on the management node. |
| |
| =cut |
| |
| sub create_management_node_directory { |
| my ($directory_path) = @_; |
| |
| # Check if the directory already exists |
| if (-d $directory_path) { |
| notify($ERRORS{'OK'}, 0, "directory already exists on management node: $directory_path"); |
| return 1; |
| } |
| |
| # Attempt to create the directory |
| my $command = "mkdir -p -v \"$directory_path\" 2>&1 && ls -1d \"$directory_path\""; |
| my ($exit_status, $output) = run_command($command, 1); |
| if (!defined($output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to run command to create directory on management node: $directory_path\ncommand: $command"); |
| return; |
| } |
| elsif (grep(/created directory/i, @$output)) { |
| notify($ERRORS{'OK'}, 0, "created directory on management node: $directory_path"); |
| return 1; |
| } |
| elsif (grep(/mkdir: /i, @$output)) { |
| notify($ERRORS{'WARNING'}, 0, "error occurred attempting to create directory on management node: $directory_path:\ncommand: $command\nexit status: $exit_status\noutput:\n" . join("\n", @$output)); |
| return; |
| } |
| elsif (grep(/^$directory_path/, @$output)) { |
| notify($ERRORS{'OK'}, 0, "directory already exists on management node: $directory_path"); |
| return 1; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "unexpected output returned from command to create directory on management node: $directory_path:\ncommand: $command\nexit status: $exit_status\noutput:\n" . join("\n", @$output)); |
| return; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 normalize_file_path |
| |
| Parameters : $path |
| Returns : string |
| Description : Normalizes a file or directory path: |
| -spaces from the beginning and end of the path are removed |
| -quotes from the beginning and end of the path are removed |
| -trailing slashes are removed |
| -escaped spaces are unescaped |
| |
| =cut |
| |
| sub normalize_file_path { |
| # Get the path argument |
| my $path = shift; |
| if (!$path) { |
| notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); |
| return; |
| } |
| |
| # Remove any spaces and quotes from the beginning and end of the path |
| # Remove any slashes from the end of the path |
| $path =~ s/(^['"\s]*|['"\s\\\/]*$)//g; |
| |
| # Unescape any spaces |
| $path =~ s/\\+(\s)/$1/g; |
| |
| return $path; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 escape_file_path |
| |
| Parameters : $path |
| Returns : string |
| Description : Escapes special characters in a file or directory path with |
| backslashes: |
| -spaces are escaped |
| |
| =cut |
| |
| sub escape_file_path { |
| # Get the path argument |
| my $path = shift; |
| if (!$path) { |
| notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); |
| return; |
| } |
| |
| $path = normalize_file_path($path); |
| |
| # Call quotemeta to escape all special character |
| $path = quotemeta $path; |
| |
| # Unescape wildcard *, +, characters or else subroutines will fail which accept a wildcard file path |
| $path =~ s/\\+([\*\+\/\~])/$1/g; |
| |
| return $path; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 parent_directory_path |
| |
| Parameters : $path |
| Returns : string |
| Description : Returns the parent directory path of the path argument. The path |
| returned is normalized. |
| |
| =cut |
| |
| sub parent_directory_path { |
| # Get the path argument |
| my $path = shift; |
| if (!$path) { |
| notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); |
| return; |
| } |
| |
| $path = normalize_file_path($path); |
| |
| # Remove everything after the last forward or backslash |
| $path =~ s/\/[^\/\\]+$//g; |
| |
| return $path; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 setup_get_array_choice |
| |
| Parameters : |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub setup_get_array_choice { |
| my (@choices) = @_; |
| notify($ERRORS{'DEBUG'}, 0, "choices argument:\n" . join("\n", @choices)); |
| |
| my $choice_count = scalar(@choices); |
| |
| while (1) { |
| for (my $i=1; $i<=$choice_count; $i++) { |
| print "$i. $choices[$i-1]\n"; |
| } |
| print "\n[" . join("/", @{$ENV{setup_path}}) . "]\n"; |
| print "Make a selection (1"; |
| print "-$choice_count" if ($choice_count > 1); |
| print ", 'c' to cancel): "; |
| |
| my $choice = <STDIN>; |
| chomp $choice; |
| |
| if ($choice =~ /^c$/i) { |
| return; |
| } |
| if ($choice !~ /^\d+$/ || $choice < 1 || $choice > $choice_count) { |
| print "\n*** Choice must be an integer between 1 and $choice_count ***\n\n"; |
| } |
| else { |
| return ($choice - 1); |
| } |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 setup_get_input_string |
| |
| Parameters : |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub setup_get_input_string { |
| my ($message, $default_value) = @_; |
| if ($default_value) { |
| print "$message [$default_value]: "; |
| } |
| else { |
| print "$message ('c' to cancel): "; |
| } |
| |
| my $input = <STDIN>; |
| chomp $input; |
| if ($input =~ /^c$/i) { |
| return; |
| } |
| elsif ($default_value && !length($input)) { |
| return $default_value; |
| } |
| else { |
| return $input; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 setup_get_hash_choice |
| |
| Parameters : $hash_ref, $display_key () |
| Returns : |
| Description : |
| |
| =cut |
| |
| sub setup_get_hash_choice { |
| my ($hash_ref, $display_key) = @_; |
| |
| my $choice_count = scalar(keys %$hash_ref); |
| |
| my %choices; |
| for my $key (keys %$hash_ref) { |
| my $display_name; |
| if ($display_key) { |
| $display_name = $hash_ref->{$key}{$display_key}; |
| } |
| else { |
| $display_name = $key; |
| } |
| |
| if ($choices{$display_name}) { |
| notify($ERRORS{'WARNING'}, 0, "duplicate hash keys containing the value '$display_key' = '$display_name', hash argument:\n" . format_data($hash_ref)); |
| } |
| |
| $choices{$display_name} = $key; |
| } |
| |
| my $choice_index = setup_get_array_choice(sort keys %choices); |
| return if (!defined($choice_index)); |
| |
| my $choice_name = (sort keys %choices)[$choice_index]; |
| return $choices{$choice_name}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 setup_confirm |
| |
| Parameters : $message |
| Returns : boolean |
| Description : Displays the message to the user and loops until they enter Y or |
| N. |
| |
| =cut |
| |
| sub setup_confirm { |
| my ($message) = @_; |
| |
| while (1) { |
| print "$message (Y/N)? "; |
| my $input = <STDIN>; |
| if ($input =~ /^y(es)?$/i) { |
| return 1; |
| } |
| elsif ($input =~ /^n(o)?$/i) { |
| return 0; |
| } |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 setup_print_wrap |
| |
| Parameters : $message, $columns (optional) |
| Returns : |
| Description : Prints a message to STDOUT formatted to the column width. 76 is |
| the default column value. |
| |
| =cut |
| |
| sub setup_print_wrap { |
| my ($message, $columns) = @_; |
| $columns = 76 if !defined($columns); |
| |
| return if !$message; |
| |
| # Save the leading and trailing lines then remove them from the string |
| # This is done so wrap doesn't lose them |
| my ($leading_newlines) = $message =~ /^(\n+)/; |
| $message =~ s/^\n+//g; |
| my ($trailing_newlines) = $message =~ /(\n+)$/; |
| $message =~ s/\n+$//g; |
| |
| # Wrap text for lines |
| local($Text::Wrap::columns) = $columns; |
| print $leading_newlines if $leading_newlines; |
| print wrap('', '', $message); |
| print $trailing_newlines if $trailing_newlines; |
| |
| return 1; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_os_info |
| |
| Parameters : none |
| Returns : hash reference |
| Description : Returns the contents of the OS table as a hash reference. The |
| hash keys are the OS IDs. |
| |
| =cut |
| |
| sub get_os_info { |
| # Create the select statement |
| my $select_statement = <<EOF; |
| SELECT |
| OS.*, |
| module.name AS module_name, |
| module.prettyname AS module_prettyname, |
| module.perlpackage AS module_perlpackage |
| FROM |
| OS, |
| module |
| WHERE |
| OS.moduleid = module.id |
| EOF |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| my %info; |
| |
| for my $row (@selected_rows) { |
| my $os_id = $row->{id}; |
| |
| foreach my $key (keys %$row) { |
| my $value = $row->{$key}; |
| |
| (my $original_key = $key) =~ s/^.+_//; |
| |
| if ($key =~ /module_/) { |
| $info{$os_id}{module}{$original_key} = $value; |
| } |
| else { |
| $info{$os_id}{$original_key} = $value; |
| } |
| } |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved OS info:\n" . format_data(\%info)); |
| return \%info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 kill_child_processes |
| |
| Parameters : $parent_pid |
| Returns : boolean |
| Description : Kills all child processes belonging to the parent PID specified |
| as the argument. |
| |
| =cut |
| |
| sub kill_child_processes { |
| my @parent_pids = @_; |
| my $parent_pid = $parent_pids[-1]; |
| my $parent_process_string = "parent PID: " . join(">", @parent_pids); |
| |
| # Make sure the parent vcld daemon process didn't call this subroutine for safety |
| # Prevents all reservations being processed from being killed |
| if ($ENV{vcld}) { |
| notify($ERRORS{'CRITICAL'}, 0, "kill_child_processes subroutine called from the parent vcld process, not killing any processes for safety"); |
| return; |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "$parent_process_string: attempting to kill child processes"); |
| |
| my $command = "pgrep -flP $parent_pid | sort -r"; |
| my ($exit_status, $output) = run_command($command, 1); |
| |
| for my $line (@$output) { |
| # Make sure the line only contains a PID |
| my ($child_pid, $child_command) = $line =~ /^(\d+)\s+(.*)/; |
| if (!defined($child_pid) || !defined($child_command)) { |
| notify($ERRORS{'WARNING'}, 0, "$parent_process_string: pgrep output line does not contain a PID and command:\nline: '$child_pid'\ncommand: '$command'"); |
| next; |
| } |
| elsif ($child_command =~ /$command/) { |
| # Ignore the pgrep command called to determine child processes |
| next; |
| } |
| |
| # Create a string containing the beginning and end of the child process command to make log output more readable |
| my $child_command_summary = join('...', ($child_command =~ /^(.{10,20}).*(.{20,30})$/)); |
| |
| notify($ERRORS{'DEBUG'}, 0, "$parent_process_string, found child process: $child_pid '$child_command_summary'"); |
| |
| # Recursively kill the child processes of the child process |
| kill_child_processes(@parent_pids, $child_pid); |
| |
| my $kill_count = kill 9, $child_pid; |
| if ($kill_count) { |
| notify($ERRORS{'DEBUG'}, 0, "$parent_process_string, killed child process: $child_pid (kill count: $kill_count)"); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "$parent_process_string, kill command returned 0 attempting to kill child process: $child_pid"); |
| } |
| } |
| |
| return 1; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_connect_method_info |
| |
| Parameters : $imagerevision_id |
| Returns : hash reference |
| Description : Returns the connect methods for the image revision specified as |
| the argument. |
| |
| =cut |
| |
| sub get_connect_method_info { |
| my ($imagerevision_id) = @_; |
| if (!defined($imagerevision_id)) { |
| notify($ERRORS{'WARNING'}, 0, "imagerevision ID argument was not supplied"); |
| return; |
| } |
| |
| my $imagerevision_info = get_imagerevision_info($imagerevision_id); |
| |
| notify($ERRORS{'DEBUG'}, 0, "attempting to retrieve connect method info:\n" . |
| "imagerevision: $imagerevision_id - " . $imagerevision_info->{imagename} . "\n" . |
| "OS: " . $imagerevision_info->{image}{OS}{id} . " - " . $imagerevision_info->{image}{OS}{name} . "\n" . |
| "OS type: " . $imagerevision_info->{image}{OS}{OStype}{id} . " - " . $imagerevision_info->{image}{OS}{OStype}{name} |
| ); |
| |
| # Get a hash ref containing the database column names |
| my $database_table_columns = get_database_table_columns(); |
| |
| my @tables = ( |
| 'connectmethod', |
| 'connectmethodmap' |
| ); |
| |
| # Construct the select statement |
| my $select_statement = "SELECT DISTINCT\n"; |
| |
| # Get the column names for each table and add them to the select statement |
| for my $table (@tables) { |
| my @columns = @{$database_table_columns->{$table}}; |
| for my $column (@columns) { |
| $select_statement .= "$table.$column AS '$table-$column',\n"; |
| } |
| } |
| |
| # Remove the comma after the last column line |
| $select_statement =~ s/,$//; |
| |
| # Complete the select statement |
| $select_statement .= <<EOF; |
| FROM |
| connectmethod, |
| connectmethodmap, |
| imagerevision |
| |
| LEFT JOIN image ON (image.id = imagerevision.imageid) |
| LEFT JOIN OS ON (OS.id = image.OSid) |
| LEFT JOIN OStype ON (OStype.name = OS.type) |
| |
| WHERE |
| connectmethodmap.connectmethodid = connectmethod.id |
| AND imagerevision.id = $imagerevision_id |
| AND connectmethodmap.autoprovisioned IS NULL |
| AND ( |
| connectmethodmap.OStypeid = OStype.id |
| OR connectmethodmap.OSid = OS.id |
| OR connectmethodmap.imagerevisionid = imagerevision.id |
| ) |
| |
| ORDER BY |
| connectmethod.id, |
| connectmethodmap.imagerevisionid, |
| connectmethodmap.OSid, |
| connectmethodmap.OStypeid, |
| connectmethodmap.disabled |
| EOF |
| |
| # Call the database select subroutine |
| my @selected_rows = database_select($select_statement); |
| |
| # Transform the array of database rows into a hash |
| my $connect_method_info = {}; |
| |
| for my $row (@selected_rows) { |
| notify($ERRORS{'DEBUG'}, 0, $row->{"connectmethod-name"} . ": " . |
| "connectmethodid=" . $row->{"connectmethod-id"} . ", " . |
| "OStypeid=" . ($row->{"connectmethodmap-OStypeid"} || 'NULL') . ", " . |
| "OSid=" . ($row->{"connectmethodmap-OSid"} || 'NULL') . ", " . |
| "imagerevisionid=" . ($row->{"connectmethodmap-imagerevisionid"} || 'NULL') . ", " . |
| "disabled=" . $row->{"connectmethodmap-disabled"}); |
| |
| my $connectmethod_id = $row->{'connectmethod-id'}; |
| |
| # Loop through all the columns returned |
| for my $key (keys %$row) { |
| next if $key eq 'connectmethod-connecttext'; |
| |
| my $value = $row->{$key}; |
| |
| # Split the table-column names |
| my ($table, $column) = $key =~ /^([^-]+)-(.+)/; |
| |
| # Add the values for the primary table to the hash |
| # Add values for other tables under separate keys |
| if ($table eq $tables[0]) { |
| $connect_method_info->{$connectmethod_id}{$column} = $value; |
| } |
| else { |
| $connect_method_info->{$connectmethod_id}{$table}{$column} = $value; |
| } |
| } |
| } |
| |
| #notify($ERRORS{'DEBUG'}, 0, "retrieved connect method info:\n" . format_data($connect_method_info)); |
| return $connect_method_info; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_random_mac_address |
| |
| Parameters : $prefix (optional) |
| Returns : string |
| Description : Generates a random MAC address. |
| |
| =cut |
| |
| sub get_random_mac_address { |
| my ($prefix) = @_; |
| |
| if ($prefix) { |
| # Add a trailing colon if not supplied in the argument |
| $prefix =~ s/:+$/:/; |
| |
| if ($prefix !~ /([a-f0-9]{2}:)+/i) { |
| notify($ERRORS{'WARNING'}, 0, "invalid MAC address prefix argument: $prefix"); |
| return; |
| } |
| elsif (length($prefix) % 3 != 0) { |
| notify($ERRORS{'WARNING'}, 0, "invalid MAC address prefix length: '$prefix'"); |
| return; |
| } |
| |
| } |
| else { |
| $prefix = '52:54:'; |
| } |
| |
| my $random_octet_count = (6 - (length($prefix) / 3)); |
| |
| # Remove the trailing colon |
| $prefix =~ s/:+$//; |
| |
| my $mac_address = uc($prefix); |
| |
| for (my $i=0; $i<$random_octet_count; $i++) { |
| $mac_address .= ":" . sprintf("%02X",int(rand(255))); |
| } |
| |
| notify($ERRORS{'DEBUG'}, 0, "generated random MAC address: '$mac_address'"); |
| return $mac_address; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 xml_string_to_hash |
| |
| Parameters : $xml_text |
| Returns : hash reference |
| Description : Converts XML text to a hash using XML::Simple:XMLin. The argument |
| may be a string of XML text, an array, or array reference of |
| lines of XML text. |
| |
| =cut |
| |
| sub xml_string_to_hash { |
| my @arguments = @_; |
| if (!@arguments) { |
| notify($ERRORS{'WARNING'}, 0, "XML text argument was not specified"); |
| return; |
| } |
| |
| my $xml_text; |
| |
| # Check if the argument is an array of lines, array reference, or string |
| if (scalar(@arguments) == 1) { |
| my $argument = $arguments[0]; |
| if (my $type = ref($argument)) { |
| if ($type eq 'ARRAY') { |
| $xml_text = join("\n", @$argument); |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "XML text argument is a $type reference, it may only be a string or array reference"); |
| return; |
| } |
| } |
| else { |
| $xml_text = $argument; |
| } |
| } |
| else { |
| $xml_text = join("\n", @arguments); |
| } |
| |
| # Override the die handler |
| local $SIG{__DIE__} = sub{}; |
| |
| # Convert the XML to a hash using XML::Simple |
| my $xml_hashref; |
| eval { |
| $xml_hashref = XMLin($xml_text, 'ForceArray' => 1, 'KeyAttr' => []); |
| }; |
| |
| if ($xml_hashref) { |
| return $xml_hashref; |
| } |
| elsif ($EVAL_ERROR) { |
| notify($ERRORS{'WARNING'}, 0, "failed to convert XML text to hash, error: $EVAL_ERROR\nXML text:$xml_text"); |
| return; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to convert XML text to hash, XML text:$xml_text"); |
| return; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 hash_to_xml_string |
| |
| Parameters : $xml_hashref |
| Returns : string |
| Description : Converts an XML hash reference to text to a hash using |
| XML::Simple:XMLout. |
| |
| =cut |
| |
| sub hash_to_xml_string { |
| my $xml_hashref = shift; |
| if (!$xml_hashref) { |
| notify($ERRORS{'WARNING'}, 0, "XML hash reference argument was not specified"); |
| return; |
| } |
| elsif (!ref($xml_hashref) || ref($xml_hashref) ne 'HASH') { |
| notify($ERRORS{'WARNING'}, 0, "argument is not a hash reference"); |
| return; |
| } |
| |
| my $root_name = shift; |
| |
| # Override the die handler |
| local $SIG{__DIE__} = sub{}; |
| |
| # Convert the XML hashref to text using XML::Simple::XMLout |
| my $xml_text; |
| eval { |
| $xml_text = XMLout($xml_hashref, 'RootName' => $root_name, 'KeyAttr' => []); |
| }; |
| |
| if ($xml_text) { |
| return $xml_text; |
| } |
| elsif ($EVAL_ERROR) { |
| notify($ERRORS{'WARNING'}, 0, "failed to convert XML hash reference to text, error: $EVAL_ERROR\nXML hash reference:" . format_data($xml_hashref)); |
| return; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "failed to convert XML hash reference to text, XML hash reference:" . format_data($xml_hashref)); |
| return; |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_array_summary_string |
| |
| Parameters : $array_ref or @array |
| Returns : string |
| Description : Formats a string from an array. If there are more than 3 elements |
| in the array, the values are summarized: |
| /vmwarelinux-RHEL54Small2251-v1-s001.vmdk |
| ...14 additional entries... |
| /vmwarelinux-RHEL54Small2251-v1-s016.vmdk |
| |
| |
| =cut |
| |
| sub get_array_summary_string { |
| my @arguments = @_; |
| if (!@arguments) { |
| return ''; |
| } |
| elsif (scalar(@arguments) == 1) { |
| if (!ref($arguments[0])) { |
| return $arguments[0]; |
| } |
| elsif (ref($arguments[0]) eq 'ARRAY') { |
| @arguments = @{$arguments[0]}; |
| } |
| else { |
| notify($ERRORS{'WARNING'}, 0, "argument is not an array reference: " . ref($arguments[0])); |
| return ''; |
| } |
| } |
| |
| my $array_size = scalar(@arguments); |
| if ($array_size == 1) { |
| return $arguments[0]; |
| } |
| if ($array_size > 3) { |
| return "$arguments[0]\n..." . ($array_size-2) . " additional entries...\n$arguments[-1]"; |
| } |
| else { |
| return join("\n", @arguments); |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 sort_by_file_name |
| |
| Parameters : @file_paths |
| Returns : array |
| Description : Sorts a list of file paths by file name. Directory paths are |
| ignored. File names beginning with numbers are sorted |
| numerically. |
| Example: |
| |
| Input array: |
| /var/file_a.txt |
| /tmp/file_b.txt |
| /var/1 file.txt |
| 100 file.txt |
| /tmp/99 data.txt |
| 50 file.txt |
| |
| Sorted result: |
| /var/1 file.txt |
| 50 file.txt |
| /tmp/99 data.txt |
| 100 file.txt |
| /var/file_a.txt |
| /tmp/file_b.txt |
| |
| =cut |
| |
| sub sort_by_file_name { |
| if (!defined($a) && !defined($b)) { |
| my @file_paths = @_; |
| if (scalar(@file_paths)) { |
| #notify($ERRORS{'DEBUG'}, 0, "not called by sort, \$a and \$b are not defined, array argument was passed"); |
| return sort sort_by_file_name @file_paths; |
| } |
| else { |
| return (); |
| } |
| } |
| |
| # Get the file names from the 2 file paths being compared |
| my $a_file_name = ($a =~ /([^\/]+)$/g)[0]; |
| my $b_file_name = ($b =~ /([^\/]+)$/g)[0]; |
| |
| # Check if both file names begin with a number |
| my $a_number = ($a_file_name =~ /^(\d+)/g)[0]; |
| my $b_number = ($b_file_name =~ /^(\d+)/g)[0]; |
| |
| # If both file names begin with a number, sort numerically |
| # Otherwise, sort alphabetically |
| if (defined($a_number) && defined($b_number) && $a_number != $b_number) { |
| #notify($ERRORS{'DEBUG'}, 0, "numeric comparison - a: $a_file_name ($a_number), b: $b_file_name ($b_number)"); |
| return $a_number <=> $b_number; |
| } |
| else { |
| #notify($ERRORS{'DEBUG'}, 0, "alphabetic comparison - a: $a_file_name, b: $b_file_name"); |
| return lc($a_file_name) cmp lc($b_file_name); |
| } |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| =head2 get_current_image_contents_noDS |
| |
| Parameters : node name |
| Returns : array |
| Description : returns contents of currentimage.txt for given node |
| |
| =cut |
| |
| sub get_current_image_contents_noDS { |
| |
| my ($computer_node_name) = @_; |
| |
| if (!defined($computer_node_name)) { |
| notify($ERRORS{'WARNING'}, 0, "computer_node_name argument was not supplied"); |
| return; |
| } |
| |
| # Attempt to retrieve the contents of currentimage.txt |
| my $cat_command = "cat ~/currentimage.txt"; |
| my ($cat_exit_status, $cat_output) = run_ssh_command($computer_node_name, $ENV{management_node_info}{keys}, $cat_command); |
| if (!defined($cat_output)) { |
| notify($ERRORS{'WARNING'}, 0, "failed to execute command to failed to retrieve currentimage.txt from $computer_node_name"); |
| return; |
| } |
| elsif ($cat_exit_status ne '0') { |
| notify($ERRORS{'WARNING'}, 0, "failed to retrieve currentimage.txt from $computer_node_name, exit status: $cat_exit_status, output:\n@{$cat_output}"); |
| return; |
| } |
| else { |
| notify($ERRORS{'DEBUG'}, 0, "retrieved currentimage.txt contents from $computer_node_name:\n" . join("\n", @$cat_output)); |
| } |
| return @{$cat_output}; |
| } |
| |
| #///////////////////////////////////////////////////////////////////////////// |
| |
| 1; |
| __END__ |
| |
| =head1 SEE ALSO |
| |
| L<http://cwiki.apache.org/VCL/> |
| |
| =cut |