mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-25 05:02:05 +00:00
3026 lines
110 KiB
Perl
3026 lines
110 KiB
Perl
#!/usr/bin/perl
|
|
###############################################################################
|
|
# IBM (C) Copyright 2016 Eclipse Public License
|
|
# http://www.eclipse.org/org/documents/epl-v10.html
|
|
###############################################################################
|
|
# COMPONENT: verifynode
|
|
#
|
|
# Verify the capabilities of a node.
|
|
###############################################################################
|
|
|
|
package xCAT_plugin::verifynode;
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
|
|
$XML::Simple::PREFERRED_PARSER='XML::Parser';
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Data::Dumper qw(Dumper);
|
|
use File::Basename;
|
|
use File::Path;
|
|
use File::Spec;
|
|
use File::Temp;
|
|
use Getopt::Long;
|
|
use Getopt::Long qw(GetOptionsFromString);
|
|
use lib "$::XCATROOT/lib/perl";
|
|
use MIME::Base64;
|
|
use POSIX qw/strftime/;
|
|
use Scalar::Util qw( looks_like_number );
|
|
use Sys::Hostname;
|
|
use Sys::Syslog qw( :DEFAULT setlogsock );
|
|
use Socket;
|
|
use Text::Wrap;
|
|
use Time::HiRes qw( gettimeofday sleep );
|
|
use Unicode::UCD qw(charinfo);
|
|
|
|
use xCAT::zvmCPUtils;
|
|
use xCAT::zvmMsgs;
|
|
use xCAT::zvmUtils;
|
|
|
|
my $version = "1";
|
|
|
|
my $comments = ''; # Comments for the IVP run
|
|
my $dataFile = ''; # Input data file
|
|
my $decode = ''; # String specified by the --decode operand.
|
|
my $disable = ''; # Disable scheduled IVP from running.
|
|
my $displayHelp = 0; # Display help information
|
|
my $driver = ''; # Name of driver script
|
|
my $driverLoc = '/var/opt/xcat/ivp'; # Local directory containing the driver script, if it exists
|
|
my $encode = ''; # String specified by the --encode operand.
|
|
my $enable = ''; # Enable scheduled IVP to run.
|
|
my $endTime = ''; # Human readable end time
|
|
my %hosts; # Information on host nodes known by xCAT MN
|
|
my $id = ''; # Id of the IVP run
|
|
my %ignored; # List of ignored messages
|
|
my $ignoreCnt = 0; # Number of times we ignored a message
|
|
my $issueCmdOverIUCV = ''; # Issue a command to a node over IUCV
|
|
my $issueCmdToNode = ''; # Issue a command to a node using IUCV or SSH
|
|
my %localIPs; # Hash of the Local IP addresses for the xCAT MN system
|
|
my $logDir = '/var/log/xcat/ivp'; # Log location
|
|
my $logFile = ''; # Log file name and location
|
|
my $logFileHandle; # File handle for the log file.
|
|
my @ivpSummary; # Summary output lines for an IVP run.
|
|
my $locApplSystemRole = '/var/lib/sspmod/appliance_system_role';
|
|
my %mnInfo; # Managed node environment information
|
|
my %msgsToIgnore; # Hash of messages to ignore
|
|
my $needToFinishLogFile = 0; # If 1 then finishLogFile() needs to be called
|
|
my $nodeName = ''; # Name of node to be verified
|
|
my $notify = 0; # Notify a user
|
|
my $notifyOnErrorOrWarning = 0; # Indicates a log file should be sent if the need to notify
|
|
# a user was determined.
|
|
my $openstackIP = ''; # IP address of the OpenStack node
|
|
my $openstackUser = 'root'; # User on the OpenStack system that can access the
|
|
# OpenStack configuration files
|
|
my $orchParms = ''; # Orchestrator parms to be stored in the zvmivp table
|
|
my $prepParms = ''; # Parameters to pass to the preparation script
|
|
my $remove = 0; # Remove a scheduled IVP
|
|
my $runBasicIVP = 0; # Run the basic xCAT IVP
|
|
my $runCron = 0; # Cron job invocation to run a possible set of IVPs
|
|
# on a periodic basis.
|
|
my $runFullIVP = 0; # Run the full end to end IVP (compute node and xCAT)
|
|
my $schedule = ''; # Hours to schedule an automated IVP
|
|
my $scheduledType = ''; # Type of IVP to schedule ('fullivp' or 'basicivp')
|
|
my $startTime = ''; # Human readable start time
|
|
my $tgtOS = ''; # Target Node's distro & version
|
|
my $todayDate = ''; # Today's date in the form 'yyyy-mm-dd'
|
|
my $verifyAccess = 0; # Verify system can be accessed
|
|
my $verifyCaptureReqs = 0; # Verify systems meets capture requirements
|
|
my $verifyCloudInit = 0; # Verify cloud-init installed
|
|
my $verifyXcatconf4z = 0; # Verify xcatconf4z
|
|
my $versionOpt = 0; # Show version information flag
|
|
my $warnErrCnt = 0; # Count of warnings and error messages for a verify set
|
|
my $zhcpTarget = ''; # ZHCP server that is related to this verification
|
|
my $zvmHost = ''; # z/VM host related to this verification
|
|
my $zxcatParms = ''; # Parmeters to pass to zxcatIVP
|
|
|
|
# Routines to drive based on the selected test.
|
|
my %verifySets = (
|
|
'$decode ne \'\'' =>
|
|
[ '', # Do not show a function banner.
|
|
'encodeDecodeString()',
|
|
],
|
|
'$disable and ! $remove and $schedule eq \'\'' =>
|
|
[ 'Disabling an IVP run',
|
|
'enableDisableRun( $id )',
|
|
],
|
|
'$enable and ! $remove and $schedule eq \'\'' =>
|
|
[ 'Enabling an IVP run',
|
|
'enableDisableRun( $id )',
|
|
],
|
|
'$encode ne \'\'' =>
|
|
[ 'Encoding a string',
|
|
'encodeDecodeString()',
|
|
],
|
|
'$issueCmdOverIUCV ne \'\'' =>
|
|
[ 'Issue a command to a node using IUCV',
|
|
'cmdOnVM( \'onlyIUCV\' )',
|
|
],
|
|
'$issueCmdToNode ne \'\'' =>
|
|
[ 'Issue a command to a node using IUCV or SSH',
|
|
'cmdOnVM( \'either\' )',
|
|
],
|
|
'$remove' =>
|
|
[ 'Remove an automated periodic IVP',
|
|
'removeIVP( $id )',
|
|
],
|
|
'$runBasicIVP' =>
|
|
[ 'Verifying the basic general setup of xCAT',
|
|
'addLogHeader( \'basicivp\' )',
|
|
'verifyBasicXCAT()',
|
|
],
|
|
'$runCron' =>
|
|
[ 'Cron job for automated verification',
|
|
'driveCronList()',
|
|
],
|
|
'$runFullIVP' =>
|
|
[ 'Verifying the full end-to-end setup of the xCAT and the compute node',
|
|
'addLogHeader( \'fullivp\' )',
|
|
'checkForDefault( \'openstackIP\', \'--openstackIP was not specified\', \'xCAT_master\' )',
|
|
'verifyAccess( $openstackIP ); $rc = 0',
|
|
'getDistro( $openstackIP )',
|
|
'runPrepScript()',
|
|
'runDriverScript()',
|
|
],
|
|
'$schedule ne \'\'' =>
|
|
[ 'Scheduling an automated periodic IVP',
|
|
'scheduleIVP()',
|
|
],
|
|
'$verifyAccess' =>
|
|
[ 'Verifying access to the system',
|
|
'verifyPower( $nodeName )',
|
|
'verifyAccess( $nodeName )'
|
|
],
|
|
'$verifyCaptureReqs' =>
|
|
[ 'Verifying system meets the image requirements',
|
|
'verifyPower( $nodeName )',
|
|
'verifyAccess( $nodeName )',
|
|
'getDistro( $nodeName )',
|
|
'verifyDistro()',
|
|
],
|
|
'$verifyCloudInit' =>
|
|
[ 'Verifying cloud-init is installed and configured',
|
|
'verifyPower( $nodeName )',
|
|
'verifyAccess( $nodeName )',
|
|
'getDistro( $nodeName )',
|
|
'verifyService($nodeName, "cloud-config,2,3,4,5 cloud-final,2,3,4,5 cloud-init,2,3,4,5 cloud-init-local,2,3,4,5")',
|
|
'showCILevel()',
|
|
],
|
|
'$verifyXcatconf4z' =>
|
|
[ 'Verifying xcatconf4z is installed and configured',
|
|
'verifyPower( $nodeName )',
|
|
'verifyAccess( $nodeName )',
|
|
'getDistro( $nodeName )',
|
|
'verifyXcatconf4z( $nodeName )',
|
|
],
|
|
'$test' =>
|
|
[ 'Script test functon.',
|
|
'verifyLogResponse()',
|
|
],
|
|
);
|
|
|
|
|
|
# set the usage message
|
|
my $usage_string = "Usage:\n
|
|
$0 -n <node> <options>\n
|
|
or\n
|
|
$0 <options>\n
|
|
or\n
|
|
$0 -v\n
|
|
or\n
|
|
$0 [ -h | --help ]\n
|
|
The following options are supported:\n
|
|
-a | --access
|
|
Verify access from xCAT MN to node.
|
|
--basicivp
|
|
Run the basic xCAT IVP.
|
|
--capturereqs
|
|
Verify that node meets system capture
|
|
requirements for OpenStack.
|
|
--cmdoveriucv <cmd>
|
|
Issue a command to a node using IUCV.
|
|
Specify a period for <cmd> when combining this
|
|
option with the --file option.
|
|
--cmdtonode <cmd> Issue a command to a node using IUCV or SSH
|
|
if IUCV is not available.
|
|
Specify a period for <cmd> when combining this
|
|
option with the --file option.
|
|
-c | --cloudinit
|
|
Verify that node has cloud-init installed.
|
|
--comments
|
|
Comments to specify on the IVP run.
|
|
--cron
|
|
Driven as a cron job to run a set of periodic
|
|
IVPs.
|
|
--decode <parm>
|
|
Decode an encoded string. This is used to
|
|
view encoded values in the zvmivp table.
|
|
--disable Indicates that the IVP being scheduled should
|
|
be disabled from running.
|
|
--enable Indicates that the IVP being scheduled should
|
|
be enabled to run. This is the default when
|
|
neither --disable or --enable is specified.
|
|
--encode <parm>
|
|
Encode a string. This used for debug in order
|
|
to create a string to compare to an encoded
|
|
string from the zvmivp table. The <parm> must be
|
|
an ASCII string. This function does not support
|
|
encoding a UTF-8 character string.
|
|
--file File location containing additional input.
|
|
When used with the --cmdoveriucv or --cmdtonode
|
|
operands, this file contains the command to issue.
|
|
--fullivp
|
|
Run the full end-to-end IVP. This
|
|
verification checks the compute node and builds
|
|
a driver script which is then used to check
|
|
the xCAT environment based on the values
|
|
gathered in the driver script.
|
|
-h | --help
|
|
Display help information.
|
|
-i | --id <idToRun>
|
|
ID to associate with the IVP run.
|
|
--ignore <val>
|
|
Blank or comma separated list of message ids or
|
|
message severities to ignore. Ignored messages are
|
|
not counted as failures and do not produce messages.
|
|
Instead the number of ignored messages and their
|
|
message numbers are displayed at the end of
|
|
processing. Recognized message severities:
|
|
'bypass', 'info', 'warning', 'error'.
|
|
--notify
|
|
Notification of IVP failures should be sent
|
|
to a z/VM userid.
|
|
-n | --node <node>
|
|
Node name of system to verify.
|
|
--openstackip <ipAddr>
|
|
IP address of the OpenStack compute node.
|
|
--openstackuser <user>
|
|
User to use to access the compute node.
|
|
The user defaults to 'root'.
|
|
--orchparms <parms>
|
|
Parameters to be stored in the zvmivp table. These
|
|
parameters are passed to this routine when a
|
|
scheduled IVP is driven.
|
|
--prepparms <parms>
|
|
Parameters to pass to the preparation script as
|
|
part of a full IVP.
|
|
--remove
|
|
Indicates that an IVP in the zvmivp table should be
|
|
removed. The id is specified with the --id
|
|
operand.
|
|
--schedule <hours>
|
|
List of hours (in 24 hour format) to periodically
|
|
run the IVP. Multiple hours are separated by blanks.
|
|
For example, \'0 6 12 18 20\'.
|
|
--type < basicivp | fullivp >
|
|
Type of IVP to be scheduled.
|
|
-v
|
|
Display the version of this script.
|
|
-x | --xcatconf4z
|
|
Verify that node has xcatconf4z installed.
|
|
--zxcatparms <parms>
|
|
Parameters to pass to the zxcatIVP script.\n";
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 addLogHeader
|
|
|
|
Description : Add a header to the log file.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = addLogHeader();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub addLogHeader {
|
|
my ( $hType ) = @_;
|
|
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
|
|
# Generate the header based on the header type.
|
|
if ( $hType eq 'basicivp' ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "This log file was created for a basic xCAT IVP. It consists of output from ".
|
|
"zxcatIVP that validates the xCAT management node (MN) and the ZHCP Agents that are used by ".
|
|
"the xCAT MN node to manage the z/VM hosts." );
|
|
} elsif ( $hType eq 'fullivp' ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "This log file was created for a full xCAT IVP which validates the z/VM OpenStack ".
|
|
"configuration files, the xCAT management node (MN) and the ZHCP Agents that are used by ".
|
|
"the xCAT MN node to manage the z/VM host. The log file contains the following output: output ".
|
|
"from the preparation script that validated the OpenStack properties, the contents of the ".
|
|
"driver script created by the preparation script to drive the second part of the IVP, output ".
|
|
"of the zxcatIVP run that is driven by the driver script to complete the full IVP." );
|
|
} else {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "VerifyNode log file." );
|
|
}
|
|
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 checkForDefault
|
|
|
|
Description : Determine if a default is needed for
|
|
the specified property and set it if
|
|
it is needed. Currently, this routine
|
|
is only used for the master IP address.
|
|
Arguments : Name of property
|
|
String to use in a DFLT01 message.
|
|
Default or function to perform.
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = checkForDefault( 'openStackIP', '--ip was not specified', 'xCAT_master' );
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub checkForDefault {
|
|
my ( $prop, $reasonString, $default ) = @_;
|
|
my $additionalInfo = '';
|
|
my $rc = 0;
|
|
my $val;
|
|
|
|
eval( "\$val = \$$prop" );
|
|
if ( $val eq '' ) {
|
|
if ( $default eq 'xCAT_master' ) {
|
|
$default = '';
|
|
if ( exists $mnInfo{'ip'} ) {
|
|
$default = $mnInfo{'ip'};
|
|
$additionalInfo = 'The value came from the IP address used as the xCAT MN\'s ' .
|
|
'master IP address that was specified in the site table.';
|
|
} else {
|
|
# Use one of the local IPv4 IPs. We don't support IPv6.
|
|
if ( keys %localIPs > 0 ) {
|
|
# Can't find the MN IP so use the first local IPv4 IP.
|
|
foreach my $ip ( keys %localIPs ) {
|
|
if ( $ip =~ /:/ ) {
|
|
next;
|
|
} else {
|
|
$default = $ip;
|
|
$additionalInfo = 'The function could not determine the master IP address for ' .
|
|
'the xCAT MN so the first local IPv4 address was used.';
|
|
last;
|
|
}
|
|
}
|
|
if ( $default eq '' ) {
|
|
$default = '127.0.0.1';
|
|
$additionalInfo = 'The function could not determine the IP address for the xCAT MN ' .
|
|
'and could not find any IPv4 addresses on the system so \'127.0.0.1\' will be used ' .
|
|
'in the hope that it will work.';
|
|
}
|
|
} else {
|
|
# Don't know the local IPs.
|
|
# Default to the IPv4 loopback and let it fail.
|
|
$default = '127.0.0.1';
|
|
$additionalInfo = 'The function cound not determine the IP addresses on the ' .
|
|
'xCAT MN system so \'127.0.0.1\' was used.';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $rc == 0 ) {
|
|
# Found a default, set it in the property.
|
|
eval( "\$$prop = \'$default\'" );
|
|
logResponse( 'DF', 'DFLT01', $reasonString, $default, $additionalInfo );
|
|
}
|
|
}
|
|
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 cmdOnVM
|
|
|
|
Description : Send a command to a node and show the result.
|
|
Arguments : Target function, either 'onlyIUCV' or 'either'.
|
|
Returns : Return code:
|
|
0 - Normal Linux success
|
|
255 - Unable to SSH to system
|
|
non-zero - command error
|
|
Output from the command or a error string on an SSH failure.
|
|
Example : $rc = cmdOnVM( 'onlyIUCV' );
|
|
$rc = cmdOnVM( 'either' );
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub cmdOnVM {
|
|
my ( $targetFunc ) = @_;
|
|
my $cmd = '';
|
|
my $hcp = '';
|
|
my @lines;
|
|
my $rc = 0;
|
|
my $out = '';
|
|
my $sudo;
|
|
my $user;
|
|
my $userid = '';
|
|
|
|
# Get the command to issue.
|
|
if ( $targetFunc eq 'onlyIUCV' ) {
|
|
if ( $issueCmdOverIUCV ne '.' ) {
|
|
$cmd = $issueCmdOverIUCV;
|
|
}
|
|
} else {
|
|
if ( $issueCmdToNode ne '.' ) {
|
|
$cmd = $issueCmdToNode;
|
|
}
|
|
}
|
|
|
|
# Use the data file if it was specified.
|
|
if ( $dataFile ne '' ) {
|
|
if ( -e $dataFile ) {
|
|
open my $handle, '<', $dataFile;
|
|
chomp( @lines = <$handle> );
|
|
close $handle;
|
|
if ( $lines[0] ne '' ) {
|
|
$cmd = $lines[0];
|
|
} else {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "$dataFile does not have anything in the first line.");
|
|
goto FINISH_cmdUsingIUCV;
|
|
}
|
|
} else {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "$dataFile does not exist.");
|
|
goto FINISH_cmdUsingIUCV;
|
|
}
|
|
}
|
|
|
|
# Verify that we have a command to issue.
|
|
if ( $cmd eq '' ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "A command to issue was not specified.");
|
|
goto FINISH_cmdUsingIUCV;
|
|
}
|
|
|
|
($user, $sudo) = xCAT::zvmUtils->getSudoer();
|
|
|
|
# Get the required properties (hcp, userid, user) from the node
|
|
$out = `/opt/xcat/bin/lsdef $nodeName -i hcp,userid`;
|
|
$rc = $?;
|
|
if ( $rc eq 0 ) {
|
|
my $fndNode = 0;
|
|
@lines = split( '\n', $out );
|
|
my $host = '';
|
|
foreach my $line ( @lines ) {
|
|
$line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string
|
|
if ( $line =~ /Object name:/ ) {
|
|
$fndNode = 1;
|
|
next;
|
|
} elsif ( $line =~ /hcp\=/ ) {
|
|
($hcp) = $line =~ m/hcp\=(.*)/;
|
|
next;
|
|
} elsif ( $line =~ /userid\=/ ) {
|
|
($userid) = $line =~ m/userid\=(.*)/;
|
|
next;
|
|
}
|
|
}
|
|
if ( $fndNode != 1 ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "'$nodeName' is not an xCAT node." );
|
|
goto FINISH_cmdUsingIUCV;
|
|
}
|
|
} else {
|
|
$rc = logResponse( 'DFS', 'GNRL01', "$cmd", $rc, $out );
|
|
goto FINISH_cmdUsingIUCV;
|
|
}
|
|
|
|
# Send the command through IUCV
|
|
logResponse( 'DF', '*NONFORMATTED*', "\n**************************\nData for the function call\n**************************" );
|
|
logResponse( 'DF', '*NONFORMATTED*', " Node: $nodeName" );
|
|
if ( $targetFunc eq 'onlyIUCV' ) {
|
|
logResponse( 'DF', '*NONFORMATTED*', "z/VM userid: $userid" );
|
|
logResponse( 'DF', '*NONFORMATTED*', " HCP: $hcp" );
|
|
logResponse( 'DF', '*NONFORMATTED*', " User: $user" );
|
|
logResponse( 'DF', '*NONFORMATTED*', " Function: xCAT::zvmUtils->execcmdthroughIUCV" );
|
|
} else {
|
|
logResponse( 'DF', '*NONFORMATTED*', " User: $user" );
|
|
logResponse( 'DF', '*NONFORMATTED*', " Function: xCAT::zvmUtils->execcmdonVM" );
|
|
}
|
|
logResponse( 'DF', '*NONFORMATTED*', " Cmd: $cmd" );
|
|
|
|
logResponse( 'DF', '*NONFORMATTED*', "\n*********************\nInvoking the function\n*********************" );
|
|
if ( $targetFunc eq 'onlyIUCV' ) {
|
|
$out = xCAT::zvmUtils->execcmdthroughIUCV( $user, $hcp, $userid, $cmd );
|
|
} else {
|
|
$out = xCAT::zvmUtils->execcmdonVM( $user, $nodeName, $cmd );
|
|
}
|
|
logResponse( 'DF', '*NONFORMATTED*', "\n******\nResult\n******\n$out" );
|
|
|
|
FINISH_cmdUsingIUCV:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 driveCronList
|
|
|
|
Description : Run thru the list of IVPs and run any that
|
|
are due to be run.
|
|
Arguments : None.
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = driveCronList();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub driveCronList {
|
|
my $attemptedRuns = 0;
|
|
my $disabledRuns = 0;
|
|
my @ivps;
|
|
my $out = '';
|
|
my $rc = 0;
|
|
my $totalRunsInTable = 0;
|
|
|
|
# Determine the timestamp and other time related information.
|
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
|
|
$year = $year + 1900;
|
|
$todayDate = sprintf( "%04d-%02d-%02d", $year, $mon + 1, $mday );
|
|
$startTime = sprintf( "%02d:%02d:%02d on %04d-%02d-%02d",
|
|
$hour, $min, $sec, $year, $mon + 1, $mday );
|
|
my $currTime = sprintf( "%04d-%02d-%02d_%02d:%02d:%02d",
|
|
$year, $mon + 1, $mday, $hour, $min, $sec);
|
|
my $newLastRun = "$year $yday $hour";
|
|
|
|
logResponse( 'DFS', '*NONFORMATTED*', "CRON job run started at $startTime." );
|
|
|
|
# Read the z/VM IVP table to get the list of known IVP runs.
|
|
$out = `/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"`;
|
|
$rc = $?;
|
|
my @lines = split( '\n', $out );
|
|
$totalRunsInTable = scalar( @lines );
|
|
if ( $totalRunsInTable != 0 ) {
|
|
my $row = 0;
|
|
my $nowTotalHours = ( ( $year ) * 8784 ) + ( $yday * 24 ) + $hour;
|
|
foreach my $line ( @lines ) {
|
|
$row++;
|
|
chomp( $line );
|
|
my @field = split( ',', $line );
|
|
for ( my $i=0; $i < scalar( @field ); $i++ ) {
|
|
$field[$i] =~ s/^\"|\"$//g; # trim quotes from both ends of the string
|
|
}
|
|
|
|
# Process the fields from the zvmIVP table. Each row is related to a
|
|
# scheduled run.
|
|
# Field 0 - Unique IVP ID
|
|
# 1 - IP property for the OpenStack system
|
|
# 2 - Schedule, hours to run the IVP
|
|
# 3 - Last time IVP was run, ( year day hour )
|
|
# 4 - Type of run, e.g. basicivp, fullivp
|
|
# 5 - OpenStack user to access the config files
|
|
# 6 - Parameters to pass to the orchestrator, verifynode
|
|
# 7 - Parameters to pass to the preparator script, prep_zxcatIVP
|
|
# 8 - Parameters to pass to the main IVP script, zxcatIVP.pl
|
|
# 9 - Comments related to the IVP run
|
|
# 10 - Disabled, 1 or YES equals disabled
|
|
my %ivpRun;
|
|
if ( defined $field[0] and $field[0] ne '' ) {
|
|
$ivpRun{'id'} = $field[0];
|
|
} else {
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row in the ivp table missing a ".
|
|
"required property, \'id\'. The row will be treated as disabled." );
|
|
$disabledRuns++;
|
|
next;
|
|
}
|
|
|
|
if ( defined $field[10] and ($field[10] eq '1' or uc( $field[10] ) eq 'YES') ) {
|
|
# IVP run is disabled
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table is ".
|
|
"disabled and will not be processed." );
|
|
$disabledRuns++;
|
|
next;
|
|
}
|
|
|
|
if ( defined $field[1] and $field[1] ne '' ) {
|
|
$ivpRun{'ip'} = $field[1];
|
|
} else {
|
|
# Assume this is a local xCAT IVP run
|
|
$ivpRun{'ip'} = 'xcat';
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table " .
|
|
"has no value specified for the \'ip\' property. " .
|
|
"The IVP run will be against the system which is " .
|
|
"running the xCAT management node." );
|
|
}
|
|
|
|
if ( defined $field[2] and $field[2] ne '' ) {
|
|
$ivpRun{'schedule'} = hexDecode( $field[2] );
|
|
} else {
|
|
# Default run time is 1 AM
|
|
$ivpRun{'schedule'} = '1';
|
|
}
|
|
|
|
my $lRYear;
|
|
my $lRYDay;
|
|
my $lRHour;
|
|
|
|
if ( defined $field[3] and $field[3] ne '' ) {
|
|
$ivpRun{'last_run'} = $field[3];
|
|
my @parts = split ( '\s', $ivpRun{'last_run'} );
|
|
if ( defined $parts[0] and $parts[0] ne '' ) {
|
|
$lRYear = $parts[0];
|
|
} else {
|
|
# Missing year, set default to immediately, 0.
|
|
$lRYear = 0;
|
|
}
|
|
if ( defined $parts[1] and $parts[1] ne '' ) {
|
|
$lRYDay = $parts[1];
|
|
} else {
|
|
# Missing day number, set default to immediately, 0.
|
|
$lRYDay = 0;
|
|
}
|
|
if ( defined $parts[2] and $parts[2] ne '' ) {
|
|
$lRHour = $parts[2];
|
|
} else {
|
|
# Missing hour, set default to immediately, -1.
|
|
$lRHour = 0;
|
|
}
|
|
} else {
|
|
# Force a schedule run
|
|
$lRYear = 0;
|
|
$lRYDay = 0;
|
|
$lRHour = -1;
|
|
}
|
|
$ivpRun{'last_run'} = "$lRYear $lRYDay $lRHour";
|
|
|
|
# Weed out any runs that are not due to be run.
|
|
# Calculate the difference (since 2000) between the last run hour and the current hour,
|
|
# ignoring leap years by saying every year is a leap year.
|
|
my $totalHourDiff = $nowTotalHours - (( ( $lRYear ) * 8784 ) + ( $lRYDay * 24 ) + $lRHour);
|
|
|
|
if ( $totalHourDiff == 0 ) {
|
|
# Same hour of the same day of the same year for some reason so ignore this IVP row.
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ".
|
|
"is being ignored because it was already run this hour." );
|
|
next;
|
|
} else {
|
|
# Determine the range of hours that are of interest within a 24 hour time period. Anything
|
|
# that is more than 24 hours old is automatically overdue and should be run.
|
|
if ( $totalHourDiff < 24) {
|
|
my $todayStartHour;
|
|
my $yesterdayStartHour;
|
|
if ( $yday > $lRYDay ) {
|
|
# Last run yesterday
|
|
$todayStartHour = 0;
|
|
$yesterdayStartHour = $lRHour + 1;
|
|
} else {
|
|
# Last run today
|
|
$todayStartHour = $hour - $totalHourDiff;
|
|
$yesterdayStartHour = 24;
|
|
}
|
|
|
|
# Less than a day so we need to worry about which scheduled hours were missed.
|
|
my $overdue = 0;
|
|
my @schedHours = split ( '\s', $ivpRun{'schedule'} );
|
|
foreach my $sHour ( @schedHours ) {
|
|
if ( $sHour == 0 and $sHour ne '0' ) {
|
|
# not numeric, ignore
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ".
|
|
"contains as schedule hour that is non-numeric: $sHour. ".
|
|
"The value will be ignored." );
|
|
next;
|
|
}
|
|
if (( $sHour >= $yesterdayStartHour ) or
|
|
( $sHour > $todayStartHour and $sHour <= $hour ) ) {
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ".
|
|
"is due to be run. Scheduled hour: $sHour, last run: $lRHour." );
|
|
$overdue = 1;
|
|
last;
|
|
}
|
|
}
|
|
if ( $overdue == 0 ) {
|
|
# did not find a scheduled hour that is due
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table is not ".
|
|
"due to be run this hour." );
|
|
next;
|
|
}
|
|
} else {
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ".
|
|
"is due to be run. Last run over 24 hours ago." );
|
|
}
|
|
}
|
|
|
|
if ( defined $field[4] and $field[4] ne '' ) {
|
|
$ivpRun{'type_of_run'} = lc( $field[4] );
|
|
} else {
|
|
$ivpRun{'type_of_run'} = 'not specified';
|
|
}
|
|
if ( $ivpRun{'type_of_run'} ne 'basicivp' and $ivpRun{'type_of_run'} ne 'fullivp' ) {
|
|
my $badValue = $ivpRun{'type_of_run'};
|
|
if ( exists $mnInfo{'ip'} and ( $ivpRun{'ip'} eq $mnInfo{'ip'} )) {
|
|
if (( $mnInfo{'role'} eq 'controller' ) or
|
|
( $mnInfo{'role'} eq 'compute' ) or
|
|
( $mnInfo{'role'} eq 'compute_mn' )) {
|
|
$ivpRun{'type_of_run'} = 'fullivp';
|
|
} else {
|
|
$ivpRun{'type_of_run'} = 'basicivp';
|
|
}
|
|
} else {
|
|
$ivpRun{'type_of_run'} = 'basicivp';
|
|
}
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table contains ".
|
|
"an unknown run type value of \'$badValue\'. Instead ".
|
|
"a run type of \'$ivpRun{'type_of_run'}\' will be performed." );
|
|
}
|
|
|
|
if ( defined $field[5] and $field[5] ne '' ) {
|
|
$ivpRun{'access_user'} = hexDecode( $field[5] );
|
|
} else {
|
|
$ivpRun{'access_user'} = '';
|
|
}
|
|
|
|
if ( defined $field[6] and $field[6] ne '' ) {
|
|
$ivpRun{'orch_parms'} = hexDecode( $field[6] );
|
|
} else {
|
|
$ivpRun{'orch_parms'} = '';
|
|
}
|
|
|
|
if ( defined $field[7] and $field[7] ne '' ) {
|
|
$ivpRun{'prep_parms'} = hexDecode( $field[7] );
|
|
} else {
|
|
$ivpRun{'prep_parms'} = '';
|
|
}
|
|
|
|
if ( defined $field[8] and $field[8] ne '' ) {
|
|
# Ensure that it is reencoded to pass along to do the actual work.
|
|
$ivpRun{'main_ivp_parms'} = hexEncode( $field[8] );
|
|
} else {
|
|
$ivpRun{'main_ivp_parms'} = '';
|
|
}
|
|
|
|
if ( defined $field[9] and $field[9] ne '' ) {
|
|
$ivpRun{'comments'} = hexDecode( $field[9] );
|
|
} else {
|
|
$ivpRun{'comments'} = '';
|
|
}
|
|
|
|
push @ivps, \%ivpRun;
|
|
}
|
|
} else {
|
|
# Table is empty. Create a single run for the z/VM related to the
|
|
# xCAT MN.
|
|
logResponse( 'DS', 'GENERIC_RESPONSE', "The ivp table is empty. Default IVPs will be enabled." );
|
|
if ( exists $mnInfo{'node'} ) {
|
|
# First IVP is a full IVP for an OpenStack related xCAT at 1 am.
|
|
# Otherwise it is a basic IVP.
|
|
my %ivpRun;
|
|
$ivpRun{'id'} = 1;
|
|
if ( exists $mnInfo{'ip'} ) {
|
|
$ivpRun{'ip'} = $mnInfo{'ip'};
|
|
} else {
|
|
$ivpRun{'ip'} = 'xcat';
|
|
}
|
|
$ivpRun{'schedule'} = '1';
|
|
if ( $mnInfo{'role'} eq 'controller' or $mnInfo{'role'} eq 'compute' or $mnInfo{'role'} eq 'compute_mn' ) {
|
|
$ivpRun{'type_of_run'} = 'fullivp';
|
|
$ivpRun{'access_user'} = 'root';
|
|
} else {
|
|
$ivpRun{'type_of_run'} = 'basicivp';
|
|
$ivpRun{'access_user'} = '';
|
|
}
|
|
$ivpRun{'orch_parms'} = '';
|
|
$ivpRun{'prep_parms'} = '';
|
|
$ivpRun{'main_ivp_parms'} = '';
|
|
$ivpRun{'comments'} = 'Default Full IVP run';
|
|
$ivpRun{'defaultRun'} = 'run';
|
|
push @ivps, \%ivpRun;
|
|
$totalRunsInTable++;
|
|
|
|
# Second IVP is a basic IVP run each hour except for 1 am when a full IVP is run.
|
|
my %ivpRun2;
|
|
$ivpRun2{'id'} = 2;
|
|
if ( exists $mnInfo{'ip'} ) {
|
|
$ivpRun2{'ip'} = $mnInfo{'ip'};
|
|
} else {
|
|
$ivpRun2{'ip'} = 'xcat';
|
|
}
|
|
$ivpRun2{'schedule'} = '0 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23';
|
|
$ivpRun2{'type_of_run'} = 'basicivp';
|
|
$ivpRun2{'access_user'} = '';
|
|
$ivpRun2{'orch_parms'} = '';
|
|
$ivpRun2{'prep_parms'} = '';
|
|
$ivpRun2{'main_ivp_parms'} = '';
|
|
$ivpRun2{'comments'} = 'Default Basic IVP run';
|
|
$ivpRun2{'defaultRun'} = 'runLater';
|
|
push @ivps, \%ivpRun2;
|
|
$totalRunsInTable++;
|
|
} else {
|
|
# xCAT MN does not exist as a node so we will only do a basic IVP the system.
|
|
# This is a very abnormal case. We deal in the abnormal so let's run some type
|
|
# of IVP but not define it in the table.
|
|
logResponse( 'DFS', 'MN03' );
|
|
my %ivpRun;
|
|
$ivpRun{'id'} = 1;
|
|
if ( exists $mnInfo{'ip'} ) {
|
|
$ivpRun{'ip'} = $mnInfo{'ip'};
|
|
} else {
|
|
$ivpRun{'ip'} = 'xcat';
|
|
}
|
|
$ivpRun{'schedule'} = '0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23';
|
|
$ivpRun{'type_of_run'} = 'basicivp';
|
|
$ivpRun{'access_user'} = '';
|
|
$ivpRun{'orch_parms'} = '';
|
|
$ivpRun{'prep_parms'} = '';
|
|
$ivpRun{'main_ivp_parms'} = '';
|
|
$ivpRun{'comments'} = 'Default Basic IVP run';
|
|
$ivpRun{'defaultRun'} = 'noTableUpdate';
|
|
push @ivps, \%ivpRun;
|
|
$totalRunsInTable++;
|
|
}
|
|
}
|
|
|
|
# Handle pruning, if necessary.
|
|
if ( $hour == 0 ) {
|
|
pruneLogs( $year, $mon + 1, $mday );
|
|
}
|
|
|
|
# Go thru the list of IVPs and drive a run for each IVP
|
|
# that is due to be run.
|
|
foreach my $ivp ( @ivps ) {
|
|
my %ivpRun = %$ivp;
|
|
|
|
# Build the verify node command.
|
|
my $cmd = '/opt/xcat/bin/verifynode -n xcat --notify';
|
|
|
|
if ( $ivpRun{'type_of_run'} eq 'fullivp' ) {
|
|
$cmd = "$cmd --fullivp --openstackip \'$ivpRun{'ip'}\' --openstackuser \'" . hexEncode( $ivpRun{'access_user'} ) . "\'";
|
|
} else {
|
|
$cmd = "$cmd --basicivp";
|
|
}
|
|
|
|
if ( $ivpRun{'orch_parms'} ne '' ) {
|
|
$cmd = "$cmd $ivpRun{'orch_parms'}"; # Don't hex encode orchestrator parms
|
|
}
|
|
|
|
if ( $ivpRun{'prep_parms'} ne '' ) {
|
|
$cmd = "$cmd --prepparms \'" . hexEncode( $ivpRun{'prep_parms'} ) . "\'";
|
|
}
|
|
|
|
if ( $ivpRun{'main_ivp_parms'} ne '' ) {
|
|
$cmd = "$cmd --zxcatparms \'" . hexEncode( $ivpRun{'main_ivp_parms'} ) . "\'";
|
|
}
|
|
|
|
# Update the table to indicate the time we start the IVP run.
|
|
if ( ! exists $ivpRun{'defaultRun'} ) {
|
|
my $chtabCmd = "/opt/xcat/sbin/chtab id=\'$ivpRun{'id'}\' zvmivp.last_run=\'$newLastRun\'";
|
|
$out = `$chtabCmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for IVP ID($ivpRun{'id'}), ip: $ivpRun{'ip'}, rc: $rc, out: $out\n" );
|
|
}
|
|
} elsif ( $ivpRun{'defaultRun'} ne 'noTableUpdate' ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "Adding IVP ID($ivpRun{'id'}) to the z/VM IVP table: $ivpRun{'comments'}\n" );
|
|
my $chtabCmd = "/opt/xcat/sbin/chtab id=\'$ivpRun{'id'}\' ".
|
|
"zvmivp.ip=\'$ivpRun{'ip'}\' ".
|
|
"zvmivp.schedule=\'$ivpRun{'schedule'}\' ".
|
|
"zvmivp.last_run=\'$newLastRun\' ".
|
|
"zvmivp.type_of_run=\'$ivpRun{'type_of_run'}\' ".
|
|
"zvmivp.access_user=\'" . hexEncode( $ivpRun{'access_user'} ) . "\' ".
|
|
"zvmivp.comments=\'" . hexEncode( $ivpRun{'comments'} ) . "\' ";
|
|
$out = `$chtabCmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($ivpRun{'id'}), ip: $ivpRun{'ip'}, rc: $rc, out: $out\n" );
|
|
}
|
|
|
|
# Skip running this one if it is marked 'runLater'.
|
|
# A basic IVP is not needed if a full IVP is going to be run.
|
|
if ( $ivpRun{'defaultRun'} eq 'runLater' ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "Ignoring IVP ID($ivpRun{'id'}) for this hour. It will be run next hour. IVP comment: $ivpRun{'comments'}\n" );
|
|
next;
|
|
}
|
|
}
|
|
|
|
# Drive the run.
|
|
$attemptedRuns++;
|
|
my $logComments = '';
|
|
if ( $ivpRun{'comments'} eq '' ) {
|
|
$logComments = "Running IVP ID($ivpRun{'id'})";
|
|
} else {
|
|
$logComments = "Running IVP ID($ivpRun{'id'}) - $ivpRun{'comments'}";
|
|
}
|
|
logResponse( 'DS', '*NONFORMATTED*', "$logComments" );
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "The IVP run for ID($ivpRun{'id'}) returned a ".
|
|
"non-zero return code, rc: $rc\n" );
|
|
}
|
|
}
|
|
|
|
logResponse( 'DFS', '*NONFORMATTED*', "IVP CRON results: $totalRunsInTable runs considered, $disabledRuns runs disabled, $attemptedRuns runs attempted" );
|
|
$rc = 0;
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 enableDisableRun
|
|
|
|
Description : Enable or Disable command line function.
|
|
Arguments : ID of the IVP to be disabled
|
|
Global input set by command operands are used
|
|
by this routine:
|
|
$disable - Disable a run.
|
|
$enable - Enable a run.
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = enableDisableRun();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub enableDisableRun {
|
|
my ( $id ) = @_;
|
|
my %ids;
|
|
my $rc = 0;
|
|
|
|
if ( $id eq '' ) {
|
|
logResponse( 'DF', 'OPER01', '--id' );
|
|
goto FINISH_enableDisableRun;
|
|
}
|
|
|
|
# Get the list of IVP ids in the zvmivp table.
|
|
my $cmd = '/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"';
|
|
my $out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc ne 0 ) {
|
|
logResponse( 'DF', 'GNRL04', $cmd, $rc, $out );
|
|
goto FINISH_enableDisableRun;
|
|
}
|
|
|
|
my @lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
chomp( $line );
|
|
my ($ivpId, $junk) = split( ',', $line, 2 );
|
|
$ivpId =~ s/^\"|\"$//g; # trim quotes from both ends of the string
|
|
$ivpId =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string
|
|
if ( $ivpId ne '' ) {
|
|
$ids{$ivpId} = 1;
|
|
}
|
|
}
|
|
|
|
# Validate the specified id.
|
|
if ( ! exists $ids{$id} ) {
|
|
logResponse( 'DF', 'ID03', $id );
|
|
}
|
|
|
|
my $action;
|
|
my $disableFlag = '';
|
|
if ( $disable ) {
|
|
$disableFlag = 'YES';
|
|
$action = 'disable';
|
|
} else {
|
|
$action = 'enable';
|
|
}
|
|
|
|
# Update the table.
|
|
logResponse( 'DS', '*NONFORMATTED*', "Updating the z/VM IVP table (zvmivp) to $action $id" );
|
|
my $chtabCmd = "/opt/xcat/sbin/chtab id='$id' zvmivp.disable=\'$disableFlag\'";
|
|
$out = `$chtabCmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($id), rc: $rc, out: $out\n" );
|
|
$rc = 1;
|
|
}
|
|
|
|
FINISH_enableDisableRun:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 encodeDecodeString
|
|
|
|
Description : Encode and decode command line function.
|
|
Arguments : Global input set by command operands are used
|
|
by this routine:
|
|
$decode - String to be decoded if not ''.
|
|
$encode - String to be encoded if not ''.
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = encodeDecodeString();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub encodeDecodeString {
|
|
my $newVal = '';
|
|
if ( $decode ne '' ) {
|
|
binmode(STDOUT, ":utf8");
|
|
print( "Value is: '" . hexDecode( $decode ) . "'\n" );
|
|
}
|
|
|
|
if ( $encode ne '' ) {
|
|
print( "Value is: '" . hexEncode( $encode ) . "'\n" );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 finishLogFile
|
|
|
|
Description : Complete the log file for the run and
|
|
send it to the notify user if necessary.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = finishLogFile();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub finishLogFile {
|
|
my $rc = 0;
|
|
$needToFinishLogFile = 0;
|
|
|
|
# Add the summary information to the log file.
|
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
|
|
$endTime = sprintf("%02d:%02d:%02d on %04d-%02d-%02d",
|
|
$hour, $min, $sec, $year + 1900, $mon + 1, $mday );
|
|
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', 'Results of the IVP run:\n' );
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "Run started at $startTime and ended at $endTime." );
|
|
foreach my $line ( @ivpSummary ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', $line );
|
|
}
|
|
if ( $ignoreCnt != 0 ){
|
|
logResponse( 'DFS', 'GENERIC_RESPONSE', "The orchestrator script (verifynode) ignored messages $ignoreCnt times." );
|
|
my @ignoreArray = sort keys %ignored;
|
|
my $ignoreList = join ( ', ', @ignoreArray );
|
|
logResponse( 'DFS', 'GENERIC_RESPONSE', "Message Ids of ignored messages by verifynode: $ignoreList" );
|
|
}
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
|
|
# Notify the z/VM userid, if necessary.
|
|
if ( $notify and $notifyOnErrorOrWarning ) {
|
|
notifyUser();
|
|
}
|
|
|
|
FINISH_finishLogFile:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 getDistro
|
|
|
|
Description : Create the dummy image file.
|
|
Arguments : Node name or IP address
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = getDistro();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub getDistro {
|
|
my ( $nodeOrIP ) = @_;
|
|
my $rc = 0;
|
|
|
|
$tgtOS = xCAT::zvmUtils->getOsVersion( $::SUDOER, $nodeOrIP );
|
|
|
|
FINISH_getDistro:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 getLocalIPs
|
|
|
|
Description : Get the IP addresses from ifconfig.
|
|
Arguments : Node name or IP address
|
|
Returns : return code (Currently always 0)
|
|
0 - No error
|
|
non-zero - Terminating error detected.
|
|
Hash of local IP addresses
|
|
Example : $rc = getLocalIPs();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub getLocalIPs {
|
|
my $ip;
|
|
my $junk;
|
|
my $rc = 0;
|
|
|
|
my $out = `/sbin/ip addr | grep -e '^\\s*inet' -e '^\\s*inet6'`;
|
|
my @lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
my @parts = split( ' ', $line );
|
|
($ip) = split( '/', $parts[1], 2 );
|
|
$localIPs{$ip} = 1;
|
|
}
|
|
|
|
FINISH_getLocalIPs:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 getMNInfo
|
|
|
|
Description : Get information related to the xCAT MN
|
|
and the z/VM system where it is running.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = getMNInfo();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub getMNInfo {
|
|
my $cmd;
|
|
my $hostNode;
|
|
my @lines;
|
|
my $masterIP = '';
|
|
my $out = '';
|
|
my @parts;
|
|
my $rc = 0;
|
|
my $xcatMN;
|
|
|
|
# Get the list of Local IP addresses
|
|
getLocalIPs();
|
|
|
|
# Wait up to 10 minutes for xcatd to come up.
|
|
my $sleepTime = 60;
|
|
my $maxWaits = 10;
|
|
for ( my $i=1; $i<=$maxWaits; $i++ ) {
|
|
# lsdef uses the xCAT daemon. We use that to see if it is started.
|
|
`/opt/xcat/bin/lsdef`;
|
|
if ( $? == 0 ) {
|
|
last;
|
|
} else {
|
|
logResponse( 'DFS', 'WAIT01', $sleepTime, $i, $maxWaits );
|
|
sleep( $sleepTime );
|
|
}
|
|
}
|
|
|
|
# Get information related to all of the host nodes
|
|
$cmd = '/opt/xcat/bin/lsdef -w mgt=zvm -w hosttype=zvm -i hcp';
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc == 0 ) {
|
|
@lines = split( '\n', $out );
|
|
my $host = '';
|
|
foreach my $line ( @lines ) {
|
|
$line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string
|
|
if ( $line =~ /Object name: / ) {
|
|
($host) = $line =~ m/Object name: (.*)/;
|
|
next;
|
|
} elsif ( $line =~ /hcp\=/ ) {
|
|
($hosts{$host}{'hcp'}) = $line =~ m/hcp\=(.*)/;
|
|
next;
|
|
}
|
|
}
|
|
} else {
|
|
$rc = logResponse( 'DFS', 'GNRL01', "$cmd", $rc, $out );
|
|
$notify = 1;
|
|
}
|
|
|
|
# Get key info related to the xCAT MN and the notify targets from the site table.
|
|
$mnInfo{'PruneIVP'} = 7;
|
|
$cmd = "/opt/xcat/sbin/tabdump site";
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc == 0 ) {
|
|
@lines = split( '\n', $out );
|
|
@lines = grep( /^\"master\"|^\"zvmnotify\"|^\"zvmpruneivp\"/, @lines );
|
|
foreach my $line ( @lines ) {
|
|
$line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string
|
|
@parts = split( ',', $line );
|
|
$parts[1] =~ s/\"//g;
|
|
if ( $parts[0] =~ /^\"master\"/ ) {
|
|
$masterIP = $parts[1];
|
|
} elsif ( $parts[0] =~ /^\"zvmnotify\"/ ) {
|
|
my @notifyInfo = split( ';', $parts[1] );
|
|
my $notifyHost = '';
|
|
my $notifyUser = '';
|
|
foreach my $hostUser ( @notifyInfo ) {
|
|
($notifyHost) = $hostUser =~ m/(.*)\(/;
|
|
($notifyUser) = $hostUser =~ m/\((.*)\)/;
|
|
if ( defined $notifyHost and $notifyHost ne '' and defined $notifyUser and $notifyUser ne '' ) {
|
|
$hosts{$notifyHost}{'notifyUser'} = uc( $notifyUser );
|
|
} else {
|
|
$rc = logResponse( 'DFS', 'SITE01', 'zvmnotify', $parts[1], $hostUser );
|
|
$notify = 1;
|
|
}
|
|
}
|
|
} elsif ( $parts[0] =~ /^\"zvmpruneivp\"/ ) {
|
|
$mnInfo{'PruneIVP'} = $parts[1];
|
|
}
|
|
}
|
|
} else {
|
|
# Unable to read the site table
|
|
$rc = logResponse( 'DFS', 'GNRL01', "$cmd", $rc, $out );
|
|
$notify = 1;
|
|
}
|
|
|
|
# Find a node that has the same address as the master IP and then find
|
|
# the host node that relates to that node.
|
|
if ( $masterIP eq '' ) {
|
|
# Unable to get the master value
|
|
$rc = logResponse( 'DFS', 'SITE02', 'master' );
|
|
} else {
|
|
$cmd = "/opt/xcat/bin/lsdef -w ip=$masterIP -i hcp";
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc == 0 ) {
|
|
@lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
$line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string
|
|
if ( $line =~ /Object name: / ) {
|
|
($xcatMN) = $line =~ m/Object name: (.*)/;
|
|
$mnInfo{'node'} = $xcatMN;
|
|
$mnInfo{'ip'} = $masterIP;
|
|
next;
|
|
} elsif ( $line =~ /hcp\=/ ) {
|
|
($mnInfo{'hcp'}) = $line =~ m/hcp\=(.*)/;
|
|
next;
|
|
}
|
|
}
|
|
} else {
|
|
$rc = logResponse( 'DFS', 'MN01', $masterIP, $cmd, $rc, $out );
|
|
}
|
|
|
|
# Find the host node which uses the hcp.
|
|
foreach $hostNode ( keys %hosts ) {
|
|
if ( exists $hosts{$hostNode}{'hcp'} and $hosts{$hostNode}{'hcp'} eq $mnInfo{'hcp'} ) {
|
|
if ( exists $hosts{$hostNode}{'notify'} ) {
|
|
$hosts{$hostNode}{'notify'} = $hosts{$hostNode}{'notify'} . 'm';
|
|
} else {
|
|
$hosts{$hostNode}{'notify'} = 'm';
|
|
}
|
|
$mnInfo{'host'} = $hostNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
# The following is useful in a z/VM CMA system only.
|
|
# As a safety measure, in case the site table is lost or does not have zvmnotify property,
|
|
# read the OPNCLOUD application system role file to find the value of the 'notify' property
|
|
# and remember it with the xCAT MN information.
|
|
# Also, determine the system role.
|
|
if ( -e $locApplSystemRole ) {
|
|
$out = `cat /var/lib/sspmod/appliance_system_role | grep -e '^notify\=' -e '^role\='`;
|
|
@lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
$line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string
|
|
my ($key, $value) = split( '=', $line, 2 );
|
|
#($mnInfo{'notifyUser'}) = $line =~ m/^notify\=(.*)/;
|
|
if ( $key eq 'role' ) {
|
|
$mnInfo{'role'} = lc( $value );
|
|
}
|
|
if ( $key eq 'notify' ) {
|
|
$mnInfo{'notifyUser'} = uc( $value );
|
|
}
|
|
}
|
|
} else {
|
|
# Not CMA. Indicate role is not set.
|
|
$mnInfo{'role'} = '';
|
|
}
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 getOpenStackLevel
|
|
|
|
Description : Get the OpenStack version level as a
|
|
name.
|
|
Arguments : IP address of OpenStack compute node.
|
|
Returns : string - OpenStack version name
|
|
(e.g. NEWTON), or
|
|
null string - error
|
|
Example : $level = getOpenStackLevel();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub getOpenStackLevel {
|
|
my ( $nodeOrIP, $openstackUser ) = @_;
|
|
my $cmd;
|
|
my $level = '';
|
|
my $numWords = 0;
|
|
my $out = '';
|
|
my @parts;
|
|
my $rc;
|
|
my %openStackVersion;
|
|
|
|
# Get the list of known versions
|
|
$cmd = 'cat /opt/xcat/openstack.versions';
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DFS', 'GNRL01', $cmd, $rc, $out );
|
|
$level = '';
|
|
goto FINISH_getOpenStackLevel;
|
|
}
|
|
|
|
my @versionLines = split ( /\n/, $out );
|
|
foreach my $versionLine ( @versionLines ) {
|
|
if ( $versionLine =~ /^#/ ) { next; }
|
|
@parts = split( ' ', $versionLine );
|
|
$openStackVersion{$parts[0]} = $parts[1];
|
|
}
|
|
|
|
# Get the version information from the OpenStack node
|
|
$cmd = "nova-manage --version 2>&1";
|
|
($rc, $out) = sshToNode( $openstackIP, $openstackUser, $cmd );
|
|
if ( $rc != 0 ) {
|
|
# SSH failed, message already sent.
|
|
goto FINISH_getOpenStackLevel;
|
|
}
|
|
$out =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string
|
|
++$numWords while $out =~ /\S+/g;
|
|
if ( $numWords != 1 ) {
|
|
logResponse( 'DFS', 'GOSL01', $cmd, $openstackIP, $rc, $out );
|
|
goto FINISH_getOpenStackLevel;
|
|
}
|
|
|
|
@parts = split( '\.', $out );
|
|
if ( !exists $parts[0] or !exists $parts[1] ) {
|
|
logResponse( 'DFS', 'GOSL01', $cmd, $openstackIP, $rc, $out );
|
|
goto FINISH_getOpenStackLevel;
|
|
} elsif ( exists $openStackVersion{"$parts[0]."} ) {
|
|
$level = $openStackVersion{"$parts[0]."}
|
|
} elsif ( exists $openStackVersion{"$parts[0].$parts[1]."} ) {
|
|
$level = $openStackVersion{"$parts[0].$parts[1]."}
|
|
} else {
|
|
logResponse( 'DFS', 'GOSL02', $cmd, $openstackIP, $rc, $out );
|
|
goto FINISH_getOpenStackLevel;
|
|
}
|
|
|
|
FINISH_getOpenStackLevel:
|
|
return $level;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 hexDecode
|
|
|
|
Description : Convert a string of printable hex
|
|
characters (4 hex characters per actual
|
|
character) into the actual string that
|
|
it represents. The string should
|
|
begin with 'HexEncoded:' which indicates
|
|
that it is encoded. The routine tolerates
|
|
getting called with a string that is not
|
|
not encoded and will return the string that
|
|
was passed to it instead of trying to decode
|
|
it.
|
|
Arguments : printable hex value
|
|
Returns : Perl string
|
|
Example : $rc = hexDecode();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub hexDecode {
|
|
my ( $hexVal ) = @_;
|
|
my $result = '';
|
|
|
|
if ( $hexVal =~ /^HexEncoded:/ ) {
|
|
($hexVal) = $hexVal =~ m/HexEncoded:(.*)/;
|
|
my @hexes = unpack( "(a4)*", $hexVal);
|
|
for ( my $i = 0; $i < scalar(@hexes); $i++ ) {
|
|
$result .= chr( hex( $hexes[$i] ) );
|
|
}
|
|
} else {
|
|
$result = $hexVal;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 hexEncode
|
|
|
|
Description : Convert a string into a string of
|
|
printable hex characters in which each
|
|
character in the original string is
|
|
represented by the 2 byte (4 char) hex
|
|
code value. The string is preceded by
|
|
'HexEncoded:' to indicate that it has
|
|
been encoded. The routine will tolerate
|
|
getting called with a string that has
|
|
already been encoded and not reencode it.
|
|
Arguments : ASCII or Unicode string
|
|
Returns : Hex string
|
|
Example : $rc = hexEncode();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub hexEncode {
|
|
my ( $str ) = @_;
|
|
my $hex;
|
|
my $result = $str; # All work done within the result variable.
|
|
|
|
if ( $result ne '' ) {
|
|
# Encode the string if is not already encoded. Otherwise, leave it alone.
|
|
if ( $result !~ /^HexEncoded:/ ) {
|
|
$result =~ s/(.)/sprintf("%04x",ord($1))/eg;
|
|
$result = "HexEncoded:$result";
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 logResponse
|
|
|
|
Description : Build and log the response.
|
|
Arguments : Target destination for the response. This is
|
|
string which can contain a charater for each
|
|
possible destination, e.g 'DSF'. The values are:
|
|
D - send to STDOUT
|
|
S - send to syslog
|
|
F - send to the log file
|
|
The default is 'DF' so that we send the output
|
|
to STDOUT and put it in the log file.
|
|
message ID or special flag:
|
|
*NONFORMATTED* indicates that the message build should not
|
|
be invoked but instead the message substitutions are
|
|
lines for the message to be produced.
|
|
*RESET* indicates that the message counter should be reset.
|
|
Array of message substitutions
|
|
Returns : 0 - No error, general response or info message detected.
|
|
1 - Non-terminating message detected.
|
|
2 - Terminating message detected.
|
|
Example : $rc = logResponse( 'D', 'VX01' );
|
|
$rc = logResponse( '', 'VX03', $nodeName, $sub2);
|
|
$rc = logResponse( 'DFS', 'VX03', $nodeName, 'sub2a');
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub logResponse {
|
|
my ( $dest, $msgId, @msgSubs ) = @_;
|
|
my $rc = 0;
|
|
my $extraInfo = '';
|
|
my $msg = '';
|
|
my @msgLines;
|
|
my $sev;
|
|
my $line;
|
|
|
|
if ( $msgId eq '*RESET*' ) {
|
|
$warnErrCnt = 0;
|
|
goto FINISH_logResponse;
|
|
} elsif ( $msgId eq '*NONFORMATTED*' ) {
|
|
$rc = 0;
|
|
if ( @msgSubs ) {
|
|
foreach my $line ( @msgSubs ) {
|
|
$msg = "$msg$line\n";
|
|
}
|
|
} else {
|
|
$msg = '\n';
|
|
}
|
|
$sev = 0;
|
|
} else {
|
|
( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('VERIFYNODE', $msgId, \@msgSubs);
|
|
if ( defined $msgsToIgnore{$msgId} ) {
|
|
# Ignore this message id
|
|
$ignored{$msgId} = 1;
|
|
$ignoreCnt += 1;
|
|
print( "Message $msgId is being ignored but would have occurred here.\n" );
|
|
goto FINISH_logResponse;
|
|
} elsif ( defined $msgsToIgnore{$sev} ) {
|
|
# Ignoring all messages of this severity.
|
|
$ignored{$msgId} = 1;
|
|
$ignoreCnt += 1;
|
|
print( "Message $msgId is being ignored but would have occurred here.\n" );
|
|
goto FINISH_logResponse;
|
|
}
|
|
}
|
|
|
|
if ( $sev >= 4 ) {
|
|
$warnErrCnt += 1;
|
|
}
|
|
|
|
# Send the message to the requested destination.
|
|
if ( $dest =~ 'D' ) {
|
|
print "$msg"; # Send message to STDOUT
|
|
}
|
|
if ( $dest =~ 'F' and defined $logFileHandle ) {
|
|
print $logFileHandle $msg;
|
|
}
|
|
if ( $dest =~ 'S' ) {
|
|
my $logMsg = $msg;
|
|
$logMsg =~ s/\t//g;
|
|
$logMsg =~ s/\n/ /g;
|
|
syslog( 'err', $logMsg );
|
|
}
|
|
|
|
# Send the extra info to the requested destination (never send it to syslog).
|
|
if ( $extraInfo ne '' ) {
|
|
if ( $dest =~ 'D' ) {
|
|
print "$extraInfo";
|
|
}
|
|
if ( $dest =~ 'F' and defined $logFileHandle ) {
|
|
print $logFileHandle $msg;
|
|
}
|
|
}
|
|
|
|
FINISH_logResponse:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 notifyUser
|
|
|
|
Description : Notify a z/VM user. Send a message
|
|
and the log as a SPOOL file.
|
|
Arguments : Node name or IP address
|
|
Returns : 0 - Always ok.
|
|
Example : $rc = notifyUser();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub notifyUser {
|
|
my $cmd;
|
|
my $host;
|
|
my @hostOrder;
|
|
my $msg = '';
|
|
my $out = '';
|
|
my $rc = 0;
|
|
my $tempFile;
|
|
my $th;
|
|
|
|
# Determine who we should notify based on the host related to the verification.
|
|
# The driver script would have set the host related to an full IVP (if we can trust it).
|
|
# If we cannot get to the host related to the run then we fall back to sending
|
|
# the notification to the user on the host in which the xCAT MN is running.
|
|
# If that fails then "tough luck, Chuck".
|
|
my $mnAdded = 0;
|
|
foreach $host ( keys %hosts ) {
|
|
if ( exists $hosts{$host}{'notify'} and exists $hosts{$host}{'hcp'} and exists $hosts{$host}{'notifyUser'} ) {
|
|
if ( $hosts{$host}{'notify'} =~ /d/ ) {
|
|
# Driver script specified hosts go on the top of the stack.
|
|
push @hostOrder, $host;
|
|
if ( $hosts{$host}{'notify'} =~ /m/ ) {
|
|
# The host related to the MN is already in the list
|
|
$mnAdded = 1;
|
|
}
|
|
} elsif ( $hosts{$host}{'notify'} =~ /m/ ) {
|
|
# Management node systems go on the bottom as a fall back plan.
|
|
unshift @hostOrder, $host;
|
|
$mnAdded = 1;
|
|
}
|
|
}
|
|
}
|
|
if ( $mnAdded == 0 and exists $mnInfo{'hcp'} and exists $mnInfo{'notifyUser'} ) {
|
|
# Did not find a host related to this management node but we have the
|
|
# necessary information to send the notification so add a dummy host
|
|
# to the hosts hash and add the dummy host to the end of the hostOrder
|
|
# stack to be used when all other notification attemps fail.
|
|
$host = '*DEFAULT_MN_HOST*';
|
|
$hosts{$host}{'notify'} = 'm';
|
|
$hosts{$host}{'notifyUser'} = $mnInfo{'notifyUser'};
|
|
$hosts{$host}{'hcp'} = $mnInfo{'hcp'};
|
|
unshift @hostOrder, $host;
|
|
}
|
|
|
|
# Prepare the message.
|
|
$msg = "The xCAT IVP detected some possible issues. A log file is being sent to your reader.";
|
|
|
|
# Prepare the log file by removing tabs and splitting the log file at 80 character lines.
|
|
$tempFile = mktemp( "$logDir/$logFile.XXXXXX" );
|
|
`expand $logDir/$logFile | fold -w 80 1>$tempFile`;
|
|
|
|
# Send the message and log file to the first entity in the host order list that lets us
|
|
# successfully do so.
|
|
my $done = 0;
|
|
my $failed = 0;
|
|
foreach $host ( @hostOrder ) {
|
|
# Check if zHCP's punch is online and online it if it is not.
|
|
($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', 'cat /sys/bus/ccw/drivers/vmur/0.0.000d/online' );
|
|
chomp( $out );
|
|
if ($rc != 0 or $out != 1) {
|
|
$cmd = '/sbin/cio_ignore -r 000d;/sbin/chccwdev -e 000d';
|
|
($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', $cmd );
|
|
chomp( $out );
|
|
if ( $rc != 0 or !( $out =~ m/Done$/i ) ) {
|
|
$rc = logResponse( 'DFS', 'GNRL03', $hosts{$host}{'hcp'}, $cmd, $rc, $out );
|
|
next;
|
|
}
|
|
($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', 'which udevadm &> /dev/null && udevadm settle || udevsettle' );
|
|
# Don't worry about the udevadm settle. If it remains a problem then we will see it on the next command to ZHCP.
|
|
}
|
|
|
|
# Send log file. It will exist temporarily on the ZHCP agent in the /tmp directory
|
|
# under its original log file name.
|
|
$rc = xCAT::zvmUtils->sendFile( 'root', $hosts{$host}{'hcp'}, "$tempFile", "/tmp/$logFile" );
|
|
if ( $rc != 0 ) {
|
|
# An error is not a problem because the zhcp node could be logged off.
|
|
next;
|
|
}
|
|
|
|
# Punch the file from the ZHCP agent to the target user.
|
|
$out = xCAT::zvmCPUtils->punch2Reader( 'root', $hosts{$host}{'hcp'}, $hosts{$host}{'notifyUser'}, "/tmp/$logFile", 'XCAT_IVP.RESULTS', '-t', 'A' );
|
|
if ( $out ne 'Done' ) {
|
|
$rc = logResponse( 'DFS', 'GNRL02', "/tmp/$logFile", $hosts{$host}{'hcp'}, $hosts{$host}{'notifyUser'}, $host, "$logDir/$logFile", $out );
|
|
$failed = 1;
|
|
}
|
|
|
|
# Clean up the ZHCP node.
|
|
($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', "rm -f /tmp/$logFile" );
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DFS', 'GNRL03', $hosts{$host}{'hcp'}, "rm -f /tmp/$logFile", $rc, $out );
|
|
next;
|
|
}
|
|
|
|
# Try another host if we failed to send the file.
|
|
if ( $failed ) {
|
|
$failed = 0;
|
|
next;
|
|
}
|
|
|
|
# Send a message if the user is logged on the system.
|
|
($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', "vmcp query user $hosts{$host}{'notifyUser'}" );
|
|
if ( $rc != 0 ) {
|
|
# User is either not logged on or we had a problem issuing the command. Not important, leave.
|
|
last;
|
|
}
|
|
$out = uc( $out );
|
|
if ( $out =~ /^$hosts{$host}{'notifyUser'}/ ) {
|
|
my ($userStatus, $junk) = split '\s', $out, 2;
|
|
chomp( $userStatus );
|
|
$userStatus =~ s/^\s+|\s+$//g; # trim both ends of the string
|
|
if ( $userStatus eq 'SSI' or $userStatus eq 'DSC' ) {
|
|
logResponse( 'DFS', 'MSG01', $hosts{$host}{'notifyUser'}, $host, $userStatus );
|
|
last;
|
|
}
|
|
}
|
|
|
|
# Don't worry if message still does not work. It is only a message.
|
|
($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', "vmcp message $hosts{$host}{'notifyUser'} $msg" );
|
|
last;
|
|
}
|
|
|
|
# Remove the temporary log file from the xCAT MN system.
|
|
$out = `rm -f $tempFile`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DFS', 'GNRL01', "rm -f $tempFile", $rc, $out );
|
|
}
|
|
|
|
$rc = 0;
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
|
|
=head3 pruneLogs
|
|
|
|
Description : Compress and prune log files. The number of days to wait
|
|
before pruning is specified in the site table with the
|
|
zvmpruneivp property. If the property does not exist
|
|
or Management node information does not exist because of
|
|
some reason then the default is to prune logs that are
|
|
7 days or older.
|
|
Arguments : Current year
|
|
Current month (numeric)
|
|
Current day (Julian day, numeric)
|
|
Returns : None.
|
|
Example : pruneLogs( $year, $mon + 1, $mday );
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub pruneLogs{
|
|
my ( $year, $month, $day ) = @_;
|
|
|
|
my @lines;
|
|
my $pruneOffset = 0;
|
|
my $out;
|
|
my $pruneDate = '';
|
|
my $rc;
|
|
my $removed = 0;
|
|
my $zipOut;
|
|
my $zipped = 0;
|
|
logResponse( 'DFS', '*NONFORMATTED*', "CRON job is pruning log files." );
|
|
|
|
# Determine the prune date value.
|
|
if ( ! exists $mnInfo{'PruneIVP'} or $mnInfo{'PruneIVP'} eq '' ) {
|
|
$pruneOffset = 7;
|
|
} else {
|
|
if ( looks_like_number( $mnInfo{'PruneIVP'} ) ) {
|
|
$pruneOffset = $mnInfo{'PruneIVP'};
|
|
} else {
|
|
$pruneOffset = 7;
|
|
logResponse( 'DFS', 'TP01', 'zvmpruneivp', 'site', 'not numeric', $mnInfo{'PruneIVP'}, $pruneOffset );
|
|
}
|
|
}
|
|
$pruneDate = strftime "%Y-%m-%d", 0, 0, 0, $day - $pruneOffset, $month-1, $year-1900;
|
|
logResponse( 'DFS', '*NONFORMATTED*', "Prune date: $pruneDate." );
|
|
|
|
# Zip any log files that are not for today. Current log files are similar to
|
|
# /var/log/xcat/ivp/IVP_XCAT_2016-09-19_01:01:03.log and will have .gz appended to the name
|
|
# after they have been zipped.
|
|
$out = `ls -1 /var/log/xcat/ivp/*.log`;
|
|
$rc = $?;
|
|
if ( $rc == 0 ) {
|
|
@lines = split( '\n', $out );
|
|
foreach my $fqFile ( @lines ) {
|
|
chomp( $fqFile );
|
|
my ($file) = $fqFile =~ /\/var\/log\/xcat\/ivp\/(.*)/;
|
|
my @parts = split( '_', $file );
|
|
if ( ! defined $parts[0] or $parts[0] ne 'IVP' or
|
|
! defined $parts[1] or ! defined $parts[2] or
|
|
$parts[2] eq $todayDate ) {
|
|
next;
|
|
}
|
|
#logResponse( 'DFS', 'GENERIC_RESPONSE', "ZIPPING: $file\n" );
|
|
$zipOut = `gzip $fqFile 2>&1`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "Unable to gzip $fqFile, rc: $rc, out: $out" );
|
|
} else {
|
|
$zipped++;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Prune any gzipped files that are too old.
|
|
$out = `ls -1 /var/log/xcat/ivp/*.log.gz`;
|
|
$rc = $?;
|
|
if ( $rc == 0 ) {
|
|
@lines = split( '\n', $out );
|
|
foreach my $fqFile ( @lines ) {
|
|
chomp( $fqFile );
|
|
my ($file) = $fqFile =~ /\/var\/log\/xcat\/ivp\/(.*)/;
|
|
my @parts = split( '_', $file );
|
|
if ( ! defined $parts[0] or $parts[0] ne 'IVP' or
|
|
! defined $parts[1] or ! defined $parts[2] or
|
|
$parts[2] ge $todayDate ) {
|
|
next;
|
|
}
|
|
if ( $parts[2] ge $pruneDate ) {
|
|
next;
|
|
}
|
|
|
|
#logResponse( 'DFS', 'GENERIC_RESPONSE', "REMOVING: $file\n" );
|
|
$zipOut = `rm $fqFile 2>&1`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "Unable to remove \'rm $fqFile\', rc: $rc, out: $out" );
|
|
} else {
|
|
$removed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
logResponse( 'DFS', '*NONFORMATTED*', "IVP log pruning completed, zipped: $zipped, pruned: $removed." );
|
|
return;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 removeIVP
|
|
|
|
Description : Remove an automated IVP.
|
|
Arguments : ID of the IVP to be removed
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = removeIVP();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub removeIVP {
|
|
my ( $id ) = @_;
|
|
my $cmd;
|
|
my $out;
|
|
my $rc;
|
|
my $ivpId;
|
|
my %ids;
|
|
my $junk;
|
|
|
|
# Get the list of IVP ids in the zvmivp table.
|
|
$cmd = '/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"';
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc ne 0 ) {
|
|
logResponse( 'DF', 'GNRL04', $cmd, $rc, $out );
|
|
goto FINISH_removeIVP;
|
|
}
|
|
my @lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
chomp( $line );
|
|
my ($ivpId, $junk) = split( ',', $line, 2 );
|
|
$ivpId =~ s/^\"|\"$//g; # trim quotes from both ends of the string
|
|
$ivpId =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string
|
|
if ( $ivpId ne '' ) {
|
|
$ids{$ivpId} = 1;
|
|
}
|
|
}
|
|
|
|
# Validate the specified id.
|
|
if ( $id eq '' or ! exists $ids{$id} ) {
|
|
logResponse( 'DF', 'ID03', $id );
|
|
}
|
|
|
|
# Update the table.
|
|
logResponse( 'DS', '*NONFORMATTED*', "Updating the z/VM IVP table (zvmivp) to remove $id" );
|
|
my $chtabCmd = "/opt/xcat/sbin/chtab -d id=\'$id\' zvmivp";
|
|
$out = `$chtabCmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($id), rc: $rc, out: $out\n" );
|
|
$rc = 1;
|
|
}
|
|
|
|
FINISH_removeIVP:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 runDriverScript
|
|
|
|
Description : Run a downloaded driver script.
|
|
Arguments : This routine is driven from the action
|
|
table, verifySets. It takes global
|
|
input that can be set as command
|
|
input or by precursor routines.
|
|
The input variables include:
|
|
$driver - Driver script name
|
|
$driverLoc - Location of the driver script
|
|
$hosts - Infor for defined host nodes
|
|
$infoCnt - Count of warnings that were generated
|
|
$warningCnt - Count of warnings that were generated
|
|
$zxcatParms - Command line parms to
|
|
pass to zxcatIVP
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = runDriverScript();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub runDriverScript{
|
|
my $infoCnt = 0;
|
|
my @lines;
|
|
my $out;
|
|
my $rc = 0;
|
|
my $warningCnt = 0;
|
|
|
|
# Analyze the driver script to determine the z/VM host that it intends to support.
|
|
my $hostNodeZhcp = '';
|
|
my $hostNode = `cat $driverLoc/$driver | grep "^export zxcatIVP_hostNode\=" | sed '/^export zxcatIVP_hostNode\=*/!d; s///; s/\"//g;q'`;
|
|
chomp( $hostNode );
|
|
|
|
if ( $hostNode ne '' ) {
|
|
if ( exists $hosts{$hostNode}{'hcp'} ) {
|
|
if ( exists $hosts{$hostNode}{'notify'} ) {
|
|
$hosts{$hostNode}{'notify'} = $hosts{$hostNode}{'notify'} . 'd';
|
|
} else {
|
|
$hosts{$hostNode}{'notify'} = 'd';
|
|
}
|
|
$zhcpTarget = $hosts{$hostNode}{'hcp'};
|
|
$zvmHost = $hostNode;
|
|
push @ivpSummary, "The ZHCP related to the host, \'$hostNode\', identified in the driver script has been identified ".
|
|
"as \'$zhcpTarget\'.";
|
|
} else {
|
|
$rc = logResponse( 'DFS', 'DRIV03', $zvmHost );
|
|
$notify = 1;
|
|
}
|
|
} else {
|
|
# Driver script did not specify the host node.
|
|
$rc = logResponse( 'DFS', 'DRIV02', "$driverLoc/$driver", 'zxcatIVP_hostNode' );
|
|
$notify = 1;
|
|
}
|
|
|
|
# Analyze the driver script to determine the ZHCP node that it intends to support.
|
|
my $zhcpNode = `cat $driverLoc/$driver | grep "^export zxcatIVP_zhcpNode\=" | sed '/^export zxcatIVP_zhcpNode\=*/!d; s///; s/\"//g;q'`;
|
|
chomp( $zhcpNode );
|
|
my $zhcpHcp = `/opt/xcat/bin/lsdef $zhcpNode | grep "hcp\=" | sed '/hcp\=*/!d; s///; s/\"//g;q'`;
|
|
if ( $zhcpHcp ne '' ) {
|
|
$zhcpHcp =~ s/^\s+|\s+$//g; # trim both ends of the string
|
|
$zhcpTarget = $zhcpHcp;
|
|
foreach $hostNode ( keys %hosts ) {
|
|
if ( exists $hosts{$hostNode}{'hcp'} and $hosts{$hostNode}{'hcp'} eq $zhcpHcp ) {
|
|
if ( exists $hosts{$hostNode}{'notify'} ) {
|
|
$hosts{$hostNode}{'notify'} = $hosts{$hostNode}{'notify'} . 'd';
|
|
} else {
|
|
$hosts{$hostNode}{'notify'} = 'd';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
my $exportCmd = '';
|
|
if ( $zxcatParms ne '' ) {
|
|
$ENV{zxcatIVP_moreCmdOps}=$zxcatParms;
|
|
} else {
|
|
delete $ENV{zxcatIVP_moreCmdOps};
|
|
}
|
|
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "Using the driver script to drive the IVP. The output from the run follows." );
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
$out = `chmod +x $driverLoc/$driver`;
|
|
$out = `$driverLoc/$driver`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DFS', 'DRIV01', "$driverLoc/$driver", $rc, $out );
|
|
$notify = 1;
|
|
goto FINISH_runDriverScript;
|
|
}
|
|
|
|
# Determine how many warnings and information messages were generated so that we can
|
|
# produce a summary message upon completion of the IVP.
|
|
@lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
if ( $line =~ /^Warning \(/ ) { $warningCnt++; }
|
|
if ( $line =~ /^Info \(/ ) { $infoCnt++; }
|
|
}
|
|
push @ivpSummary, "The driver script generated $warningCnt warnings and $infoCnt information messages.";
|
|
logResponse( 'DF', '*NONFORMATTED*', $out );
|
|
|
|
FINISH_runDriverScript:
|
|
if ( $warningCnt != 0 ) {
|
|
$notify = 1;
|
|
}
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 runPrepScript
|
|
|
|
Description : Push the appropriate level of preparation
|
|
script to a compute node and run it to
|
|
validate the system and build the driver
|
|
script.
|
|
Arguments : Global defaults set as command line parameters provide input
|
|
to this routine:
|
|
$driver - Name of driver script
|
|
$driverLoc - Location of the driver script
|
|
$infoCnt - Count of warnings that were generated
|
|
$openstackIP - IP for OpenStack compute node
|
|
$openstackUser - User for OpenStack compute node
|
|
$prepParms - Parms for preparation script
|
|
$warnErrCnt - Count of warnings and errors that were generated
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = runPrepScript();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub runPrepScript {
|
|
my $cmd = '';
|
|
my $infoCnt = 0;
|
|
my $line;
|
|
my @lines;
|
|
my $out = '';
|
|
my $rc = 0;
|
|
my $retRC = 0;
|
|
my $tmpDir = '';
|
|
my $warningCnt = 0;
|
|
|
|
# Determine the name of the driver script based on the IP address for the compute node.
|
|
$driver = "zxcatIVPDriver_$openstackIP.sh";
|
|
|
|
# Create the local IVP directory.
|
|
if ( !-d $driverLoc ) {
|
|
$out = `mkdir -m a=rwx,g=rx,o= -p $driverLoc`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DFS', 'PREP01', $driverLoc, $rc, $out );
|
|
$notify = 1;
|
|
goto FINISH_runPrepScript;
|
|
}
|
|
}
|
|
|
|
# Determine the OpenStack level.
|
|
my $level = getOpenStackLevel( $openstackIP, $openstackUser );
|
|
if ( $level eq '' ) {
|
|
$notify = 1;
|
|
$rc = logResponse( 'DFS', 'PREP06', $openstackIP );
|
|
goto FINISH_runPrepScript;
|
|
}
|
|
my $levelLetter = substr( $level, 0, 1);
|
|
push @ivpSummary, "OpenStack system at $openstackIP is running the Nova $level release.";
|
|
|
|
# Determine the appropriate preparation script to send to the compute node.
|
|
my $prepDir = '/opt/xcat/share/xcat/tools/zvm';
|
|
my $prepScript = "prep_zxcatIVP_$level.pl";
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', "Attempting to push $prepScript to the OpenStack system ".
|
|
"at $openstackIP. It will be used to validate the OpenStack environment and create ".
|
|
"a driver script to be used for the validation on the xCAT management node." );
|
|
my $runPrepScript = 1;
|
|
|
|
# Create a directory on the compute node to hold the preparation script and driver script.
|
|
$cmd = 'mktemp -d /tmp/xCAT.XXXXXXXXXX';
|
|
($rc, $tmpDir) = sshToNode( $openstackIP, $openstackUser, $cmd );
|
|
if ( $rc != 0 ) {
|
|
# Unable to create the directory. Let's see if we have an old driver script that we can use.
|
|
$rc = logResponse( 'DFS', 'PREP02', $openstackIP, "$driverLoc/$driver", $cmd, $rc, $out );
|
|
$notify = 1;
|
|
goto FINISH_runPrepScript;
|
|
} else {
|
|
# Push the preparation script to the compute node and run it.
|
|
chomp($tmpDir);
|
|
$cmd = "/usr/bin/scp $prepDir/$prepScript $openstackUser\@$openstackIP:$tmpDir";
|
|
$out = `/usr/bin/scp "$prepDir/$prepScript" "$openstackUser\@$openstackIP:$tmpDir"`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
# Unable to push the preparation script. Let's see if we have an old driver script that we can use.
|
|
$rc = logResponse( 'DFS', 'PREP02', $openstackIP, "$driverLoc/$driver", $cmd, $rc, $out );
|
|
$notify = 1;
|
|
goto FINISH_runPrepScript;
|
|
}
|
|
}
|
|
|
|
if ( $runPrepScript ) {
|
|
# Run the preparation script.
|
|
($rc, $out) = sshToNode( $openstackIP, $openstackUser, "chmod +x $tmpDir/$prepScript");
|
|
($rc, $out) = sshToNode( $openstackIP, $openstackUser, "$tmpDir/$prepScript '--driver' $tmpDir/$driver $prepParms" );
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "The output from the preparation script that is run on the compute node follows. ".
|
|
"The preparation script validates the z/VM related OpenStack configuration file properties." );
|
|
logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' );
|
|
logResponse( 'DF', '*NONFORMATTED*', $out );
|
|
|
|
# Determine how many warnings were generated so that we can produce a summary message upon completion of the IVP.
|
|
if ( $levelLetter gt 'L' ) {
|
|
@lines = split( '\n', $out );
|
|
foreach $line ( @lines ) {
|
|
if ( $line =~ /^Warning \(/ ) { $warningCnt++; }
|
|
if ( $line =~ /^Info \(/ ) { $infoCnt++; }
|
|
}
|
|
} else {
|
|
@lines = split( '\n', $out );
|
|
foreach $line ( @lines ) {
|
|
if ( $line =~ /^Warning:/ ) { $warningCnt++; }
|
|
if ( $line =~ /^Info:/ ) { $infoCnt++; }
|
|
}
|
|
}
|
|
push @ivpSummary, "The preparation script generated $warningCnt warnings and $infoCnt information messages.";
|
|
|
|
# Pull back the driver script.
|
|
if ( -e "$driverLoc/$driver" ) {
|
|
$out = `mv -f $driverLoc/$driver $driverLoc/$driver.old`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'S', 'GENERIC_RESPONSE', "Unable to move $driverLoc/$driver to $driverLoc/$driver.old, rc: $rc" );
|
|
$notify = 1;
|
|
}
|
|
}
|
|
|
|
$cmd = "/usr/bin/scp $openstackUser\@$openstackIP:$tmpDir/$driver $driverLoc/$driver";
|
|
$out = `/usr/bin/scp "$openstackUser\@$openstackIP:$tmpDir/$driver" "$driverLoc/$driver"`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DFS', 'PREP04', "$driverLoc/$driver", $openstackIP, $cmd, $rc, $out );
|
|
$notify = 1;
|
|
goto FINISH_runPrepScript;
|
|
}
|
|
|
|
$cmd = "chmod 660 $driverLoc/$driver 2>&1";
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'FS', 'PREP07', "$driverLoc/$driver", $cmd, $rc, $out );
|
|
$notify = 1;
|
|
}
|
|
}
|
|
|
|
FINISH_runPrepScript:
|
|
if ( !-s "$driverLoc/$driver" && -s "$driverLoc/$driver.old" ) {
|
|
# New driver does not exist but old driver exists, use the old one.
|
|
$out = `mv -f $driverLoc/$driver.old $driverLoc/$driver`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'S', 'GENERIC_RESPONSE', "Unable to move $driverLoc/$driver.old to $driverLoc/$driver, rc: $rc" );
|
|
}
|
|
}
|
|
|
|
if ( -s "$driverLoc/$driver" ) {
|
|
$retRC = 0;
|
|
# Add the driver script to the log file.
|
|
$out = `cat $driverLoc/$driver`;
|
|
logResponse( 'F', '*NONFORMATTED*', '*******************************************************************************' );
|
|
logResponse( 'F', 'GENERIC_RESPONSE_NOINDENT', "The contents of the driver script used for the rest of this IVP follows. ".
|
|
"The driver script is used to run the rest of the IVP on the xCAT Management Node." );
|
|
logResponse( 'F', 'GENERIC_RESPONSE_NOINDENT', 'Note: Any line which set a password related property has been removed.' );
|
|
logResponse( 'F', '*NONFORMATTED*', '*******************************************************************************' );
|
|
$out = `echo "$out" | grep -v "^export zxcatIVP_xcatUserPw"`;
|
|
logResponse( 'F', '*NONFORMATTED*', $out );
|
|
}
|
|
|
|
if ( $warningCnt != 0 ) {
|
|
$notify = 1;
|
|
}
|
|
|
|
if ( $tmpDir ne '' ) {
|
|
$cmd = "rm -Rf $tmpDir 2>&1";
|
|
($rc, $out) = sshToNode( $openstackIP, $openstackUser, "$cmd");
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DFS', 'CLNUP01', $openstackIP, $tmpDir, $cmd, $rc, $out );
|
|
$notify = 1;
|
|
}
|
|
}
|
|
return $retRC;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 scheduleIVP
|
|
|
|
Description : Schedule an automated IVP.
|
|
Arguments : Global defaults set as command line parameters provide input
|
|
to this routine:
|
|
$comments - Comments for IVP
|
|
$disable - Disable option
|
|
$id - ID to update or 'NEW'
|
|
$openstackIP - IP for OpenStack compute node
|
|
$openstackUser - User for OpenStack compute node
|
|
$orchParms - Parms for this script when IVP run
|
|
$prepParms - Parms for preparation script
|
|
$schedule - Hours to schedule
|
|
$scheduledType - Type of IVP
|
|
$zxcatParms - Parms for zxcatIVP script
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = scheduleIVP();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub scheduleIVP {
|
|
my $cmd;
|
|
my $out;
|
|
my $rc;
|
|
my $ivpId;
|
|
my %ids;
|
|
my $junk;
|
|
|
|
# Get the list of IVP ids in the zvmivp table.
|
|
$cmd = '/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"';
|
|
$out = `$cmd`;
|
|
$rc = $?;
|
|
if ( $rc ne 0 ) {
|
|
logResponse( 'DF', 'GNRL04', $cmd, $rc, $out );
|
|
goto FINISH_scheduleIVP;
|
|
}
|
|
my @lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
chomp( $line );
|
|
my ($ivpId, $junk) = split( ',', $line, 2 );
|
|
$ivpId =~ s/^\"|\"$//g; # trim quotes from both ends of the string
|
|
$ivpId =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string
|
|
if ( $ivpId ne '' ) {
|
|
$ids{$ivpId} = 1;
|
|
}
|
|
}
|
|
|
|
# Validate the specified id. Either generate a new ID if this is a new
|
|
# request (specified as 'new' or omitted or use the id that was passed.
|
|
if ( $id eq '' or $id eq 'NEW' ) {
|
|
# Generate the new Id
|
|
my $i;
|
|
for ( $i = 10; $i < 10010; $i++ ) {
|
|
if ( ! exists $ids{$i} ) {
|
|
last;
|
|
}
|
|
}
|
|
if ( $i <= 10010 ) {
|
|
$id = $i;
|
|
} else {
|
|
# Could not find an available number in the first 10000 IDs.
|
|
logResponse( 'DF', 'ID01' );
|
|
goto FINISH_scheduleIVP;
|
|
}
|
|
} else {
|
|
# Validate the Id
|
|
if ( ! exists $ids{$id} ) {
|
|
logResponse( 'DF', 'ID02', $id );
|
|
}
|
|
}
|
|
|
|
if ( $scheduledType eq '' ) {
|
|
logResponse( 'DF', 'OPER01', '--type' );
|
|
goto FINISH_scheduleIVP;
|
|
}
|
|
$scheduledType = lc( $scheduledType );
|
|
if ( $scheduledType ne 'basicivp' and $scheduledType ne 'fullivp' ) {
|
|
logResponse( 'DF', 'OPER02', '--type', $scheduledType, '\'basicivp\' or \'fullivp\'' );
|
|
goto FINISH_scheduleIVP;
|
|
}
|
|
if ( $scheduledType eq 'basicivp' ) {
|
|
# Ignore any FULLIVP parms
|
|
$openstackIP = '';
|
|
$openstackUser = '';
|
|
$prepParms = '';
|
|
}
|
|
|
|
# Normalize the schedule.
|
|
my %hours;
|
|
my @parts = split( ' ', $schedule );
|
|
foreach my $hour ( @parts ) {
|
|
$hour =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string
|
|
$hours{$hour} = 1;
|
|
}
|
|
$schedule = '';
|
|
for ( my $hour = 0; $hour <= 23; $hour++ ) {
|
|
if ( exists $hours{$hour} ) {
|
|
$schedule = "$schedule $hour";
|
|
delete $hours{$hour};
|
|
}
|
|
}
|
|
$schedule =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string
|
|
my (@leftOver) = keys %hours;
|
|
if ( scalar @leftOver != 0 ) {
|
|
my $badValues = join( '\' and \'', @leftOver );
|
|
logResponse( 'DF', 'OPER02', '--schedule', $badValues, '0-23' );
|
|
goto FINISH_scheduleIVP;
|
|
}
|
|
|
|
if ( $comments =~ /,/ ) {
|
|
$comments =~ s/,//g; # trim commas from the string
|
|
}
|
|
|
|
my $disableFlag = '';
|
|
if ( $disable == 1 ) {
|
|
$disableFlag = 'YES';
|
|
}
|
|
|
|
# Update the table.
|
|
logResponse( 'DS', '*NONFORMATTED*', "Updating the z/VM IVP table (zvmivp) for $id" );
|
|
my $chtabCmd = "/opt/xcat/sbin/chtab id=\'$id\' ".
|
|
"zvmivp.ip=\'$openstackIP\' ".
|
|
"zvmivp.schedule=\'$schedule\' ".
|
|
"zvmivp.last_run=\'\' ".
|
|
"zvmivp.type_of_run=\'$scheduledType\' ".
|
|
"zvmivp.access_user=\'" . hexEncode( $openstackUser ) . "\' ".
|
|
"zvmivp.orch_parms=\'" . hexEncode( $orchParms ) . "\' ".
|
|
"zvmivp.prep_parms=\'" . hexEncode( $prepParms ) . "\' ".
|
|
"zvmivp.main_ivp_parms=\'" . hexEncode( $zxcatParms ) . "\' ".
|
|
"zvmivp.comments=\'" . hexEncode( $comments ) . "\' ".
|
|
"zvmivp.disable=\'$disableFlag\'";
|
|
$out = `$chtabCmd`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($id), rc: $rc, out: $out\n" );
|
|
$rc = 1;
|
|
}
|
|
|
|
FINISH_scheduleIVP:
|
|
return $rc;
|
|
}
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 setupLogFile
|
|
|
|
Description : Set up the log file for this run.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = setupLogFile();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub setupLogFile {
|
|
my $out;
|
|
my $rc = 0;
|
|
my $ipString = '';
|
|
|
|
# Indicate that the log file will need to be finished.
|
|
# This is a safety in case something goes wrong during
|
|
# the IVP run and causes the program to break.
|
|
$needToFinishLogFile = 1;
|
|
|
|
# Determine the IP address to associate with the log file.
|
|
if ( $openstackIP eq '' ) {
|
|
# Must be doing either a generic zxcatIVP or a full
|
|
# IVP for the compute node sharing the system with this
|
|
# xCAT MN. Use the string "XCAT".
|
|
$ipString = 'XCAT';
|
|
} else {
|
|
if ( exists $localIPs{$openstackIP} ) {
|
|
$ipString = 'XCAT';
|
|
} else {
|
|
$ipString = $openstackIP;
|
|
}
|
|
}
|
|
|
|
# Determine the timestamp to use for the log.
|
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
|
|
$startTime = sprintf("%02d:%02d:%02d on %04d-%02d-%02d",
|
|
$hour, $min, $sec, $year + 1900, $mon + 1, $mday );
|
|
my $currTime = sprintf("%04d-%02d-%02d_%02d:%02d:%02d",
|
|
$year + 1900, $mon + 1, $mday, $hour, $min, $sec);
|
|
|
|
# If the log directory does not exist then create it.
|
|
if ( !-d $logDir ) {
|
|
# Directory did not exist. Create it.
|
|
$out = `mkdir -p $logDir`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DFS', 'GENERIC_RESPONSE', 'Failed to set up the log directory. IVP will run without a log file.' );
|
|
$rc = 0; # Continue processing.
|
|
goto FINISH_setupLogFile;
|
|
}
|
|
}
|
|
|
|
# Create the log file.
|
|
$logFile = "IVP\_$ipString\_$currTime\.log";
|
|
open $logFileHandle, ">", "$logDir/$logFile";
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
logResponse( 'DFS', 'GENERIC_RESPONSE', "Failed to open the log file: $logFile. IVP will run without a log file. $!" );
|
|
$rc = 0; # Continue processing.
|
|
goto FINISH_setupLogFile;
|
|
}
|
|
$out = `chmod 644 "$logDir/$logFile"`;
|
|
|
|
push @ivpSummary, "The following logfile was created for this run: $logFile";
|
|
|
|
FINISH_setupLogFile:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 showCILevel
|
|
|
|
Description : Show the cloud-init version installed
|
|
in the node.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = showCILevel();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub showCILevel {
|
|
my $ciLevel = '';
|
|
my $out = '';
|
|
my $rc = 0;
|
|
|
|
# Attempted to obtain the version using the routine
|
|
# accessible through the $PATH variable.
|
|
($rc, $out) = sshToNode( $nodeName, '', 'cloud-init --version 2>&1');
|
|
if ( $rc == 255 ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', $out );
|
|
goto FINISH_showCILevel;
|
|
} elsif ( $rc == 0 ) {
|
|
if ( $out =~ /^cloud-init / ) {
|
|
($ciLevel) = $out =~ /^cloud-init (.*)/;
|
|
my $msgText = "Version of cloud-init: $ciLevel";
|
|
logResponse( 'DF', 'GENERIC_RESPONSE', $msgText );
|
|
# <todo> Add code to compare the version to what is available in xcat.
|
|
goto FINISH_showCILevel;
|
|
}
|
|
}
|
|
|
|
# Attempt to locate the version in the various cloud-init scripts
|
|
# in the target node.
|
|
# <todo> Add support to look for versions on the node if the invocation
|
|
# using the path did not show the version.
|
|
|
|
FINISH_showCILevel:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 showHelp
|
|
|
|
Description : Show the help inforamtion.
|
|
Arguments : None.
|
|
Returns : None.
|
|
Example : showHelp();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub showHelp{
|
|
print "$0 verify the capabilities of a node.\n\n";
|
|
print $usage_string;
|
|
return;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 sshToNode
|
|
|
|
Description : SSH to a node and issue a command.
|
|
Check for SSH errors.
|
|
Arguments : Node name or IP address/DNS name
|
|
user if we need to specify SUDO
|
|
Command to issue
|
|
Options (char string):
|
|
q - Quiet, Don't generate an error message on failure
|
|
Returns : Return code:
|
|
0 - Normal Linux success
|
|
255 - Unable to SSH to system
|
|
non-zero - command error
|
|
Output from the command or a error string on an SSH failure.
|
|
Example : ($rc, $out) = sshToNode( $nodeName, $user, $command );
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub sshToNode{
|
|
my ( $nodeName, $user, $cmd, $options ) = @_;
|
|
my $rc = 0;
|
|
my $out = '';
|
|
|
|
if ( ! defined $options ) {
|
|
$options = '';
|
|
}
|
|
|
|
if ( $user eq '' ) {
|
|
$out = `ssh $nodeName -qoBatchMode=yes '$cmd'`;
|
|
$rc = $? >> 8;
|
|
} else {
|
|
$out = `ssh $user\@$nodeName -qoBatchMode=yes '$cmd'`;
|
|
$rc = $? >> 8;
|
|
}
|
|
|
|
if ( $rc == 255 and $options !~ /q/ ) {
|
|
logResponse( 'DFS', 'VSTN01', $nodeName ); # Keep $rc = 255.
|
|
$out = "Unable to SSH to $nodeName";
|
|
}
|
|
|
|
return ($rc, $out);
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyAccess
|
|
|
|
Description : Verify the xCAT MN can access a system.
|
|
Arguments : Node name or IP address
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyAccess();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyAccess{
|
|
my ( $nodeOrIP ) = @_;
|
|
|
|
my $rc = 0;
|
|
my $out = '';
|
|
|
|
($rc, $out) = sshToNode( $nodeOrIP, '', 'pwd 2>/dev/null' );
|
|
goto FINISH_verifyAccess if ( $rc == 255 ); # Already generated a message
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DFS', 'VA01' );
|
|
}
|
|
|
|
FINISH_verifyAccess:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyBasicXCAT
|
|
|
|
Description : Verify the basic setup of xCAT.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyBasicXCAT();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyBasicXCAT{
|
|
my $infoCnt = 0;
|
|
my @lines;
|
|
my $rc = 0;
|
|
my $warningCnt = 0;
|
|
|
|
$rc = logResponse( 'DF', 'GENERIC_RESPONSE', 'The IVP will be invoked with BYPASS messages suppressed. '.
|
|
'This is because a basic IVP run will produce a number of BYPASS messages due to the fact that '.
|
|
'it is not driven with a full set of configuration operands causing it to avoid some tests. '.
|
|
'Instead you will see an information line indicating that the message was ignored.' );
|
|
$ENV{'zxcatIVP_bypassMsg'} = 0; # No bypass messages
|
|
$ENV{'zxcatIVP_moreCmdOps'} = $zxcatParms;
|
|
my $out = `/opt/xcat/bin/zxcatIVP.pl`;
|
|
|
|
$rc = logResponse( 'DF', '*NONFORMATTED*', $out );
|
|
# Determine how many warnings were generated so that we can produce a summary message upon completion of the IVP.
|
|
@lines = split( '\n', $out );
|
|
foreach my $line ( @lines ) {
|
|
if ( $line =~ /^Warning/ ) { $warningCnt++; }
|
|
if ( $line =~ /^Info/ ) { $infoCnt++; }
|
|
}
|
|
push @ivpSummary, "The zxcatIVP.pl script generated $warningCnt warnings and $infoCnt information messages.";
|
|
|
|
FINISH_verifyBasicXCAT:
|
|
if ( $warningCnt != 0 ) {
|
|
$notify = 1;
|
|
}
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyDistro
|
|
|
|
Description : Verify that the distro is supported.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyDistro();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyDistro{
|
|
my $rc = 0;
|
|
|
|
my $supported = xCAT::zvmUtils->isOSVerSupported( $tgtOS );
|
|
if ( !$supported ) {
|
|
$rc = logResponse( 'DF', 'VD01', ($tgtOS) );
|
|
}
|
|
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyLogResponse
|
|
|
|
Description : Script test function to test functioning
|
|
of the logResponse() routine.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyLogResponse();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyLogResponse{
|
|
my $rc = 0;
|
|
my $sub2 = 'sub2';
|
|
|
|
$rc = logResponse( 'DF', 'VX01' );
|
|
print "rc: $rc\n";
|
|
logResponse( 'DF', 'VPF01', ($nodeName, 0, 'result message') );
|
|
$rc = logResponse( 'DF', 'VX03', $nodeName, $sub2);
|
|
print "rc: $rc\n";
|
|
$rc = logResponse( 'DF', 'VX03', $nodeName, 'sub2a');
|
|
print "rc: $rc\n";
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyPower
|
|
|
|
Description : Verify the node is powered on.
|
|
Arguments : None
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyPower();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyPower {
|
|
my ( $nodeName ) = @_;
|
|
my $rc = 0;
|
|
my $powerState;
|
|
|
|
my $out = `/opt/xcat/bin/rpower $nodeName stat 2>&1`;
|
|
$rc = $?;
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DF', 'VPF01', ($nodeName, $rc, $out) );
|
|
goto FINISH_verifyPower if ( $rc > 0 );
|
|
}
|
|
|
|
$out = lc( $out );
|
|
if ( $out =~ /$nodeName: / ) {
|
|
chomp $out;
|
|
($powerState) = $out =~ /$nodeName: (.*)/;
|
|
}
|
|
|
|
if ( $powerState ne 'on' ) {
|
|
$rc = logResponse( 'DFS', 'VP02', ($nodeName) );
|
|
}
|
|
|
|
FINISH_verifyPower:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyService
|
|
|
|
Description : Verify that specified services are running.
|
|
Arguments : Node name or IP address
|
|
Blank delimitted list of service names
|
|
service name begins with the name and
|
|
has a comma separated list of run levels
|
|
which are to be verified as 'on'
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyService();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyService {
|
|
my ( $nodeOrIP, $namesAndLevels ) = @_;
|
|
my @serviceList = split ( / /, $namesAndLevels );
|
|
|
|
my $rc = 0;
|
|
my $serviceOut = '';
|
|
|
|
# Get the list of service names and run levels into an array.
|
|
|
|
# Get the list of configured services. The output looks similar to:
|
|
# service_name 0:off 1:off 2:off 3:off 4:off 5:off 6:off
|
|
($rc, $serviceOut) = sshToNode( $nodeOrIP, '', "chkconfig --list 2>&1" );
|
|
goto FINISH_verifyService if ( $rc == 255 );
|
|
|
|
# Look for missing services
|
|
my @missingService;
|
|
my @foundService;
|
|
my @nonrunningService;
|
|
foreach my $serviceInfo ( @serviceList ) {
|
|
my @offLevels;
|
|
my $levels;
|
|
my @serviceRunLevels = split( /,/, $serviceInfo );
|
|
my $service = shift @serviceRunLevels;
|
|
($levels) = $serviceOut =~ /^$service(.*)\s(.*)\n/m;
|
|
$levels =~ s/^\s+|\s+$//g if ( defined $levels );
|
|
if (( defined $levels ) and ( $levels ne '' )) {
|
|
# Verify the run levels are enabled.
|
|
foreach my $level ( @serviceRunLevels ) {
|
|
if ( $levels !~ /$level:on/ ) {
|
|
push @offLevels, $level;
|
|
}
|
|
}
|
|
if ( @offLevels ) {
|
|
$levels = join(", ", @offLevels);
|
|
$rc = logResponse( 'DF', 'VS06', $service, $levels );
|
|
goto FINISH_verifyService if ( $rc > 0 );
|
|
} else {
|
|
push @foundService, $service;
|
|
}
|
|
} else {
|
|
push @nonrunningService, $service;
|
|
}
|
|
}
|
|
|
|
if ( @foundService ) {
|
|
my $list = join(", ", @foundService);
|
|
$rc = logResponse( 'DF', 'GENERIC_RESPONSE', "The following services are configured to start: $list" );
|
|
goto FINISH_verifyService if ( $rc > 0 );
|
|
}
|
|
if ( @nonrunningService ) {
|
|
my $list = join(", ", @nonrunningService);
|
|
$rc = logResponse( 'DF', 'VS05', $list );
|
|
goto FINISH_verifyService if ( $rc > 0 );
|
|
}
|
|
|
|
FINISH_verifyService:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------
|
|
|
|
=head3 verifyXcatconf4z
|
|
|
|
Description : Verify xcatconf4z is properly installed
|
|
and is the correct version.
|
|
Arguments : Node name or IP address
|
|
Returns : 0 - No error
|
|
non-zero - Terminating error detected.
|
|
Example : $rc = verifyXcatconf4z();
|
|
|
|
=cut
|
|
|
|
#-------------------------------------------------------
|
|
sub verifyXcatconf4z{
|
|
my ( $nodeOrIP ) = @_;
|
|
my $rc = 0;
|
|
my $out;
|
|
my ($mnVersion, $tgtVersion);
|
|
|
|
# Verify service is installed
|
|
$rc = verifyService( $nodeOrIP, 'xcatconf4z,2,3,5' );
|
|
goto FINISH_verifyXcatconf4z if ( $rc == 255 );
|
|
|
|
# Get the xCAT MN's xcatconf4z version level.
|
|
$out = `/opt/xcat/share/xcat/scripts/xcatconf4z version`;
|
|
if ( $out =~ /xcatconf4z version: / ) {
|
|
chomp $out;
|
|
($mnVersion) = $out =~ /xcatconf4z version: (.*)/;
|
|
} else {
|
|
$rc = logResponse( 'DFS', 'VX01' );
|
|
goto FINISH_verifyXcatconf4z if ( $rc > 0 );
|
|
}
|
|
|
|
# <todo> Verify that the node contains xcatconf4z in the correct location and is executable.
|
|
#$out = `ssh $nodeOrIP 'ls -al /opt/xcatconf4z 2>&1'`;
|
|
($rc, $out) = sshToNode( $nodeOrIP, '', 'ls -al /opt/xcatconf4z 2>&1');
|
|
goto FINISH_verifyXcatconf4z if ( $rc == 255 );
|
|
if ( $out =~ /No such file or directory/ ) {
|
|
$rc = logResponse( 'DF', 'VX06' );
|
|
goto FINISH_verifyXcatconf4z if ( $rc > 0 );
|
|
} else {
|
|
# <todo> Verify that it is executable
|
|
}
|
|
|
|
# Get the node's xcatconf4z version level.
|
|
#$out = `ssh $nodeOrIP '/opt/xcatconf4z version'`;
|
|
($rc, $out) = sshToNode( $nodeOrIP, '', '/opt/xcatconf4z version');
|
|
goto FINISH_verifyXcatconf4z if ( $rc == 255 );
|
|
if ( $out =~ /xcatconf4z version: / ) {
|
|
chomp $out;
|
|
($tgtVersion) = $out =~ /xcatconf4z version: (.*)/;
|
|
} else {
|
|
$rc = logResponse( 'DF', 'VX02' );
|
|
goto FINISH_verifyXcatconf4z if ( $rc > 0 );
|
|
}
|
|
|
|
# Verify the version is up to date.
|
|
if ( defined $tgtVersion ) {
|
|
if ( $mnVersion > $tgtVersion ) {
|
|
$rc = logResponse( 'DF', 'VX03', ($tgtVersion, $mnVersion) );
|
|
goto FINISH_verifyXcatconf4z if ( $rc > 0 );
|
|
}
|
|
}
|
|
|
|
# Verify that authorized senders is setup.
|
|
#$out = `ssh $nodeOrIP '/opt/xcatconf4z status'`;
|
|
($rc, $out) = sshToNode( $nodeOrIP, '', '/opt/xcatconf4z status');
|
|
goto verifyXcatconf4z if ( $rc == 255 );
|
|
if ( $out =~ /xcatconf4z is disabled / ) {
|
|
$rc = logResponse( 'DF', 'VX04' );
|
|
goto FINISH_verifyXcatconf4z if ( $rc > 0 );
|
|
} else {
|
|
# Get list of authorized users and put out an info message.
|
|
}
|
|
|
|
# Verify that mkisofs is available
|
|
($rc, $out) = sshToNode( $nodeOrIP, '', 'stat /opt/bin/mkisofs');
|
|
goto verifyXcatconf4z if ( $rc == 255 );
|
|
if ( $rc != 0 ) {
|
|
$rc = logResponse( 'DF', 'VX07' );
|
|
goto FINISH_verifyXcatconf4z if ( $rc > 0 );
|
|
}
|
|
|
|
FINISH_verifyXcatconf4z:
|
|
return $rc;
|
|
}
|
|
|
|
|
|
#*****************************************************************************
|
|
# Main routine
|
|
#*****************************************************************************
|
|
my $ignoreOpt;
|
|
my $rc = 0;
|
|
my $thisScript = $0;
|
|
my $out;
|
|
|
|
# Parse the arguments
|
|
$Getopt::Long::ignorecase = 0;
|
|
Getopt::Long::Configure( "bundling" );
|
|
if (!GetOptions(
|
|
'a|access' => \$verifyAccess,
|
|
'basicivp' => \$runBasicIVP,
|
|
'capturereqs' => \$verifyCaptureReqs,
|
|
'c|cloudinit' => \$verifyCloudInit,
|
|
'comments=s' => \$comments,
|
|
'cron' => \$runCron,
|
|
'decode=s' => \$decode,
|
|
'disable' => \$disable,
|
|
'enable' => \$enable,
|
|
'encode=s' => \$encode,
|
|
'file=s' => \$dataFile,
|
|
'fullivp' => \$runFullIVP,
|
|
'h|help' => \$displayHelp,
|
|
'i|id=s' => \$id,
|
|
'ignore=s' => \$ignoreOpt,
|
|
'cmdoveriucv=s' => \$issueCmdOverIUCV,
|
|
'cmdtonode=s' => \$issueCmdToNode,
|
|
'n|node=s' => \$nodeName,
|
|
'notify' => \$notifyOnErrorOrWarning,
|
|
'openstackuser=s' => \$openstackUser,
|
|
'openstackip=s' => \$openstackIP,
|
|
'orchparms=s' => \$orchParms,
|
|
'prepparms=s' => \$prepParms,
|
|
'remove' => \$remove,
|
|
'schedule=s' => \$schedule,
|
|
'type=s' => \$scheduledType,
|
|
'v|version' => \$versionOpt,
|
|
'x|xcatconf4z' => \$verifyXcatconf4z,
|
|
'zxcatparms=s' => \$zxcatParms,
|
|
)) {
|
|
print $usage_string;
|
|
}
|
|
|
|
if ( $versionOpt ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "Version: $version\n" );
|
|
}
|
|
|
|
if ( $displayHelp ) {
|
|
showHelp();
|
|
}
|
|
|
|
if ( $displayHelp or $versionOpt ) {
|
|
goto FINISH_main;
|
|
}
|
|
|
|
# Convert any encoded operand values back to their unencoded value.
|
|
my @convertibleParms = ( 'comments', 'nodeName', 'openstackUser', 'orchParms', 'prepParms', 'zxcatParms' );
|
|
foreach my $parmName ( @convertibleParms ) {
|
|
eval( "\$$parmName = hexDecode( \$$parmName )" );
|
|
}
|
|
|
|
if ( $schedule eq '' and $orchParms ne '' ) {
|
|
# If not scheduling an IVP run and orchestrator parms are present then use them.
|
|
my $unrecognizedOps = '';
|
|
$rc = GetOptionsFromString(
|
|
$orchParms,
|
|
'ignore=s' => \$ignoreOpt,
|
|
);
|
|
if ( $rc == 0 ) {
|
|
print "Unrecognized option in --orchParms. ".
|
|
"Only --ignore is allowed as an orchestrator option for an immediate run.\n";
|
|
$rc = 2;
|
|
goto FINISH_main;
|
|
}
|
|
}
|
|
|
|
# Handle messages to ignore.
|
|
if ( defined( $ignoreOpt ) ) {
|
|
# Make hash from the specified ignore operands
|
|
$ignoreOpt = uc( $ignoreOpt );
|
|
my @ingoreList;
|
|
if ( $ignoreOpt =~ ',' ) {
|
|
@ingoreList = split( ',', $ignoreOpt );
|
|
} else {
|
|
@ingoreList = split( ' ', $ignoreOpt );
|
|
}
|
|
|
|
%msgsToIgnore = map { $_ => 1 } @ingoreList;
|
|
|
|
# Convert general severity type operands to their numeric value.
|
|
if ( $msgsToIgnore{'BYPASS'} ) {
|
|
delete $msgsToIgnore{'BYPASS'};
|
|
$msgsToIgnore{'2'} = 1;
|
|
}
|
|
if ( $msgsToIgnore{'INFO'} ) {
|
|
delete $msgsToIgnore{'INFO'};
|
|
$msgsToIgnore{'3'} = 1;
|
|
}
|
|
if ( $msgsToIgnore{'WARNING'} ) {
|
|
delete $msgsToIgnore{'WARNING'};
|
|
$msgsToIgnore{'4'} = 1;
|
|
}
|
|
if ( $msgsToIgnore{'ERROR'} ) {
|
|
delete $msgsToIgnore{'ERROR'};
|
|
$msgsToIgnore{'5'} = 1;
|
|
}
|
|
}
|
|
|
|
if ( $openstackIP ne '' ) {
|
|
$openstackIP = uc( $openstackIP );
|
|
}
|
|
|
|
# Handle the combinations of disable and enable options.
|
|
if ( $disable eq '' ) {
|
|
$disable = 0;
|
|
}
|
|
if ( $enable eq '' ) {
|
|
$enable = 0;
|
|
}
|
|
if ( $disable == 1 and $enable == 1 ) {
|
|
$rc = logResponse( 'DFS', 'OPER03', '--disable and --enable' );
|
|
goto FINISH_main;
|
|
}
|
|
|
|
if ( $runFullIVP or $runBasicIVP ) {
|
|
# Set up a log file for an IVP run.
|
|
setupLogFile();
|
|
}
|
|
|
|
# Determine the information about the xCAT managed node and the z/VM
|
|
# host environment where it is running (if running on z/VM).
|
|
getMNInfo();
|
|
|
|
if ( $decode ne '' or
|
|
$disable or
|
|
$enable or
|
|
$encode ne '' or
|
|
$runFullIVP or
|
|
$runCron or
|
|
$runBasicIVP or
|
|
$remove or
|
|
$schedule ne '' ) {
|
|
# IVP runs do not need the node.
|
|
} elsif ( $nodeName eq '' ) {
|
|
$rc = logResponse( 'DFS', 'OPER01', '-n or --node' );
|
|
goto FINISH_main;
|
|
}
|
|
|
|
($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer();
|
|
|
|
# Setup for a test run of logResponse. Normally, commented out.
|
|
#($verifyAccess, $verifyCaptureReqs, $verifyCloudInit, $verifyXcatconf4z) = 0;
|
|
#my $test = 1;
|
|
|
|
# Run the selected tests
|
|
foreach my $verify (keys %verifySets) {
|
|
if ( eval $verify ) {
|
|
if ( $verifySets{$verify}[0] ne '' ) {
|
|
logResponse( 'D', 'GENERIC_RESPONSE_NOINDENT', "\n********************************************************************\n" );
|
|
logResponse( 'D', 'GENERIC_RESPONSE_NOINDENT', "$verifySets{$verify}[0]\n" );
|
|
logResponse( 'D', 'GENERIC_RESPONSE_NOINDENT', "********************************************************************\n" );
|
|
}
|
|
my $size = @{$verifySets{$verify}};
|
|
logResponse( 'D', '*RESET*' );
|
|
for (my $i = 1; $i < $size; $i++) {
|
|
($rc) = eval $verifySets{$verify}[$i];
|
|
if ( ! defined $rc ) {
|
|
logResponse( 'DFS', 'PERL01', $thisScript, $verifySets{$verify}[$i], $@ );
|
|
$rc = 666;
|
|
last;
|
|
}
|
|
|
|
if ( $rc > 0 ) {
|
|
last;
|
|
}
|
|
}
|
|
|
|
if ( $warnErrCnt == 0 ) {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "\nProcessing completed." );
|
|
} else {
|
|
logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "\nProcessing completed with warning or error messages. " .
|
|
"See previous messages for information.\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
FINISH_main:
|
|
if ( $needToFinishLogFile ) {
|
|
# A log file was started. We need to complete it by adding the summary
|
|
# section. If necessary, we will send the log file to the notify user.
|
|
finishLogFile();
|
|
}
|
|
|
|
exit $rc;
|