2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-30 09:36:41 +00:00
xcat-core/xCAT-client/bin/zxcatIVP.pl
Chuck Brazie a4b658037f merge of xCAT_Client plus add one new file missed in xCAT
Change-Id: Iaa3b073333e75c64f7a0da35fb6dd1228e822059
2017-02-01 14:13:16 -05:00

2608 lines
93 KiB
Perl

#!/usr/bin/perl
###############################################################################
# IBM (C) Copyright 2014, 2016 Eclipse Public License
# http://www.eclipse.org/org/documents/epl-v10.html
###############################################################################
# COMPONENT: zxcatIVP.pl
#
# This is an Installation Verification Program for z/VM's xCAT Management Node
# and zHCP agent.
###############################################################################
package xCAT::verifynode;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
$XML::Simple::PREFERRED_PARSER='XML::Parser';
use strict;
#use warnings;
use Getopt::Long;
use Getopt::Long qw(GetOptionsFromString);
use MIME::Base64;
use Sys::Syslog qw( :DEFAULT setlogsock);
use Text::Wrap;
use LWP;
use JSON;
use lib "$::XCATROOT/lib/perl";
use xCAT::zvmMsgs;
require HTTP::Request;
# Global variables set based on input from the driver program.
my $glob_bypassMsg = 1; # Display bypass messages
my $glob_CMA = 0; # CMA appliance indicator, Assume not running in CMA
my $glob_CMARole = ''; # CMA role, CONTROLLER, COMPUTE or '' (unknown)
my $glob_cNAddress; # IP address or hostname of the compute node that
# is accessing this xCAT MN. (optional)
my $glob_defaultUserProfile; # Default profile used in creation of server instances.
my $glob_displayHelp = 0; # Display help instead of running the IVP
my @glob_diskpools; # Array of disk pools, e.g. ('POOLSCSI', 'POOL1'),
# that are expected to exist. (optional)
my $glob_expectedReposSpace; # Minimum amount of repository space.
my $glob_expUser; # User name used for importing and exporting images
my @glob_instFCPList; # Array of FCPs used by server instances
my $glob_hostNode; # Node of host being managed. If blank,
# IVP will search for the host node. (optional)
my $glob_macPrefix; # User prefix for MAC Addresses of Linux level 2 interfaces
my %glob_msgsToIgnore; # Hash of messages to be ignored
my $glob_mgtNetmask;
my $glob_mnNode; # Node name for xCAT MN (optional).
my $glob_moreCmdOps; # Additional command operands passed with an environment variable
my @glob_networks; # Array of networks and possible VLAN ranges
# eg. ( 'xcatvsw1', 'xcatvsw2:1:4999'). (optional)
my $obfuscatePw; # Obfuscate the PW in the driver file that is built
my $glob_signalTimeout; # Signal Shutdown mininum acceptable timeout
my $glob_syslogErrors; # Log errors in SYSLOG: 0 - no, 1 - yes
my %glob_vswitchOSAs; # Hash list of vswitches and their OSAs
my @glob_xcatDiskSpace; # Array information of directories in xCAT MN that should be validated for disk space
my $glob_xcatMNIp; # Expected IP address of this xCAT MN
my $glob_xcatMgtIp; # Expected IP address of this xCAT MN on the Mgt Network
my $glob_xcatUser; # User defined to communicate with xCAT MN
my $glob_xcatUserPw; # User's password defined to communicate with xCAT MN
my @glob_zhcpDiskSpace; # Array information of directories in zhcp that should be validated for disk space
my @glob_zhcpFCPList; # Array of FCPs used by zHCP
my $glob_zhcpNode; # Node name for xCAT zHCP server (optional)
# Global IVP run time variables
my $glob_versionInfo; # Version info for xCAT MN and zHCP
my $glob_successfulTestCnt = 0; # Number of successful tests
my @glob_failedTests; # List of failed tests.
#my @glob_hostNodes; # Array of host node names
my %glob_hostNodes; # Hash of host node information
my %glob_ignored; # List of ignored messages
my $glob_ignoreCnt = 0; # Number of times we ignored a message
my %glob_localIPs; # Hash of local IP addresses for the xCAT MN's system
my $glob_localZHCP = ''; # Local ZHCP node name and hostname when ZHCP is on xCAT MN's system
my $glob_totalFailed = 0; # Number of failed tests
my $glob_testNum = 0; # Number of tests that were run
my $glob_versionFileCMA = '/opt/ibm/cmo/version';
my $glob_versionFileXCAT = '/opt/xcat/version';
my $glob_applSystemRole = '/var/lib/sspmod/appliance_system_role';
# Tables for environment variable and command line operand processing.
my ( $cmdOp_bypassMsg, $cmdOp_cNAddress, $cmdOp_defaultUserProfile, $cmdOp_diskpools,
$cmdOp_expectedReposSpace, $cmdOp_expUser, $cmdOp_hostNode, $cmdOp_ignore,
$cmdOp_instFCPList, $cmdOp_instFCPList, $cmdOp_macPrefix, $cmdOp_mgtNetmask,
$cmdOp_mnNode, $cmdOp_moreCmdOps, $cmdOp_bypassMsg, $cmdOp_networks, $cmdOp_pw_obfuscated,
$cmdOp_syslogErrors, $cmdOp_signalTimeout, $cmdOp_vswitchOSAs,
$cmdOp_xcatDiskSpace, $cmdOp_xcatMgtIp, $cmdOp_xcatMNIp,
$cmdOp_xcatUser, $cmdOp_zhcpFCPList, $cmdOp_zhcpNode, $cmdOp_zhcpDiskSpace );
my @cmdOps = (
{
'envVar' => 'zxcatIVP_bypassMsg',
'opName' => 'bypassMsg',
'inpVar' => 'cmdOp_bypassMsg',
'var' => 'glob_bypassMsg',
'type' => 'scalar',
'desc' => "Controls whether bypass messages are produced:\n" .
"0: do not show bypass messages, or\n" .
"1: show bypass messages.\n" .
"Bypass messages indicate when a test is not run. " .
"This usually occurs when a required environment variable or command line operand is " .
"missing.",
},
{
'envVar' => 'zxcatIVP_cNAddress',
'opName' => 'cNAddress',
'inpVar' => 'cmdOp_cNAddress',
'var' => 'glob_cNAddress',
'type' => 'scalar',
'desc' => 'Specifies the IP address or hostname of the OpenStack compute node that is ' .
'accessing the xCAT MN.',
},
{
'envVar' => 'zxcatIVP_defaultUserProfile',
'opName' => 'defaultUserProfile',
'inpVar' => 'cmdOp_defaultUserProfile',
'var' => 'glob_defaultUserProfile',
'type' => 'scalar',
'desc' => 'Specifies the default profile that is used in creation of server instances.',
},
{
'envVar' => 'zxcatIVP_diskpools',
'opName' => 'diskpools',
'inpVar' => 'cmdOp_diskpools',
'var' => 'glob_diskpools',
'type' => 'array',
'case' => 'uc',
'separator' => ';, ',
'desc' => 'Specifies an array of disk pools that are expected to exist. ' .
'The IVP will verify that space exists in those disk pools. ',
},
{
'envVar' => 'zxcatIVP_expectedReposSpace',
'opName' => 'expectedReposSpace',
'inpVar' => 'cmdOp_expectedReposSpace',
'var' => 'glob_expectedReposSpace',
'type' => 'scalar',
'default' => '1G',
'desc' => 'Specifies the expected space available in the xCAT MN image repository. ' .
'The OpenStack compute node attempts to ensure that the space is available ' .
'in the xCAT image repository by removing old images. This can cause ' .
'images it be removed and added more often than desired if the value is too high.',
},
{
'envVar' => 'zxcatIVP_expUser',
'opName' => 'expUser',
'inpVar' => 'cmdOp_expUser',
'var' => 'glob_expUser',
'type' => 'scalar',
'desc' => 'Specifies the name of the user under which the OpenStack Nova component runs ' .
'on the compute node. The IVP uses this name when it attempts to verify access ' .
'of the xCAT MN to the compute node.',
},
{
'envVar' => 'zxcatIVP_hostNode',
'opName' => 'hostNode',
'inpVar' => 'cmdOp_hostNode',
'var' => 'glob_hostNode',
'type' => 'scalar',
'desc' => 'Specifies the node of host being managed by the compute node. ' .
'The IVP will verify that the node name exists and use this to determine the ' .
'ZHCP node that supports the host. If this value is missing or empty, the ' .
'IVP will validate all host nodes that it detects on the xCAT MN.',
},
{
'envVar' => 'zxcatIVP_ignore',
'opName' => 'ignore',
'inpVar' => 'cmdOp_ignore',
'var' => 'glob_msgsToIgnore',
'type' => 'hash',
'case' => 'uc',
'separator' => ';, ',
'desc' => 'Specifies a comma separated list of message Ids that should be ignored. ' .
'Ignored messages do not generate a full message and are not counted as ' .
'trigger to notify the monitoring userid (see XCAT_notify property in the ' .
'DMSSICNF COPY file). Instead a line will be generated in the output indicating ' .
'that the message was ignored.',
},
{
'envVar' => 'zxcatIVP_instFCPList',
'opName' => 'instFCPList',
'inpVar' => 'cmdOp_instFCPList',
'var' => 'glob_instFCPList',
'type' => 'array',
'separator' => ';, ',
'desc' => 'Specifies the list of FCPs used by instances.',
},
{
'envVar' => 'zxcatIVP_macPrefix',
'opName' => 'macPrefix',
'inpVar' => 'cmdOp_macPrefix',
'var' => 'glob_macPrefix',
'type' => 'scalar',
'desc' => 'Specifies user prefix for MAC Addresses of Linux level 2 interfaces.',
},
{
'envVar' => 'zxcatIVP_mgtNetmask',
'opName' => 'mgtNetmask',
'inpVar' => 'cmdOp_mgtNetmask',
'var' => 'glob_mgtNetmask',
'type' => 'scalar',
'desc' => 'Specifies xCat management interface netmask.',
},
{
'envVar' => 'zxcatIVP_mnNode',
'opName' => 'mnNode',
'inpVar' => 'cmdOp_mnNode',
'var' => 'glob_mnNode',
'type' => 'scalar',
'desc' => 'Specifies the node name for xCAT MN.',
},
{
'envVar' => 'zxcatIVP_moreCmdOps',
'opName' => 'moreCmdOps',
'inpVar' => 'cmdOp_moreCmdOps',
'var' => 'glob_moreCmdOps',
'type' => 'scalar',
'desc' => 'Specifies additional command operands to be passed to the zxcatIVP script. ' .
'This is used interally by the IVP programs.',
},
{
'envVar' => 'zxcatIVP_networks',
'opName' => 'networks',
'inpVar' => 'cmdOp_networks',
'var' => 'glob_networks',
'type' => 'array',
'separator' => ';, ',
'desc' => "Specifies an array of networks and possible VLAN ranges. " .
"The array is a list composed of network names and optional " .
"vlan ranges in the form: vswitch:vlan_min:vlan_max where " .
"each network and vlan range components are separated by a colon.\n" .
"For example, 'vsw1,vsw2:1:4095' specifies two vswitches vsw1 without a " .
"vlan range and vsw2 with a vlan range of 1 to 4095.",
},
{
'envVar' => 'zxcatIVP_pw_obfuscated',
'opName' => 'pw_obfuscated',
'inpVar' => 'cmdOp_pw_obfuscated',
'var' => 'obfuscatePw',
'type' => 'scalar',
'desc' => "Indicates whether the password zxcatIVP_xcatUserPw is obfuscated:\n" .
"1 - obfuscated,\n0 - in the clear.",
},
{
'envVar' => 'zxcatIVP_signalTimeout',
'opName' => 'signalTimeout',
'inpVar' => 'cmdOp_signalTimeout',
'var' => 'glob_signalTimeout',
'type' => 'scalar',
'default' => '30',
'desc' => "Specifies the minimum acceptable time value that should be specified ".
"in the z/VM using the SET SIGNAL SHUTDOWNTIME configuration statement. ".
"A value less than this value will generate a warning in the IVP.",
},
{
'envVar' => 'zxcatIVP_syslogErrors',
'opName' => 'syslogErrors',
'inpVar' => 'cmdOp_syslogErrors',
'var' => 'glob_syslogErrors',
'type' => 'scalar',
'default' => '1',
'desc' => "Specifies whether Warnings and Errors detected by the IVP are ".
"logged in the xCAT MN syslog:\n0: do not log,\n1: log to syslog.",
},
{
'envVar' => 'zxcatIVP_vswitchOSAs',
'opName' => 'vswitchOSAs',
'inpVar' => 'cmdOp_vswitchOSAs',
'var' => 'glob_vswitchOSAs',
'type' => 'hash',
'separator' => ';, ',
'desc' => 'Specifies vswitches and their related OSAs that are used by ' .
'systems created by the OpenStack compute node.',
},
{
'envVar' => 'zxcatIVP_xcatDiskSpace',
'opName' => 'xcatDiskSpace',
'inpVar' => 'cmdOp_xcatDiskSpace',
'var' => 'glob_xcatDiskSpace',
'type' => 'array',
'separator' => ';,',
'default' => '/ 80 . 10M;/install 90 1g .',
'desc' => 'Specifies a list of directories in the xCAT server that should be '.
'verified for storage availability. Each directory consists of '.
'a blank separated list of values:'.
"\n".
'* directory name,'.
"\n".
'* maximum in use percentage (a period indicates that the value should not be tested),'.
"\n".
'* minumum amount of available storage (period indicates that '.
'available storage based on size should not be validated),'.
"\n".
'* minimum file size at which to generate a warning when available space tests '.
'detect a size issue (a period indicates that the value should not be tested).'.
"\n\n".
"For example: '/ 80 5G 3M' will cause the IVP to check the space for the ".
'root directory (/) to verify that it has at least 80% space available or '.
'5G worth of space available. If the space tests fail, a warning will be '.
'generated for each file (that is not a jar or so file) which is 3M or larger. '.
'Additional directories may be specified using a list separator.',
},
{
'envVar' => 'zxcatIVP_xcatMgtIp',
'opName' => 'xcatMgtIp',
'inpVar' => 'cmdOp_xcatMgtIp',
'var' => 'glob_xcatMgtIp',
'type' => 'scalar',
'desc' => 'Specifies xCat MN\'s IP address on the xCAT management network.',
},
{
'envVar' => 'zxcatIVP_xcatMNIp',
'opName' => 'xcatMNIp',
'inpVar' => 'cmdOp_xcatMNIp',
'var' => 'glob_xcatMNIp',
'type' => 'scalar',
'desc' => 'Specifies the expected IP address of the xcat management node.',
},
{
'envVar' => 'zxcatIVP_xcatUser',
'opName' => 'xcatUser',
'inpVar' => 'cmdOp_xcatUser',
'var' => 'glob_xcatUser',
'type' => 'scalar',
'desc' => 'Specifies the user defined to communicate with xCAT management node.',
},
{
'envVar' => 'zxcatIVP_xcatUserPw',
'opName' => 'xcatUserPw',
'inpVar' => 'cmdOp_xcatUserPw',
'var' => 'glob_xcatUserPw',
'type' => 'scalar',
'desc' => 'Specifies the user password defined to communicate with xCAT MN over the REST API.',
},
{
'envVar' => 'zxcatIVP_zhcpFCPList',
'opName' => 'zhcpFCPList',
'inpVar' => 'cmdOp_zhcpFCPList',
'var' => 'glob_zhcpFCPList',
'type' => 'array',
'separator' => ';, ',
'desc' => 'Specifies the list of FCPs used by zHCP.',
},
{
'envVar' => 'zxcatIVP_zhcpNode',
'opName' => 'zhcpNode',
'inpVar' => 'cmdOp_zhcpNode',
'var' => 'glob_zhcpNode',
'type' => 'scalar',
'desc' => 'Specifies the expected ZHCP node name that the compute node ' .
'expects to be used to manage the z/VM host.',
},
{
'envVar' => 'zxcatIVP_zhcpDiskSpace',
'opName' => 'zhcpDiskSpace',
'inpVar' => 'cmdOp_zhcpDiskSpace',
'var' => 'glob_zhcpDiskSpace',
'type' => 'array',
'separator' => ';,',
'default' => '/ 90 . 10M',
'desc' => "Specifies a list of directories in the ZHCP server that should be ".
'verified for storage availability. Each directory consists of '.
'a blank separated list of values:'.
"\n".
'* directory name,'.
"\n".
'* maximum in use percentage (a period indicates that the value should not be tested),'.
"\n".
'* minumum amount of available storage (period indicates that '.
'available storage based on size should not be validated),'.
"\n".
'* minimum file size at which to generate a warning when available space tests '.
'detect a size issue (a period indicates that the value should not be tested).'.
"\n\n".
"For example: '/ 80 5G 3M' will cause the IVP to check the space for the ".
'root directory (/) to verify that it has at least 80% space available or '.
'5G worth of space available. If the space tests fail, a warning will be '.
'generated for each file (that is not a jar or so file) which is 3M or larger. '.
'Additional directories may be specified using a list separator.',
},
);
my $usage_string = "Usage:\n
zxcatIVP
or
zxcatIVP <operands>
or
zxcatIVP --help
or
zxcatIVP -h\n\n";
#-------------------------------------------------------
=head3 applyOverrides
Description : Apply the overrides from either
the environment variables or command
line to the target global variables.
Arguments : None
Returns : None.
Example : applyOverrides();
=cut
#-------------------------------------------------------
sub applyOverrides{
# Handle the normal case variables and values.
foreach my $opHash ( @cmdOps ) {
my $opVarRef = eval('\$' . $opHash->{'inpVar'});
if ( ! defined $$opVarRef) {
#print "Did not find \$$opHash->{'inpVar'}\n";
next;
} else {
#print "key: $opHash->{'inpVar'}, value: $$opVarRef\n"
}
# Modify the case of the value, if necessary.
if ( ! exists $opHash->{'case'} ) {
# Ignore case handling
} elsif ( $opHash->{'case'} eq 'uc' ) {
$$opVarRef = uc( $$opVarRef );
} elsif ( $opHash->{'case'} eq 'lc' ) {
$$opVarRef = lc( $$opVarRef );
}
# Process the value to set the variable in this script.
if ( $opHash->{'type'} eq "scalar" ) {
my $globRef = eval('\$' . $opHash->{'var'});
$$globRef = $$opVarRef;
} elsif ( $opHash->{'type'} eq "array" ) {
my $globRef = eval('\@' . $opHash->{'var'});
my @array;
if ( $opHash->{'separator'} =~ /,/ and $$opVarRef =~ /,/ ) {
@array = split( ',', $$opVarRef );
} elsif ( $opHash->{'separator'} =~ /;/ and $$opVarRef =~ /;/ ) {
@array = split( ';', $$opVarRef );
} elsif ( $opHash->{'separator'} =~ /\s/ and $$opVarRef =~ /\s/ ) {
@array = split( '\s', $$opVarRef );
} else {
push @array, $$opVarRef;
}
@$globRef = @array;
} elsif ( $opHash->{'type'} eq "hash" ) {
my $globRef = eval('\%' . $opHash->{'var'});
my @array;
my %hash;
if ( $opHash->{'separator'} =~ /,/ and $$opVarRef =~ /,/ ) {
@array = split( ',', $$opVarRef );
%hash = map { $_ => 1 } @array;
} elsif ( $opHash->{'separator'} =~ /;/ and $$opVarRef =~ /;/ ) {
@array = split( ';', $$opVarRef );
%hash = map { $_ => 1 } @array;
} elsif ( $opHash->{'separator'} =~ /\s/ and $$opVarRef =~ /\s/ ) {
@array = split( '\s', $$opVarRef );
%hash = map { $_ => 1 } @array;
} else {
$hash{$$opVarRef} = 1;
}
%$globRef = %hash;
} else {
print "Internal error: Unsupported \%cmdOpRef type '$opHash->{'type'}' for $opHash->{'inpVar'}\n";
}
}
}
#-------------------------------------------------------
=head3 calculateRoutingPrefix
Description : Calculate the routing prefix for a given subnet mask.
Arguments : Subnet Mask
Returns : -1 - Unable to calculate routing prefix
zero or non-zero - Routing prefix number
Example : $rc = calculateRoutingPrefix( $subnetMask );
=cut
#-------------------------------------------------------
sub calculateRoutingPrefix{
my ( $subnetMask ) = @_;
my $routingPrefix = 0;
my @parts;
# Determine the inet version based on the mask separator and
# calculate the routing prefix.
if ( $subnetMask =~ m/\./ ) {
# inet 4 mask
@parts = split( /\./, $subnetMask );
foreach my $part ( @parts ) {
if (( $part =~ /\D/ ) || ( length($part) > 3 )) {
return -1; # subnet mask is not valid
}
foreach my $i ( 1, 2, 4, 8,
16, 32, 64, 128 ) {
if ( $part & $i ) {
$routingPrefix++;
}
}
}
} elsif ( $subnetMask =~ m/\:/ ) {
# inet 6 mask
@parts = split( /:/, $subnetMask );
foreach my $part ( @parts ) {
if (( $part =~ /[^0-9^a-f^A-F]/ ) || ( length($part) > 4 )) {
print "part failed: $part\n";
return -1; # subnet mask is not valid
}
$part = hex $part;
foreach my $i ( 1, 2, 4, 8,
16, 32, 64, 128,
256, 512, 1024, 2048,
4096, 8192, 16384, 32768 ) {
if ( $part & $i ) {
$routingPrefix++;
}
}
}
}
return $routingPrefix
}
#-------------------------------------------------------
=head3 convertDiskSize
Description : Reduce a size with a magnitude (eg. 25G or 25M)
to a common scalar value.
Arguments : Size to convert.
Returns : non-negative - No error
-1 - Error detected.
Example : my $size = convertDiskSize( $diskType, $diskSize );
=cut
#-------------------------------------------------------
sub convertDiskSize{
my ( $diskType, $diskSize ) = @_;
my $size;
my $bytesPer3390Cylinder = 849960;
my $bytesPer3380Cylinder = 712140;
my $bytesPer9345Cylinder = 696840;
my $bytesPerFbaBlock = 512;
my $cylindersPer3390_03 = 3339;
my $kilobyte = 1024;
my $megabyte = 1024 ** 2;
my $gigabyte = 1024 ** 3;
$diskType = uc( $diskType );
if ( $diskType =~ '3390-' ) {
$size = $diskSize * $bytesPer3390Cylinder / $gigabyte;
$size = sprintf("%.2f", $size);
$size = "$diskSize(cyl) -> $size" . "Gig";
} elsif ( $diskType =~ '3380-') {
$size = $diskSize * $bytesPer3380Cylinder / $gigabyte;
$size = sprintf("%.2f", $size);
$size = "$diskSize(cyl) -> $size" . "Gig";
} elsif ( $diskType =~ '9345-') {
$size = $diskSize * $bytesPer9345Cylinder / $gigabyte;
$size = sprintf("%.2f", $size);
$size = "$diskSize(cyl) -> $size" . "Gig";
} elsif ( $diskType =~ '9336-') {
$size = $diskSize * $bytesPerFbaBlock / $gigabyte;
$size = sprintf("%.2f", $size);
$size = "$diskSize(block) -> $size" . "Gig";
} elsif ( $diskType =~ '9332') {
$size = $diskSize * $bytesPerFbaBlock / $gigabyte;
$size = "$diskSize(block)";
} else {
$size = "$diskSize";
}
return $size;
}
#-------------------------------------------------------
=head3 convertSize
Description : Reduce a size with a magnitude (eg. 25G or 25M)
to a common scalar value.
Arguments : Size to convert.
Returns : non-negative - No error
-1 - Error detected.
Example : my $size = convertSize( "25G" );
=cut
#-------------------------------------------------------
sub convertSize{
my ( $magSize ) = @_;
my $size;
my $numeric;
my $kilobyte = 1024;
my $megabyte = 1024 ** 2;
my $gigabyte = 1024 ** 3;
my $terabyte = 1024 ** 4;
my $petabyte = 1024 ** 5;
my $exabyte = 1024 ** 6;
$magSize = uc( $magSize );
$numeric = substr( $magSize, 0, -1 );
if ( length $magSize == 0 ) {
logTest( 'misc', "size is less than expected, value: $magSize." );
} elsif ( $magSize =~ m/K$/ ) {
$size = $numeric * $kilobyte;
} elsif ( $magSize =~ m/M$/ ) {
$size = $numeric * $megabyte;
} elsif ( $magSize =~ m/G$/ ) {
$size = $numeric * $gigabyte;
} elsif ( $magSize =~ m/T$/ ) {
$size = $numeric * $terabyte;
} elsif ( $magSize =~ m/P$/ ) {
$size = $numeric * $petabyte;
} elsif ( $magSize =~ m/E$/ ) {
$size = $numeric * $exabyte;
} else {
logTest( 'misc', "magnitude of $magSize is unknown." );
return -1;
}
return $size;
}
#-------------------------------------------------------
=head3 driveREST
Description : Verify the REST interface is running.
Arguments : IP address
User
Password
Rest Object ( e.g. nodes/xcat )
Method ( GET | PUT | POST | DELETE )
Format
Returns : Response structure
Example : my $response = driveREST( $glob_xcatMNIp, $glob_xcatUser,
$glob_xcatUserPw, "nodes/$glob_mnNode", "GET", "json", \@restOps );
=cut
#-------------------------------------------------------
sub driveREST{
my ( $addr, $user, $pw, $obj, $method, $format, $restOps) = @_;
my $url = "https://$addr/xcatws/$obj" . "?userName=$user&password=$pw" .
"&format=$format";
my @updatearray;
my $fieldname;
my $fieldvalue;
my @args = ();
if ( scalar( @args ) > 0 ){
foreach my $tempstr (@args) {
push @updatearray, $tempstr;
}
}
my $request;
my $ua = LWP::UserAgent->new();
my $response;
if (( $method eq 'PUT' ) or ( $method eq 'POST' )) {
my $tempstr = encode_json \@updatearray;
$request = HTTP::Request->new( $method => $url );
$request->header('content-type' => 'text/plain');
$request->header( 'content-length' => length( $tempstr ) );
$request->content( $tempstr );
} elsif (( $method eq 'GET' ) or ( $method eq 'DELETE' )) {
$request = HTTP::Request->new( $method=> $url );
}
$response = $ua->request( $request );
#print $response->content . "\n";
#print "code: " . $response->code . "\n";
#print "message: " .$response->message . "\n";
return $response;
}
#-------------------------------------------------------
=head3 findZhcpNode
Description : Find the object name of the zHCP node.
Arguments : Target node whose ZHCP we want to find
Returns : zHCP node name, if found
undefined, if not found
Example : my $zhcpNode = findZhcpNode();
=cut
#-------------------------------------------------------
sub findZhcpNode{
my ( $targetNode) = @_;
my $rc = 0;
my $zhcpNode;
# Get the HCP hostname from the node
my %targetInfo = getLsdefNodeInfo( $targetNode );
my $hcpHostname = $targetInfo{'hcp'};
# Find the node that owns the zHCP hostname
my @nodes = getNodeNames();
foreach my $node (@nodes){
my %nodeInfo = getLsdefNodeInfo( $node );
if ( $nodeInfo{'hostnames'} =~ $hcpHostname ) {
$zhcpNode = $node;
last;
}
}
return $zhcpNode;
}
#-------------------------------------------------------
=head3 getDiskPoolNames
Description : Obtain the list of disk pools for a
z/VM host.
Arguments : Host Node
Returns : Array of disk pool names - No error
empty array - Error detected.
Example : my @pools = getDiskPoolNames($node);
=cut
#-------------------------------------------------------
sub getDiskPoolNames{
my ( $hostNode) = @_;
my @pools;
# Find the related zHCP node
my $zhcpNode = findZhcpNode( $hostNode );
if ( !defined $zhcpNode ) {
return @pools;
}
my $out = `/opt/xcat/bin/lsvm $zhcpNode --diskpoolnames | awk '{print \$NF}'`;
@pools = split /\n/, $out;
return @pools;
}
#-------------------------------------------------------
=head3 getHostNodeNames
Description : Get a list of the host nodes defined to this
xCAT MN.
Arguments : none
Returns : List of host nodes
undefined - Error detected.
Example : my @hostNodes = getHostNodeNames();
=cut
#-------------------------------------------------------
sub getHostNodeNames{
my @nodes = getNodeNames();
my @hostNodes;
foreach my $node (@nodes){
my %nodeInfo = getLsdefNodeInfo( $node );
if ( ! %nodeInfo ) {
next;
}
if (( exists $nodeInfo{'hosttype'} ) and ( $nodeInfo{'hosttype'} =~ 'zvm' )) {
push( @hostNodes, $node);
}
}
return @hostNodes;
}
#-------------------------------------------------------
=head3 getLocalIPs
Description : Get the IP addresses from ifconfig.
Arguments : Node name or IP address
Returns : Hash of local IP addresses
Example : %localIPs = getLocalIPs();
=cut
#-------------------------------------------------------
sub getLocalIPs {
my $ip;
my $junk;
my %localIPs;
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 %localIPs;
}
#-------------------------------------------------------
=head3 getLsdefNodeInfo
Description : Obtain node info from LSDEF.
Arguments : Name of node to retrieve
Returns : Hash of node properties.
Example : my %hash = getLsdefNodeInfo($node);
=cut
#-------------------------------------------------------
sub getLsdefNodeInfo{
my ( $node) = @_;
my %hash;
my $out = `/opt/xcat/bin/lsdef $node`;
my @list1 = split /\n/, $out;
foreach my $item (@list1) {
if ( $item !~ "Object name:" ) {
my ($i,$j) = split(/=/, $item);
$i =~ s/^\s+|\s+$//g; # trim both ends of the string
$hash{$i} = $j;
}
}
return %hash;
}
#-------------------------------------------------------
=head3 getNodeNames
Description : Get a list of the nodes defined to this
xCAT MN.
Arguments : none
Returns : Array of nodes
undefined - Error detected.
Example : my @nodes = getNodeNames();
=cut
#-------------------------------------------------------
sub getNodeNames{
my $out = `/opt/xcat/bin/lsdef | sed "s/ (node)//g"`;
my @nodes = split( /\n/, $out );
return @nodes;
}
#-------------------------------------------------------
=head3 getUseridFromLinux
Description : Obtain the z/VM virtual machine userid from
/proc/sysinfo file.
Arguments : Variable to receive the output
Returns : 0 - No error
non-zero - Can't get the virtual machine id.
Example : my $rc = getUseridFromLinux( \$userid );
=cut
#-------------------------------------------------------
sub getUseridFromLinux{
my ( $userid) = @_;
my $rc = 0;
$$userid = `cat /proc/sysinfo | grep 'VM00 Name:' | awk '{print \$NF}'`;
$$userid =~ s/^\s+|\s+$//g; # trim both ends of the string
if ( $$userid ne '' ) {
$rc = 1;
}
return $rc;
}
#-------------------------------------------------------
=head3 getVswitchInfo
Description : Query a vswitch and produce a hash of the data.
Arguments : zHCP node
Name of switch to be queried
Returns : hash of switch data, if found.
hash contains either:
$switchInfo{'Base'}{$property} = $value;
$switchInfo{'Authorized users'}{'User'} = $value;
$switchInfo{'Connections'}{$property} = $value;
$switchInfo{'Real device xxxx'}{$property} = $value;
Example : $rc = getVswitchInfo( $zhcpNode, $switch );
=cut
#-------------------------------------------------------
sub getVswitchInfo{
my ( $zhcpNode, $switch ) = @_;
my %switchInfo;
my @word;
my $device;
my $out = `ssh $zhcpNode smcli Virtual_Network_Vswitch_Query -T xxxx -s $switch`;
if ( $out !~ /^Failed/ ) {
# Got some information. Process it.
my @lines = split( "\n", $out );
pop( @lines );
my $subsection = 'Base';
foreach my $line ( @lines ) {
#print "line: $line\n";
my $indent = $line =~ /\S/ ? $-[0] : length $line; # Get indentation level
$line =~ s/^\s+|\s+$//g; # trim both ends of the line;
if ( $line eq '' ) {
next;
} elsif ( $indent == 0 ) {
if ( $line =~ 'VSWITCH:' ) {
$line = substr( $line, 8 );
$line =~ s/^\s+|\s+$//g; # trim both ends of the line;
@word = split( /:/, $line );
$word[1] =~ s/^\s+|\s+$//g; # trim both ends of the line;
$switchInfo{'Base'}{$word[0]} = $word[1];
}
} elsif ( $indent == 2 ) {
if ( $line =~ /Devices:/ ) {
$subsection = 'Real device';
} elsif ( $line =~ /Authorized users:/ ) {
$subsection = 'Authorized users';
} elsif ( $line =~ /Connections:/ ) {
$subsection = 'Connections';
} else {
$subsection = 'Base';
@word = split( /:/, $line );
$switchInfo{$subsection}{$word[0]} = $word[1];
}
} elsif ( $indent == 4 ) {
if ( $subsection eq 'Real device' ) {
@word = split( ':', $line );
if ( $line =~ /Real device:/ ) {
$device = $word[1];
$device =~ s/^\s+|\s+$//g; # trim both ends of the string
} else {
if ( !exists $word[1] ) {
$word[1] = '';
}
my $key = "$subsection $device";
$switchInfo{$key}{$word[0]} = $word[1];
}
} elsif ( $subsection eq 'Authorized users' ) {
@word = split( ':', $line );
if ( $word[1] eq '' ) {
next;
}
if ( exists $switchInfo{$subsection} ) {
$switchInfo{$subsection} = "$switchInfo{$subsection} $word[1]";
} else {
$switchInfo{$subsection} = "$word[1]";
}
} elsif ( $subsection eq 'Connections' ) {
@word = split( ' ', $line );
if ( !exists $word[2] ) {
next;
}
$switchInfo{$subsection}{$word[2]} = $word[5];
}
}
}
}
return %switchInfo;
}
#-------------------------------------------------------
=head3 getVswitchInfoExtended
Description : Query a vswitch and produce a hash of the data
using the extended (keyword) related API.
Arguments : zHCP node
Name of switch to be queried
Returns : hash of switch data, if found.
hash contains sections and hash/value pairs:
$switchInfo{'Base'}{$property} = $value;
$switchInfo{'Real device'}{$device}{$property} = $value;
$switchInfo{'Authorized users'}{$authUser}{$property} = $value;
$switchInfo{'Connections'}{$adapter_owner}{$property} = $value;
Example : $rc = getVswitchInfoExtended( $zhcpNode, $switch );
=cut
#-------------------------------------------------------
sub getVswitchInfoExtended{
my ( $zhcpNode, $switch ) = @_;
my %switchInfo;
my @word;
my $device;
my $authUser;
my $out = `ssh $zhcpNode smcli Virtual_Network_Vswitch_Query_Extended -T xxxx -k switch_name=$switch`;
if ( $out !~ /^Failed/ ) {
# Got some information. Process it.
my @lines = split( "\n", $out );
pop( @lines );
my $subsection = 'Base';
my $authPort;
my $adapter_owner;
foreach my $line ( @lines ) {
#print "line: $line\n";
my $indent = $line =~ /\S/ ? $-[0] : length $line; # Get indentation level
$line =~ s/^\s+|\s+$//g; # trim both ends of the line;
if ( $line eq '' ) {
next;
}
$line =~ s/^\s+|\s+$//g; # trim both ends of the line;
@word = split( /:/, $line );
$word[1] =~ s/^\s+|\s+$//g; # trim both ends of the line;
if ( !exists $word[1] ) {
$word[1] = '';
}
if ( $word[0] eq 'switch_name' ) {
$switchInfo{'Base'}{$word[0]} = $word[1];
$subsection = 'Base';
next;
} elsif ( $word[0] eq 'real_device_address' ) {
$subsection = 'Real device';
$device = $word[1];
if ( !exists $switchInfo{$subsection}{'RDEVs'} ) {
$switchInfo{$subsection}{'RDEVs'} = $device;
} else {
$switchInfo{$subsection}{'RDEVs'} = $switchInfo{$subsection}{'RDEVs'} . ' ' . $device;
}
next;
} elsif ( $word[0] eq 'port_num' ) {
$subsection = 'Authorized users';
$authPort = $word[1];
next;
} elsif ( $word[0] eq 'adapter_owner' ) {
$subsection = 'Connections';
$adapter_owner = $word[1];
if ( !exists $switchInfo{$subsection}{'ConnectedUsers'} ) {
$switchInfo{$subsection}{'ConnectedUsers'} = $adapter_owner;
} else {
$switchInfo{$subsection}{'ConnectedUsers'} = $switchInfo{$subsection}{'ConnectedUsers'} . ' ' . $adapter_owner;
}
next;
}
# Fill in hash based upon the subsection we are handling.
my $key;
if ( $subsection eq 'Base' ) {
$switchInfo{$subsection}{$word[0]} = $word[1];
} elsif ( $subsection eq 'Real device' ) {
$switchInfo{$subsection}{$device}{$word[0]} = $word[1];
} elsif ( $subsection eq 'Authorized users' ) {
if ( $word[0] eq 'grant_userid' ) {
$authUser = $word[1];
$switchInfo{$subsection}{$authUser}{'port_num'} = $authPort;
if ( !exists $switchInfo{$subsection}{'AuthorizedUsers'} ) {
$switchInfo{$subsection}{'AuthorizedUsers'} = $authUser;
} else {
$switchInfo{$subsection}{'AuthorizedUsers'} = $switchInfo{$subsection}{'AuthorizedUsers'} . ' ' . $authUser;
}
} else {
$switchInfo{$subsection}{$authUser}{$word[0]} = $word[1];
}
} elsif ( $subsection eq 'Connections' ) {
$switchInfo{$subsection}{$adapter_owner}{$word[0]} = $word[1];
}
}
}
return %switchInfo;
}
#-------------------------------------------------------
=head3 hexDecode
Description : Convert a string of printable hex
characters (4 hex characters per actual
character) into the actual string that
it represents.
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 logTest
Description : Log the start and result of a test.
Failures are added to syslog and printed as script output.
Arguments : Status of the test:
bypassed: Bypassed a test (STDOUT, optionally SYSLOGged)
failed: Failed test (STDOUT, optionally SYSLOGged)
passed: Successful test (STDOUT only)
started: Start test (STDOUT only, increments test number)
misc: Miscellaneous output (STDOUT only)
miscNF: Miscellaneous non-formatted output (STDOUT only)
info: Information output similar to miscellaneous output
but has a message number (STDOUT only)
Message ID (used for "bypassed", "failed", or "warning" messages) or
message TEXT (used for "misc", "passed", or "started" messages).
Message id should begin with the initials of the subroutine
generating the message and begin at 1. For example, the
first error message from verifyNode subroutine would be 'VN01'.
Message substitution values (used for "bypassed", "failed", or
"warning" messages)
Returns : None
Example : logTest( 'failed', "VMNI01", $name, $tgtIp );
logTest( 'started', "xCAT MN has a virtual storage size of at least $vstorMin." );
=cut
#-------------------------------------------------------
sub logTest{
my ( $testStatus, $msgInfo, @msgSubs ) = @_;
my $extraInfo;
my $rc;
my $sev;
my $msg;
if ( $testStatus eq 'misc' ) {
# Miscellaneous output
$msgSubs[0] = "$msgInfo\n";
( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'GENERIC_RESPONSE', \@msgSubs );
print( "$msg\n" );
} elsif ( $testStatus eq 'miscNF' ) {
# Miscellaneous output
print("$msgInfo\n");
} elsif ( $testStatus eq 'passed' ) {
# Test was successful. Log it as ok.
if ( $msgInfo ne '' ) {
$msgSubs[0] = "$msgInfo\n";
( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'GENERIC_RESPONSE', \@msgSubs );
print( "$msg\n" );
}
} elsif ( $testStatus eq 'started' ) {
# Start test
$glob_testNum++;
print( "\n" );
$msgSubs[0] = "Test $glob_testNum: Verifying $msgInfo";
( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'GENERIC_RESPONSE', \@msgSubs );
print( "$msg\n" );
} else {
( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', $msgInfo, \@msgSubs );
# Determine whether we need to ignore the message or produce it and count it.
if ( defined $glob_msgsToIgnore{$msgInfo} ) {
# Ignore this message id
$glob_ignored{$msgInfo} = 1;
$glob_ignoreCnt += 1;
print( "Message $msgInfo is being ignored but would have occurred here.\n" );
} elsif ( defined $glob_msgsToIgnore{$sev} ) {
# Ignoring all messages of this severity.
$glob_ignored{$msgInfo} = 1;
$glob_ignoreCnt += 1;
print( "Message $msgInfo is being ignored but would have occurred here.\n" );
} else {
# Handle the failed, warning and bypassed messages
if ( $testStatus eq 'failed' ) {
# Test failed. Log it as failure and produce necessary messages
$glob_totalFailed += 1;
if ( $glob_totalFailed == 1 || $glob_failedTests[-1] != $glob_testNum ) {
push( @glob_failedTests, $glob_testNum );
}
print( "$msg" );
if ( $extraInfo ne '' ) {
print( "$extraInfo" );
}
} elsif ( $testStatus eq 'warning' ) {
# Warning unrelated to a test.
print("$msg");
if ( $extraInfo ne '' ) {
print( "$extraInfo" );
}
} elsif ( $testStatus eq 'info' ) {
# Information message
print("$msg");
if ( $extraInfo ne '' ) {
print( "$extraInfo" );
}
} elsif ( $testStatus eq 'bypassed' ) {
# Bypass message
if ( $glob_bypassMsg != 0 ) {
print("$msg");
if ( $extraInfo ne '' ) {
print( "$extraInfo" );
}
}
}
# Write the message to syslog
if ( $testStatus ne 'info' ) {
my $logMsg = $msg;
$logMsg =~ s/\t//g;
$logMsg =~ s/\n/ /g;
syslog( 'err', $logMsg );
}
}
}
}
#-------------------------------------------------------
=head3 setOverrides
Description : Set global variables based on input from
an external driver perl script, the
command line or the zxcatIVP_moreCmdOps
environment variable. This allows
the script to be run standalone or overriden
by a driver perl script.
Arguments : None
Returns : None.
Example : setOverrides();
=cut
#-------------------------------------------------------
sub setOverrides{
my $rc;
my $unrecognizedOps = '';
my $val;
# Read the environment variables.
foreach my $opHash ( @cmdOps ) {
my $inpRef = eval('\$' . $opHash->{'inpVar'});
# Update the local input variable with the value from the environment
# variable or set the default.
if ( defined $ENV{ $opHash->{'envVar'} } ) {
$$inpRef = $ENV{ $opHash->{'envVar'} };
} else {
if ( exists $opHash->{'default'} ) {
$$inpRef = $opHash->{'default'};
} else {
next;
}
}
}
# Apply the environent variables as overrides to the global variables in this script.
applyOverrides();
# Clear the input variables so that we can use them for command line operands.
foreach my $opHash ( @cmdOps ) {
my $inpRef = eval('\$' . $opHash->{'inpVar'});
$$inpRef = undef;
}
# Handle options from the command line.
$Getopt::Long::ignorecase = 0;
Getopt::Long::Configure( "bundling" );
if ( !GetOptions(
'bypassMsg=s' => \$cmdOp_bypassMsg,
'cNAddress=s' => \$cmdOp_cNAddress,
'defaultUserProfile=s' => \$cmdOp_defaultUserProfile,
'diskpools=s' => \$cmdOp_diskpools,
'expectedReposSpace=s' => \$cmdOp_expectedReposSpace,
'expUser=s' => \$cmdOp_expUser,
'h|help' => \$glob_displayHelp,
'hostNode=s' => \$cmdOp_hostNode,
'ignore=s' => \$cmdOp_ignore,
'instFCPList=s' => \$cmdOp_instFCPList,
'macPrefix=s' => \$cmdOp_macPrefix,
'mgtNetmask=s' => \$cmdOp_mgtNetmask,
'mnNode=s' => \$cmdOp_mnNode,
'moreCmdOps=s' => \$glob_moreCmdOps,
'networks=s' => \$cmdOp_networks,
'pw_obfuscated' => \$cmdOp_pw_obfuscated,
'signalTimeout=s' => \$cmdOp_signalTimeout,
'syslogErrors' => \$cmdOp_syslogErrors,
'vswitchOSAs=s' => \$cmdOp_vswitchOSAs,
'xcatDiskSpace=s' => \$cmdOp_xcatDiskSpace,
'xcatMgtIp=s' => \$cmdOp_xcatMgtIp,
'xcatMNIp=s' => \$cmdOp_xcatMNIp,
'xcatUser=s' => \$cmdOp_xcatUser,
'zhcpDiskSpace=s' => \$cmdOp_zhcpDiskSpace,
'zhcpFCPList=s' => \$cmdOp_zhcpFCPList,
'zhcpNode=s' => \$cmdOp_zhcpNode,
)) {
print $usage_string;
}
# Handle options passed using the environment variable.
# This will override the same value that was passed in the command line.
# Don't specify the same option on both the command line and in the environment variable.
if ( defined $glob_moreCmdOps ) {
$glob_moreCmdOps =~ hexDecode( $glob_moreCmdOps );
($rc, $unrecognizedOps) = GetOptionsFromString(
$glob_moreCmdOps,
'bypassMsg=s' => \$cmdOp_bypassMsg,
'cNAddress=s' => \$cmdOp_cNAddress,
'defaultUserProfile=s' => \$cmdOp_defaultUserProfile,
'diskpools=s' => \$cmdOp_diskpools,
'expectedReposSpace=s' => \$cmdOp_expectedReposSpace,
'expUser=s' => \$cmdOp_expUser,
'h|help' => \$glob_displayHelp,
'hostNode=s' => \$cmdOp_hostNode,
'ignore=s' => \$cmdOp_ignore,
'instFCPList=s' => \$cmdOp_instFCPList,
'macPrefix=s' => \$cmdOp_macPrefix,
'mgtNetmask=s' => \$cmdOp_mgtNetmask,
'mnNode=s' => \$cmdOp_mnNode,
'networks=s' => \$cmdOp_networks,
'pw_obfuscated' => \$cmdOp_pw_obfuscated,
'signalTimeout=s' => \$cmdOp_signalTimeout,
'syslogErrors' => \$cmdOp_syslogErrors,
'vswitchOSAs=s' => \$cmdOp_vswitchOSAs,
'xcatDiskSpace=s' => \$cmdOp_xcatDiskSpace,
'xcatMgtIp=s' => \$cmdOp_xcatMgtIp,
'xcatMNIp=s' => \$cmdOp_xcatMNIp,
'xcatUser=s' => \$cmdOp_xcatUser,
'zhcpDiskSpace=s' => \$cmdOp_zhcpDiskSpace,
'zhcpFCPList=s' => \$cmdOp_zhcpFCPList,
'zhcpNode=s' => \$cmdOp_zhcpNode,
);
if ( $rc == 0 ) {
print $usage_string;
}
}
# Apply the command line operands as overrides to the global variables in this script.
applyOverrides();
# Special handling for the deobfuscation of the user pw.
if ( defined $glob_xcatUserPw and $glob_xcatUserPw ne '' and $obfuscatePw ) {
# Unobfuscate the password so that we can use it.
$glob_xcatUserPw = decode_base64($val);
}
# Special processing for ignore messages to convert general severity type
# operands to their numeric value.
if ( $glob_msgsToIgnore{'BYPASS'} ) {
delete $glob_msgsToIgnore{'BYPASS'};
$glob_msgsToIgnore{'2'} = 1;
}
if ( $glob_msgsToIgnore{'INFO'} ) {
delete $glob_msgsToIgnore{'INFO'};
$glob_msgsToIgnore{'3'} = 1;
}
if ( $glob_msgsToIgnore{'WARNING'} ) {
delete $glob_msgsToIgnore{'WARNING'};
$glob_msgsToIgnore{'4'} = 1;
}
if ( $glob_msgsToIgnore{'ERROR'} ) {
delete $glob_msgsToIgnore{'ERROR'};
$glob_msgsToIgnore{'5'} = 1;
}
FINISH_setOverrides:
return;
}
#-------------------------------------------------------
=head3 showHelp
Description : Show the help inforamtion.
Arguments : None.
Returns : None.
Example : showHelp();
=cut
#-------------------------------------------------------
sub showHelp{
my ($rc, $sev, $extraInfo, @array);
my $msg;
print "$0 run tests to verify the xCAT installation.\n\n";
print $usage_string;
print "The following environment variables (indicated by env:) ".
"and command line\noperands (indicated by cmd:) are supported.\n\n";
foreach my $opHash ( @cmdOps ) {
if ( exists $opHash->{'desc'} ) {
if ( exists $opHash->{'envVar'} ) {
print "env: $opHash->{'envVar'}\n";
}
if ( exists $opHash->{'opName'} ) {
print "cmd: --$opHash->{'opName'} <value>\n";
}
if ( exists $opHash->{'separator'} ) {
print "List separator: '$opHash->{'separator'}'\n"
}
if ( exists $opHash->{'default'} ) {
print "Default: $opHash->{'default'}\n";
}
print wrap( '', '', "Value: $opHash->{'desc'}" ). "\n";
print "\n";
}
}
print (
"Usage notes:\n" .
"1. An input value can be specified in one of three ways, either as: \n" .
" * an environment variable, \n" .
" * an operand on the command line using the --<operandName> \n" .
" operand, or \n" .
" * a --moreCmdOps operand. \n" .
" The input value in the --<operandName> operand overrides the value \n" .
" specifed by the environment variable. The --moreCmdOps operand \n" .
" overrides both the --<operandName> operand and the environment \n" .
" variable. \n" .
"2. The value for an operand that has a list value may have the \n" .
" members of the list separated by one of the indicated \n" .
" 'List separator' operands. The same separator should be used to \n" .
" separator all members of the list. For example, do NOT separate \n" .
" the first and second element by a comma and separate the second . \n" .
" and third element by a semi-colon. \n" .
"3. If blank is an allowed list separator for the operand and is used \n" .
" then the list should be enclosed in quotes or double quotes so that\n" .
" the list is interpretted as a value associated with the operand. \n" .
" You may need to escape the quotes depending on how you are invoking\n" .
" the command. For this reason, it is often better to choose a \n" .
" separator other than blank. \n"
);
return;
}
#-------------------------------------------------------
=head3 showPoolInfo
Description : Show available space for each disk in the pool
Arguments : Disk pool name
Array of disk information for the pool
Returns : None.
Example : showPoolInfo($node, $args);
=cut
#-------------------------------------------------------
sub showPoolInfo{
my ( $diskPool, $diskLines ) = @_;
my $lines;
$lines = "$diskPool contains the following disks that have space available:";
foreach my $disk ( @$diskLines ) {
my @diskInfo = split( / /, $disk );
my $size = convertDiskSize( $diskInfo[2], $diskInfo[4] );
$lines = $lines . "\nvolid: $diskInfo[1], type: $diskInfo[2], available: $size";
}
logTest( 'misc', $lines );
}
#-------------------------------------------------------
=head3 verifyCMAProperties
Description : Verify key CMA properties are specified
and set the global ROLE property for
user by other functions.
Arguments : None
Returns : 0 - No error
non-zero - Error detected.
Example : $rc = verifyCMAProperties();
=cut
#-------------------------------------------------------
sub verifyCMAProperties{
logTest( "started", "some key CMA properties");
my $out = `cat $glob_versionFileCMA`;
chomp( $out );
if ( $out ne '' ) {
$glob_versionInfo = "$out";
# Determine CMA role: Controller or Compute
my $delim = "=";
open( FILE, $glob_applSystemRole );
while ( <FILE> ) {
my $line = $_;
if ( $line =~ "^role$delim" or $line =~ "^role $delim" ) {
my @array = split( /$delim/, $line, 2 );
$array[1] =~ s/^\s+|\s+$//g; # trim both ends of the string
$glob_CMARole = uc( $array[1] );
}
}
close(FILE);
if ( ' CONTROLLER COMPUTE COMPUTE_MN MN ' !~ / $glob_CMARole / ) {
logTest( "failed", "VCMAP01", $glob_applSystemRole );
$glob_CMARole = '';
}
} else {
$glob_versionInfo = "CMO Appliance version unknown";
logTest( "failed", "VCMAP02", $glob_versionFileCMA );
}
}
#-------------------------------------------------------
=head3 verifyComputeNodeConnectivity
Description : Verify the xCAT MN can SSH to the
Compute Node.
Arguments : Node address (IP or hostname) of the
Compute Node.
User underwhich remote exports will be performed.
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyComputeNodeConnectivity( $nodeAddress );
=cut
#-------------------------------------------------------
sub verifyComputeNodeConnectivity{
my ( $nodeAddress, $user ) = @_;
logTest( 'started', "xCAT MN can ssh to $nodeAddress with user $user." );
my $out = `ssh -o "NumberOfPasswordPrompts 0" $user\@$nodeAddress pwd`;
my $rc = $? >> 8;
if ( $rc != 0 ) {
logTest( 'failed', "VCNC01", $nodeAddress, $user );
return 0; # Non-critical error detected
}
return 0;
}
#-------------------------------------------------------
=head3 verifyDirectorySpace
Description : Verify disk directory space is sufficient.
Arguments : Array of directories containing:
directory name,
maximum percentage in use,
maximum file size (or empty if we should not check)
Printable node name or ZHCP host name (if remote = 1)
Remote processing flag (1 - use SSH to contact the node)
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : $rc = verifyDirectorySpace( \@dirInfo, $zhcpIP );
=cut
#-------------------------------------------------------
sub verifyDirectorySpace{
my ( $dirInfoRef, $system, $remote ) = @_;
my @dirInfo = @$dirInfoRef;
my $minAvailableSpace = '100M';
my $largeFileSize = '30000k';
my $out;
my $rc;
my @sizes;
# If system is the ZHCP running on this xCAT MN's system then bypass the test
# because we would have already tested the directories when we ran the tests
# for the xCAT MN.
if ( $system eq $glob_localZHCP ) {
logTest( 'bypassed', "BPVDS01" );
goto FINISH_verifyDirectorySpace;
}
foreach my $line ( @dirInfo ) {
chomp( $line );
$line =~ s/^\s+|\s+$//g; # trim both ends of the string
my @info = split( ' ', $line );
if ( ! defined $info[0] or $info[0] eq '' or ! defined $info[1] or $info[1] eq '' ) {
# Empty array item and/or maximum percentage is missing.
next;
}
if ( defined $info[2] and $info[2] ne '' and $info[2] ne '.' ) {
$minAvailableSpace = $info[2];
}
if ( defined $info[3] and $info[3] ne '' and $info[3] ne '.' ) {
$largeFileSize = $info[3];
}
# Special case for old ZHCP servers with a memory backed / directory.
if ( $remote and $info[0] eq '/' ) {
$out = `ssh $system ls /persistent 1>/dev/null 2>/dev/null`;
$rc = $? >> 8;
if ( $rc == 255 ) {
logTest( 'failed', "STN01", $system );
next;
} elsif ( $rc == 0 ) {
# Validate /persistent directory instead of /
$info[0] = '/persistent';
}
}
logTest( 'started', "the file system related to $info[0] on the $system system has sufficient space available." );
my $sizeTestFailed = 0;
if( $remote ) {
$out = `ssh $system df -h $info[0] | sed '1d' | sed 'N;s/\\n/ /' | awk '{print \$4,\$5}'`;
$rc = $? >> 8;
if ( $rc == 255 ) {
logTest( 'failed', "STN01", $system );
next;
}
} else {
$out = `df -h $info[0] | sed '1d' | sed 'N;s/\\n/ /' | awk '{print \$4,\$5}'`;
}
chomp( $out );
if ( $out ) {
@sizes = split( ' ', $out, 2 );
# Percentage In Use test
$sizes[1] =~ s/\%+$//g; # trim percent from end of the string
if ( $info[1] ne '.' and $sizes[1] > $info[1] ) {
logTest( 'failed', "VDS01", $info[0], $system, $sizes[1], $info[1] );
$sizeTestFailed = 1;
}
# Minimum Available Size test
if ( $info[2] ne '.' and convertSize( $sizes[0] ) < convertSize( $minAvailableSpace ) ) {
logTest( 'failed', "VDS02", $info[0], $system, $sizes[0], $minAvailableSpace );
$sizeTestFailed = 1;
}
if ( $sizeTestFailed == 0 ) {
logTest( 'misc', "The file system related to $info[0] on the $system system is $sizes[1] percent in use with $sizes[0] available." );
}
} else {
logTest( 'failed', "VDS03", $system, $info[0] );
$sizeTestFailed = 1;
return 0;
}
if ( $info[3] ne '.' and $sizeTestFailed == 1 ) {
# Show any large files in the directory space.
logTest( 'started', "the file system related to $info[0] directory on $system system has reasonable size files." );
if( $remote ) {
$out = `ssh $system find $info[0] -mount -type f -size +$largeFileSize 2>/dev/null -exec ls -lh {} \\; | grep -v -e .so. -e .so -e .jar | awk \'{ print \$9 \": \" \$5 }\'`;
$rc = $? >> 8;
if ( $rc == 255 ) {
logTest( 'failed', "STN01", $system );
next;
}
} else {
$out = `find $info[0] -mount -type f -size +$largeFileSize 2>/dev/null -exec ls -lh {} \\; | grep -v -e .so. -e .so -e .jar | awk \'{ print \$9 \": \" \$5 }\'`;
}
if ( $out ne '' ) {
$out =~ s/\n+$//g; # remove last newline
logTest( 'failed', "VDS04", $largeFileSize, $out );
} else {
logTest( 'passed', "" );
}
}
}
if ( $system eq 'xCAT MN' and $glob_expectedReposSpace ne '' ) {
if ( convertSize( $sizes[0] ) < convertSize( $glob_expectedReposSpace ) ) {
logTest( 'failed', "VDS05", $sizes[0], $glob_expectedReposSpace );
} else {
logTest( 'passed', "" );
}
}
FINISH_verifyDirectorySpace:
return 0;
}
#-------------------------------------------------------
=head3 verifyDiskPools
Description : Verify disk pools are defined and have
at least a minimum amount of space.
Arguments : Array of expected disk pools.
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : $rc = verifyDiskPools( $hostNode, $diskpools );
lsvm zhcp --diskpoolnames
lsvm zhcp --diskpool pool1 free
lsvm zhcp --diskpool pool1 used
=cut
#-------------------------------------------------------
sub verifyDiskPools{
my ( $hostNode, $diskPools ) = @_;
my $out;
my $zhcpNode;
if ( exists $glob_hostNodes{$hostNode}{'zhcp'} ) {
$zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'};
} else {
return 0;
}
logTest( 'started', "disk pools for host: $hostNode." );
$out = `/opt/xcat/bin/lsvm $zhcpNode --diskpoolnames | awk '{print \$NF}'`;
my @definedPools = split /\n/, $out;
# Warn if no disk pools are defined.
if ( @definedPools == 0 ) {
logTest( 'failed', "VDP03", $hostNode );
return 0;
}
logTest( 'misc', "$hostNode has the following disk pools defined: " . join(', ', @$diskPools) . "." );
foreach my $diskPool ( @$diskPools ) {
$diskPool = uc( $diskPool );
# Verify pool is in the list of pools
if ( grep { $_ eq $diskPool } @definedPools ) {
} else {
logTest( 'failed', "VDP01", $diskPool );
next;
}
# Warn if we have very little disk space available
$out = `/opt/xcat/bin/lsvm $zhcpNode --diskpool $diskPool free | grep $zhcpNode | sed '1d'`;
my @disks = split /\n/, $out;
my $numberOfDisks = @disks;
if ( $numberOfDisks == 0 ) {
if (( $diskPool ne 'XCAT' ) and ( $diskPool ne 'XCAT1' )) {
logTest( 'failed', "VDP02", $diskPool );
}
} else {
showPoolInfo( $diskPool, \@disks );
}
}
return 0;
}
#-------------------------------------------------------
=head3 verifyHost
Description : Verify the Host node is defined properly.
Arguments : Host node
ZHCP node
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyHost( $node );
=cut
#-------------------------------------------------------
sub verifyHost{
my ( $node) = @_;
my %hostInfo = getLsdefNodeInfo( $node );
# Verify node is defined
logTest( 'started', "that the host node ($node) is defined in xCAT." );
my $count = keys %hostInfo;
if ( $count == 0 ) {
logTest( 'failed', "VHN01", $node );
return 1; # Critical error detected. IVP should exit.
}
# Verify the 'hcp' is defined for the node
logTest( 'started', "a zHCP is associated with the host node ($node)." );
if ( $hostInfo{'hcp'} eq '' ) {
logTest( 'failed', "VHN02" );
return 1; # Critical error detected. IVP should exit.
}
return 0;
}
#-------------------------------------------------------
=head3 verifyHostNode
Description : Verify the Host node is defined properly.
Arguments : Host node
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyHostNode( $node );
=cut
#-------------------------------------------------------
sub verifyHostNode{
my ( $node ) = @_;
my %hostInfo = getLsdefNodeInfo( $node );
# Verify node is defined
logTest( 'started', "that the host node ($node) is defined in xCAT." );
my $count = keys %hostInfo;
if ( $count == 0 ) {
logTest( 'failed', "VHN01", $node );
return 1; # Critical error detected. IVP should exit.
}
# Verify the 'hcp' is defined for the node
logTest( 'started', "a zHCP is associated with the host node ($node)." );
if ( $hostInfo{'hcp'} eq '' ) {
logTest( 'failed', "VHN02" );
return 1; # Critical error detected. IVP should exit.
}
return 0;
}
#-------------------------------------------------------
=head3 verifyMACUserPrefix
Description : Verify that the specified MACADDR
user prefix matches the one on the host.
Arguments : Host node
MACADDR user prefix
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : verifyMACUserPrefix( $hostNode, $userPrefix );
=cut
#-------------------------------------------------------
sub verifyMACUserPrefix{
my ( $hostNode, $userPrefix ) = @_;
$userPrefix = uc( $userPrefix );
logTest( 'started', "the z/VM system's MACID user prefix matches the one specified in the OpenStack configuration file." );
my %nodeInfo = getLsdefNodeInfo($hostNode);
my $hostUserPrefix = `ssh $nodeInfo{'hcp'} vmcp QUERY VMLAN | sed '1,/VMLAN MAC address assignment:/d' | grep ' MACADDR Prefix:' | awk '{print \$6}'`;
chomp( $hostUserPrefix );
if ( $userPrefix ne $hostUserPrefix ) {
logTest( 'failed', "VMUP01", $hostNode, $hostUserPrefix, $userPrefix );
} else {
logTest( 'passed', "" );
}
return 0;
}
#-------------------------------------------------------
=head3 verifyMemorySize
Description : Verify the virtual machine has
sufficient memory.
Arguments : None
Returns : 0 - No error
non-zero - Error detected.
Example : $rc = verifyMemorySize($node, $args);
=cut
#-------------------------------------------------------
sub verifyMemorySize{
my $vstorMin = '8G';
my ( $out, $tag, $storSize );
# Verify the virtual machine has the recommended virtual storage size.
logTest( 'started', "xCAT MN has a virtual storage size of at least $vstorMin." );
$out = `vmcp query virtual storage | grep STORAGE`;
if ( $out eq '' ) {
logTest( 'failed', "VMS01" );
return 0;
}
($tag, $storSize) = split(/=/, $out, 2);
$storSize =~ s/^\s+|\s+$//g; # trim both ends of the string
my $convStorSize = convertSize( $storSize );
my $convVStorMin = convertSize( $vstorMin );
if ( $convStorSize < $convVStorMin ) {
logTest( 'failed', "VMS02", $storSize, $vstorMin );
}
return 0;
}
#-------------------------------------------------------
=head3 verifyMiscHostStuff
Description : Verify miscellaneous items related to
the host.
Arguments : Host node
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyMiscHostStuff( $node );
=cut
#-------------------------------------------------------
sub verifyMiscHostStuff{
my ( $hostNode) = @_;
my $out;
my $rc;
my $zhcpNode;
if ( exists $glob_hostNodes{$hostNode}{'zhcp'} ) {
$zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'};
} else {
return 0;
}
# Verify the signal shutdown time is not too small
logTest( 'started', "the signal shutdown timeout on $hostNode is more than $glob_signalTimeout." );
my $timeVal;
$out = `ssh $zhcpNode vmcp query signal shutdowntime`;
$rc = $? >> 8;
if ( $rc == 255 ) {
logTest( 'failed', 'STN01', $zhcpNode );
} elsif ( $out !~ "System default shutdown signal timeout:" ) {
logTest( 'failed', 'VMHS01', $hostNode, $rc, $out );
} else {
($timeVal) = $out =~ m/System default shutdown signal timeout: (.*) seconds/;
if ( $timeVal < $glob_signalTimeout ) {
logTest( 'failed', 'VMHS02', $hostNode, $timeVal, $glob_signalTimeout );
}
}
return 0;
}
#-------------------------------------------------------
=head3 verifyMnIp
Description : Verify the xCAT MN Ip is the same one used
by this xCAT MN.
Arguments : xCAT MN IP address or host name
hostName flag, 1 - can be a hostname, 0 - must be an IP address
descriptive name string
subnet mask (optional)
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyMnIp( $ip, $possibleHostname, $name, $subnetMask );
=cut
#-------------------------------------------------------
sub verifyMnIp{
my ( $ip, $possibleHostname, $name, $subnetMask ) = @_;
my ( $out, $rest );
my $tgtIp = $ip;
my $localRP;
my $addrType = 4;
# Verify the IP address or hostname is defined for this machine
logTest( 'started', "xCAT MN has an interface for the $name defined as $ip." );
if ( $possibleHostname ) {
# Assume the input is a hostname, obtain the IP address associated with that name.
# Look for the name in /etc/hosts
$out = `grep " $ip " < /etc/hosts`;
if ( $out ne '' ) {
($tgtIp, $rest) = split(/\s/, $out, 2);
}
}
if ( $tgtIp =~ /:/ ) {
$addrType = 6;
}
# Verify the IP address is defined
$out=`ip addr show to $tgtIp`;
if ( $out eq '' ) {
logTest( 'failed', "VMNI01", $name, $tgtIp );
return 0; # Non-critical error detected
} else {
my $inetString;
if ( $addrType == 4 ) {
$inetString = " inet";
} else {
$inetString = " inet$addrType";
}
my @lines= split( /\n/, $out );
@lines= grep( /$inetString/, @lines );
if ( @lines == 0 ) {
logTest( 'failed', "VMNI02", $name, $tgtIp );
return 0; # Non-critical error detected
}
my @ipInfo = split( ' ', $lines[0] ); # split, ignoring leading spaces
my @parts = split( '/', $ipInfo[1] );
$localRP = $parts[1];
}
# Verify the subnet mask matches what is set on the system
if ( defined $subnetMask ) {
logTest( 'started', "xCAT MN's subnet mask is $subnetMask." );
my $rp = calculateRoutingPrefix( $subnetMask );
if ( $rp == -1 ) {
logTest( 'failed', "VMNI03", $subnetMask );
}
elsif ( $rp != $localRP ) {
logTest( 'failed', "VMNI04", $tgtIp, $localRP, $rp, $subnetMask, $name );
}
}
return 0;
}
#-------------------------------------------------------
=head3 verifyMnNode
Description : Verify the xCAT MN node is defined properly.
Arguments : xCAT MN node
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyMnNode( $node );
=cut
#-------------------------------------------------------
sub verifyMnNode{
my ( $node) = @_;
my %mnInfo = getLsdefNodeInfo( $node );
# Verify node is defined
logTest( 'started', "xCAT MN node ($node) is defined in xCAT." );
my $count = keys %mnInfo;
if ( $count == 0 ) {
logTest( 'failed', "VMN01", $node );
return 0; # Non-critical error detected
}
return 0;
}
#-------------------------------------------------------
=head3 verifyNodeExists
Description : Verify that a named node exists in xCAT.
Arguments : Node name
Function of the node (e.g. "zHCP node")
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : verifyNodeExists($node, $function);
=cut
#-------------------------------------------------------
sub verifyNodeExists{
my ( $node, $function ) = @_;
logTest( 'started', "$function is defined and named $node." );
my %hash = getLsdefNodeInfo($node);
if ( %hash ) {
logTest( 'passed', "" );
} else {
logTest( 'failed', "VNE01", $node );
}
return 0;
}
#-------------------------------------------------------
=head3 verifyNetworks
Description : Verify the specified networks are defined
and have the expected VLAN settings.
Arguments : Host node name
Reference to array of networks to be verified
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyNetworks($hostNode, $network);
lsvm zhcp --getnetworknames
lsvm zhcp --getnetwork xcatvsw2
=cut
#-------------------------------------------------------
sub verifyNetworks{
my ( $hostNode, $networks ) = @_;
my $out;
my $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'};
foreach my $network ( @$networks ) {
$network = uc( $network );
# Split off any VLAN information from the input
my $match;
my @vlans = split( /:/, $network );
if ( exists $vlans[1] ) {
$network = $vlans[0]; # Remove vlan info from the $network variable
$match = "VLAN Aware";
} else {
$match = "VLAN Unaware";
}
logTest( 'started', "$network is defined as a network to $hostNode." );
# Obtain the network info
my %switchInfo = getVswitchInfo( $zhcpNode, $network );
if ( !%switchInfo ) {
logTest( 'failed', "VN01", $network );
next; # Non-critical error detected, iterate to the next switch
}
# Verify that the defined network matches the expectations.
logTest( 'started', "$network is $match" );
if (( $match eq "VLAN Aware" && $switchInfo{'Base'}{'VLAN ID'} != 0 ) ||
( $match eq "VLAN Unaware" && $switchInfo{'Base'}{'VLAN ID'} == 0 )) {
logTest( 'passed', "" );
} else {
logTest( 'failed', "VN02", $network, $match );
}
}
return 0;
}
#-------------------------------------------------------
=head3 verifyProfile
Description : Verify profile is defined in the z/VM directory.
Arguments : Host node name
Profile to be verified
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : $rc = verifyProfile($hostNode, $profile);
=cut
#-------------------------------------------------------
sub verifyProfile{
my ( $hostNode, $profile ) = @_;
$profile = uc( $profile );
logTest( 'started', "$hostNode has the profile ($profile) in the z/VM directory." );
my $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'};
my $out = `/opt/xcat/bin/chhypervisor $hostNode --smcli 'Image_Query_DM -T $profile'`;
if ( $out !~ "PROFILE $profile" ) {
logTest( 'failed', "VP01", $profile );
}
return 0;
}
#-------------------------------------------------------
=head3 verifyREST
Description : Verify the REST interface is running.
Arguments : None
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyREST();
=cut
#-------------------------------------------------------
sub verifyREST{
logTest( 'started', "REST API is accepting requests from user $glob_xcatUser." );
my @restOps = ();
my $response = driveREST( $glob_xcatMNIp, $glob_xcatUser, $glob_xcatUserPw, "nodes/$glob_mnNode", "GET", "json", \@restOps );
#print "Content: " . $response->content . "\n";
#print "Code: " . $response->code . "\n";
#print "Message: " .$response->message . "\n";
if ( $response->message ne "OK" or $response->code != 200 ) {
logTest( 'failed', "VR01", $response->code, $response->message, $response->content );
}
return 0;
}
#-------------------------------------------------------
=head3 verifyUser
Description : Verify a user is authorized for xCAT MN.
Arguments : User
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : $rc = verifyUser( $user );
=cut
#-------------------------------------------------------
sub verifyUser{
my ( $user ) = @_;
my $out;
logTest( 'started', "user ($user) is in the xCAT policy table." );
$out = `/opt/xcat/bin/gettab name=\'$user\' policy.rule`;
$out =~ s/^\s+|\s+$//g; # trim both ends of the string
if ( $out eq '' ) {
logTest( 'failed', "VU01", $user );
} elsif ( $out ne 'allow' and $out ne 'accept' ) {
logTest( 'failed', "VU02", $user, $out );
} else {
logTest( 'passed', "The test is successful. The user ($user) is in the policy table with the rule: \'$out\'." );
}
return 0;
}
#-------------------------------------------------------
=head3 verifyVswitchOSAs
Description : Verify the specified vswitches OSA exist.
Arguments : Hash of Vswitches and their OSAs
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyVswitchOSAs( \%vswitchOSAs );
=cut
#-------------------------------------------------------
sub verifyVswitchOSAs{
my ( $hostNode, $vswitchOSAs ) = @_;
my $out;
logTest( 'started', "vswitches with related OSAs are valid." );
my $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'};
# For each vswitch, verify that it has the specified OSA associated
# with it and it is active or a backup.
foreach my $switch ( keys %$vswitchOSAs ) {
my %switchInfo = getVswitchInfoExtended( $zhcpNode, uc( $switch ) );
if ( !%switchInfo ) {
logTest( 'failed', "VVO01", $switch );
next; # Non-critical error detected, iterate to the next switch
}
my @devices = split( ',', $$vswitchOSAs{$switch} );
foreach my $device ( @devices ) {
$device = uc( $device );
my @osa = split( /\./, $device );
# Verify the RDEV
$device = substr( "000$osa[0]", -4 ); # pad with zeroes
if ( $switchInfo{'Real device'}{'RDEVs'} !~ $device ) {
logTest( 'failed', "VVO02", $switch, $device );
}
}
}
return 0;
}
#-------------------------------------------------------
=head3 verifyZHCPNode
Description : Verify the xCAT zHCP node is defined properly.
Arguments : Host node
Returns : 0 - OK, or only a non-critical error detected
non-zero - Critical error detected, IVP should exit.
Example : my $rc = verifyZHCPNode( $node );
=cut
#-------------------------------------------------------
sub verifyZHCPNode{
my ( $hostNode ) = @_;
my $out;
my $rc;
logTest( 'started', "that a zHCP node is associated with the host: $hostNode." );
my $zhcpNode = findZhcpNode( $hostNode );
if ( ! defined $zhcpNode ) {
logTest( "failed", "VZN05", $hostNode );
return 1; # Critical error detected. IVP should exit.
}
my %zhcpNodeInfo = getLsdefNodeInfo( $zhcpNode );
# Check if this ZHCP node is on the same system as the xCAT MN
if ( exists $zhcpNodeInfo{'ip'} and exists $glob_localIPs{$zhcpNodeInfo{'ip'}} ) {
$glob_localZHCP = $zhcpNode;
}
# Verify that we can ping zHCP
logTest( 'started', "zHCP node ($zhcpNode) is running." );
$out = `/opt/xcat/bin/pping $zhcpNode`;
if ( $out !~ "$zhcpNode: ping" ) {
logTest( 'failed', "VZN01", $zhcpNode );
return 1; # Critical error detected. IVP should exit.
}
# Obtain and zHCP version information.
$out = `ssh $zhcpNode "[ -e \"/opt/zhcp/version\" ] \&\& cat \"/opt/zhcp/version\""`;
$rc = $? >> 8;
if ( $rc == 255) {
logTest( 'failed', "STN01", $zhcpNode );
return 1; # Critical error detected. IVP should exit.
}
if ( $out ne '' ) {
chomp( $out );
$glob_versionInfo = "$glob_versionInfo\nOn $zhcpNode node: $out";
} else {
$glob_versionInfo = "$glob_versionInfo\nOn $zhcpNode node: ZHCP version level is unknown.";
}
# Drive a simple rpower request to zHCP which talks to CP
logTest( 'started', "zHCP ($zhcpNode) can handle a simple request to talk to CP." );
$out = `/opt/xcat/bin/rpower $zhcpNode stat | grep '$zhcpNode:'`;
if ( $out !~ "$zhcpNode: on" ) {
logTest( 'failed', "VZN02", $zhcpNode );
return 1; # Critical error detected. IVP should exit.
}
# Drive a simple SMAPI request thru zHCP
logTest( 'started', "zHCP ($zhcpNode) can handle a simple request to SMAPI." );
$out = `ssh $zhcpNode /opt/zhcp/bin/smcli Query_API_Functional_Level -T dummy 2>&1`;
$rc = $? >> 8;
if ( $rc == 255) {
logTest( 'failed', "STN01", $zhcpNode );
return 1; # Critical error detected. IVP should exit.
}
if ( $out !~ "The API functional level is" ) {
chomp( $out );
logTest( 'failed', "VZN04", $zhcpNode, $out );
return 1; # Critical error detected. IVP should exit.
}
# Yea, We can talk to SMAPI. Remember that we can use this ZHCP for other tests.
$glob_hostNodes{$hostNode}{'zhcp'} = $zhcpNode;
# Drive a more complex request to zHCP, an LSVM command
logTest( 'started', "zHCP ($zhcpNode) can handle a more complex xCAT LSVM request." );
$out = `/opt/xcat/bin/lsvm $zhcpNode | grep '$zhcpNode:'`;
$rc = $? >> 8;
if ( $rc == 255) {
logTest( 'failed', "STN01", $zhcpNode );
return 1; # Critical error detected. IVP should exit.
}
my $zhcpUserid = uc( $zhcpNodeInfo{'userid'} );
if ( $out !~ "USER $zhcpUserid" and $out !~ "IDENTITY $zhcpUserid" ) {
logTest( 'failed', "VZN03", $zhcpNode, $zhcpUserid );
return 1; # Critical error detected. IVP should exit.
}
return 0;
}
#*****************************************************************************
# Main IVP routine
#*****************************************************************************
my $rc;
my $out;
my $userid;
my $terminatingError = 0;
# Update global variables based on overrides from an external perl script.
setOverrides();
# Handle help function.
if ( $glob_displayHelp == 1 ) {
showHelp();
goto FINISH;
}
# Establish SYSLOG logging for errors if function is desired.
if ( $glob_syslogErrors == 1 ) {
my $user = $ENV{ 'USER' };
setlogsock( 'unix' );
openlog( 'xcatIVP', '', 'user');
syslog( 'info', "Began xcatIVP test" );
}
# Detect CMA and obtain the CMA's version information
if ( -e $glob_versionFileCMA ) {
$glob_CMA = 1;
verifyCMAProperties();
} else {
$glob_CMA = 0;
}
%glob_localIPs = getLocalIPs();
# Obtain the xCAT MN's version information
my $xcatVersion;
if ( -e $glob_versionFileXCAT ) {
$out = `cat $glob_versionFileXCAT`;
chomp( $out );
$xcatVersion = "$out";
} else {
$xcatVersion = "xCAT version: unknown";
}
if ( $glob_versionInfo eq '' ) {
$glob_versionInfo = $xcatVersion;
} else {
$glob_versionInfo = "$glob_versionInfo\n$xcatVersion";
}
# Verify the memory size.
if ( $glob_CMA == 1 ) {
verifyMemorySize();
}
# Verify xCAT MN's IP address
if ( defined $glob_xcatMNIp) {
verifyMnIp( $glob_xcatMNIp, 1, "xCAT server address" );
}
# Verify xCAT MN mgt network IP address
if ( defined $glob_xcatMgtIp) {
verifyMnIp( $glob_xcatMgtIp, 0, "Mgt network IP address", $glob_mgtNetmask );
}
# Verify the management node is properly defined in xCAT
if ( defined $glob_mnNode ) {
verifyMnNode( $glob_mnNode );
} else {
logTest( 'bypassed', "BPVMN01" );
}
# Create the list of host node information.
if ( defined $glob_hostNode ) {
$glob_hostNodes{$glob_hostNode}{'input'} = 1;
} else {
my @hostNodes = getHostNodeNames();
foreach my $hostNode (@hostNodes) {
$glob_hostNodes{$hostNode}{'input'} = 0;
}
if ( keys %glob_hostNodes ) {
$glob_hostNode = split( ' ', keys( %glob_hostNodes ), 1 );
}
}
# Verify the host node is properly defined in xCAT and has a ZHCP agent.
foreach my $hostNode ( keys %glob_hostNodes ) {;
$terminatingError = verifyHostNode( $hostNode );
if ( $terminatingError and scalar keys( %glob_hostNodes ) == 1 ) {
goto FINISH;
}
$terminatingError = 0;
# Verify the zHCP node is properly defined in xCAT
$terminatingError = verifyZHCPNode( $hostNode );
if ( $terminatingError and keys( %glob_hostNodes ) == 1 ) {
goto FINISH;
}
$terminatingError = 0;
}
# Verify the zHCP node specified as input is accessible.
if ( defined $glob_zhcpNode ) {
# Verify the zHCP node is properly defined in xCAT
verifyNodeExists( $glob_zhcpNode, "zHCP node" );
}
# Verify the disk pools used to create virtual machines exist
# in the Directory Manager.
if ( @glob_diskpools ) {
verifyDiskPools( $glob_hostNode, \@glob_diskpools );
} else {
logTest( 'bypassed', "BPVDP01" );
foreach my $hostNode ( keys %glob_hostNodes ) {
my @pools = getDiskPoolNames( $hostNode );
verifyDiskPools( $hostNode, \@pools );
}
}
# Verify the networks used by deployed virtual machines exist.
if ( @glob_networks ) {
verifyNetworks( $glob_hostNode, \@glob_networks );
} else {
logTest( 'bypassed', "BPVN01" );
}
# Verify the MACADDR user prefix matches the one on the host.
if ( defined $glob_macPrefix ) {
verifyMACUserPrefix( $glob_hostNode, $glob_macPrefix );
}
# Verify vswitches with related OSAs are valid.
if ( %glob_vswitchOSAs ) {
verifyVswitchOSAs( $glob_hostNode, \%glob_vswitchOSAs );
}
# Verify file system space on the xCAT MN.
verifyDirectorySpace( \@glob_xcatDiskSpace, 'xCAT MN', 0 );
# Verify file system space on the zhcp server for each host.
foreach my $hostNode ( keys %glob_hostNodes ) {
verifyDirectorySpace( \@glob_zhcpDiskSpace, $glob_hostNodes{$hostNode}{'zhcp'}, 1 );
}
# Verify Host related items for each host in the hostNodes hash.
foreach my $hostNode ( keys %glob_hostNodes ) {
# Verify default user profile is defined to the Directory Manager.
if ( defined $glob_defaultUserProfile ) {
verifyProfile( $hostNode, $glob_defaultUserProfile );
}
# Verify the signal shutdown interval is appropriate.
if ( $glob_signalTimeout != 0 ) {
verifyMiscHostStuff( $hostNode );
}
}
# Verify the xCAT user is defined in the xCAT policy table.
if ( defined $glob_xcatUser ) {
verifyUser( $glob_xcatUser );
}
# Verify the REST Interface is responsive.
if ( defined $glob_xcatUser and defined $glob_xcatUserPw and defined $glob_xcatMNIp and defined $glob_mnNode ) {
verifyREST();
}
# Verify that xCAT MN can access the compute node.
if ( defined $glob_cNAddress and defined $glob_expUser ) {
verifyComputeNodeConnectivity( $glob_cNAddress, $glob_expUser );
} else {
logTest( "bypassed", "BPVCNC01" );
}
FINISH:
if ( $terminatingError ) {
logTest( "warning", "MAIN01" );
}
logTest( "misc", "" );
if ( $glob_versionInfo ) {
logTest( "miscNF", "The following versions of code were detected:\n" . $glob_versionInfo );
logTest( "misc", "" );
}
if ( scalar(@glob_failedTests) != 0 ) {
logTest( "misc", "The following tests generated warning(s): " . join(", ", @glob_failedTests) . '.' );
}
if ( $glob_displayHelp != 1 ) {
logTest( "misc", "$glob_testNum IVP tests ran, " . $glob_totalFailed . " tests generated warnings." );
}
if ( $glob_ignoreCnt != 0 ){
logTest( "misc", "Ignored messages $glob_ignoreCnt times." );
my @ignoreArray = sort keys %glob_ignored;
my $ignoreList = join ( ', ', @ignoreArray );
logTest( "misc", "Message Ids of ignored messages: $ignoreList" );
}
# Close out our use of syslog
if ( $glob_syslogErrors == 1 ) {
syslog( 'info', "Ended zxcatIVP test" );
closelog();
}
exit 0;