From 5c5e59dc4d794bd9c88f8307e7fb854b5b67bb95 Mon Sep 17 00:00:00 2001 From: Chuck Brazie Date: Mon, 30 Jan 2017 10:13:57 -0500 Subject: [PATCH] Perl-xCAT merge updates into 2.13 Change-Id: I8338c02d541608a6bc339b07e1270c178be6a415 --- perl-xCAT/perl-xCAT.spec | 4 + perl-xCAT/xCAT/DSHCLI.pm | 27 +- perl-xCAT/xCAT/Schema.pm | 62 +- perl-xCAT/xCAT/Usage.pm | 13 +- perl-xCAT/xCAT/zvmCPUtils.pm | 721 +++++++-- perl-xCAT/xCAT/zvmMsgs.pm | 1382 ++++++++++++++++ perl-xCAT/xCAT/zvmUtils.pm | 2900 ++++++++++++++++++++++++++++------ 7 files changed, 4463 insertions(+), 646 deletions(-) create mode 100644 perl-xCAT/xCAT/zvmMsgs.pm diff --git a/perl-xCAT/perl-xCAT.spec b/perl-xCAT/perl-xCAT.spec index 86c15f41a..e780ae817 100644 --- a/perl-xCAT/perl-xCAT.spec +++ b/perl-xCAT/perl-xCAT.spec @@ -26,6 +26,8 @@ Includes xCAT::Table, xCAT::NodeRange, among others. %define zvm %(if [ "$zvm" = "1" ];then echo 1; else echo 0; fi) %define fsm %(if [ "$fsm" = "1" ];then echo 1; else echo 0; fi) +%define xcatver %(cat /root/xcat-build/xcat-core/Version) +%define builddate %(date) %prep %setup -q -n perl-xCAT %build @@ -97,6 +99,8 @@ chmod 644 $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/perl-xCAT/* cp README $RPM_BUILD_ROOT/%{prefix} chmod 644 $RPM_BUILD_ROOT/%{prefix}/README +echo "xCAT version: "%{xcatver} "Built on: "%{builddate} > $RPM_BUILD_ROOT/opt/xcat/version + %if %fsm %else # These were built dynamically in the build phase diff --git a/perl-xCAT/xCAT/DSHCLI.pm b/perl-xCAT/xCAT/DSHCLI.pm index a0b5788c9..cbd1eb9dd 100644 --- a/perl-xCAT/xCAT/DSHCLI.pm +++ b/perl-xCAT/xCAT/DSHCLI.pm @@ -3846,10 +3846,10 @@ sub usage_dsh my $usagemsg1 = " xdsh -h \n xdsh -q \n xdsh -V \n"; my $usagemsg1a = "xdsh [-K] [-l logonuserid]\n"; my $usagemsg2 = " [-B | --bypass ] [-c] [-e] [-E environment_file] - [--devicetype type_of_device] [-f fanout]\n"; + [--devicetype type_of_device] [-f fanout] [--ip ip_addr]\n"; my $usagemsg3 = " [-l user_ID] [-L] "; my $usagemsg4 = "[-m] [-o options][-q] [-Q] [-r remote_shell] - [-i image] [-s] [-S ksh | csh] [-t timeout]\n"; + [-i image] [-s] [-S ksh | csh] [--show key|script] [-t timeout]\n"; my $usagemsg5 = " [-T] [-X environment variables] [-v] [-z] [--sudo]\n"; my $usagemsg6 = " "; my $usagemsg .= $usagemsg1 .= $usagemsg1a .= $usagemsg2 .= $usagemsg3 .= @@ -3953,8 +3953,10 @@ sub parse_and_run_dsh 'c|cleanup' => \$options{'cleanup'}, 'E|environment=s' => \$options{'environment'}, 'I|ignore-sig|ignoresig=s' => \$options{'ignore-signal'}, + 'ip=s' => \$options{'ip'}, 'K|keysetup' => \$options{'ssh-setup'}, 'L|no-locale' => \$options{'no-locale'}, + 'show=s' => \$options{'show'}, 'Q|silent' => \$options{'silent'}, 'S|syntax=s' => \$options{'syntax'}, 'T|trace' => \$options{'trace'}, @@ -4122,7 +4124,7 @@ sub parse_and_run_dsh # with error, if the Management Node is in the Database and in the # noderange my @mname = xCAT::Utils->noderangecontainsMn(@nodelist); - if (@mname) { # MN in the nodelist + if ( @mname and !$options{'ip'} ) { # MN in the nodelist and --ip not specified my $nodes = join(',', @mname); my $rsp = {}; $rsp->{error}->[0] = @@ -4156,7 +4158,7 @@ sub parse_and_run_dsh # password for the key update. This was setup in xdsh client # frontend. - if (!($ENV{'DSH_REMOTE_PASSWORD'})) + if (!($ENV{'DSH_REMOTE_PASSWORD'}) && !(defined $options{'show'})) { my $rsp = {}; $rsp->{error}->[0] = @@ -4230,7 +4232,22 @@ sub parse_and_run_dsh # # setup ssh keys on the nodes or ib switch # - my $rc = xCAT::TableUtils->setupSSH($options{'nodes'}, $options{'timeout'}); + my $rc; + + # If 'show' was specified then pass the value in an environment variable. + if ( $options{'show'} ) { + $ENV{'DSH_SHOW'} = $options{'show'}; + } + + # Go perform the setup of the SSH keys. + if ( $options{'ip'} ) { + # If IPs were passed then use them as the target of the unlock. + $rc = xCAT::TableUtils->setupSSH($options{'ip'} ); + } else { + # Unlock the nodes + $rc = xCAT::TableUtils->setupSSH($options{'nodes'}, $options{'timeout'}); + } + my @results = "return code = $rc"; return (@results); } diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index 228ab4770..4f1ee635c 100755 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -276,7 +276,7 @@ qq{ link,ro - The file is readonly, and will be placed in tmpfs on the booted no table_descr => 'Node storage resources', descriptions => { node => 'The node name', - controller => 'The management address to attach/detach new volumes. + controller => 'The management address to attach/detach new volumes. In the scenario involving multiple controllers, this data must be passed as argument rather than by table value', osvolume => "Specification of what storage to place the node OS image onto. Examples include: @@ -649,13 +649,13 @@ passed as argument rather than by table value', servicenode => 'A comma separated list of node names (as known by the management node) that provides most services for this node. The first service node on the list that is accessible will be used. The 2nd node on the list is generally considered to be the backup service node for this node when running commands like snmove.', netboot => 'The type of network booting to use for this node. Valid values: - Arch OS valid netboot options - x86, x86_64 ALL pxe, xnba + Arch OS valid netboot options + x86, x86_64 ALL pxe, xnba ppc64 <=rhel6, <=sles11.3 yaboot ppc64 >=rhels7, >=sles11.4 grub2,grub2-http,grub2-tftp ppc64le NonVirtualize ALL petitboot ppc64le PowerKVM Guest ALL grub2,grub2-http,grub2-tftp - + ', tftpserver => 'The TFTP server for this node (as known by this node). If not set, it defaults to networks.tftpserver.', tftpdir => 'The directory that roots this nodes contents from a tftp and related perspective. Used for NAS offload by using different mountpoints.', @@ -776,16 +776,16 @@ passed as argument rather than by table value', exlist => 'The fully qualified name of the file that stores the file names and directory names that will be excluded from the image during packimage command. It is used for diskless image only.', postinstall => 'Supported in diskless image only. The fully qualified name of the scripts running in non-chroot mode after the package installation but before initrd generation during genimage. If multiple scripts are specified, they should be seperated with comma ",". A set of osimage attributes are exported as the environment variables to be used in the postinstall scripts: - IMG_ARCH(The architecture of the osimage, such as "ppc64le","x86_64"), - IMG_NAME(The name of the osimage, such as "rhels7.3-ppc64le-netboot-compute"), - IMG_OSVER(The os release of the osimage, such as "rhels7.3","sles11.4"), + IMG_ARCH(The architecture of the osimage, such as "ppc64le","x86_64"), + IMG_NAME(The name of the osimage, such as "rhels7.3-ppc64le-netboot-compute"), + IMG_OSVER(The os release of the osimage, such as "rhels7.3","sles11.4"), IMG_KERNELVERSION(the "kernelver" attribute of the osimage), - IMG_PROFILE(the profile of the osimage, such as "service","compute"), - IMG_PKGLIST(the "pkglist" attribute of the osimage), - IMG_PKGDIR(the "pkgdir" attribute of the osimage), - IMG_OTHERPKGLIST(the "otherpkglist" attribute of the osimage), - IMG_OTHERPKGDIR(the "otherpkgdir" attribute of the osimage), - IMG_ROOTIMGDIR(the "rootimgdir" attribute of the osimage)', + IMG_PROFILE(the profile of the osimage, such as "service","compute"), + IMG_PKGLIST(the "pkglist" attribute of the osimage), + IMG_PKGDIR(the "pkgdir" attribute of the osimage), + IMG_OTHERPKGLIST(the "otherpkglist" attribute of the osimage), + IMG_OTHERPKGDIR(the "otherpkgdir" attribute of the osimage), + IMG_ROOTIMGDIR(the "rootimgdir" attribute of the osimage)', rootimgdir => 'The directory name where the image is stored. It is generally used for diskless image. it also can be used in sysclone environment to specify where the image captured from golden client is stored. in sysclone environment, rootimgdir is generally assigned to some default value by xcat, but you can specify your own store directory. just one thing need to be noticed, wherever you save the image, the name of last level directory must be the name of image. for example, if your image name is testimage and you want to save this image under home directoy, rootimgdir should be assigned to value /home/testimage/', kerneldir => 'The directory name where the 3rd-party kernel is stored. It is used for diskless image only.', nodebootif => 'The network interface the stateless/statelite node will boot over (e.g. eth0)', @@ -1037,7 +1037,7 @@ passed as argument rather than by table value', " nodes, use a simple comma-separated list of NICs. To specify different \n" . " NICs for different nodes, use the format: \"xcatmn|eth1,eth2;service|bond0\", \n" . " where xcatmn is the name of the management node, and DNS should listen on\n" . -" the eth1 and eth2 interfaces. All the nods in group 'service' should \n" . +" the eth1 and eth2 interfaces. All the nods in group 'service' should \n" . " listen on the 'bond0' interface.\n\n" . " NOTE: If using this attribute to block certain interfaces, make sure\n" . " the IP maps to your hostname of xCAT MN is not blocked since xCAT needs\n" . @@ -1449,8 +1449,30 @@ passed as argument rather than by table value', }, }, +zvmivp => { + cols => [qw(id ip schedule last_run type_of_run access_user orch_parms prep_parms main_ivp_parms comments disable)], + keys => [qw(id)], + table_desc => 'List of z/VM Installation Verification Procedures (IVPs) to be periodically run.', + descriptions => { + id => 'Unique identifier associated with the IVP run, e.g. 1.', + ip => 'IP address of the target system, either the IP of the OpenStack compute node or the xCAT management node.', + schedule => 'The hours (0-24) that the IVP should be run. Multiple hours are separated by a blank.', + last_run => 'The last time the IVP was run specified as a set of 3 blank delimeted words: year, Julian date, and hour (in 24 hour format).', + type_of_run => 'The type of run requested, \'fullivp\' or \'basicivp\'.', + access_user => 'User on the OpenStack node that is used to: push the IVP preparation script to the OpenStack system, '. + 'drive the preparation script to validate the OpenStack configuration files, and return the created '. + 'driver script to the xCAT MN system for the next part of the IVP. This user should be '. + 'able to access the OpenStack configuration files that are scanned by the IVP.', + orch_parms => 'Parameters to pass to the IVP orchestrator script, verifynode.', + prep_parms => 'Parameters to pass to the phase 1 IVP preparation script.', + main_ivp_parms => 'Parameters to pass to the main IVP script.', + comments => 'Any user provided notes or description of the run.', + disable => "Set to 'yes' or '1' to disable this IVP run.", + }, + }, + zvm => { - cols => [qw(node hcp userid nodetype parent comments disable)], + cols => [qw(node hcp userid nodetype parent comments disable discovered status)], keys => [qw(node)], table_desc => 'List of z/VM virtual servers.', descriptions => { @@ -1461,6 +1483,8 @@ passed as argument rather than by table value', parent => 'The parent node. For LPAR, this specifies the CEC. For z/VM, this specifies the LPAR. For VM, this specifies the z/VM host operating system.', comments => 'Any user provided notes.', disable => "Set to 'yes' or '1' to comment out this row.", + discovered => "Set to '1' to indicate this node was discovered.", + status => "The processing status. Key value pairs (key=value) indicating status of the node. Multiple pairs are separated by semi-colons. Keys include: CLONING, CLONE_ONLY.", }, }, @@ -1497,12 +1521,12 @@ passed as argument rather than by table value', !|,!|,..., for example, eth0!-eth0|-eth0-ipv6,ib0!-ib0|-ib0-ipv6. The xCAT object definition commands support to use nichostnamesuffixes. as the sub attributes. Note: According to DNS rules a hostname must be a text string up to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus sign (-),and period (.). When you are specifying "nichostnamesuffixes" or "nicaliases" make sure the resulting hostnames will conform to this naming convention', - nichostnameprefixes => 'Comma-separated list of hostname prefixes per NIC. + nichostnameprefixes => 'Comma-separated list of hostname prefixes per NIC. If only one ip address is associated with each NIC: !,!,..., for example, eth0!eth0-,ib0!ib- If multiple ip addresses are associated with each NIC: - !|,!|,..., for example, eth0!eth0-|eth0-ipv6i-,ib0!ib-|ib-ipv6-. - The xCAT object definition commands support to use nichostnameprefixes. as the sub attributes. + !|,!|,..., for example, eth0!eth0-|eth0-ipv6i-,ib0!ib-|ib-ipv6-. + The xCAT object definition commands support to use nichostnameprefixes. as the sub attributes. Note: According to DNS rules a hostname must be a text string up to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus sign (-),and period (.). When you are specifying "nichostnameprefixes" or "nicaliases" make sure the resulting hostnames will conform to this naming convention', nictypes => 'Comma-separated list of NIC types per NIC. !,!, e.g. eth0!Ethernet,ib0!Infiniband. The xCAT object definition commands support to use nictypes. as the sub attributes.', niccustomscripts => 'Comma-separated list of custom scripts per NIC. !,!, e.g. eth0!configeth eth0, ib0!configib ib0. The xCAT object definition commands support to use niccustomscripts. as the sub attribute @@ -1813,7 +1837,7 @@ foreach my $tabname (keys(%xCAT::ExtTab::ext_tabspec)) { rack => { attrs => [], attrhash => {}, objkey => 'rackname' }, osdistro => { attrs => [], attrhash => {}, objkey => 'osdistroname' }, osdistroupdate => { attrs => [], attrhash => {}, objkey => 'osupdatename' }, - zone => { attrs => [], attrhash => {}, objkey => 'zonename' }, + zvmivp => { attrs => [], attrhash => {}, objkey => 'id' }, firmware => { attrs => [], attrhash => {}, objkey => 'cfgfile' }, taskstate => { attrs => [], attrhash => {}, objkey => 'node' }, diff --git a/perl-xCAT/xCAT/Usage.pm b/perl-xCAT/xCAT/Usage.pm index 5dfb9494c..b66133394 100755 --- a/perl-xCAT/xCAT/Usage.pm +++ b/perl-xCAT/xCAT/Usage.pm @@ -83,7 +83,7 @@ my %usage = ( BMC specific: rinv [mprom|deviceid|uuid|guid|vpd|all] OpenPOWER server specific: - rinv [model|serial|deviceid|uuid|guid|vpd|mprom|firm|all] + rinv [model|serial|deviceid|uuid|guid|vpd|mprom|firm|all] MPA specific: rinv [firm|bios|diag|mprom|sprom|mparom|mac|mtm] PPC specific(with HMC): @@ -230,13 +230,16 @@ my %usage = ( lsvm [-a|--all] PPC (using Direct FSP Management) specific: lsvm [-l|--long] --p775 - lsvm + lsvm zVM specific: lsvm noderange lsvm noderange --getnetworknames lsvm noderange --getnetwork network_name lsvm noderange --diskpoolnames - lsvm noderange --diskpool pool_name", + lsvm noderange --diskpool pool_name + lsvm noderange --queryalldisks + lsvm noderange --querypagevolumes + lsvm noderange --queryspoolvolumes", "chvm" => "Usage: Common: @@ -250,7 +253,7 @@ my %usage = ( chvm [lparname=<*|name>] chvm [vmcpus=min/req/max] [vmmemory=min/req/max] [vmothersetting=hugepage:N,bsr:N] - [add_physlots=drc_index1,drc_index2...] + [add_physlots=drc_index1,drc_index2...] [add_vmnics=vlan1,vlan2] [add_vmstorage=] [--vios] chvm [del_physlots=drc_index1,drc_index2...] chvm [del_vadapter=slotid] @@ -420,7 +423,7 @@ Options: [-f|--snsync] Performs File Syncing to the service nodes that service the nodes in the noderange. - [-g|--genmypost] Will generate a new mypostscript file for the + [-g|--genmypost] Will generate a new mypostscript file for the the nodes in the noderange, if site precreatemypostscripts is 1 or YES. [-l|--user] User name to run the updatenode command. It overrides the diff --git a/perl-xCAT/xCAT/zvmCPUtils.pm b/perl-xCAT/xCAT/zvmCPUtils.pm index 7c12ab418..3b3b75e4f 100644 --- a/perl-xCAT/xCAT/zvmCPUtils.pm +++ b/perl-xCAT/xCAT/zvmCPUtils.pm @@ -2,7 +2,7 @@ #------------------------------------------------------- =head1 - + This is a CP utility plugin for z/VM. =cut @@ -23,14 +23,14 @@ use warnings; Node Returns : UserID Example : my $userID = xCAT::zvmCPUtils->getUserId($node); - + =cut #------------------------------------------------------- sub getUserId { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -38,10 +38,15 @@ sub getUserId { } # Get user ID using VMCP - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; - my @results = split(' ', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; + my $cmd = "$sudo /sbin/vmcp q userid"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @results = split( ' ', $out ); - return ($results[0]); + return ( $results[0] ); } #------------------------------------------------------- @@ -53,14 +58,14 @@ sub getUserId { Node Returns : z/VM host Example : my $host = xCAT::zvmCPUtils->getHost($node); - + =cut #------------------------------------------------------- sub getHost { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -68,8 +73,13 @@ sub getHost { } # Get host using VMCP - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; - my @results = split(' ', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; + my $cmd = "$sudo /sbin/vmcp q userid"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @results = split( ' ', $out ); my $host = $results[2]; return ($host); @@ -84,14 +94,14 @@ sub getHost { Node Returns : Privilege class Example : my $class = xCAT::zvmCPUtils->getPrivileges($node); - + =cut #------------------------------------------------------- sub getPrivileges { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -99,10 +109,15 @@ sub getPrivileges { } # Get privilege class - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q priv"`; - my @out = split('\n', $out); - $out[1] = xCAT::zvmUtils->trimStr($out[1]); - $out[2] = xCAT::zvmUtils->trimStr($out[2]); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q priv"`; + my $cmd = "$sudo /sbin/vmcp q priv"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @out = split( '\n', $out ); + $out[1] = xCAT::zvmUtils->trimStr( $out[1] ); + $out[2] = xCAT::zvmUtils->trimStr( $out[2] ); my $str = " $out[1]\n $out[2]\n"; return ($str); @@ -116,15 +131,15 @@ sub getPrivileges { Arguments : User (root or non-root) Node Returns : Memory - Example : my $memory = xCAT::zvmCPUtils->getMemory($node); - + Example : my $memory = xCAT::zvmCPUtils->getMemory($user, $node); + =cut #------------------------------------------------------- sub getMemory { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -132,10 +147,15 @@ sub getMemory { } # Get memory - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual storage"`; - my @out = split(' ', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual storage"`; + my $cmd = "$sudo /sbin/vmcp q virtual storage"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @out = split( ' ', $out ); - return (xCAT::zvmUtils->trimStr($out[2])); + return ( xCAT::zvmUtils->trimStr( $out[2] ) ); } @@ -148,15 +168,15 @@ sub getMemory { Arguments : User (root or non-root) Node Returns : Processor(s) - Example : my $proc = xCAT::zvmCPUtils->getCpu($node); - + Example : my $proc = xCAT::zvmCPUtils->getCpu( $user, $node ); + =cut #------------------------------------------------------- sub getCpu { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -164,7 +184,12 @@ sub getCpu { } # Get processors - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual cpus"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual cpus"`; + my $cmd = "$sudo /sbin/vmcp q virtual cpus"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } my $str = xCAT::zvmUtils->tabStr($out); return ($str); @@ -179,14 +204,14 @@ sub getCpu { Node Returns : NIC(s) Example : my $nic = xCAT::zvmCPUtils->getNic($node); - + =cut #------------------------------------------------------- sub getNic { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -194,7 +219,12 @@ sub getNic { } # Get NIC - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual nic"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual nic"`; + my $cmd = "$sudo /sbin/vmcp q virtual nic"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } my $str = xCAT::zvmUtils->tabStr($out); return ($str); @@ -209,14 +239,38 @@ sub getNic { Node Returns : Network names Example : my $lans = xCAT::zvmCPUtils->getNetworkNames($user, $node); - + =cut #------------------------------------------------------- sub getNetworkNames { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + my $hcp; + my $hcpUserId; + # Get node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # Get zHCP. If not propVals then the node is probably a zhcp + if (!$propVals) { + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $node); + $hcpUserId =~ tr/a-z/A-Z/; + $hcp = $node; + } else { + $hcp = $propVals->{'hcp'}; + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + } + + if ( !$hcpUserId ) { + xCAT::zvmUtils->printSyslog("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + return ("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + } my $sudo = "sudo"; if ($user eq "root") { @@ -224,28 +278,126 @@ sub getNetworkNames { } # Get network names - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan | egrep 'LAN|VSWITCH'"`; - my @lines = split('\n', $out); - my @parms; + my $out; my $names; - foreach (@lines) { + my $count; + my @parms; + my @lines; + my $switchNamesFound = ""; + my $start = 0; + my $switchName; + my $lanType; + my $lanName; + my $lanOwner; + my $rc; - # Trim output - $_ = xCAT::zvmUtils->trimStr($_); - @parms = split(' ', $_); + #If this is zhcp then use SMAPI calls to get network names; otherwise use q lan + if (!$propVals) { + my $retStr; + # First get the VSWITCH information, saving the switch name + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name=*'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name=*'"`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_Vswitch_Query_Extended. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + return ($out); + } + @lines = split( '\n', $out ); + # Just get the lines with "switch_name" + @lines = grep /switch_name/, @lines; + $count = @lines; - # Get the network name - if ($parms[0] eq "LAN") { + # Grep output is 1 line for each item + for (my $i=0; $i < $count; $i++) { + @parms = split( ' ', $lines[$i]); + $switchName = $parms[1]; + $switchNamesFound .= "+" . $switchName . "+"; + $names .= "VSWITCH" . " " . "SYSTEM" . " " . $switchName . "\n"; + } - # Determine if this network is a hipersocket - # Only hipersocket guest LANs are supported - if ($_ =~ m/Type: HIPERS/i) { - $names .= $parms[0] . ":HIPERS " . $parms[1] . " " . $parms[2] . "\n"; + # Next get the LAN information, skipping switches we have + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n '*' -o '*'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n '*' -o '*'"`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_LAN_Query. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + return ($out); + } + @lines = split( '\n', $out ); + # Just get the lines with "Name:|Owner:|LAN type:" + @lines = grep /Name:|Owner:|LAN type:/, @lines; + $count = @lines; + + # Grep output is 3 lines for each item + for (my $i=0; $i < ($count/3); $i++) { + @parms = split( ' ', $lines[$start]); + $lanName = $parms[1]; + $start++; + @parms = split(' ', $lines[$start]); + $lanOwner = $parms[1]; + $start++; + if ( $lines[$start] =~ m/QDIO/i ) { + $lanType = ":QDIO "; } else { - $names .= $parms[0] . ":QDIO " . $parms[1] . " " . $parms[2] . "\n"; + $lanType = ":HIPERS "; + } + $start++; + # Skip any lanNames that were found by VSWITCH query + my $search = "+$lanName+"; + if (index($switchNamesFound, $search) == -1) { + $names .= "LAN" . $lanType . $lanOwner . " " . $lanName . "\n"; + } + } + # use vmcp q lan if this is not the zhcp node + } else { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan | egrep 'LAN|VSWITCH'"`; + my $cmd = $sudo . ' /sbin/vmcp q lan | egrep "LAN|VSWITCH"'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + @lines = split( '\n', $out ); + + foreach (@lines) { + + # Trim output + $_ = xCAT::zvmUtils->trimStr($_); + @parms = split( ' ', $_ ); + + # sample output from q lan + # LAN SYSTEM GLAN1 Type: QDIO Connected: 1 Maxconn: INFINITE + # VSWITCH SYSTEM XCATVSW1 Type: QDIO Connected: 2 Maxconn: INFINITE + # Get the network name + if ( $parms[0] eq "LAN" ) { + + # Determine if this network is a hipersocket + # Only hipersocket guest LANs are supported + if ( $_ =~ m/Type: HIPERS/i ) { + $names .= $parms[0] . ":HIPERS " . $parms[1] . " " . $parms[2] . "\n"; + } else { + $names .= $parms[0] . ":QDIO " . $parms[1] . " " . $parms[2] . "\n"; + } + } elsif ( $parms[0] eq "VSWITCH" ) { + $names .= $parms[0] . " " . $parms[1] . " " . $parms[2] . "\n"; } - } elsif ($parms[0] eq "VSWITCH") { - $names .= $parms[0] . " " . $parms[1] . " " . $parms[2] . "\n"; } } @@ -261,14 +413,14 @@ sub getNetworkNames { Node Returns : Array of networks names Example : my @networks = xCAT::zvmCPUtils->getNetworkNamesArray($user, $node); - + =cut #------------------------------------------------------- sub getNetworkNamesArray { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my @networks; my %netHash; @@ -278,25 +430,28 @@ sub getNetworkNamesArray { } # Get the networks used by the node - my $out = `ssh $user\@$node "$sudo /sbin/vmcp q v nic" | egrep -i "VSWITCH|LAN"`; - my @lines = split('\n', $out); + #my $out = `ssh $user\@$node "$sudo /sbin/vmcp q v nic" | egrep -i "VSWITCH|LAN"`; + my $cmd = $sudo . ' /sbin/vmcp q v nic | egrep -i "VSWITCH|LAN"'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @lines = split( '\n', $out ); # Loop through each line my $line; my @words; my $name; - foreach (@lines) { - + foreach(@lines) { # Get network name # Line should contain: MAC: 02-00-01-00-00-12 VSWITCH: SYSTEM VSW1 - $line = xCAT::zvmUtils->trimStr($_); - @words = split(' ', $line); + $line = xCAT::zvmUtils->trimStr( $_ ); + @words = split( ' ', $line ); if (@words) { - $name = xCAT::zvmUtils->trimStr($words[4]); + $name = xCAT::zvmUtils->trimStr( $words[4] ); # If network is not 'None' if ($name ne 'None') { - # Save network $netHash{$name} = 1; } @@ -304,7 +459,7 @@ sub getNetworkNamesArray { } # Push networks into array - foreach $name (keys %netHash) { + foreach $name ( keys %netHash ) { push(@networks, $name); } @@ -321,26 +476,110 @@ sub getNetworkNamesArray { Network name Returns : Network configuration Example : my $config = xCAT::zvmCPUtils->getNetwork($node, $netName); - + =cut #------------------------------------------------------- sub getNetwork { # Get inputs - my ($class, $user, $node, $netName) = @_; + my ( $class, $user, $node, $netName, $netType ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - # Get network info + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + my $hcp; + my $hcpUserId; my $out; + my $retStr; + my $rc; + + my $netNameQuery = $netName; if ($netName eq "all") { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan"`; + $netNameQuery = "*"; + } + + xCAT::zvmUtils->printSyslog("getNetwork for NetType:$netType NetName:$netName NetNameQuery:$netNameQuery"); + + # Get node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # Get zHCP. If not propVals then the node is probably a zhcp + if (!$propVals) { + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $node); + $hcpUserId =~ tr/a-z/A-Z/; + $hcp = $node; } else { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; + $hcp = $propVals->{'hcp'}; + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + } + + if ( !$hcpUserId ) { + xCAT::zvmUtils->printSyslog("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + return ("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + } + + #If this is zhcp then use SMAPI calls to get network names; otherwise use q lan + if (!$propVals) { + # If this is a vswitch, use: Virtual_Network_Vswitch_Query_Extended + if (index($netType, "VSWITCH") >= 0) { + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name='$netNameQuery -k 'VEPA_STATUS=YES'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name='$netNameQuery -k 'VEPA_STATUS=YES'"`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_Vswitch_Query_Extended. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + } + } else { + # Get the LAN information + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n $netNameQuery -o '*'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n $netNameQuery -o '*' "`; + $rc = $? >> 8; + if ($rc) { + $retStr = "(Error) unable to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_LAN_Query. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + } + } + # if not zhcp use q lan output + } else { + # Get network info. + if ( $netName eq "all" ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan"`; + my $cmd = "$sudo /sbin/vmcp q lan"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + } else { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; + my $cmd = "$sudo /sbin/vmcp q lan $netName"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + } } return ($out); @@ -355,14 +594,14 @@ sub getNetwork { Node Returns : Disk(s) Example : my $storage = xCAT::zvmCPUtils->getDisks($node); - + =cut #------------------------------------------------------- sub getDisks { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -370,7 +609,12 @@ sub getDisks { } # Get disks - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual dasd"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual dasd"`; + my $cmd = "$sudo /sbin/vmcp q virtual dasd"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } my $str = xCAT::zvmUtils->tabStr($out); return ($str); @@ -383,16 +627,17 @@ sub getDisks { Description : Load Linux VMCP module on a given node Arguments : User (root or non-root) Node - Returns : Nothing + Returns : Nothing, or error string + Example : xCAT::zvmCPUtils->loadVmcp($node); - + =cut #------------------------------------------------------- sub loadVmcp { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -400,8 +645,8 @@ sub loadVmcp { } # Load Linux VMCP module - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/modprobe vmcp"`; - return; + my $cmd = "$sudo /sbin/modprobe vmcp"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER } #------------------------------------------------------- @@ -413,14 +658,14 @@ sub loadVmcp { Node Returns : VSwitch ID(s) Example : my @vswitch = xCAT::zvmCPUtils->getVswitchId($node); - + =cut #------------------------------------------------------- sub getVswitchId { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -428,13 +673,18 @@ sub getVswitchId { } # Get VSwitch - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v nic" | grep "VSWITCH"`; - my @lines = split('\n', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v nic" | grep "VSWITCH"`; + my $cmd = $sudo . ' /sbin/vmcp q v nic | grep "VSWITCH"'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @lines = split( '\n', $out ); my @parms; my @vswitch; foreach (@lines) { - @parms = split(' ', $_); - push(@vswitch, $parms[4]); + @parms = split( ' ', $_ ); + push( @vswitch, $parms[4] ); } return @vswitch; @@ -444,21 +694,23 @@ sub getVswitchId { =head3 grantVSwitch - Description : Grant VSwitch access for a given userID + Description : Grant VSwitch access for a given userID Arguments : User (root or non-root) zHCP User ID VSWITCH ID + lan port type ( or '') + vlan id (or '') Returns : Operation results (Done/Failed) - Example : my $out = xCAT::zvmCPUtils->grantVswitch($callback, $hcp, $userId, $vswitchId); - + Example : my $out = xCAT::zvmCPUtils->grantVswitch($callback, $hcp, $userId, $vswitchId, [$vlanporttype, $vlanid]); + =cut #------------------------------------------------------- sub grantVSwitch { # Get inputs - my ($class, $callback, $user, $hcp, $userId, $vswitchId) = @_; + my ( $class, $callback, $user, $hcp, $userId, $vswitchId, $vlanporttype, $vlanid ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -468,23 +720,80 @@ sub grantVSwitch { $sudo = ""; } - # Use SMAPI EXEC - my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId"`; - xCAT::zvmUtils->printSyslog("grantVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId"); + my $lanidparm = ''; + if ($vlanporttype ne "") { + $lanidparm = " -k \'port_type=" + $vlanporttype + "\'"; + } + if ($vlanid ne "") { + $lanidparm += " -k \'user_vlan_id=" + $vlanid + "\'"; + } + + # Use SMAPI EXEC, use new extended SMAPI vs old one + # my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId -u 2"`; + # xCAT::zvmUtils->printSyslog("grantVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId -u 2"); + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set_Extended -T SYSTEM -k 'switch_name='$vswitchId -k 'grant_userid='$userId -k 'persist=YES' $lanidparm"`; + xCAT::zvmUtils->printSyslog("grantVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set_Extended -T SYSTEM -k 'switch_name='$vswitchId -k 'grant_userid='$userId -k 'persist=YES '$lanidparm"); $out = xCAT::zvmUtils->trimStr($out); # If return string contains 'Done' - Operation was successful my $retStr; - if ($out =~ m/Done/i) { + if ( $out =~ m/Done/i ) { $retStr = "Done\n"; } else { - $retStr = "Failed\n"; + $retStr = "Failed " . "Error output: $out\n"; + xCAT::zvmUtils->printSyslog("Error output: $out"); return $retStr; } return $retStr; } +=head3 revokeVSwitch + + Description : Revoke VSwitch access for a given userID + Arguments : User (root or non-root) + zHCP + User ID + VSWITCH ID + Returns : Operation results (Done/Failed) + Example : my $out = xCAT::zvmCPUtils->revokeVswitch($callback, $hcp, $userId, $vswitchId); + +=cut + +#------------------------------------------------------- +sub revokeVSwitch { + + # Get inputs + my ( $class, $callback, $user, $hcp, $userId, $vswitchId ) = @_; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Use SMAPI EXEC, use new extended SMAPI + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set_Extended -T SYSTEM -k 'switch_name='$vswitchId -k 'revoke_userid='$userId -k 'persist=YES'"`; + xCAT::zvmUtils->printSyslog("revokeVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -k 'switch_name='$vswitchId -k 'revoke_userid='$userId -k 'persist=YES'"); + $out = xCAT::zvmUtils->trimStr($out); + + xCAT::zvmUtils->printSyslog($out); + + + # If return string contains 'Done' - Operation was successful + my $retStr; + if ( $out =~ m/Done/i ) { + $retStr = "Done\n"; + } else { + $retStr = "Failed " . "Error output: $out\n"; + xCAT::zvmUtils->printSyslog("Error output: $out"); + return $retStr; + } + + return $retStr; +} #------------------------------------------------------- =head3 flashCopy @@ -498,14 +807,14 @@ sub grantVSwitch { Target address Returns : Operation results (Done/Failed) Example : my $results = xCAT::zvmCPUtils->flashCopy($user, $hcp, $srcAddr, $targetAddr); - + =cut #------------------------------------------------------- sub flashCopy { # Get inputs - my ($class, $user, $hcp, $srcAddr, $tgtAddr) = @_; + my ( $class, $user, $hcp, $srcAddr, $tgtAddr ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -516,17 +825,20 @@ sub flashCopy { } # Flash copy using CP + + xCAT::zvmUtils->printSyslog("CP FlashCopy- ssh $user\@$hcp $sudo /sbin/vmcp flashcopy $srcAddr 0 end to $tgtAddr 0 end synchronous"); my $out = `ssh $user\@$hcp "$sudo /sbin/vmcp flashcopy $srcAddr 0 end to $tgtAddr 0 end synchronous"`; $out = xCAT::zvmUtils->trimStr($out); # If return string contains 'Command complete' - Operation was successful my $retStr = ""; - if ($out =~ m/Command complete/i) { + if ( $out =~ m/Command complete/i ) { $retStr = "Copying data via CP FLASHCOPY... Done\n"; } else { $out = xCAT::zvmUtils->tabStr($out); $retStr = "Copying data via CP FLASHCOPY... Failed\n$out"; + xCAT::zvmUtils->printSyslog("$out"); } return $retStr; @@ -545,18 +857,20 @@ sub flashCopy { Target address Returns : Operation results (Done/Failed) Example : my $results = xCAT::zvmCPUtils->smapiFlashCopy($user, $node, $srcId, $srcAddr, $tgtId, $targetAddr); - + =cut #------------------------------------------------------- sub smapiFlashCopy { # Get inputs - my ($class, $user, $hcp, $srcId, $srcAddr, $tgtId, $tgtAddr) = @_; + my ( $class, $user, $hcp, $srcId, $srcAddr, $tgtId, $tgtAddr ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; + my $retStr = ""; + my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; @@ -565,19 +879,23 @@ sub smapiFlashCopy { my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); # Use SMAPI EXEC to flash copy - my $cmd = '\"' . "CMD=FLASHCOPY $srcId $srcAddr 0 END $tgtId $tgtAddr 0 END" . '\"'; - my $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c $cmd"`; + my $cmd = '\"' . "CMD=FLASHCOPY $srcId $srcAddr 0 END $tgtId $tgtAddr 0 END SYNC" . '\"'; xCAT::zvmUtils->printSyslog("smapiFlashCopy- ssh $user\@$hcp $sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c $cmd"); + my $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c $cmd"`; + my $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcp output:$out"; + return $retStr; + } $out = xCAT::zvmUtils->trimStr($out); # If return string contains 'Done' - Operation was successful - my $retStr = ""; - if ($out =~ m/Done/i) { + if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) { $retStr = "Copying data via SMAPI FLASHCOPY... Done\n"; } else { $out = xCAT::zvmUtils->tabStr($out); - $retStr = "Copying data via SMAPI FLASHCOPY... $out"; + $retStr = "An Error occurred copying data via SMAPI FLASHCOPY... $out"; } return $retStr; @@ -593,44 +911,138 @@ sub smapiFlashCopy { UserID to receive file Source file Target file to be created by punch (e.g. sles.parm) - Options, e.g. -t (Convert EBCDIC to ASCII) + Options, e.g. -t (Convert EBCDIC to ASCII) or "" for no options + SPOOL file class to be assigned to the punched file + "" means that the current class is to be used + anything else is the class to set on the punched file Returns : Operation results (Done/Failed) - Example : my $rc = xCAT::zvmCPUtils->punch2Reader($hcp, $userId, $srcFile, $tgtFile, $options); - + Example : my $rc = xCAT::zvmCPUtils->punch2Reader($hcp, $userId, $srcFile, $tgtFile, $options, $spoolClass ); + =cut #------------------------------------------------------- sub punch2Reader { - my ($class, $user, $hcp, $userId, $srcFile, $tgtFile, $options) = @_; + my ( $class, $user, $hcp, $userId, $srcFile, $tgtFile, $options, $spoolClass ) = @_; + + my $out = ""; + my $punched = 0; + my $rc = 0; + my $subResp = ""; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } + my $punchTarget = ""; + if ( $spoolClass eq "" ) { + $punchTarget = "-u $userId"; + } + # Get source node OS my $os = xCAT::zvmUtils->getOsVersion($user, $hcp); - # Punch to reader # VMUR located in different directories on RHEL and SLES - my $out; - if ($os =~ m/sles10/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmur punch $options -u $userId -r $srcFile -N $tgtFile"`; - } elsif ($os =~ m/rhel/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /usr/sbin/vmur punch $options -u $userId -r $srcFile -N $tgtFile"`; + my $vmur; + if ( $os =~ m/sles10/i ) { + $vmur = "/sbin/vmur"; } else { - $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /usr/sbin/vmur punch $options -u $userId -r $srcFile -N $tgtFile"`; + $vmur = "/usr/sbin/vmur"; } - # If punch is successful -- Look for this string - my $searchStr = "created and transferred"; - if (!($out =~ m/$searchStr/i)) { - $out = "Failed\n"; - } else { - $out = "Done\n"; + my $done = 0; + + # Punch the file. A loop is done in case the punch is currently in use. + until ( $done ) { + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo $vmur punch $options $punchTarget -r $srcFile -N $tgtFile" 2>&1`; + $rc = $? >> 8; + if ( $rc == 255 ) { + $subResp = "Failed to communicate with the zHCP system: $hcp"; + $done = 1; + } elsif ( $out =~ m/A concurrent instance of vmur is already active/i ) { + # Recoverable error: retry the command after a delay + xCAT::zvmUtils->printSyslog( "punch2Reader() Punch in use on $hcp, retrying in 15 seconds" ); + sleep( 15 ); + } else { + # Punch appears successful -- Look for the completion string + my $searchStr = "created"; + if ( $out =~ m/$searchStr/i ) { + $punched = 1; + } else { + chomp( $out ); + $subResp = "Failed, punch info: '$out'"; + xCAT::zvmUtils->printSyslog( "punch2Reader() Failed punching $srcFile to $userId from $hcp, rc: $rc, out: '$out'" ); + } + $done = 1; + } } - return $out; + # If we successfully punched the file, we may have some final steps. + if ( $punched == 1 ) { + # If a spool class was specified then we punched to the zHCP's reader instead of + # punching directly to the target virtual machine and now we need to transfer it + # to the target virtual machine. Otherwise, we are done. + if ( $spoolClass ne "" ) { + # Split the punch response line so we can access the spoolid of the created punch file. + # e.g. Reader file with spoolid 0002 created. + my @words = split( / /, $out ); + my $spoolId = $words[4]; + + # Change the class of the spool file that we created so that it is the requested class. + # We could not spool the punch to the desired class because of the possibility of + # multiple threads using the punch that require different classes. Thus, we need to + # change the individual punch files. + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo vmcp change rdr $spoolId class $spoolClass" 2>&1`; + $rc = $? >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. Nothing to do, file remains in zHCP's reader. + $subResp = "Failed to communicate with the zHCP system to change the reader file $spoolId to class $spoolClass: $hcp"; + } elsif ( $rc != 0 ) { + # Generic failure of transfer command. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "punch2Reader() Change of spool file $spoolId on $hcp to class $spoolClass failed, rc: $rc, out: $out" ); + $subResp = "Failed, change rdr info rc: $rc, out: '$out'"; + } + + # If we did not have any errors, then transfer the spoolfile to the targer user's reader. + if ( $subResp eq "" ) { + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo vmcp transfer rdr $spoolId to $userId" 2>&1`; + $rc = $? >> 8; + if ( $rc == 0 ) { + # Successful transfer + $subResp = "Done"; + } elsif ( $rc == 255 ) { + # SSH failure to communicate with zHCP. Nothing to do, file remains in zHCP's reader. + $subResp = "Failed to communicate with the zHCP system to transfer reader file $spoolId: $hcp"; + } else { + # Generic failure of transfer command. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "punch2Reader() Transfer of spool file $spoolId from $hcp to $userId failed, rc: $rc, out: $out" ); + $subResp = "Failed, transfer info rc: $rc, out: '$out'"; + } + } + + # If we had any error then attempt to purge the file from the zHCP machine. + if ( $subResp ne "Done" ) { + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo vmcp purge reader $spoolId"`; + $rc = $? >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. Nothing to do, file remains in zHCP's reader. + $subResp = $subResp. "\nFailed to communicate with the zHCP system to purge reader file $spoolId: $hcp"; + } elsif ( $rc != 0 ) { + # Any failure is bad and unrecoverable. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "punch2Reader() Unable to purge spool file $spoolId on $hcp, rc: $rc, out: $out" ); + $subResp = $subResp . "\nUnable to purge reader rc: $rc, out: '$out'"; + } + } + } else { + # Successful punch directly to the target virtual machine. + $subResp = "Done"; + } + } + + return $subResp; } #------------------------------------------------------- @@ -643,12 +1055,12 @@ sub punch2Reader { UserID to purge reader Returns : Nothing Example : my $rc = xCAT::zvmCPUtils->purgeReader($hcp, $userId); - + =cut #------------------------------------------------------- sub purgeReader { - my ($class, $user, $hcp, $userId) = @_; + my ( $class, $user, $hcp, $userId ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -662,13 +1074,11 @@ sub purgeReader { my $out; if (xCAT::zvmUtils->smapi4xcat($user, $hcp)) { - # Use SMAPI EXEC to purge reader my $cmd = '\"' . "CMD=PURGE $userId RDR ALL" . '\"'; $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $userId -c $cmd"`; xCAT::zvmUtils->printSyslog("smcli xCAT_Commands_IUO -T $userId -c $cmd"); } else { - # Purge reader using CP $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp purge $userId rdr all"`; xCAT::zvmUtils->printSyslog("/sbin/vmcp purge $userId rdr all"); @@ -688,12 +1098,12 @@ sub purgeReader { UserID to send CP command Returns : Nothing Example : xCAT::zvmCPUtils->sendCPCmd($hcp, $userId, $cmd); - + =cut #------------------------------------------------------- sub sendCPCmd { - my ($class, $user, $hcp, $userId, $cmd) = @_; + my ( $class, $user, $hcp, $userId, $cmd ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -707,13 +1117,11 @@ sub sendCPCmd { my $out; if (xCAT::zvmUtils->smapi4xcat($user, $hcp)) { - # Use SMAPI EXEC to send command $cmd = '\"' . "CMD=SEND CP $userId " . uc($cmd) . '\"'; $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $userId -c $cmd"`; xCAT::zvmUtils->printSyslog("smcli xCAT_Commands_IUO -T $userId -c $cmd"); } else { - # Send CP command to given user $out = `ssh $user\@$hcp "$sudo /sbin/vmcp send cp $userId $cmd"`; xCAT::zvmUtils->printSyslog("/sbin/vmcp send cp $userId $cmd"); @@ -734,13 +1142,14 @@ sub sendCPCmd { Returns : 2 - Layer 2 3 - Layer 3 -1 - Failed to get network layer + error string if SSH fails Example : my $layer = xCAT::zvmCPUtils->getNetworkLayer($node); - + =cut #------------------------------------------------------- sub getNetworkLayer { - my ($class, $user, $node, $netName) = @_; + my ( $class, $user, $node, $netName ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -748,23 +1157,28 @@ sub getNetworkLayer { } # Exit if the network name is not given - if (!$netName) { + if ( !$netName ) { return -1; } # Get network type (Layer 2 or 3) - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; - if (!$out) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; + my $cmd = "$sudo /sbin/vmcp q lan $netName"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + if ( !$out ) { return -1; } # Go through each line - my $layer = 3; # Default to layer 3 - my @lines = split('\n', $out); + my $layer = 3; # Default to layer 3 + my @lines = split( '\n', $out ); foreach (@lines) { # If the line contains ETHERNET, then it is a layer 2 network - if ($_ =~ m/ETHERNET/i) { + if ( $_ =~ m/ETHERNET/i ) { $layer = 2; } } @@ -782,12 +1196,12 @@ sub getNetworkLayer { Name of network Returns : Network type (VSWITCH/HIPERS/QDIO) Example : my $netType = xCAT::zvmCPUtils->getNetworkType($hcp, $netName); - + =cut #------------------------------------------------------- sub getNetworkType { - my ($class, $user, $hcp, $netName) = @_; + my ( $class, $user, $hcp, $netName ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -798,22 +1212,22 @@ sub getNetworkType { my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q lan $netName" | grep "Type"`; # Go through each line and determine network type - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); my $netType = ""; foreach (@lines) { # Virtual switch - if ($_ =~ m/VSWITCH/i) { + if ( $_ =~ m/VSWITCH/i ) { $netType = "VSWITCH"; } # HiperSocket guest LAN - elsif ($_ =~ m/HIPERS/i) { + elsif ( $_ =~ m/HIPERS/i ) { $netType = "HIPERS"; } # QDIO guest LAN - elsif ($_ =~ m/QDIO/i) { + elsif ( $_ =~ m/QDIO/i ) { $netType = "QDIO"; } } @@ -828,16 +1242,16 @@ sub getNetworkType { Description : Add processor(s) to given node Arguments : User (root or non-root) Node - Returns : Nothing + Returns : Nothing or error string if failure Example : my $out = xCAT::zvmCPUtils->defineCpu($node, $addr, $type); - + =cut #------------------------------------------------------- sub defineCpu { # Get inputs - my ($class, $user, $node, $addr, $type) = @_; + my ( $class, $user, $node, $addr, $type ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -845,7 +1259,34 @@ sub defineCpu { } # Define processor(s) - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp define cpu $addr type $type"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp define cpu $addr type $type"`; + my $cmd = "$sudo /sbin/vmcp define cpu $addr type $type"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return ($out); } + +#------------------------------------------------------- + +=head3 getIplTime + + Description : Get the IPL time + Arguments : User (root or non-root) + zHCP + Returns : IPL time + Example : my $out = xCAT::zvmCPUtils->getIplTime($user, $hcp); + +=cut + +#------------------------------------------------------- +sub getIplTime { + my ($class, $user, $hcp) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q cplevel"`; + return ((split("\n", $out))[2]); +} diff --git a/perl-xCAT/xCAT/zvmMsgs.pm b/perl-xCAT/xCAT/zvmMsgs.pm new file mode 100644 index 000000000..a22066b1e --- /dev/null +++ b/perl-xCAT/xCAT/zvmMsgs.pm @@ -0,0 +1,1382 @@ +# IBM(c) 2016 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + This is a message utility plugin for z/VM. + +=cut + +#------------------------------------------------------- +package xCAT::zvmMsgs; +use Text::Wrap; +use strict; +use warnings; +1; + + +# Messages +# Severity strings +my @sevInfo = ( '', # No header + 'Bypassing test', # Bypassing message + 'Info', # Information message + 'unknown', # Unknown severity + 'Warning', # Warning message + 'Error' # Error message + ); + +# Hash of message ids. Message ids should be in uppercase. 'ALL' should not +# be used as a message id as this indicates that all messages are wanted. +# For each message id there is another hash with additional info: +# severity - Severity of message and indicate whether it gets a header to the +# message text (e.g. "Error (IVP:MAINT01) " ) +# 0 - no message header, unrated output +# 1 - bypass message used by automated tests +# 2 - information message with "Info" header +# 3 - unknown message severity +# 4 - warning message with "Warning" header +# 5 - error message with "Error" header +# recAction - Recommended action: +# 0 - non-fatal message, continue processing +# 1 - fatal message, end further processing +# explain - Further explanation of the message. +# subTab - (optional) Indicates the tabbing characters to use. This is +# used to control whether subsequent lines of a message are +# indented. The default is '\t', to indent the lines. +# sysAct - System action to be performed. Normally, this is not specified +# because the severity generates a default system action. +# userResp - Suggested user response. + +#****************************************************************************** +# verifynode messages +#****************************************************************************** +my %verifyMsgs = ( + 'FATAL_DEFAULTS' => + { + 'sysAct' => 'No further verification will be performed.' + }, + 'NONFATAL_DEFAULTS' => + { + 'sysAct' => 'Verification continues.' + }, + 'GENERIC_RESPONSE' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + }, + 'GENERIC_RESPONSE_NOINDENT' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + 'subTab' => '', + }, + 'CLNUP01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to remove the temporary directory, %s, from the'. + 'from the compute node system at %s. '. + 'The following command failed: %s with rc: %s, out: %s', + 'explain' => 'The IVP created a temporary directory on the target system. '. + 'It was unable to remove the directory when it was finished with it. '. + 'The message indicate the command, return code and output of the command.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. You may want '. + 'to access the system and remove the directory.', + }, + 'DFLT01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '%s so the function will use \'%s\' as the default value. %s', + 'explain' => 'A value was not specified so the function will use the indicated default.', + 'userResp' => 'You can specify the missing value in order to avoid this message in the future. ' . + 'If the value is acceptable then you can ingnore this message.', + }, + 'DRIV01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'The driver script, %s, failed, rc: %s, out: %s', + 'explain' => 'The driver script failed with the indicated return code and output. '. + 'The IVP is unable to run the full installation verification test.', + 'sysAct' => 'IVP processing stops.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the IVP after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'DRIV02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The driver script, %s, did not specify the z/VM host node information in the %s property', + 'explain' => 'The driver script did not specify the z/VM host node property. '. + 'The IVP will default to notifying the user on the system where the '. + 'xCAT management node runs.', + 'userResp' => 'Determine why the property was not defined in OpenStack and correct the error. Run the IVP after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'DRIV03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The ZHCP agent related to the host, \'%s\', could not be determined from the xCAT node information.', + 'explain' => 'The driver script specified a z/VM host. However, the IVP was not able to determine the ZHCP '. + 'agent associated with the host.', + 'sysAct' => 'IVP processing continues. The IVP will attempt to determine the ZHCP agent using some other information.', + 'userResp' => 'Verify that the host node in xCAT has an hcp property and that hosttype is \'zvm\'. '. + 'Run the IVP after you have corected the error.', + }, + 'GNRL01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'An unexpected command failure occurred, cmd: \'%s\', rc: %s, out: %s', + 'explain' => 'An unexpected failure was encountered during processing. The command that '. + 'failed is shown along with the return code and output of the command.', + 'sysAct' => 'This failure will cause some processing to be bypassed but the IVP run will continue.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the IVP after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'GNRL02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'An error occurred attempting to punch \'%s\' from %s to user %s on %s. '. + 'The file remains on the source system as \'%s\'. Output from the punch invocation: %s', + 'explain' => 'An unexpected failure occurred while attempting to punch a file.', + 'sysAct' => 'An attempt will be made to remove the file but this may fail. '. + 'The IVP will try to send the file to an alternate user and will indicate if this was successful. '. + 'This failure may prevent the IVP from sending the log file to the notify user on the target system.', + 'userResp' => 'The original log file exists on the xCAT management node in the /var/log/xcat/ivp directory. '. + 'You may access it on that system if you did not receive the file on an alternate userid and system.', + }, + 'GNRL04' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'An unexpected command failure occurred, cmd: \'%s\', rc: %s, out: %s', + 'explain' => 'An unexpected failure was encountered during processing. The command that '. + 'failed is shown along with the return code and output of the command.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the command again after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'GOSL01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The command \'%s\' was sent to %s to determine the OpenStack level but '. + 'did not return an expected response. Instead it returned rc: %s and out: %s.', + 'explain' => 'The value returned by the command was not an expected value. '. + 'This is expected to be a single word, e.g. \'14.0.2\', with the '. + 'components of the version separated by a period. There should be '. + 'at least two components.', + 'userResp' => 'Determine the reason that the OpenStack level is not a recognized value. '. + 'Run the command again after you have corrected the error. '. + 'Otherwise, consult the support team.', + }, + 'GOSL02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The command \'%s\' was sent to %s to determine the OpenStack level but '. + 'did not return one of the recognized versions. '. + 'Instead, it returned rc: %s and out: %s.', + 'explain' => 'The value returned by the command was not an expected value. '. + 'The expected values are listed in /opt/xcat/openstack.versions.', + 'userResp' => 'Determine the reason that the OpenStack level is not a recognized value. '. + 'Run the command again after you have corrected the error. '. + 'Otherwise, consult the support team.', + }, + 'ID01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'The system attempted to generate a unique IVP id but failed to do so after 10000 attempts.', + 'explain' => 'An IVP id was not specified so the system attempted to generate a unique IVP id. '. + 'This begins with an id of 10 and increments by 1 upto 10010. Unfortunately, '. + 'a unique unused Id was not found in the zvmivp table. You may have a corrupted '. + 'zvmivp table or have somehow filled the table with 10000 IVPs which is highly abnormal.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the command again after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'ID02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The IVP id, %s, does not exist. A new IVP will be scheduled using that id.', + 'explain' => 'The specified IVP id was not found in the zvmivp table.', + 'sysAct' => 'Processing continues and a new IVP will be added to the table of scheduled IVPs using the '. + 'specified id.', + 'userResp' => 'If you incorrectly specified the IVP id and did not want to add a new ID, then you should '. + 'remove the IVP that you do not want. '. + 'From the xCAT GUI, go to Help->Verify xCAT to remove the IVP.', + }, + 'ID03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The IVP id does not exist in the zvmivp table: %s', + 'explain' => 'The specified IVP id was not found in the zvmivp table.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the correct id and retry.', + }, + 'MN01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Could not find an xCAT management node which had an IP address of %s, cmd: \'%s\', rc: %s, out: %s', + 'explain' => 'An xCAT node that represents the xCAT management node could not be found. '. + 'The xCAT management node is not required by the IVP but some processing '. + 'such as default notification of the z/VM host on which the xCAT management '. + 'node runs will not occur.', + 'userResp' => 'If you expected the xCAT management node to exist, please review the '. + 'command that was issued and the return code and output to determine '. + 'why the xCAT management node was not found and correct the issue. '. + 'Run the IVP or other command that you issuedd after you have corrected the issue.', + }, + 'MN02' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'The xCAT management node is not defined as a node in xCAT and the function will not '. + 'be performed.', + 'explain' => 'The issued command requires the existence of the xCAT MN as an xCAT node. '. + 'The requrested processing cannot be performed.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Determine the cause of the error and correct the error.', + }, + 'MN03' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'The xCAT management node is not defined as a node so only a basic IVP will be run.', + 'explain' => 'The xCAT MN should exist as an xCAT node. The failure to find the node indicates '. + 'a larger failure. An automated IVP will be performed to verify the general '. + 'functioning of the environment but it will not be added to the zvmivp table. '. + 'We expect the installation to define the xCAT node upon seeing this message '. + 'and that will allow the automated IVP to create default IVPs in the zvmivp table.', + 'sysAct' => 'Processing continues.', + 'userResp' => 'Define the xCAT management node in xCAT.', + }, + 'MSG01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '%s on %s is unable to receive messages at this time, status: \'%s\'. '. + 'They will not be sent the IVP results message.', + 'explain' => 'The IVP attempted to send a message to the user on the target system '. + 'to notify them of the status of the IVP run. The user was '. + 'unavailable to receive the message. '. + 'Status of \'SSI\' indicates they are logged on another system in the SSI '. + 'cluster while \'DSC\' indicates the machine is running disconnected. '. + 'The userid is defined by the XCAT_notify property in the '. + 'DMSSICNF COPY file or the zvmnotify property in the xCAT site table.', + 'sysAct' => 'Processing continues. The log file from the run was '. + 'sent to the userid as a spool file.', + 'userResp' => 'If the userid is not the userid that you expected to receive the '. + 'IVP status message then correct the XCAT_notify property in '. + 'the DMSSICNF COPY file and recycle SMAPI to pick up the new value or '. + 'as a temporary measure until the next recycle of SMAPI, update the '. + 'zvmnotify property in the xCAT site table.', + }, + 'OPER01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Required operand %s is missing.', + 'explain' => 'The indicated operand is required but missing.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the command with the required operand.', + }, + 'OPER02' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Operand %s has a value of \'%s\' which is not one of the recognized values: %s.', + 'explain' => 'The value for the operand is not recognized.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the command with the correct operand value.', + }, + 'OPER03' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Conflicting operands were specified: %s.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the command with the correct operand value.', + }, + 'PERL01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'A perl script error was detected in %s.\n'. + ' Calling parms: %s\n'. + ' Reason: %s', + 'explain' => 'The indicated perl script encountered a perl scripting error '. + 'that prevents it from operating correctly. The script is '. + 'ending the current run. This error message is a restatement of '. + 'a previous message so that you are ensured to see the error.', + 'userResp' => 'Review the previous messages to determine whether the error '. + 'is caused by bad invocation parameters or an error within '. + 'the script. Please report this problem if it is not caused '. + 'by a user error.', + }, + 'PREP01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Unable to create the %s directory in the system running the xCAT management node. rc: %s, out: %s', + 'explain' => 'The specified directory is used to contain the driver script created '. + 'by the IVP preparation script. This directory exists in the virtual '. + 'server that runs the xCAT management node. The directory '. + 'could not be created.'. + 'The current OpenStack properties cannot be validated. '. + 'A saved version of the driver script from a previous run will be used, if it exists.', + 'userResp' => 'Determine the cause of the error. It could be caused by running the '. + 'verifynode script under a user that does not have root authority. '. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to send the IVP preparation script, %s, to the OpenStack system at %s. '. + 'The script was intended to be used to create a driver script for the IVP '. + 'on the system running the xCAT management node at %s. '. + 'A previous version of the driver script will be used. '. + 'The following command failed: %s with rc: %s, out: %s', + 'explain' => 'The xCAT Management Node was unable to send the IVP preparation script to the '. + 'specified system in order to analyze the OpenStack properties and '. + 'construct the driver script for the next phase of the IVP. The command failed '. + 'with the indicated return code and output. '. + 'The current OpenStack properties cannot be validated. '. + 'A saved version of the driver script from a previous run will be used, if it exists.', + 'userResp' => 'Determine the cause of the error and correct it, if possible.'. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP04' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to retrieve the driver script created by the IVP preparation script, %s, '. + 'from the OpenStack system at %s. '. + 'The driver script was intended to be used to drive the IVP '. + 'on the system running the xCAT management node at %s. '. + 'The following command failed: %s with rc: %s, out: %s', + 'explain' => 'The xCAT Management Node was unable to retrieve the driver script from the '. + 'specified system for the next phase of the IVP. '. + 'A saved version of the driver script from a previous run will be used, it it exists.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. '. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP06' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Unable to determine the OpenStack level of the OpenStack system at %s. '. + 'The full IVP cannot be run.', + 'explain' => 'The xCAT Management Node was unable to level of OpenStack running the in the indicated system. '. + 'This is necessary so that the correct level of analysis code can be run '. + 'to validate the system. '. + 'A saved version of the driver script from a previous run will be used, if it exists.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. If OpenStack '. + 'Nova services are not actively running on the system then please start them '. + 'and reattempt the IVP.'. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP07' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to reduce the file permissions on %s with command: %s,\nrc: %s, out: %s', + 'explain' => 'The xCAT Management Node was unable to lower the file permission on the indicated file.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. You may want to change the '. + 'permissions on the file.', + }, + 'SITE01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The \'%s\' property from the site table has a value that is not valid. Value: \'%s\', '. + 'bad portion of the value: \'%s\'', + 'explain' => 'The site table contains a property that is used in the IVP processing. The value '. + 'of the property has the following format:'. + "\n". + 'zvmnotify: \'hostnode(userid_on_the_host)\' If additional host/userid combinations are specified '. + 'then they are connected by a semicolon and no blanks are allowed between the '. + 'components that make up the value. '. + 'For example, \'host1(usera);host2(userb)\'', + 'userResp' => 'Correct the error and rerun the IVP. Otherwise, consult the support team.', + }, + 'SITE02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The \'%s\' property from the site table is missing or has an empty string value.', + 'explain' => 'The site table contains a property that is used in the IVP processing. The property '. + 'is missing from the table or has a value that is an empty string. The \'master\' '. + 'property should specify the IP address of the xCAT management node that matches the value '. + 'specified for the \'ip\' property of the node that represents the xCAT management node.', + 'userResp' => 'Correctly specify the master property in the site table and rerun the IVP. '. + 'Otherwise, consult the support team.', + }, + 'VSTN01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to SSH to %s.', + 'explain' => 'The xCAT management node uses SSH to obtain information '. + 'from the target system or to make changes within Linux on the '. + 'target system. This does not appear to be working. '. + 'This may be due to the system not being unlocked to the xCAT management node for '. + 'communication or a TCP/IP error.', + 'sysAct' => 'The system action depends upon the severity of the problem. '. + 'It will attempt to continue, if possible.', + 'userResp' => 'First, attempt to unlock the target system from the xCAT GUI by selecting the ' . + 'node on the Nodes->Nodes panel and choosing \'unlock\' in ' . + 'the \'Configuration\' pulldown. If that does not work then investigate '. + 'TCP/IP errors or network configuration errors within the target system.', + }, + 'TP01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The \'%s\' property from the %s table is %s: \'%s\'. A default of \'%s\' will be used instead of that property.', + 'explain' => 'A property in the indicated table has a value that is not valid. '. + 'The default will be used instead. ', + 'userResp' => 'Correctly specify the property in the table.', + }, + 'VA01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'xCAT MN is unable to issue a simple \'pwd\' command on the target system.', + 'explain' => 'The xCAT management node uses SSH to obtain information '. + 'from the target system or to make changes within Linux on the '. + 'target system. The xCAT MN can access the system but is unable to issue '. + 'a simple command.', + 'userResp' => 'Unlock the target node from the xCAT GUI by selecting the ' . + 'node on the Nodes->Nodes panel and choosing \'unlock\' in ' . + 'the \'Configuration\' pulldown. If this does not work then log onto the '. + 'virtual machine to see why simple commands are failing.', + }, + 'VD01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => '%s is not a version supported by xCAT.', + 'explain' => 'The Linux distribution name and version was compared to the list of '. + 'versions that xCAT supports and was not found in the list.', + 'sysAct' => 'Some xCAT functions may be unavailable such as image capture and deploy '. + 'or work incorrectly.', + 'userResp' => 'Either update the OS running in the target node or monitor the xCAT ' . + 'responses to commands directed to the node to ensure that it is doing '. + 'what you desire.', + }, + 'VPF01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to determine the power status of %s, rpower rc: %s, msg: %s', + 'explain' => 'An xCAT rpower command with the stat option was issued to determine '. + 'the power status of the specified node and returned a non-zero return code.', + 'userResp' => 'The return code and the message returned by rpower (shown in the message '. + 'after msg:) should be used to determine the cause of the error. '. + 'Please correct the error and retry the test.', + }, + 'VP02' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => '%s is not logged on.', + 'explain' => 'The virtual machine related to the node is not currently logged on.', + 'userResp' => 'Log the virtual machine on to the z/VM host and retry the verification.', + }, + 'VS05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Linux is not configured for the service: %s.', + 'explain' => 'The specified service should be configured on the target system. '. + 'This ensures that images '. + 'captured from this system will have the service started when the '. + 'new system boots to allow xCAT and/or OpenStack to complete the configuration of the '. + 'system the first time it boots.', + 'userResp' => 'Configure Linux to start service on boot of the server.' + }, + 'VS06' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The %s service is not configured for the following run levels: %s', + 'explain' => 'The specified service should be configured on the target '. + 'system for the specified run levels. This ensures that images '. + 'captured from this system will have the service started when the '. + 'new system boots to allow xCAT and/or OpenStack to complete the configuration of the '. + 'system the first time it boots.', + 'userResp' => 'Configure the service to be \'on\' for indicated run levels.' + }, + 'VX01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to determine the version number of the xcatconf4z that is shipped in the xCAT MN.', + 'explain' => 'The verification code runs xcatconf4z with the \'version\' operand ' . + 'on the target node. The script did not return a version string. ' . + 'This normally indicates that xcatconf4z is a very early version.', + 'userResp' => 'xcatconf4z should be updated to the latest level.', + }, + 'VX02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xcatconf4z on the system is back level. Unable to determine the version installed.', + 'explain' => 'xcatconf4z exists on the system but is very old and does not respond to the version query.', + 'userResp' => 'xcatconf4z should be updated to the latest level.', + }, + 'VX03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xcatconf4z on the system is back level at %s. It should be at %s.', + 'explain' => 'The xcatconf4z on that target node is compared to the one shipped with ' . + 'the xCAT MN. The xcatconf4z should be at the same level as the one ' . + 'on xCAT MN or at a later level.', + 'userResp' => 'xcatconf4z should be updated to the latest level.' + }, + 'VX04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xcatconf4z is not set up to receive configuration directive files from its reader.', + 'explain' => 'The xcatconf4z script reads configuration directives sent as reader files to it ' . + 'from authorized userids. The script contains a variable, authorized_senders, that ' . + 'lists the names of authorized virtual machines.', + 'userResp' => 'Configure the authorized_senders variable in the script to contain the userids '. + 'of ZHCP virtual machines that will send configuration directives to this node or '. + 'to images create from this node.' + }, + 'VX05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Linux is not setup to start xcatconf4z on boot.', + 'explain' => 'The xcatconf4z script should be configured on the target '. + 'system to start when the system boots. This ensures that images '. + 'captured from this system will have the service started when the '. + 'new system boots to allow xCAT to complete the configuration of the '. + 'system the first time it boots.', + 'userResp' => 'Configure Linux to start xcatconf4z on boot of the server.' + }, + 'VX06' => + { 'severity' => 4, + 'recAction'=> 1, + 'text' => 'xcatconf4z was not found in the /opt directory on the target node.', + 'explain' => 'The xcatconf4z script should exist in the /opt directory on the target node.', + 'userResp' => 'Obtain xcatconf4z and put it in the /opt directory on the target node.' + }, + 'VX07' => + { 'severity' => 4, + 'recAction'=> 0, + 'text' => '/opt/bin/mkisofs was not found on the target system.', + 'explain' => 'The xcatconf4z script invokes the /opt/bin/mkisofs to create ' . + 'an ISO9660 disk in which it stores configuration '. + 'data used by the activation engine. The mkisofs file is missing '. + 'and will prevent proper operation of the xcatconf4z script during '. + 'deploy of an image.', + 'userResp' => 'This function should be obtained from the Linux distribution and '. + 'installed.' + }, + ); + +#****************************************************************************** +# zxcatIVP messages +#****************************************************************************** +my %zxcativpMsgs = ( + 'FATAL_DEFAULTS' => + { + 'sysAct' => 'No further verification will be performed.' + }, + 'NONFATAL_DEFAULTS' => + { + 'sysAct' => 'Verification continues.' + }, + 'GENERIC_RESPONSE' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + }, + 'GENERIC_RESPONSE_NOINDENT' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + 'subTab' => '', + }, + 'BPVMN01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'The node id for the xCAT MN was not specified, so the IVP will not verify that it exists.', + 'explain' => 'The node name of the xCAT MN is a configuration property, zvm_xcat_master '. + 'in /etc/nova/nova.conf file, that is used by z/VM OpenStack plugin code. '. + 'The driver script created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_mnNode environment variable. '. + 'The IVP perform tests related to the value. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'sysAct' => 'Some tests which use the node name of the xCAT MN will not be run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.' + }, + 'BPVDP01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Disk pool names were not specified, so IVP will verify the pools associated with each host.', + 'explain' => 'Directory manager disk pools are used for obtaining OpenStack ephemeral '. + 'disks. The zvm_diskpool property in /etc/nova/nova.conf file specifies the disk pools. '. + 'The driver script created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_diskpools environment variable. '. + 'The IVP perform tests related to the value. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.' + }, + 'BPVDS01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Directory space tests related to ZHCP agent will not be run.', + 'explain' => 'The IVP detected that the ZHCP agent is on the same system as the xCAT management node. ' . + 'The space related tests would have already be run for the xCAT MN so similar tests ' . + 'will not be run for the ZHCP agent.', + 'userResp' => 'If you want to verify additional directories for the ZHCP running on the xCAT MN\'s system '. + 'then specify the directory information as part of the xcatDiskSpace command line operand or ' . + 'zxcatIVP_xcatDiskSpace environment variable. Please remember to include directories '. + 'that are defaults for the xCAT MN\'s directory space tests. You can see the defaults in the ' . + 'help output for zxcatIVP.' + }, + 'BPVN01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Networks were not specified, so IVP will not verify them.', + 'explain' => 'Virtual switches are used for internet access by the deployed '. + 'virtual servers. The flat_networks property in section ml2_type_flat '. + 'and the network_vlan_ranges property in section ml2_type_vlan '. + 'in the /etc/neutron/plugins/ml2/ml2_conf.ini file specify the vswitches. '. + 'The driver script created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_networks environment variable. '. + 'The IVP perform tests related to the value. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.' + }, + 'BPVCNC01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Compute Node address or export user was not specified, so the IVP will '. + 'not verify that the xCAT MN can communicate with the compute node.', + 'explain' => 'The address of the virtual server where the nova compute services '. + 'are running is configured in the '. + 'my_ip property in the /etc/nova/nova.conf file. The driver script '. + 'created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_cNAddress environment variable. '. + "\n\n". + 'The export user is normally set to \'nova\' by the IVP preparation script '. + 'and is passed in the zxcatIVP_expUser environment variable.'. + 'The IVP will perform tests related to these values. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.', + }, + 'MAIN01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Significant error detected, no further verification will be performed.', + 'explain' => 'A significant warning was detected, which prevents further tests '. + 'from accurately validating the system.', + 'sysAct' => 'No further verification will be performed for this run.', + 'userResp' => 'You should correct the situation indicated by previous warning '. + 'messages and then rerun the IVP.', + }, + 'STN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to SSH to %s.', + 'explain' => 'The xCAT management node uses SSH to obtain information '. + 'from the target node or to make changes within Linux on the '. + 'target node. This does not appear to be working.', + 'userResp' => 'You should investigate TCP/IP errors or network configuration errors within the target node.', + }, + 'VCMAP01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to determine the role of the Cloud Manager Appliance from the %s file.', + 'explain' => 'The Cloud Manager Appliance has a /var/lib/sspmod/appliance_system_role '. + 'file that cannot be read or does not contain the expected role property '. + 'in its expected form of "role=value" where the value is either: '. + 'CONTROLLER, COMPUTE, COMPUTE_MN, or MN.'. + 'This indicates corruption of the file and possibly other files, or a failed '. + 'install.', + 'userResp' => 'You should correct the corruption of the /var/lib/sspmod/appliance_system_role '. + 'file and then rerun the IVP.', + }, + 'VCMAP02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the version of Cloud Manager Appliance from the %s file.', + 'explain' => 'The Cloud Manager Appliance contains a /opt/ibm/cmo/version file '. + 'that cannot be read or does not contain a line indicating the '. + 'version of the appliance. This indicates a possible corruption of '. + 'the file system or a failed install.', + 'userResp' => 'You should verify that the file exists on the appliance and has the proper '. + 'permissions set to allow user root to read the file.', + }, + 'VCNC01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xCAT MN is unable to SSH to %s with user %s.', + 'explain' => 'The xCAT MN could not SSH into the compute node. This can occur for the '. + 'following reasons:'. + "\n". + '* the wrong user name was specified,'. + "\n". + '* the wrong IP address was specified,'. + "\n". + '* or the SSH keys were not set up on the compute '. + 'node to allow the xCAT MN to push data to that node.'. + "\n\n". + 'The error message indicates the IP address and user name that the IVP thinks '. + 'xCAT MN will use.', + 'userResp' => 'Verify the my_ip property, if it was specified in '. + '/etc/nova/nova.conf, or verify the local IP address of the OpenStack system. '. + 'The local IP address is used by the IVP preparation script as a default '. + 'when the my_ip property is not set. The IVP uses this '. + 'address to verify that the xCAT MN can access the compute node. xCAT MN '. + 'communicates with the compute node using this IP address. An address '. + 'which is not accessible by the xCAT MN will cause various xCAT functions '. + 'to fail. If the my_ip is not set, an incorrect default for your compute '. + 'node might have been chosen. You may wish to specify the my_ip property '. + 'with a valid value for your environment. Also, verify the value of the '. + 'zxcatIVP_expUser specified in the driver script by the IVP preparation script '. + 'script. This value defaults to "nova", which is the user name under '. + 'which the compute node will allow xCAT MN to access the system.', + }, + 'VDP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Disk pool %s was not found as a disk pool.', + 'explain' => 'The specified disk pool was not found. The disk pool is specified in '. + '/etc/nova/nova.conf and is passed to the IVP in the driver script '. + 'by the zxcatIVP_diskpools environment variable.', + 'userResp' => 'Verify the zvm_diskpool property in /etc/nova/nova.conf is correct.' + }, + 'VDP02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Disk pool %s has no free space', + 'explain' => 'The indicated disk pool has no space for minidisk creation. '. + 'You may need to add disks to the directory manager disk pool '. + 'or specify a different disk pool for the zvm_diskpool property in '. + '/etc/nova/nova.conf.'. + "\n\n". + 'If you are running a basic IVP and see this message for disk pool '. + 'XCAT1, you can ignore the warning. Disk pool XCAT1 is a special '. + 'disk pool that normally has no available space. This disk pool '. + 'is not intended to be used as a disk pool by the compute node.', + 'userResp' => 'Add space to the disk pool in the directory manager.' + }, + 'VDP03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to obtain a list of disk pools for host %s.', + 'explain' => 'The list of disk pools defined in the directory manager could not be obtained for '. + 'the specified host.', + 'userResp' => 'This can be caused by a few different problems. They include: '. + "\n". + '* No disk pools/regions defined in the directory manager - Use directory manager '. + 'commands to verify that disk pools have been defined, or to define them. '. + "\n". + '* The ZHCP server is unresponsive - Ensure that the ZHCP server is running. Other '. + 'error messages will occur if it is not running. '. + "\n". + '* SMAPI is unable to communicate with the directory manager - Use the SMAPI '. + 'SMSTATUS command to collect SMAPI debug information and verify the configuration '. + 'between SMAPI and the directory manager. '. + "\n". + '* If the name of the host node is incorrect in the message, then the xCAT server '. + 'may have been previously started prior to modifying the properties in '. + 'DMSSICNF COPY. This can cause an invalid xCAT node to be created for the host. Subsequent '. + 'restarts with a correctly configured DMSSICNF COPY file will not remove the '. + 'incorrect node but only add the new one. When the IVP attempts to verify the '. + 'host, it fails. See "zxcatIVP Issues" in the Enabling Manual for information on how to '. + 'remove the invalid node.', + }, + 'VDS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The file system related to the %s directory on the %s system has %s percent space '. + 'in use which is more than the expected maximum of %s percent.', + 'explain' => 'The file system is over the recommended percentage of space in use. This can cause processing errors.', + 'userResp' => 'You can eliminate this warning by freeing up space in the directories related to the file system. '. + 'See the troubleshooting sections on space issues related to the type of system, whether it is running '. + 'xCAT MN or the ZHCP server, in the Enabling z/VM for OpenStack Manual for instructions on addressing space issues.' + }, + 'VDS02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The file system related to the %s directory on the %s system has %s available space '. + 'which is less than the expected minimum of %s.', + 'explain' => 'The file system has less than the minimum available space. This can cause processing errors.', + 'userResp' => 'You can eliminate this warning by freeing up space in the directories related to the file system. '. + 'See the troubleshooting sections on space issues related to the type of system, whether it is running '. + 'xCAT MN or the ZHCP server, in the Enabling z/VM for OpenStack Manual for instructions on addressing space issues.' + }, + 'VDS03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the percentage of disk space used by the %s system\'s file system '. + 'related to the %s directory.', + 'explain' => 'The IVP attempts to determine the size of the file system disk space related to the specified directory '. + 'using the df -h command and is unable to do so. This error is normally '. + 'related to disk corruption issue or problems with a logical volume associated with the directory.', + 'userResp' => 'Please review the messages log file on the affected system for errors related to the disks.' + }, + 'VDS04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The following files are larger than the expected maximum of \'%s\': %s', + 'explain' => 'Very large files in the indicated directory and it subdirectories can consume space needed '. + 'for normal operations. The IVP program warns of very large files.', + 'userResp' => 'You should consider removing the identified files. Please note that '. + 'the IVP program compiles a simple '. + 'list of large files in the directories. It only removes some files from the list such as '. + 'files ending in .so or with .so. in the name or .jar at the end of the filename. For this reason, '. + 'you should only remove files which you have identified as log files or which you know '. + 'are files which can be safely removed. See "Space Issues on persistent '. + 'Directory Can Lead to xCAT MN Issues" in the Enabling Manual for '. + 'instructions on addressing space issues.' + }, + 'VDS05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The size of the logical volume on which the xCAT MN image repository resides (%s) '. + 'is less than image pruning goal (%s).', + 'explain' => 'The xCAT MN image repository is a subdirectory of the /install '. + 'directory. The IVP compares the size of the logical volume '. + 'providing storage for the directory to the value specified in the '. + 'xcat_free_space_threshold property in the /etc/install/nova.conf '. + 'file in the OpenStack systen (passed using the '. + 'xcatIVP_expectedReposSpace property in the driver script). When '. + 'the OpenStack performs automated pruning of older OpenStack '. + 'images from the xCAT image repository (images that still exist in '. + 'Glance), it will attempt to free up enough space to match the '. + 'xcat_free_space_threshold. An xcat_free_space_threshold value '. + 'that is greater than the size of the logical volume can result in '. + 'more images being removed than necessary when automatic pruning '. + 'occurs. This will result in images being transferred between '. + 'OpenStack and xCAT unnecessarily.', + 'userResp' => 'You should consider adding additional volumes the logical volume '. + 'by updating the XCAT_iso property in the DMSSICNF COPY file for '. + 'an xCAT MN system, or to the cmo_data_disk property in the '. + 'DMSSICMO COPY file for a CMA in controller role.' + }, + 'VHN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The host node (%s) was not defined.', + 'explain' => 'The specified host node was not defined to xCAT. ', + 'userResp' => 'Verify that the zvm_host property is specified '. + 'correctly in /etc/nova/nova.conf. This property is passed by the driver '. + 'script in the zxcatIVP_hostNode environment variable.' + }, + 'VHN02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Host node does not have an ZHCP associated with it.', + 'explain' => 'The specified host node, specified with the zvm_host property in '. + '/etc/nova/nova.conf, does not have a ZHCP agent associated with it. '. + 'This property is passed by the driver script in the zxcatIVP_hostNode '. + 'environment variable.', + 'userResp' => 'You should correct the host node in xCAT by associating a ZHCP agent '. + 'using the hcp property so that it can be managed by xCAT and the services '. + 'services that use xCAT.' + }, + 'VMN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'MN node(%s) was not defined.', + 'explain' => 'The xCAT management node specified on the zvm_xcat_master '. + 'property in the /etc/nova/nova.conf file was not in the list of '. + 'defined xCAT nodes. The probable cause of this message is an '. + 'incorrectly specified property value for the xCAT node that '. + 'represents the xCAT MN.', + 'userResp' => 'You can view the list of nodes using the xCAT GUI in the '. + 'Nodes->Nodes tab. The xCAT MN node should be in the list of '. + 'nodes for the "all" group. You can view other xCAT node groups '. + 'by selecting the group name in the "Groups" frame on the left '. + 'side of the web page.' + }, + 'VMNI01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xCAT MN does not have an interface defined for %s with address: %s.', + 'explain' => 'The xCAT MN does not have the specified IP address defined. This '. + 'is most likely caused by a typo in the configuration files. The '. + 'IVP driver script contains the values used in the test, and which '. + 'configuration file and property provided the value.', + 'userResp' => 'Use the information to identify the property in error and correct '. + 'the address in the configuration file.' + }, + 'VMNI02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the routing prefix information for %s %s.', + 'explain' => 'While attempting to verify the information specified for the IVP, '. + 'the routing prefix for the xCAT MN IP address could not be determined. '. + 'This could indicate a problem with the IP interface for xCAT MN.', + 'userResp' => 'Use the information to identify the property in error and correct '. + 'the address in the configuration file.' + }, + 'VMNI03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Subnet mask specified as input is not valid: %s.', + 'explain' => 'The subnet mask specified as input to the IVP is not a valid subnet mask.', + 'userResp' => 'Please verify the subnet mask used as input to the installation '. + 'verification program.' + }, + 'VMNI04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xCAT MN subnet mask for %s has a routing prefix of %s which '. + 'does not match the calculated routing prefix of %s for the subnet '. + 'mask passed as input: %s.', + 'explain' => 'The routing prefix for the xCAT MN does not match the calculated '. + 'routing prefix for the subnet mask that was specified as input '. + 'to the IVP.', + 'userResp' => 'Please verify the subnet mask used as input to the installation verification program.' + }, + 'VMHS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the signal shutdown time on %s. Query returned, rc: %s, output: %s', + 'explain' => 'The IVP program attempted to determine the signal shutdown duration on the z/VM host '. + 'and either encountered an unexpected return code or response from the command. '. + 'The response is expected to be in English.', + 'userResp' => 'Determine the cause of the error and correct it.' + }, + 'VMHS02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The signal shutdown time on %s is %s seconds which is less than the tested minimum of %s seconds.', + 'explain' => 'The signal shutdown time defines the time CP will wait for a virtual machine to respond to '. + 'a shutdown signal sent to it by SIGNAL, FORCE, or SHUTDOWN commands before forcing a virtual '. + 'machine off the z/VM system. A value of 0 causes CP to immediately force a virtual machine '. + 'without sending it a shutdown signal. xCAT and SMAPI use one or more of these commands in the '. + 'process of powering off a virtual machine.'. + "\n\n". + 'Linux systems cache their disk reads and writes to improve performance. For this reason, a sufficient '. + 'delay is recommended to allow the Linux operating system to clear the disk cache. '. + 'Failure to clear the cache can cause disk problems when the virtual machine logs back on '. + 'the z/VM system.', + 'userResp' => 'You should change the signal shutdown time for the z/VM host. The system configuration '. + 'file supports a SET SIGNAL SHUTDOWNTIME statement that allows you to set the time. '. + 'In addition, the CP class A and C SET SIGNAL SHUTDOWNTIME command can be used to '. + 'change the time for the current system IPL. '. + "\n\n". + 'The test run by the IVP checks for a minimum amount of time. You should choose a time that '. + 'allows sufficient time for the Linux virtual machines that are running on your system. This '. + 'will depend upon the performance of your system and the activity performed by the Linux virtual '. + 'machines. In general, setting a larger time interval such as 300 seconds (5 minutes) is safer '. + 'than an interval that is too short. In most cases, the virtual machines will shutdown upon '. + 'receiving the signal in a much shorter time and thus avoid the forced log off that results '. + 'from the time interval expiring.' + }, + 'VMS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the virtual storage size.', + 'explain' => 'An attempt to verify the virtual storage size of the z/VM virtual '. + 'machine running xCAT MN using the z/VM CP QUERY VIRTUAL STORAGE '. + 'command failed. This can indicate a command authorization problem.', + 'userResp' => 'Please ensure that the virtual machine is permitted to run the command.' + }, + 'VMS02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Virtual machine storage size (%s) is less than the recommended '. + 'size of %s.', + 'explain' => 'The virtual storage size of the z/VM virtual machine which is '. + 'running xCAT MN is less than the recommended size.', + 'userResp' => 'Please update the user directory for that virtual machine to '. + 'increase its virtual storage.' + }, + 'VMUP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'MACID user prefix for the %s is \'%s\' and is not the expected value of \'%s\'', + 'explain' => 'The MACADDR user prefix portion of the base_mac property in '. + '/etc/neutron/neutron.conf does not match the value specified '. + 'on the z/VM VMLAN system configuration property in the z/VM '. + 'host. The IVP uses the z/VM CP Query VMLAN '. + 'command along with the information in the "VMLAN MAC address '. + 'assignment" portion of the command response, specifically the '. + 'user prefix information.', + 'userResp' => 'This error is most often caused by an error in the OpenStack configuration '. + 'property. Either the OpenStack property should be changed to match the z/VM '. + 'system or the z/VM system\'s value should be changed.', + }, + 'VN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Network %s was not found as a network.', + 'explain' => 'The specified network is not a known network on the host. The most '. + 'likely cause of this problem is a typo in the flat_networks and/or '. + 'network_vlan_ranges properties of the /etc/neutron/plugins/ml2/ml2_conf.ini file.', + 'userResp' => 'Correct the OpenStack configuration files.' + }, + 'VN02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Network %s is not %s as expected.', + 'explain' => 'The specified network on the host is not configured with the '. + 'expected VLAN awareness. The most likely cause of this problem '. + 'is an error in the flat_networks and/or network_vlan_ranges '. + 'properties of the /etc/neutron/plugins/ml2/ml2_conf.ini file.', + 'userResp' => 'Correct the OpenStack configuration files.', + }, + 'VNE01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Node %s is not defined to xCAT.', + 'explain' => 'The indicated node is not defined to xCAT. '. + 'This most often indicates a typo in a configuration file.', + 'userResp' => 'If using a driver script created by the IVP preparation script, '. + 'please review the driver script to determine the OpenStack '. + 'property and configuration file which specified the node. You can '. + 'do this by locating the node name in the driver script and then '. + 'reading the comments which indicate what property was used to set '. + 'the value in the driver script.' + }, + 'VP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to get the directory statements for profile %s', + 'explain' => 'The profile specified as input is not in the z/VM directory. '. + 'The cause of this error is either a typo in the zvm_user_profile '. + 'property in the /etc/nova/nova.conf file or that the profile was not'. + 'defined to z/VM.', + 'userResp' => 'Correct the OpenStack configuration file.', + }, + 'VR01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'REST API failed to successfully respond to request.\n' . + ' Response code: %s, Message: %s\n' . + ' Response content: %s', + 'explain' => 'A REST communication to the xCAT Management Node from the same '. + 'server on which the management node was running failed. This '. + 'can be caused by a typo in the zvm_xcat_username, zvm_xcat_password, '. + 'and/or zvm_xcat_server properties in /etc/nova/nova.conf file. The xCAT '. + 'management node obtains the value that it recognizes for the user name from the '. + 'XCAT_MN_admin property in DMSSICNF COPY. The user name properties '. + 'should match. It can also occur if there are configuration errors '. + 'or TCP/IP errors in the xCAT management node.', + 'userResp' => 'Review the REST response data that is provided with the message. '. + 'It should help isolate the problem.' + }, + 'VU01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'user %s is not in the policy table.', + 'explain' => 'The specified xCAT user is not a known xCAT user. This is most likely '. + 'caused by a typo in the zvm_xcat_username property in the '. + '/etc/nova/nova.conf file. The xCAT management node obtains the value that it '. + 'recognizes for the user name from the XCAT_MN_admin property in DMSSICNF COPY.', + 'userResp' => 'Correct the OpenStack configuration file.', + }, + 'VVO01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'vswitch %s does not exist.', + 'explain' => 'The specified virtual switch does not exist in the z/VM system.', + 'userResp' => 'The vswitch name was specified as the section name in the '. + '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini file before the '. + 'rdev_list property. The neutron agent creates the virtual switches '. + 'when it starts up. If the switch is not defined, you should determine '. + 'whether the neutron agent was started and is successfully communicating '. + 'with xCAT.' + }, + 'VVO02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'vswitch %s does not use real device %s', + 'explain' => 'The /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini file indicated that '. + 'the virtual switch has a real device associated with it at the specified '. + 'address which does not match the actual vswitch definition in z/VM.', + 'userResp' => 'This is most likely an error in the rdev_list property for the section '. + 'indicated by the vswitch name in the /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini file. '. + 'Correct the OpenStack configuration file.', + }, + 'VZN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'zHCP node %s did not respond to a ping.', + 'explain' => 'The indicated ZHCP node did not respond to a ping. '. + 'The virtual machine may be logged off the z/VM system '. + 'or there may be problems in the configuration of this server causing it '. + 'to fail to IPL, or configure its TCP/IP interfaces. '. + 'ZHCP is necessary to manage the z/VM host and its virtual servers. '. + 'The ZHCP node is associated with the host specified by the zvm_host property in the '. + '/etc/nova/nova.conf file.', + 'userResp' => 'There are a number of possibilities that you should consider: '. + "\n". + '* Verify that the virtual machine is logged on. '. + 'If it is not logged on then verify that you have performed the necessary steps to '. + 'tell SMAPI to start the server when SMAPI starts. '. + 'This is discussed in the z/VM Systems Management Application Programming manual.'. + "\n". + '* Obtain the console log from the virtual server and verify that the system has '. + 'not stopped itself due to an error. This is not a normal condition and '. + 'usually indicates a serious configuration error.', + }, + 'VZN02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'zHCP node %s is either not powered on or responding.', + 'explain' => 'The indicated ZHCP node did not respond to simple power status request. '. + 'There may be problems with the set up of this ZHCP.', + 'userResp' => 'Pay particular attention to whether there are SSL key problems. Another '. + 'possible cause is an error preventing ZHCP from communicating with the '. + 'z/VM CP. The ZHCP node is associated with the host specified by the zvm_host '. + 'property in the /etc/nova/nova.conf file.', + }, + 'VZN03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to get the directory statements for %s. The statements should be for userid %s.', + 'explain' => 'The indicated ZHCP node did not provide the proper response to a request for '. + 'information on a user in the z/VM directory. '. + 'The IVP checks the directory statements to verify that a USER or IDENTITY statement '. + 'was returned with the userid that was specified in the xCAT zvm table. '. + 'In addition, the SMAPI servers or the directory manager '. + 'may not be configured to communicate with the ZHCP agent. '. + "\n". + 'The ZHCP node is associated with the host specified by the zvm_host property in the '. + '/etc/nova/nova.conf file.', + 'userResp' => 'Verify that:'. + "\n". + '* the zvm table has the userid property correctly listed for the '. + 'virtual machine that is running the ZHCP node. If not then correct it.'. + "\n". + '* the Directory manager is operational. This can be accomplished by issuing '. + 'a command to review a userid\'s directory entries to see if you get a valid response.'. + "\n". + '* the Directory manager is properly configuring to communicate with the SMAPI servers. '. + 'This is discussed in the z/VM Systems Management Application Programming manual.', + }, + 'VZN04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'zHCP node %s is encountering errors communicating with SMAPI, out: %s', + 'explain' => 'The indicated ZHCP node was not able to execute a simple query to the SMAPI '. + 'servers and receive an expected result. This indicates a possible error '. + 'in the SMAPI configuration.', + 'userResp' => 'You should review the SMAPI configuration to ensure all steps were '. + 'accomplished and that the virtual machine running the ZHCP agent is '. + 'authorized to communicate with the SMAPI servers and that '. + 'the SMAPI servers are running. For example, verify that '. + 'the VSMWORK1 AUTHLIST file in \'VMSYS:VSMWORK1.\' SFS directory lists '. + 'the virtual machine that is running the ZHCP agent and that the information in the file '. + 'is in the correct columns.', + }, + 'VZN05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to find a zHCP node related to host %s.', + 'explain' => 'The indicated host node does not have a ZHCP node which is defined and '. + 'associated with it. The IVP program expects each host to have an hcp property '. + 'defined and that property to be related to an xCAT node that represents '. + 'the ZHCP server and has the same host name as specified for the host\'s hcp property.', + 'userResp' => 'Define a node in xCAT for the ZHCP server.' + }, + ); + +my %reposList = ( + 'VERIFYNODE' => \%verifyMsgs, + 'ZXCATIVP' => \%zxcativpMsgs, + ); + + + + +#------------------------------------------------------- + +=head3 buildMsg + + Description : Build a message from a message repository file. + Arguments : Group identifier + Message identifier + 'BLANK_LINE' is a special identifier that does not appear in + the message repository but instead causes a blank line to + be printed to the display. + Message substitutions + Returns : Recommended action: + 0: Continue processing + 1: Fatal, end processing + severity - Severity of message and indicate whether it gets + a header to the message text (e.g. "Error (IVP:MAINT01) " ) + 0 - no message header, unrated output + 1 - bypass message used by automated tests + 2 - information message with "Info" header + 3 - unknown message severity + 4 - warning message with "Warning" header + 5 - error message with "Error" header + Constructed message + Additional information (e.g. explanation, system action, user action) + Example : ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'IVP', $msgInfo, \@msgSubs); + +=cut + +#------------------------------------------------------- +sub buildMsg { + my ( $class, $groupId, $msgId, $subs ) = @_; + my @msgSubs = (); + my $recAction = 0; + my %respMsgs; + my $respHash; + my $retMsg = ''; + my $retExtra = ''; + my $sev = 0; + my ( $init_tab, $subsequent_tab ); + + $Text::Wrap::unexpand = 0; + + if ( defined $subs ) { + @msgSubs = @$subs; + } + + # Get the hash of the messages. + if ( exists $reposList{$groupId} ) { + $respHash = $reposList{$groupId}; + } else { + $respHash = \%verifyMsgs; + } + %respMsgs = %$respHash; + + # Find the message + if ( $msgId eq 'BLANK_LINE' ) { + $retMsg = "\n"; + } elsif ( !exists $respMsgs{$msgId} ) { + $retMsg = "Warning ($groupId:$msgId): Message was not found! Unable to find " . + "\'$msgId\' in \'$groupId\' messages.\n"; + $sev = 3; + # Recommended Action is 'continue'. + } else { + # Build severity and message Id portion of the message. + my $msg = ''; + if ( exists $respMsgs{$msgId}{'severity'} ) { + $sev = $respMsgs{$msgId}{'severity'}; + if ( $respMsgs{$msgId}{'severity'} != 0 ) { + $msg = $sevInfo[ $respMsgs{$msgId}{'severity'} ] . " ($groupId:$msgId) "; + } + } else { + # Unknown severity + $msg = $sevInfo[1] . " ($groupId:$msgId) "; + $sev = 3; + } + + # Determine the recommended action to return to the caller. + if ( exists $respMsgs{$msgId}{'recAction'} ) { + $recAction = $respMsgs{$msgId}{'recAction'}; + } + + # Build text portion of the message. + if ( exists $respMsgs{$msgId}{'text'} ) { + # Determine the number of '%s' in the message and pad @msgSubs + # so that we do not get a sprintf error due to having to few + # substitution values. + my $numSubs = 0; + while ( $respMsgs{$msgId}{'text'} =~ /\%s/g ) { $numSubs++ } + if ( $numSubs > 0 and $numSubs > ( scalar @msgSubs ) ) { + # Too few subs. Push on some empty strings so that sprintf does not complain. + for ( my $i = (scalar @msgSubs); $i <= $numSubs; $i++ ) { + push @msgSubs, ''; + } + } + + if ( @msgSubs ) { + # Ensure all elements in @msgSubs are defined. + for ( my $i = 0; $i < (scalar @msgSubs); $i++) { + if ( ! defined $msgSubs[$i] ) { + $msgSubs[$i] = ''; + } + } + + # Insert the substitutions + my $msgText = sprintf( $respMsgs{$msgId}{'text'}, @msgSubs); + $msg = "$msg$msgText"; + } else { + $msg = $msg . $respMsgs{$msgId}{'text'}; + } + } + + # Set the subsequent wrap/tab value for formatting. + if ( exists $respMsgs{$msgId}{'initTab'} ) { + $init_tab = $respMsgs{$msgId}{'initTab'}; + } else { + $init_tab = ""; + } + if ( exists $respMsgs{$msgId}{'subTab'} ) { + $subsequent_tab = $respMsgs{$msgId}{'subTab'}; + } else { + $subsequent_tab = "\t"; + } + + # Format the messages lines with proper indentation. + my $line; + chomp $msg; + my @msgLines = split( /\\n/, $msg ); + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + $retMsg = $retMsg . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + + if ( $respMsgs{$msgId}{'severity'} >= 1 ) { + # Build explanation portion of the known messages that are bypass, + # info, warning or error. These can have extra information. + if ( exists $respMsgs{$msgId}{'explain'} ) { + my $expLines = " Explanation: $respMsgs{$msgId}{'explain'}"; + @msgLines = split( /\\n/, $expLines ); + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + if ( $i != 0 ) { + $line = "\t$line"; + } + $retExtra = $retExtra . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + } + + # Build system action portion of the message. + my $sysAction; + if ( exists $respMsgs{$msgId}{'sysAct'} ) { + $sysAction = " System Action: $respMsgs{$msgId}{'sysAct'}"; + } else { + if ( $recAction == 0 and exists $respMsgs{'NONFATAL_DEFAULTS'}{'sysAct'} ) { + $sysAction = " System Action: $respMsgs{'NONFATAL_DEFAULTS'}{'sysAct'}"; + } elsif ( $recAction == 1 and exists $respMsgs{'FATAL_DEFAULTS'}{'sysAct'} ) { + $sysAction = " System Action: $respMsgs{'FATAL_DEFAULTS'}{'sysAct'}"; + } + } + if ( defined $sysAction ) { + #@msgLines = split( /\\n/, $sysAction ); + #$retMsg = $retMsg . wrap( "", "\t", @msgLines ) . "\n"; + + @msgLines = split( /\\n/, $sysAction ); + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + if ( $i != 0 ) { + $line = "\t$line"; + } + $retExtra = $retExtra . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + } + + # Build user response portion of the message. + if ( exists $respMsgs{$msgId}{'userResp'} ) { + @msgLines = split( /\\n/, " User Response: $respMsgs{$msgId}{'userResp'}" ); + #$retMsg = $retMsg . wrap( "", "\t", @msgLines ) . "\n"; + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + if ( $i != 0 ) { + $line = "\t$line"; + } + $retExtra = $retExtra . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + } + } + } + + return ( $recAction, $sev, $retMsg, $retExtra ); +} \ No newline at end of file diff --git a/perl-xCAT/xCAT/zvmUtils.pm b/perl-xCAT/xCAT/zvmUtils.pm index 2ef8880bd..55d188613 100644 --- a/perl-xCAT/xCAT/zvmUtils.pm +++ b/perl-xCAT/xCAT/zvmUtils.pm @@ -1,4 +1,4 @@ -# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html +# IBM(c) 2013-2016 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 @@ -14,10 +14,37 @@ use xCAT::Utils; use xCAT::Table; use xCAT::NetworkUtils; use File::Basename; +use Net::Ping; use strict; use warnings; +use Encode; +use JSON; +use Data::Dumper; +use Cwd; 1; +my $locOpenStackUpdateName = '/var/lib/sspmod/setnewname.py'; + +# Files which contain OS distribution and version information. +my $locEtcDebianVersion = '/etc/debian_version'; +my $locEtcFedoraRelease = '/etc/fedora-release'; +my $locEtcIssue = '/etc/issue'; +my $locEtcLsbRelease = '/etc/lsb-release'; +my $locEtcOsRelease = '/etc/os-release'; +my $locEtcRedhatRelease = '/etc/redhat-release'; +my $locEtcStarRelease = '/etc/*-release'; +my $locEtcSuseRelease = '/etc/SuSE-release'; +my $locEtcUnitedLinux = '/etc/UnitedLinux-release'; +my $locAllEtcVerFiles = "/etc/*-release /etc/issue /etc/debian_version"; + +# Supported Operating System distros and versions +my %supportedVersions = ( + rhel5 => 1, + rhel6 => 1, + sles10 => 1, + sles11 => 1, + ); + #------------------------------------------------------- =head3 getNodeProps @@ -27,20 +54,20 @@ use warnings; Properties Returns : Node properties from given table Example : my $propVals = xCAT::zvmUtils->getNodeProps($tabName, $node, $propNames); - + =cut #------------------------------------------------------- sub getNodeProps { # Get inputs - my ($class, $tabName, $node, @propNames) = @_; + my ( $class, $tabName, $node, @propNames ) = @_; # Get table my $tab = xCAT::Table->new($tabName); # Get property values - my $propVals = $tab->getNodeAttribs($node, [@propNames]); + my $propVals = $tab->getNodeAttribs( $node, [@propNames] ); return ($propVals); } @@ -55,21 +82,21 @@ sub getNodeProps { Requested properties Returns : Table entry properties Example : my $propVals = xCAT::zvmUtils->getTabPropsByKey($tabName, $key, $keyValue, @reqProps); - + =cut #------------------------------------------------------- sub getTabPropsByKey { # Get inputs - my ($class, $tabName, $key, $keyVal, @propNames) = @_; + my ( $class, $tabName, $key, $keyVal, @propNames ) = @_; # Get table my $tab = xCAT::Table->new($tabName); my $propVals; # Get table attributes matching given key - $propVals = $tab->getAttribs({ $key => $keyVal }, @propNames); + $propVals = $tab->getAttribs( { $key => $keyVal }, @propNames ); return ($propVals); } @@ -80,14 +107,14 @@ sub getTabPropsByKey { Arguments : Table name Returns : All table entries Example : my $entries = xCAT::zvmUtils->getAllTabEntries($tabName); - + =cut #------------------------------------------------------- sub getAllTabEntries { # Get inputs - my ($class, $tabName) = @_; + my ( $class, $tabName ) = @_; # Get table my $tab = xCAT::Table->new($tabName); @@ -109,20 +136,20 @@ sub getAllTabEntries { Property value Returns : Nothing Example : xCAT::zvmUtils->setNodeProp($tabName, $node, $propName, $propVal); - + =cut #------------------------------------------------------- sub setNodeProp { # Get inputs - my ($class, $tabName, $node, $propName, $propVal) = @_; + my ( $class, $tabName, $node, $propName, $propVal ) = @_; # Get table - my $tab = xCAT::Table->new($tabName, -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Set property - $tab->setAttribs({ 'node' => $node }, { $propName => $propVal }); + $tab->setAttribs( { 'node' => $node }, { $propName => $propVal } ); # Save table $tab->commit; @@ -140,20 +167,20 @@ sub setNodeProp { Reference to property name/value hash Returns : Nothing Example : xCAT::zvmUtils->setNodeProps($tabName, $node, \%propHash); - + =cut #------------------------------------------------------- sub setNodeProps { # Get inputs - my ($class, $tabName, $node, $propHash) = @_; + my ( $class, $tabName, $node, $propHash ) = @_; # Get table - my $tab = xCAT::Table->new($tabName, -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Set property - $tab->setAttribs({ 'node' => $node }, $propHash); + $tab->setAttribs( { 'node' => $node }, $propHash ); # Save table $tab->commit; @@ -168,24 +195,24 @@ sub setNodeProps { Description : Delete a table entry Arguments : Table Key name - Key value + Key value Returns : Nothing Example : xCAT::zvmUtils->delTabEntry($tabName, $keyName, $keyVal); - + =cut #------------------------------------------------------- sub delTabEntry { # Get inputs - my ($class, $tabName, $keyName, $keyVal) = @_; + my ( $class, $tabName, $keyName, $keyVal ) = @_; # Get table - my $tab = xCAT::Table->new($tabName, -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Delete entry from table - my %key = ($keyName => $keyVal); - $tab->delEntries(\%key); + my %key = ( $keyName => $keyVal ); + $tab->delEntries( \%key ); # Save table $tab->commit; @@ -201,15 +228,15 @@ sub delTabEntry { Arguments : String Returns : Tabbed string Example : my $str = xCAT::zvmUtils->tabStr($str); - + =cut #------------------------------------------------------- sub tabStr { # Get inputs - my ($class, $inStr) = @_; - my @lines = split("\n", $inStr); + my ( $class, $inStr ) = @_; + my @lines = split( "\n", $inStr ); # Tab output my $outStr; @@ -228,14 +255,14 @@ sub tabStr { Arguments : String Returns : Trimmed string Example : my $str = xCAT::zvmUtils->trimStr($str); - + =cut #------------------------------------------------------- sub trimStr { # Get string - my ($class, $str) = @_; + my ( $class, $str ) = @_; # Trim right $str =~ s/\s*$//; @@ -254,14 +281,14 @@ sub trimStr { Arguments : String Returns : New string Example : my $str = xCAT::zvmUtils->replaceStr($str, $pattern, $replacement); - + =cut #------------------------------------------------------- sub replaceStr { # Get string - my ($class, $str, $pattern, $replacement) = @_; + my ( $class, $str, $pattern, $replacement ) = @_; # Replace string $str =~ s/$pattern/$replacement/g; @@ -277,25 +304,24 @@ sub replaceStr { Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printLn($callback, $str); - + =cut #------------------------------------------------------- sub printLn { # Get inputs - my ($class, $callback, $str) = @_; + my ( $class, $callback, $str ) = @_; # Print string my $rsp; my $type = "I"; - if ($str =~ m/error/i) { # Set to print error if the string contains error + if ($str =~ m/(\(error\)|\s*failed)/i) { # Set to print error if the string contains (error) or starts with failed $type = "E"; } $rsp->{data}->[0] = "$str"; - xCAT::MsgUtils->message($type, $rsp, $callback); - + xCAT::MsgUtils->message( $type, $rsp, $callback ); # xCAT::MsgUtils->message( "S", $str ); # Print to syslog return; @@ -309,20 +335,20 @@ sub printLn { Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printSyslog($str); - + =cut #------------------------------------------------------- sub printSyslog { # Get inputs - my ($class, $str) = @_; + my ( $class, $str ) = @_; # Prepend where this message came from $str = $class . " " . $str; # Print string - xCAT::MsgUtils->message("S", $str); + xCAT::MsgUtils->message( "S", $str ); return; } @@ -336,19 +362,19 @@ sub printSyslog { Returns : TRUE Node exists FALSE Node does not exists Example : my $out = xCAT::zvmUtils->isZvmNode($node); - + =cut #------------------------------------------------------- sub isZvmNode { # Get inputs - my ($class, $node) = @_; + my ( $class, $node ) = @_; # Look in 'zvm' table - my $tab = xCAT::Table->new('zvm', -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); - my @results = $tab->getAllAttribsWhere("node like '%" . $node . "%'", 'userid'); + my @results = $tab->getAllAttribsWhere( "node like '%" . $node . "%'", 'userid' ); foreach (@results) { # Return 'TRUE' if given node is in the table @@ -370,14 +396,14 @@ sub isZvmNode { Node Returns : Hardware configuration file path Example : my $hwcfg = xCAT::zvmUtils->getHwcfg($user, $node); - + =cut #------------------------------------------------------- sub getHwcfg { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -392,10 +418,15 @@ sub getHwcfg { my @parms; # If it is SUSE - hwcfg-qeth file is in /etc/sysconfig/hardware - if ($os =~ m/SUSE/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"`; - @parms = split('\n', $out); - return ($parms[0]); + if ( $os =~ m/SUSE/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"`; + my $cmd = "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If no file is found - Return nothing @@ -410,20 +441,20 @@ sub getHwcfg { Arguments : Node Returns : IP address of given node Example : my $ip = xCAT::zvmUtils->getIp($node); - + =cut #------------------------------------------------------- sub getIp { # Get inputs - my ($class, $node) = @_; + my ( $class, $node ) = @_; # Get IP address # You need the extra space in the pattern, # else it will confuse gpok2 with gpok21 - my $out = `cat /etc/hosts | egrep -i "$node | $node."`; - my @parms = split(' ', $out); + my $out = `cat /etc/hosts | egrep -i "$node | $node."`; + my @parms = split( ' ', $out ); return $parms[0]; } @@ -439,14 +470,14 @@ sub getIp { Node Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfg($user, $node); - + =cut #------------------------------------------------------- sub getIfcfg { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -461,17 +492,27 @@ sub getIfcfg { my @parms; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts - if ($os =~ m/Red Hat/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; - @parms = split('\n', $out); - return ($parms[0]); + if ( $os =~ m/Red Hat/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If it is SUSE - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($os =~ m/SUSE/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"`; - @parms = split('\n', $out); - return ($parms[0]); + elsif ( $os =~ m/SUSE/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If no file is found - Return nothing @@ -488,14 +529,14 @@ sub getIfcfg { NIC address Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfgByNic($user, $node, $nic); - + =cut #------------------------------------------------------- sub getIfcfgByNic { # Get inputs - my ($class, $user, $node, $nic) = @_; + my ( $class, $user, $node, $nic ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -510,15 +551,25 @@ sub getIfcfgByNic { my @parms; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts - if ($os =~ m/Red Hat/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; - @parms = split('\n', $out); + if ( $os =~ m/Red Hat/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); # Go through each line foreach (@parms) { # If the network file contains the NIC address - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | egrep -i "$nic"`; + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | egrep -i "$nic"`; + my $cmd = $sudo . ' cat $_ | egrep -i "' . $nic .'"'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } if ($out) { # Return network file path @@ -528,24 +579,39 @@ sub getIfcfgByNic { } # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($os =~ m/SUSE Linux Enterprise Server 10/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*" | grep -i "$nic"`; - @parms = split('\n', $out); - return ($parms[0]); + elsif ( $os =~ m/SUSE Linux Enterprise Server 10/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*" | grep -i "$nic"`; + my $cmd = $sudo . ' ls /etc/sysconfig/network/ifcfg-qeth* | grep -i "' . $nic .'"'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($os =~ m/SUSE Linux Enterprise Server 11/i) { + elsif ( $os =~ m/SUSE Linux Enterprise Server 11/i ) { # Get a list of ifcfg-eth files found - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-eth*"`; - my @file = split('\n', $out); + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-eth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network/ifcfg-eth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + my @file = split( '\n', $out ); # Go through each ifcfg-eth file foreach (@file) { # If the network file contains the NIC address - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | grep -i "$nic"`; + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | grep -i "$nic"`; + my $cmd = $sudo . ' cat $_ | grep -i "' . $nic .'"'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } if ($out) { # Return ifcfg-eth file path @@ -567,24 +633,36 @@ sub getIfcfgByNic { Node Source file Target file - Returns : Nothing - Example : xCAT::zvmUtils->sendFile($user, $node, $srcFile, $trgtFile); - + Returns : Return code from SCP + Example : $rc = xCAT::zvmUtils->sendFile($user, $node, $srcFile, $trgtFile); + =cut #------------------------------------------------------- sub sendFile { # Get inputs - my ($class, $user, $node, $srcFile, $trgtFile) = @_; + my ( $class, $user, $node, $srcFile, $trgtFile ) = @_; + + my $out; + my $rc; # Create destination string my $dest = "$user\@$node"; # SCP directory entry file over to HCP - my $out = `/usr/bin/scp $srcFile $dest:$trgtFile`; + foreach my $wait ( 1, 2, 3, 5, 8, 15, 22, 34, 60 ) { + $out = `/usr/bin/scp $srcFile $dest:$trgtFile 2>&1`; + $rc = $?; + if ( $rc == 0 ) { + last; + } - return; + xCAT::zvmUtils->printSyslog("SCP $srcFile $dest:$trgtFile - failed with rc: $rc, out: $out"); + sleep( $wait ); + } + + return $rc; } #------------------------------------------------------- @@ -596,26 +674,43 @@ sub sendFile { Node Returns : Root device address Example : my $deviceAddr = xCAT::zvmUtils->getRootDeviceAddr($user, $node); - + =cut #------------------------------------------------------- sub getRootDeviceAddr { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } # Get the root device node # LVM is not supported - my $out = `ssh $user\@$node "mount" | grep "/ type" | sed 's/1//'`; - my @parms = split(" ", $out); - @parms = split("/", xCAT::zvmUtils->trimStr($parms[0])); + #my $out = `ssh $user\@$node "mount" | grep "/ type" | sed 's/1//'`; + my $cmd = $sudo . ' mount | grep "/ type" | sed \'s/1//\''; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + + my @parms = split( " ", $out ); + @parms = split( "/", xCAT::zvmUtils->trimStr( $parms[0] ) ); my $devNode = $parms[0]; # Get disk address - $out = `ssh $user\@$node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; - @parms = split(" ", $out); - return ($parms[0]); + #$out = `ssh $user\@$node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; + $cmd = $sudo . ' cat /proc/dasd/devices | grep "' . $devNode . '" | sed "s/(ECKD)//" | sed "s/(FBA )//" | sed \'s/0.0.//\''; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + + @parms = split( " ", $out ); + return ( $parms[0] ); } #------------------------------------------------------- @@ -629,14 +724,14 @@ sub getRootDeviceAddr { Option (-d|-e) Returns : Nothing Example : my $out = xCAT::zvmUtils->disableEnableDisk($callback, $user, $node, $option, $devAddr); - + =cut #------------------------------------------------------- sub disableEnableDisk { # Get inputs - my ($class, $user, $node, $option, $devAddr) = @_; + my ( $class, $user, $node, $option, $devAddr ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -645,11 +740,23 @@ sub disableEnableDisk { # Disable/enable disk my $out; - if ($option eq "-d" || $option eq "-e") { - $out = `ssh $user\@$node "$sudo /sbin/chccwdev $option $devAddr"`; + if ( $option eq "-d" || $option eq "-e" ) { + # Can't guarantee the success of online/offline disk, need to wait + # Until it's done because we may detach the disk after -d option + # or use the disk after the -e option + my @sleepTime = (1,2,3,5,8,15,22,34,60,60,60,60,60,90,120); + foreach (@sleepTime) { + #$out = system("ssh $user\@$node '$sudo /sbin/chccwdev $option $devAddr'"); + my $cmd = "$sudo /sbin/chccwdev $option $devAddr"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { # try again if an error + sleep($_); + } else { + return "ssh $user\@$node $sudo /sbin/chccwdev $option $devAddr... Done"; + } + } } - - return ($out); + return "Error: failed to ssh $user\@$node $sudo /sbin/chccwdev $option $devAddr"; } #------------------------------------------------------- @@ -661,14 +768,14 @@ sub disableEnableDisk { Node Returns : MDISK statements Example : my @mdisks = xCAT::zvmUtils->getMdisks($callback, $user, $node); - + =cut #------------------------------------------------------- sub getMdisks { # Get inputs - my ($class, $callback, $user, $node) = @_; + my ( $class, $callback, $user, $node ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -679,8 +786,8 @@ sub getMdisks { } # Get HCP - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userID @@ -689,13 +796,17 @@ sub getMdisks { my $out = `ssh $user\@$hcp "$sudo $dir/getuserentry $userId" | grep "MDISK"`; # Get MDISK statements - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); my @disks; foreach (@lines) { + # skip comment lines that start with * or whitespace * + if ( $_ =~ m/^\s*\*/) { + next; + } $_ = xCAT::zvmUtils->trimStr($_); # Save MDISK statements - push(@disks, $_); + push( @disks, $_ ); } return (@disks); @@ -710,14 +821,14 @@ sub getMdisks { Node Returns : DEDICATE statements Example : my @dedicates = xCAT::zvmUtils->getDedicates($callback, $user, $node); - + =cut #------------------------------------------------------- sub getDedicates { # Get inputs - my ($class, $callback, $user, $node) = @_; + my ( $class, $callback, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -728,8 +839,8 @@ sub getDedicates { my $dir = '/opt/zhcp/bin'; # Get zHCP - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userId @@ -738,13 +849,13 @@ sub getDedicates { my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId" | egrep -i "DEDICATE"`; # Get DEDICATE statements - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); my @dedicates; foreach (@lines) { $_ = xCAT::zvmUtils->trimStr($_); # Save statements - push(@dedicates, $_); + push( @dedicates, $_ ); } return (@dedicates); @@ -752,23 +863,72 @@ sub getDedicates { #------------------------------------------------------- +=head3 getCommands + + Description : Get the COMMAND statements in the user entry of a given node + Arguments : User (root or non-root) + Node + Returns : COMMAND statements + Example : my @commands = xCAT::zvmUtils->getCommands($callback, $user, $node); + +=cut + +#------------------------------------------------------- +sub getCommands { + + # Get inputs + my ( $class, $callback, $user, $node ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + # Get zHCP + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + my $hcp = $propVals->{'hcp'}; + + # Get node userId + my $userId = $propVals->{'userid'}; + + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId" | egrep -i "COMMAND"`; + + # Get COMMAND statements + my @lines = split( '\n', $out ); + my @commands; + foreach (@lines) { + $_ = xCAT::zvmUtils->trimStr($_); + + # Save statements + push( @commands, $_ ); + } + + return (@commands); +} + +#------------------------------------------------------- + =head3 getUserEntryWODisk - Description : Get the user entry of a given node without MDISK statments, + Description : Get the user entry of a given node without MDISK statments, and save it to a file Arguments : User (root or non-root) Node File name to save user entry under Returns : Nothing Example : my $out = xCAT::zvmUtils->getUserEntryWODisk($callback, $user, $node, $file); - + =cut #------------------------------------------------------- sub getUserEntryWODisk { # Get inputs - my ($class, $callback, $user, $node, $file) = @_; + my ( $class, $callback, $user, $node, $file ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -779,30 +939,30 @@ sub getUserEntryWODisk { my $dir = '/opt/zhcp/bin'; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "Error: Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "Error: Missing node ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); return; } my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId" | sed '\$d' | grep -v "MDISK"`; # Create a file to save output - open(DIRENTRY, ">$file"); + open( DIRENTRY, ">$file" ); # Save output - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); foreach (@lines) { # Trim line @@ -825,15 +985,15 @@ sub getUserEntryWODisk { String Returns : String appended with hostname Example : my $str = xCAT::zvmUtils->appendHostname($hostname, $str); - + =cut #------------------------------------------------------- sub appendHostname { - my ($class, $hostname, $str) = @_; + my ( $class, $hostname, $str ) = @_; # Append hostname to every line - my @outLn = split("\n", $str); + my @outLn = split( "\n", $str ); $str = ""; foreach (@outLn) { $str .= "$hostname: " . $_ . "\n"; @@ -850,20 +1010,20 @@ sub appendHostname { Arguments : Output string Returns : 0 Good output -1 Bad output - Example : my $rtn = xCAT::zvmUtils->checkOutput($callback, $out); - + Example : my $rtn = xCAT::zvmUtils->checkOutput( $out ); + =cut #------------------------------------------------------- sub checkOutput { - my ($class, $callback, $out) = @_; + my ( $class, $out ) = @_; # Check output string - my @outLn = split("\n", $out); + my @outLn = split( "\n", $out ); foreach (@outLn) { - # If output contains 'Failed', return -1 - if ($_ =~ m/Failed/i || $_ =~ m/Error/i) { + # If output contains 'Failed', or Error return -1 + if ( $_ =~ m/Failed/i || $_ =~ m/Error/i ) { return -1; } } @@ -880,18 +1040,17 @@ sub checkOutput { Reason (passed as a reference) Returns : 0 Good output -1 Bad output - Example : my $rtn = xCAT::zvmUtils->checkOutput($callback, $out, \$reason); - + Example : my $rtn = xCAT::zvmUtils->checkOutput( $out, \$reason ); + =cut #------------------------------------------------------- sub checkOutputExtractReason { - my ($class, $callback, $out, $reason) = @_; + my ( $class, $out, $reason ) = @_; # Check output string my @outLn = split("\n", $out); foreach (@outLn) { - # If output contains 'ERROR: ', return -1 and pass back the reason. if ($_ =~ /(.*?ERROR: )/) { $$reason = substr($_, index($_, "ERROR: ") + length("ERROR: ")); @@ -902,6 +1061,43 @@ sub checkOutputExtractReason { return 0; } + +#------------------------------------------------------- + +=head3 checkSSHOutput + + Description : Check for SSH errors, and command failure + Arguments : $? + command that was issued + Returns : rc = 0 Good output + rc = -1 Error occurred + $outmsg = error message string if $rc = -1 + + + Example : ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "Command being issued"); + +=cut + +#------------------------------------------------------- +sub checkSSHOutput { + my ( $class, $rc, $cmd ) = @_; + + my $msgTxt = ''; + $rc = $rc >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. + $msgTxt = "SSH Failed to communicate when trying command: $cmd"; + xCAT::zvmUtils->printSyslog("$msgTxt"); + return (-1, $msgTxt); + } elsif ( $rc != 0 ) { + # Generic failure of the command. + $msgTxt = "Command failed with return code $rc trying to issue cmd: $cmd"; + xCAT::zvmUtils->printSyslog("$msgTxt"); + return ($rc, $msgTxt); + } + return 0; +} + #------------------------------------------------------- =head3 getDeviceNode @@ -912,12 +1108,12 @@ sub checkOutputExtractReason { Disk address Returns : Device node Example : my $devNode = xCAT::zvmUtils->getDeviceNode($user, $node, $tgtAddr); - + =cut #------------------------------------------------------- sub getDeviceNode { - my ($class, $user, $node, $tgtAddr) = @_; + my ( $class, $user, $node, $tgtAddr ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -925,7 +1121,12 @@ sub getDeviceNode { } # Determine device node - my $out = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep ".$tgtAddr("`; + #my $out = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep ".$tgtAddr("`; + my $cmd = $sudo . ' cat /proc/dasd/devices | grep ".' . $tgtAddr . '("'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } my @words = split(' ', $out); my $tgtDevNode; @@ -951,12 +1152,12 @@ sub getDeviceNode { Device node Returns : Virtual device address Example : my $addr = xCAT::zvmUtils->getDeviceNodeAddr($user, $node, $deviceNode); - + =cut #------------------------------------------------------- sub getDeviceNodeAddr { - my ($class, $user, $node, $deviceNode) = @_; + my ( $class, $user, $node, $deviceNode ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -966,7 +1167,12 @@ sub getDeviceNodeAddr { # Find device node and determine virtual address # /proc/dasd/devices look similar to this: # 0.0.0100(ECKD) at ( 94: 0) is dasda : active at blocksize: 4096, 1802880 blocks, 7042 MB - my $addr = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep -i "is $deviceNode"`; + #my $addr = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep -i "is $deviceNode"`; + my $cmd = $sudo . ' cat /proc/dasd/devices | grep -i "is ' . $deviceNode . '"'; + my $addr = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $addr ) == -1) { + return $addr; + } $addr =~ s/ +/ /g; $addr =~ s/^0.0.([0-9a-f]*).*/$1/; chomp($addr); @@ -985,12 +1191,12 @@ sub getDeviceNodeAddr { Returns : 0 Address used -1 Address not used Example : my $ans = xCAT::zvmUtils->isAddressUsed($user, $node, $address); - + =cut #------------------------------------------------------- sub isAddressUsed { - my ($class, $user, $node, $address) = @_; + my ( $class, $user, $node, $address ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -998,7 +1204,12 @@ sub isAddressUsed { } # Search for disk address - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v dasd" | grep "DASD $address"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v dasd" | grep "DASD $address"`; + my $cmd = $sudo . ' /sbin/vmcp q v dasd | grep "DASD '. $address. '"'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } if ($out) { return 0; } @@ -1015,12 +1226,12 @@ sub isAddressUsed { zHCP Returns : MACID Example : my $macId = xCAT::zvmUtils->getMacID($user, $hcp); - + =cut #------------------------------------------------------- sub getMacID { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1029,11 +1240,11 @@ sub getMacID { # Check /opt/zhcp/conf directory on HCP my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -d /opt/zhcp/conf && echo 'Directory exists'"`; - if ($out =~ m/Directory exists/i) { + if ( $out =~ m/Directory exists/i ) { # Check next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -e /opt/zhcp/conf/next_macid && echo 'File exists'"`; - if ($out =~ m/File exists/i) { + if ( $out =~ m/File exists/i ) { # Do nothing } else { @@ -1060,17 +1271,17 @@ sub getMacID { =head3 generateMacId - Description : Generate a new MACID + Description : Generate a new MACID Arguments : User (root or non-root) zHCP Returns : Nothing Example : my $macId = xCAT::zvmUtils->generateMacId($user, $hcp); - + =cut #------------------------------------------------------- sub generateMacId { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1079,11 +1290,11 @@ sub generateMacId { # Check /opt/zhcp/conf directory on HCP my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -d /opt/zhcp/conf && echo 'Directory exists'"`; - if ($out =~ m/Directory exists/i) { + if ( $out =~ m/Directory exists/i ) { # Check next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -e /opt/zhcp/conf/next_macid && echo 'File exists'"`; - if ($out =~ m/File exists/i) { + if ( $out =~ m/File exists/i ) { # Do nothing } else { @@ -1109,14 +1320,14 @@ sub generateMacId { if ($macId) { # Convert hexadecimal - decimal - $int = hex($macId); - $macId = sprintf("%d", $int); + $int = hex($macId); + $macId = sprintf( "%d", $int ); # Generate new MAC suffix $macId = $macId - 1; # Convert decimal - hexadecimal - $macId = sprintf("%X", $macId); + $macId = sprintf( "%X", $macId ); # Save new MACID $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo $macId > /opt/zhcp/conf/next_macid"`; @@ -1135,12 +1346,12 @@ sub generateMacId { MAC suffix Returns : MAC address Example : my $mac = xCAT::zvmUtils->createMacAddr($user, $node, $suffix); - + =cut #------------------------------------------------------- sub createMacAddr { - my ($class, $user, $node, $suffix) = @_; + my ( $class, $user, $node, $suffix ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1149,11 +1360,11 @@ sub createMacAddr { # Get node properties from 'zvm' table my @propNames = ('hcp'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { + if ( !$hcp ) { return -1; } @@ -1179,18 +1390,18 @@ sub createMacAddr { my $mac = $prefix . $suffix; # If length is less than 12, append a zero - if (length($mac) != 12) { + if ( length($mac) != 12 ) { $mac = "0" . $mac; } # Format MAC address $mac = - substr($mac, 0, 2) . ":" - . substr($mac, 2, 2) . ":" - . substr($mac, 4, 2) . ":" - . substr($mac, 6, 2) . ":" - . substr($mac, 8, 2) . ":" - . substr($mac, 10, 2); + substr( $mac, 0, 2 ) . ":" + . substr( $mac, 2, 2 ) . ":" + . substr( $mac, 4, 2 ) . ":" + . substr( $mac, 6, 2 ) . ":" + . substr( $mac, 8, 2 ) . ":" + . substr( $mac, 10, 2 ); return $mac; } @@ -1204,14 +1415,14 @@ sub createMacAddr { Node Returns : Operating system name Example : my $osName = xCAT::zvmUtils->getOs($user, $node); - + =cut #------------------------------------------------------- sub getOs { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1219,9 +1430,14 @@ sub getOs { } # Get operating system - my $out = `ssh -o ConnectTimeout=10 $user\@$node "$sudo cat /etc/*release" | egrep -v "LSB_VERSION"`; - my @results = split('\n', $out); - return (xCAT::zvmUtils->trimStr($results[0])); + #my $out = `ssh -o ConnectTimeout=10 $user\@$node "$sudo cat /etc/*release" | egrep -v "LSB_VERSION"`; + my $cmd = $sudo . ' cat /etc/*release | egrep -v "LSB_VERSION"'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + my @results = split( '\n', $out ); + return ( xCAT::zvmUtils->trimStr( $results[0] ) ); } #------------------------------------------------------- @@ -1233,14 +1449,14 @@ sub getOs { Node Returns : Architecture of node Example : my $arch = xCAT::zvmUtils->getArch($user, $node); - + =cut #------------------------------------------------------- sub getArch { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1248,9 +1464,14 @@ sub getArch { } # Get host using VMCP - my $arch = `ssh $user\@$node "$sudo uname -p"`; + #my $arch = `ssh $user\@$node "$sudo uname -m"`; + my $cmd = "$sudo uname -m"; + my $arch = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $arch ) == -1) { + return $arch; + } - return (xCAT::zvmUtils->trimStr($arch)); + return ( xCAT::zvmUtils->trimStr($arch) ); } #------------------------------------------------------- @@ -1262,14 +1483,14 @@ sub getArch { Profile name Returns : User profile Example : my $profile = xCAT::zvmUtils->getUserProfile($user, $hcp, $name); - + =cut #------------------------------------------------------- sub getUserProfile { # Get inputs - my ($class, $user, $hcp, $profile) = @_; + my ( $class, $user, $hcp, $profile ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1283,10 +1504,8 @@ sub getUserProfile { # Set directory for cache my $cache = '/var/opt/zhcp/cache'; - # If the cache directory does not exist if (!(`ssh $user\@$hcp "$sudo test -d $cache && echo Exists"`)) { - # Create cache directory $out = `ssh $user\@$hcp "$sudo mkdir -p $cache"`; } @@ -1305,7 +1524,7 @@ sub getUserProfile { # If the current time is greater than 5 minutes of the file timestamp my $interval = 300; # 300 seconds = 5 minutes * 60 seconds/minute - if ($curTime > $fileTime + $interval) { + if ( $curTime > $fileTime + $interval ) { # Get user profiles and save it in a file $out = `ssh $user\@$hcp "$sudo $hcpDir/smcli Profile_Query_DM -T $profile > $file"`; @@ -1323,6 +1542,60 @@ sub getUserProfile { #------------------------------------------------------- +=head3 getVswitchIdsFromDirectory + + Description : Get the nicdef switch names of a given node + Arguments : User (root or non-root) + zHCP + Userid + Returns : Vswitch names + Example : my $vSwitchNamers = xCAT::zvmCPUtils->getVswitchIdsFromDirectory($user, $hcp, $userId); + +=cut + +#------------------------------------------------------- +sub getVswitchIdsFromDirectory { + + # Get inputs + my ( $class, $user, $hcp ,$userId ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + my @vswitch; + + # Get VSwitchs in directory + # only get lines that have SYSTEM switchname in them + # smcli Image_Definition_Query_DM -T xcat -k nicdef + # NICDEF=VDEV=0600 TYPE=QDIO LAN=SYSTEM SWITCHNAME=XCATVSW1 + # NICDEF=VDEV=0700 TYPE=QDIO LAN=SYSTEM SWITCHNAME=XCATVSW2 + # + + xCAT::zvmUtils->printSyslog("Calling: ssh $user\@$hcp $sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF | egrep -i 'SWITCHNAME'"); + my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF | egrep -i 'SWITCHNAME'"`; + # if there is nothing found, log that and return; + if ( !length($out) ) { + xCAT::zvmUtils->printSyslog("No SWITCHNAME found in NICDEF statement for userid $userId"); + return (@vswitch); + } + xCAT::zvmUtils->printSyslog("$userId output: $out"); + my @lines = split( '\n', $out ); + my @parms; + my $vswitchToken = ''; + + foreach (@lines) { + @parms = split( ' ', $_ ); + $vswitchToken = $parms[3]; + @parms = split( '=', $vswitchToken ); + push( @vswitch, $parms[1] ); + } + + return (@vswitch); +} + +#------------------------------------------------------- + =head3 inArray Description : Checks if a value exists in an array @@ -1330,15 +1603,15 @@ sub getUserProfile { Search array Returns : The searched expression Example : my $rtn = xCAT::zvmUtils->inArray($needle, @haystack); - + =cut #------------------------------------------------------- sub inArray { # Get inputs - my ($class, $needle, @haystack) = @_; - return grep { $_ eq $needle } @haystack; + my ( $class, $needle, @haystack ) = @_; + return grep{ $_ eq $needle } @haystack; } #------------------------------------------------------- @@ -1347,54 +1620,350 @@ sub inArray { Description : Get the operating system of a given node Arguments : User (root or non-root) - Node - Returns : Operating system name + Node or IP Address + Callback handle (optional). Allows the routine, + to write error messages when SSH fails. + Returns : Operating system name & version as a single word. + Otherwise, an empty string. Example : my $os = xCAT::zvmUtils->getOsVersion($user, $node); - + my $os = xCAT::zvmUtils->getOsVersion($user, $node, $callback); + =cut #------------------------------------------------------- sub getOsVersion { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node, $callback ) = @_; + + my $osVer = ''; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $os = ''; - my $version = ''; + # Contact the system to extract the possible files which contain pertinent data. + #my $releaseInfo = `ssh -qo ConnectTimeout=2 $user\@$node "$sudo ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; + my $cmd = $sudo . ' ls /dev/null '. $locAllEtcVerFiles . ' 2>/dev/null | xargs grep ""'; + my $releaseInfo = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); + if (xCAT::zvmUtils->checkOutput( $releaseInfo ) == -1) { + return ''; + } + $osVer = buildOsVersion( $callback, $releaseInfo, 'all' ); + + return $osVer; +} + +#------------------------------------------------------- + +=head3 getOSFromIP + + Description : Get the operating system name and + version for the system at the specified + IP Address. + Arguments : Callback handle + z/VM userid of the system + IP Address + Type of IP address (not currently used) + Returns : Operating system name & version as a single word. + Otherwise, an empty string. + Example : my $os = xCAT::zvmUtils->getOSFromIP( $callback, $userid, $ipAddr, $ipVersion }; + +=cut + +#------------------------------------------------------- +sub getOSFromIP { + my ( $class, $callback, $activeSystem, $ipAddr, $ipVersion ) = @_; + + my $osVer = ''; + my $rc = 0; # Get operating system - my $release = `ssh -o ConnectTimeout=2 $user\@$node "$sudo cat /etc/*release"`; - my @lines = split('\n', $release); - if (grep(/SUSE Linux Enterprise Server/, @lines)) { - $os = 'sles'; - $version = `echo "$release" | grep "VERSION ="`; - $version =~ s/\s*$//; - $version =~ s/^\s*//; - $version =~ tr/\.//; - $version =~ s/[^0-9]*([0-9]+).*/$1/; - $os = $os . $version; - - # Append service level - $version = `echo "$release" | grep "LEVEL ="`; - $version =~ s/\s*$//; - $version =~ s/^\s*//; - $version =~ tr/\.//; - $version =~ s/[^0-9]*([0-9]+).*/$1/; - $os = $os . 'sp' . $version; - } elsif (grep(/Red Hat Enterprise Linux Server/, @lines)) { - $os = 'rhel'; - $version = $lines[0]; - $version =~ tr/\.//; - $version =~ s/([A-Za-z\s\(\)]+)//g; - $os = $os . $version; + my $releaseInfo = `ssh -qo ConnectTimeout=2 $ipAddr "ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; + $rc = $? >> 8; + if ( $rc == 0 ) { + $osVer = buildOsVersion( $callback, $releaseInfo, 'all' ); + } else { + if ( $callback ) { + # We have a callback so we can write an error message. + if ( $rc == 255 ) { + my $rsp; + push @{$rsp->{data}}, "Unable to communicate with $activeSystem at $ipAddr using ssh."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } else { + my $rsp; + push @{$rsp->{data}}, "Received an unexpected ssh return code $rc from $activeSystem at $ipAddr."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + } } - return xCAT::zvmUtils->trimStr($os); + return $osVer; +} + +#------------------------------------------------------- + +=head3 stripHeader + + Description : Scans an input string to find only + lines that match a specified header string + and return an array of those lines without + the prefix. + Arguments : Multi-line string to scan + String to match + Returns : Array of matching lines. + Example : my @lines = stripHeader( $releaseInfo, '^/etc/os-release:' ); + ($ver) = stripHeader( $releaseInfo, "^$locEtcUnitedLinux:" ); + +=cut + +#------------------------------------------------------- +sub stripHeader { + my ( $inputLines, $matchString ) = @_; + my @outputLines; + + my @lines = split( /\n/, $inputLines ); + foreach my $line ( @lines ){ + if ( $line =~ m/$matchString/ ) { + $line =~ s/$matchString//g; + chomp $line; + if ( $line eq '' ) { next } + push @outputLines, $line; + } + } + + return @outputLines; +} + + +#------------------------------------------------------- + +=head3 buildOsVersion + + Description : Build the OS version string from the + provided data files. + + Note: Only RHEL and SLES are supported + z/VM xCAT supported distributions. + This routine has code for other distributions + supported by common xCAT. If z/VM + ever supports another distribution + then we need to verify that the code + gets the correct version information. + Arguments : Callback (or undef) + Operating system data file(s) output + Type of data to return: + 'all' - $os$ver.$rel + 'all_comma' - $os,$ver.$rel + 'os' - $os + 'version' - $ver + 'release' - $rel + '' - $os$ver + Returns : Operating system name & version as a + single word. + Example : my $osVer = buildOsVersion( $callback, $releaseInfo, $type ); + +=cut + +#------------------------------------------------------- +sub buildOsVersion { + my $callback = shift; + my $releaseInfo = shift; + my $type = shift; + + my $os = 'unknown'; # OS indicator, e.g. "rhel", "SLES" + my $ver = ''; # Version indicator, e.g. "7.1", + my $version = ''; + my $rel = ''; + my $line = ''; + my @lines; + + # Test strings that were used when we added the original support. These + # strings are what we used to simulate the other common Linux distros. + #$releaseInfo = "$locEtcSuseRelease:SUSE Linux Enterprise Server 11 (s390x)\n$locEtcSuseRelease:VERSION = 11\n$locEtcSuseRelease:PATCHLEVEL = 4.32"; + #$releaseInfo = "$locEtcDebianVersion:1.0\n$locEtcIssue:debian release"; + #$releaseInfo = "$locEtcUnitedLinux:2.1a"; + #$releaseInfo = "$locEtcLsbRelease:Ubuntu\n$locEtcLsbRelease: DISTRIB_ID=Ubuntu\n$locEtcLsbRelease: DISTRIB_RELEASE=4.1"; + + my @relOut = split('\n', $releaseInfo); + if ( grep( /^$locEtcOsRelease:/, @relOut ) ) { + my $version = ''; + my $version_id; + my $id; + my $id_like; + my $name; + my $prettyname; + my $verrel = ''; + + my @text = stripHeader( $releaseInfo, "^$locEtcOsRelease:" ); + foreach my $line ( @text ) { + if ( $line =~ /^\s*VERSION=\"?([0-9\.]+).*/ ) { + $version = $1; + } + if($line =~ /^\s*VERSION_ID=\"?([0-9\.]+).*/){ + $version_id = $1; + } + + if ( $line =~ /^\s*Base release\s?([0-9\.]+).*/ ) { + $version = $1; + $id = 'BASE'; + } + + if ( $line =~ /^\s*ID=\"?([0-9a-z\_\-\.]+).*/ ) { + $id = $1; + } + if ( $line =~ /^\s*ID_LIKE=\"?([0-9a-z\_\-\.]+).*/ ) { + $id_like = $1; + } + + if ( $line =~ /^\s*NAME=\"?(.*)/ ) { + $name = $1; + } + if($line =~ /^\s*PRETTY_NAME=\"?(.*)/){ + $prettyname = $1; + } + } + + $os = $id; + if ( !$os and $id_like ) { + $os = $id_like; + } + + $verrel = $version; + if ( !$verrel and $version_id ) { + $verrel = $version_id; + } + + if( !$name and $prettyname ){ + $name = $prettyname; + } + + # Note: xcat::Utils->osver() sets this value with an 's' but zvm.pm + # does not use it. So for now, we don't set 's'. + #if ( $os =~ /rhel/ and $name =~ /Server/i ) { + # # $os = "rhels"; + #} + + if ( $verrel =~ /([0-9]+)\.?(.*)/ ) { + $ver = $1; + $rel = $2; + } + } elsif ( grep( /^$locEtcRedhatRelease:/, @relOut ) ) { + my @text = stripHeader( $releaseInfo, "^$locEtcRedhatRelease:" ); + my $line = $text[0]; + chomp( $line ); + $os = "rh"; + my $verrel = $line; + $ver = $line; + + if ( $type ) { + $verrel =~ s/[^0-9]*([0-9.]+).*/$1/; + ($ver,$rel) = split /\./, $verrel; + } else { + $ver=~ tr/\.//; + $ver =~ s/[^0-9]*([0-9]+).*/$1/; + } + + if ( $line =~ /AS/ ) { $os = 'rhas' } + elsif ( $line =~ /ES/ ) { $os = 'rhes' } + elsif ( $line =~ /WS/ ) { $os = 'rhws' } + elsif ( $line =~ /Server/ ) { + if ( $type ) { + $os = 'rhel'; + # Note: xcat::Utils->osver() sets this value with an 's' but zvm.pm + # does not use it. So for now, we don't set 's'. + #$os = 'rhels'; + } else { + $os = 'rhserver'; + } + } elsif ( $line =~ /Client/ ) { + if ( $type ) { + $os = 'rhel'; + } else { + $os = 'rhclient'; + } + } elsif ( grep( /$locEtcFedoraRelease:/, @relOut ) ) { $os = 'rhfc' } + } elsif ( grep( /^$locEtcSuseRelease:/, @relOut ) ) { + my @lines = stripHeader( $releaseInfo, "^$locEtcSuseRelease:" ); + if ( grep /SLES|Enterprise Server/, @lines ) { $os = "sles" } + if ( grep /SLEC/, @lines ) { $os = "slec" } + $ver = $lines[0]; + $ver =~ tr/\.//; + $ver =~ s/[^0-9]*([0-9]+).*/$1/; + + $rel = $lines[2]; + $rel =~ tr/\.//; + $rel =~ s/[^0-9]*([0-9]+).*/$1/; + } elsif ( grep( /^$locEtcUnitedLinux:/, @relOut ) ) { + # Note: Not a z/VM xCAT supported distribution. + # If we ever support this then we need to verify this code + # gets the correct version information. + ($ver) = stripHeader( $releaseInfo, "^$locEtcUnitedLinux:" ); + $os = "ul"; + $ver =~ tr/\.//; + $ver =~ s/[^0-9]*([0-9]+).*/$1/; + } elsif ( grep( /$locEtcLsbRelease:/, @relOut ) ) { + # Ubuntu release + my @text = stripHeader( $releaseInfo, "^$locEtcLsbRelease:" ); + chomp( @text ); + my $distrib_id = ''; + my $distrib_rel = ''; + + foreach ( @text ) { + if ( $_ =~ /^\s*DISTRIB_ID=(.*)$/ ) { + $distrib_id = $1; # last DISTRIB_ID value in file used + } elsif ( $_ =~ /^\s*DISTRIB_RELEASE=(.*)$/ ) { + $distrib_rel = $1; # last DISTRIB_RELEASE value in file used + } + } + + if ( $distrib_id =~ /^(Ubuntu|"Ubuntu")\s*$/ ) { + $os = "ubuntu"; + + if ( $distrib_rel =~ /^(.*?)\s*$/ ) { # eliminate trailing blanks, if any + $distrib_rel = $1; + } + if ( $distrib_rel =~ /^"(.*?)"$/ ) { # eliminate enclosing quotes, if any + $distrib_rel = $1; + } + $ver = $distrib_rel; + } + } elsif ( grep( /^$locEtcDebianVersion:/, @relOut ) ) { + # Debian release + if ( grep( /^$locEtcIssue:/, @relOut ) ) { + ($line) = stripHeader( $releaseInfo, "^$locEtcIssue:" ); + if ( $line =~ /debian.*/i ) { + $os = "debian"; + ($ver) = stripHeader( $releaseInfo, "^$locEtcDebianVersion:" ); + } + } + } + + my $outString = ''; + if ( $type eq 'all_comma' ) { + if ( $rel ne "") { + $outString = "$os,$ver.$rel"; + } else { + $outString = "$os,$ver"; + } + } elsif ( $type eq 'all' ) { + if ( $rel ne "") { + $outString = "$os$ver.$rel"; + } else { + $outString = "$os$ver"; + } + } elsif ( $type eq 'os' ) { + $outString = $os; + } elsif ( $type eq 'version' ) { + $outString = $ver; + } elsif ( $type eq 'release' ) { + $outString = $os; + } else { + $outString = "$os$ver"; + } + + return $outString; } #------------------------------------------------------- @@ -1406,14 +1975,13 @@ sub getOsVersion { Node Returns : zFCP device info Example : my $info = xCAT::zvmUtils->getZfcpInfo($user, $node); - + =cut #------------------------------------------------------- sub getZfcpInfo { - # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1421,14 +1989,22 @@ sub getZfcpInfo { } # Get zFCP device info - my $info = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/lszfcp -D"`; + #my $info = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/lszfcp -D"`; + my $cmd = "$sudo /sbin/lszfcp -D"; + my $info = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + # will not check for connection errors here my @zfcp = split("\n", $info); if (!$info || $info =~ m/No zfcp support/i || $info =~ m/No fcp devices found/i) { return; } # Get SCSI device and their attributes - my $scsi = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/lsscsi"`; + #my $scsi = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/lsscsi"`; + $cmd = "$sudo /usr/bin/lsscsi"; + my $scsi = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $info ) == -1) { + return $info; + } $info = ""; my @args; @@ -1441,16 +2017,16 @@ sub getZfcpInfo { foreach (@zfcp) { @args = split(" ", $_); - $id = $args[1]; + $id = $args[1]; @args = split("/", $args[0]); $device = $args[0]; - $wwpn = $args[1]; - $lun = $args[2]; + $wwpn = $args[1]; + $lun = $args[2]; # Make sure WWPN and LUN do not have 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); # Find the device name $tmp = `echo "$scsi" | egrep -i $id`; @@ -1458,7 +2034,12 @@ sub getZfcpInfo { chomp($tmp); # Find the size in MiB - $size = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/sg_readcap $tmp" | egrep -i "Device size:"`; + #$size = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/sg_readcap $tmp" | egrep -i "Device size:"`; + my $cmd = $sudo . ' /usr/bin/sg_readcap ' . $tmp . ' | egrep -i Device size:'; + $size = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $size ) == -1) { + return $size; + } $size =~ s/Device size: //g; @args = split(",", $size); $size = xCAT::zvmUtils->trimStr($args[1]); @@ -1479,19 +2060,19 @@ sub getZfcpInfo { Returns : 1 Node exists 0 Node does not exists Example : my $out = xCAT::zvmUtils->isHypervisor($node); - + =cut #------------------------------------------------------- sub isHypervisor { # Get inputs - my ($class, $node) = @_; + my ( $class, $node ) = @_; # Look in 'zvm' table - my $tab = xCAT::Table->new("hypervisor", -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( "hypervisor", -create => 1, -autocommit => 0 ); - my @results = $tab->getAllAttribsWhere("node like '%" . $node . "%'", 'type'); + my @results = $tab->getAllAttribsWhere( "node like '%" . $node . "%'", 'type' ); foreach (@results) { # Return 'TRUE' if given node is in the table @@ -1503,6 +2084,37 @@ sub isHypervisor { return 0; } +#------------------------------------------------------- + +=head3 isOSVerSupported + + Description : Determine if the specified OS version supported. + Arguments : OS version string (e.g. RHEL6, RHEL6.1, SLES11sp2) + Returns : 1 - version is supported + 0 - version is not supported + Example : my $supported = xCAT::zvmUtils->isOSVerSupported( $os ); + +=cut + +#------------------------------------------------------- +sub isOSVerSupported { + my ( $class, $osVer ) = @_; + + # Keep just the OS distro name and the version, ie. drop any release info. + $osVer = lc( $osVer ); + if ( $osVer =~ /([a-z]+[0-9]+)/ ) { + $osVer = $1; + } + + # Check against our list of supported versions. + if ( $supportedVersions{$osVer} ) { + return 1; + } else { + return 0; + } +} + + #------------------------------------------------------- =head3 getSudoer @@ -1512,18 +2124,17 @@ sub isHypervisor { Returns : Sudoer user name Sudo keyword Example : my ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); - + =cut #------------------------------------------------------- sub getSudoer { - # Get inputs - my ($class) = @_; + my ( $class ) = @_; # Use sudo or not on zHCP my @propNames = ('username'); - my $propVals = xCAT::zvmUtils->getTabPropsByKey('passwd', 'key', 'sudoer', @propNames); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'passwd', 'key', 'sudoer', @propNames ); my $sudo = "sudo"; my $user = $propVals->{'username'}; @@ -1549,12 +2160,12 @@ sub getSudoer { Returns : vdev An address which is free to use -1 No free address is left Example : my $vdev = xCAT::zvmUtils->getFreeAddress($user, $node, $type); - + =cut #------------------------------------------------------- sub getFreeAddress { - my ($class, $user, $node, $type) = @_; + my ( $class, $user, $node, $type ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1566,43 +2177,47 @@ sub getFreeAddress { my $freeAddressHex = sprintf('%04X', $freeAddress); # All device type names in VM, do not contain CPU - my $deviceTypesVm = 'CONS|CTCA|DASD|FCP|GRAF|LINE|MSGD|OSA|PRT|PUN|RDR|SWCH|TAPE'; - + #my $deviceTypesVm = 'CONS|CTCA|DASD|FCP|GRAF|LINE|MSGD|OSA|PRT|PUN|RDR|SWCH|TAPE'; + my $deviceTypesVm = '^CONS|^CTCA|^DASD|^FCP|^GRAF|^LINE|^MSGD|^OSA|^PRT|^PUN|^RDR|^SWCH|^TAPE'; # All device type names in user directory, do not contain CPU my $deviceTypesUserDir = 'CONSOLE|MDISK|NICDEF|SPOOL|RDEVICE'; + # Search for all address that is in use my $allUsedAddr; if ($type eq 'vmcp') { - # When the node is up, vmcp can be used - $allUsedAddr = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v all | awk '$1 ~/^($deviceTypesVm)/ {print $2}' | sort"`; + #$allUsedAddr = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v all | awk '\$1 ~/^($deviceTypesVm)/ {print \$2}' | sort"`; + my $cmd = $sudo . '/sbin/vmcp q v all | egrep "' . $deviceTypesVm . '"'; + my $allUsedAddr = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $allUsedAddr ) == -1) { + return -1; + } + $allUsedAddr = `echo '$allUsedAddr' | awk '\$1 ~/^($deviceTypesVm)/ {print \$2}' | sort`; } else { - # When the node is down, use zHCP to get its user directory entry # Get HCP my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userID my $userId = $propVals->{'userid'}; # Get user directory entry - my $userDirEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"'`; + my $userDirEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`; # Get profile if user directory entry include a profile if ($userDirEntry =~ "INCLUDE ") { - my $profileName = `cat $userDirEntry | awk '$1 ~/^(INCLUDE)/ {print $2}`; + my $profileName = `cat $userDirEntry | awk '\$1 ~/^(INCLUDE)/ {print \$2}'`; $profileName = xCAT::zvmUtils->trimStr($profileName); $userDirEntry .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $profileName"`; } # Get all defined device address - $allUsedAddr = `cat $userDirEntry | awk '$1 ~/^($deviceTypesUserDir)/ {print $2}' | sort`; - + $allUsedAddr = `cat $userDirEntry | awk '\$1 ~/^($deviceTypesUserDir)/ {print \$2}' | sort`; # Get all linked device address - $allUsedAddr .= `cat $userDirEntry | awk '$1 ~/^(LINK)/ {print $4}' | sort`; + $allUsedAddr .= `cat $userDirEntry | awk '\$1 ~/^(LINK)/ {print \$4}' | sort`; } # Loop to get the lowest free address @@ -1628,12 +2243,12 @@ sub getFreeAddress { node Returns : In nanoseconds for used CPU time Example : my $out = xCAT::zvmUtils->getUsedCpuTime($hcp, $node); - + =cut #------------------------------------------------------- sub getUsedCpuTime { - my ($class, $user, $hcp, $node) = @_; + my ( $class, $user, $hcp , $node ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -1669,30 +2284,33 @@ sub getUsedCpuTime { Node Returns : Running time Example : my $out = xCAT::zvmUtils->getUpTime($user, $node); - + =cut #------------------------------------------------------- sub getUpTime { - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo uptime"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo uptime"`; + my $cmd = "$sudo uptime"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } $out = xCAT::zvmUtils->trimStr($out); $out =~ /.*up +(?:(\d+) days?,? +)?(\d+):(\d+),.*/; my $uptime; if (!$1 && !$2) { - # Special case for less than 1 hour, will display X min $out =~ /.*up +(\d+) min,.*/; $uptime = "0 days $3 min"; } elsif (!$1) { - # Special case for less than 1 day, will display X hr X min $uptime = "0 days $2 hr $3 min"; } else { @@ -1710,24 +2328,22 @@ sub getUpTime { Arguments : Bytes Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromByte($bytes); - + =cut #------------------------------------------------------- sub getSizeFromByte { - my ($class, $bytes) = @_; + my ( $class, $bytes ) = @_; - my $size = ($bytes) / (1024 * 1024); - if ($size > (1024 * 5)) { + my $size = ($bytes)/(1024*1024); + if ($size > (1024*5)) { $size = ($size / 1024); - # If the size > 5G, will use G to represent - $size = sprintf("%.1f", $size); + $size = sprintf("%.1f",$size); $size = $size . 'G'; } else { - # If the size < 5G, will use M to represent - $size = sprintf("%d", $size); + $size = sprintf("%d",$size); $size = $size . 'M'; } @@ -1743,7 +2359,7 @@ sub getSizeFromByte { Arguments : Node Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromCyl($cyl); - + =cut #------------------------------------------------------- @@ -1751,7 +2367,7 @@ sub getSizeFromCyl { my ($class, $cyl) = @_; my $bytes = ($cyl * 737280); - my $size = xCAT::zvmUtils->getSizeFromByte($bytes); + my $size = xCAT::zvmUtils->getSizeFromByte($bytes); return ($size); } @@ -1764,15 +2380,15 @@ sub getSizeFromCyl { Arguments : Page Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromPage($page); - + =cut #------------------------------------------------------- sub getSizeFromPage { - my ($class, $page) = @_; + my ( $class, $page ) = @_; my $bytes = ($page * 4096); - my $size = xCAT::zvmUtils->getSizeFromByte($bytes); + my $size = xCAT::zvmUtils->getSizeFromByte($bytes); return ($size); } @@ -1787,7 +2403,7 @@ sub getSizeFromPage { zHCP Returns : Total CPU count Example : my $out = xCAT::zvmCPUtils->getLparCpuTotal($user, $hcp); - + =cut #------------------------------------------------------- @@ -1814,7 +2430,7 @@ sub getLparCpuTotal { zHCP Returns : Used CPU count Example : my $out = xCAT::zvmCPUtils->getLparCpuUsed($user, $hcp); - + =cut #------------------------------------------------------- @@ -1841,7 +2457,7 @@ sub getLparCpuUsed { zHCP Returns : Model of this CEC Example : my $out = xCAT::zvmCPUtils->getCecModel($user, $hcp); - + =cut #------------------------------------------------------- @@ -1868,12 +2484,12 @@ sub getCecModel { zHCP Returns : Vendor of this CEC Example : my $out = xCAT::zvmCPUtils->getCecVendor($user, $hcp); - + =cut #------------------------------------------------------- sub getCecVendor { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1895,7 +2511,7 @@ sub getCecVendor { zHCP Returns : Name & version of this hypervisor Example : my $out = xCAT::zvmCPUtils->getHypervisorInfo($user, $hcp); - + =cut #------------------------------------------------------- @@ -1922,9 +2538,9 @@ sub getHypervisorInfo { Description : Get the total physical memory of this LPAR Arguments : User (root or non-root) zHCP - Returns : Total physical memory + Returns : Total physical memory Example : my $out = xCAT::zvmCPUtils->getLparMemoryTotal($user, $hcp); - + =cut #------------------------------------------------------- @@ -1949,9 +2565,9 @@ sub getLparMemoryTotal { Description : Get the offline physical memory of this LPAR Arguments : User (root or non-root) zHCP - Returns : Offline physical memory + Returns : Offline physical memory Example : my $out = xCAT::zvmCPUtils->getLparMemoryOffline($user, $hcp); - + =cut #------------------------------------------------------- @@ -1976,9 +2592,9 @@ sub getLparMemoryOffline { Description : Get the used physical memory of this LPAR Arguments : User (root or non-root) zHCP - Returns : Used physical memory + Returns : Used physical memory Example : my $out = xCAT::zvmCPUtils->getLparMemoryUsed($user, $hcp); - + =cut #------------------------------------------------------- @@ -1993,8 +2609,8 @@ sub getLparMemoryUsed { my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Performance_Info_Query " | grep "Used memory pages:"`; my @results = split(':', $out); - my $page = xCAT::zvmUtils->trimStr($results[1]); - my $size = xCAT::zvmUtils->getSizeFromPage($page); + my $page = xCAT::zvmUtils->trimStr( $results[1] ); + my $size = xCAT::zvmUtils->getSizeFromPage( $page ); return ($size); } @@ -2003,13 +2619,13 @@ sub getLparMemoryUsed { =head3 getDiskPoolUsed - Description : Get the used size of specified disk pool + Description : Get the used size of specified disk pool Arguments : User (root or non-root) zHCP Disk pool - Returns : Used size of specified disk pool + Returns : Used size of specified disk pool Example : my $out = xCAT::zvmCPUtils->getDiskPoolUsed($user, $hcp, $diskpool); - + =cut #------------------------------------------------------- @@ -2031,9 +2647,8 @@ sub getDiskPoolUsed { foreach (@lines) { @results = split(' ', $_); if ($results[1] =~ '^9336') { - # Change the format from blocks (512 byte) to cylinder (737280) - my $cyls = ($results[3] * 512) / (737280); + my $cyls = ($results[3] * 512)/(737280); $used += $cyls; } elsif ($results[1] =~ '^3390') { $used += $results[3]; @@ -2047,13 +2662,13 @@ sub getDiskPoolUsed { =head3 getDiskPoolFree - Description : Get the free size of specified disk pool + Description : Get the free size of specified disk pool Arguments : User (root or non-root) - zHCP + zHCP Disk pool - Returns : Free size of specified disk pool + Returns : Free size of specified disk pool Example : my $out = xCAT::zvmCPUtils->getDiskPoolFree($user, $hcp, $diskpool); - + =cut #------------------------------------------------------- @@ -2075,9 +2690,8 @@ sub getDiskPoolFree { foreach (@lines) { @results = split(' ', $_); if ($results[1] =~ '^9336') { - # Change the format from blocks (512 byte) to cylinder (737280) - my $cyls = ($results[3] * 512) / (737280); + my $cyls = ( $results[3] * 512 ) / ( 737280 ); $free += $cyls; } elsif ($results[1] =~ '^3390') { $free += $results[3]; @@ -2093,23 +2707,23 @@ sub getDiskPoolFree { Description : Get the max memory of a given node Arguments : User (root or non-root) - zHCP + zHCP Node Returns : Max memory Example : my $maxMemory = xCAT::zvmCPUtils->getMaxMemory($user, $hcp, $node); - + =cut #------------------------------------------------------- sub getMaxMemory { - my ($class, $user, $hcp, $node) = @_; + my ($class, $user, $hcp , $node) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $userId = xCAT::zvmCPUtils->getUserId($user, $node); + my $userId = xCAT::zvmCPUtils->getUserId( $user, $node ); # Query the maximum memory allowed in user directory entry my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k STORAGE_MAXIMUM"`; @@ -2118,6 +2732,436 @@ sub getMaxMemory { return ($results[1]); } + +#------------------------------------------------------- + +=head3 handlePowerUp + + Description : Handle power up of nodes whose IP information + may change on power up. The routine will weed out + non-s390x architectures and do processing for nodes whose + status in the zvm table is "POWER_UP=1". + Arguments : Callback + Nodes that are to be handled. + Returns : None + Example : xCAT::zvmUtils->handlePowerUp( $callback, $nodes, \%args ); + +=cut + +#------------------------------------------------------- +sub handlePowerUp { + my ( $class, $callback, $nodes, $argsRef ) = @_; + my %propHash; # Property hash used to fill in various tables + my %sysInfo; # Hash to hold system information + my %args = %$argsRef; # Command arguments, verbose is one such operand + + my $nodetypeTab = xCAT::Table->new('nodetype'); + my $nodetypeHash = $nodetypeTab->getNodesAttribs( $nodes, ['arch'] ); + my $zvmTab = xCAT::Table->new('zvm'); + my $zvmHash = $zvmTab->getNodesAttribs( $nodes, ['hcp', 'status', 'userid'] ); + + foreach my $node ( keys %{$nodetypeHash} ) { + next if ( $nodetypeHash->{$node}->[0]->{'arch'} !~ 's390x' ); + my $status = $zvmHash->{$node}->[0]->{'status'}; + if ( $status =~ 'POWER_UP=1' ) { + my $userid = $zvmHash->{$node}->[0]->{'userid'}; + my $rc = xCAT::zvmUtils->findAccessIP( $callback, + $userid, + $zvmHash->{$node}->[0]->{'hcp'}, + \%sysInfo, + \%args ); + if ( $rc == 0 ) { + # Got what we needed so we can update tables with the current information. + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Updating xCAT tables with IP information for $node:\n" . + " ip: $sysInfo{$userid}{'ipAddr'}, hostname: $sysInfo{$userid}{'hostname'}\n". + " macAddr: $sysInfo{$userid}{'macAddr'}, switch: $sysInfo{$userid}{'switch'}\n". + " Network Adapter VDEV: $sysInfo{$userid}{'adapterAddr'}"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + + substr( $sysInfo{$userid}{'macAddr'}, 10, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 8, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 6, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 4, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 2, 0 ) = ':'; + %propHash = ( + 'disable' => 0, + 'interface' => $sysInfo{$userid}{'vdev'}, + 'mac' => $sysInfo{$userid}{'macAddr'}, + ); + xCAT::zvmUtils->setNodeProps( 'mac', $node, \%propHash ); + + %propHash = ( + 'disable' => 0, + 'hostnames' => $sysInfo{$userid}{'hostname'}, + 'ip' => $sysInfo{$userid}{'ipAddr'}, + ); + xCAT::zvmUtils->setNodeProps( 'hosts', $node, \%propHash ); + + $status =~ s/POWER_UP=1/POWER_UP=0/g; + %propHash = ( + 'status' => $status, + ); + xCAT::zvmUtils->setNodeProps( 'zvm', $node, \%propHash ); + + my $out = `/opt/xcat/sbin/makehosts $node 2>&1`; + if ( $out ne '' ) { + my $rsp; + push @{$rsp->{data}}, "'makehosts' failed for $node. " . + "'makehosts' response: $out"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + + # Inform OpenStack of the hostname. + if ( -e $locOpenStackUpdateName ) { + # Call the python change instance name command + my $renamed = 0; + my $args = "--nodename $node --hostname $sysInfo{$userid}{'hostname'}"; + xCAT::MsgUtils->message( "S", "Invoking $locOpenStackUpdateName $args" ); + my $out = `python $locOpenStackUpdateName $args`; + xCAT::MsgUtils->message( "S", "Returned from OpenStack node name update for $node with $out" ); + if ( $out ) { + if ( $out =~ m/^Success!/ ) { + $renamed = 1; + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Renamed the OpenStack instance."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + } + + if ( !$renamed ) { + # Return an information message but do not fail the nodeset with an error + # message. This error is minor to the overall operation. + my $rsp; + push @{$rsp->{data}}, "Unable to update the OpenStack node name: $node"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + } else { + my $rsp; + push @{$rsp->{data}}, "Did not find sufficient IP information for $node."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } + } +} + + +#------------------------------------------------------- + +=head3 findAccessIP + + Description : Obtain TCP/IP and hostname information about + the virtual machine related to the node. + Arguments : Callback in case we want to produce a message + Virtual machine userid + ZHCP node handling the z/VM host node + Hash to contain information on the system + Command invocation argument hash. 'ipfilter' and + 'verbose' keys are used in this routine. + SUDO issuer ($sudoer) or 'root' user for the SSH into ZHCP. + This parameter avoids the call to getsudoer() to obtain + the sudo and sudoer value. 'root' user does not use 'sudo'. + Returns : 0 - No error + non-zero - Error detected or filtered out as a usable IP address + Example : $rc = findAccessIP( $callback, 'linux001', $hcp + \%sysInfo, \%args ); + +=cut + +#------------------------------------------------------- +sub findAccessIP +{ + my $class = shift; # Perl class + my $callback = shift; # Callback for messaging + my $activeSystem = shift; # Userid of the system being queried + my $hcp = shift; # HCP + my $sysInfoRef = shift; # Hash reference for system IP information + my $argsRef = shift; # Command arguments, verbose, ipFilter + my %args = %$argsRef; # Access hash for easier reference + my $sudoer = shift; # SUDO issuer + + my %adapter; # Adapter hash info, used mainly for verbose processing + my @failureInfo; # Information on IP contact failures + my $hostname = ''; # Hostname from the target node + my @hostnameCmds = ( # List of host name resolution commands to be issued in a virtual OS. + 'hostname --fqdn', + 'hostname --long', + 'hostname', + ); + my %ips; # Hash of IP info obtained from the various calls + my $out; # Output buffer work area + my $rc; # Return code + my $rsp; # Message work buffer + my $sudo = ''; # Assume we are not going to use SUDO on ZHCP call. + + # Use sudo or not + if ( $sudoer eq '' ) { + # Looks in the passwd table for a key = sudoer. + ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); + } elsif ( $sudoer ne 'root' ) { + # Non-root user specified so we will invoke 'sudo' on the SSH call to ZHCP. + $sudo = 'sudo'; + } + + # Get the list of IP addresses currently in use by the virtual machine. + $out = `ssh -q $sudoer\@$hcp $sudo /opt/zhcp/bin/smcli "Virtual_Network_Adapter_Query_Extended -T '$activeSystem' -k 'image_device_number=*'"`; + $rc = $? >> 8; + if ($rc == 255) { + push @{$rsp->{data}}, "Unable to communicate with the zhcp system: $hcp"; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_findAccessIP; + } elsif ( $rc == 1 ) { + my ( $smcliRC, $smcliRS, $smcliDesc ); + my $errorOut = `echo "$out" | egrep -i '(^ Return Code: |^ Reason Code: |^ Description: )'`; + my @errorLines = split( "\n", $errorOut ); + foreach my $errorLine ( @errorLines ) { + if ( $errorLine =~ /^ Return Code: / ) { + ($smcliRC) = $errorLine =~ /^ Return Code: (.*)/; + } + if ( $errorLine =~ /^ Reason Code: / ) { + ($smcliRS) = $errorLine =~ /^ Reason Code: (.*)/; + } + if ( $errorLine =~ /^ Description: / ) { + ($smcliDesc) = $errorLine =~ /^ Description: (.*)/; + } + } + if (( $smcliRC == 212 ) && ( $smcliRS == 8 )) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, the virtual machine does not have any network adapters."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + push @failureInfo, "The virtual machine does not have any network adapters"; + goto FINISH_findAccessIP; + } else { + push @{$rsp->{data}}, "An unexpected return code $smcliRC and reason code $smcliRS was received from " . + "the zhcp server $hcp for an smcli Virtual_Network_Adapter_Query_Extended " . + "request. Error description: $smcliDesc"; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_findAccessIP; + } + + } elsif ( $rc != 0 ) { + push @{$rsp->{data}}, "An unexpected return code $rc was received from " . + "the zhcp server $hcp for an smcli Virtual_Network_Adapter_Query_Extended " . + "request. SMAPI servers may be unavailable. " . + "Received response: $out"; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_findAccessIP; + } + + my $filteredOut = `echo "$out" | egrep -i '(adapter_address=|adapter_status=|mac_address=|mac_ip_address=|mac_ip_version=|lan_name=|lan_owner=)'`; + my @ipOut = split( "\n", $filteredOut ); + my ($adapterAddr, $adapterStatus, $ipAddr, $ipVersion, $lanName, $lanOwner, $macAddr ); + foreach my $ipLine ( @ipOut ) { + if ( $ipLine =~ /adapter_address=/ ) { + ($adapterAddr) = $ipLine =~ /adapter_address=(.*)/; + $adapter{$adapterAddr}{'ipCnt'} = 0; + $adapter{$adapterAddr}{'macCnt'} = 0; + ($lanName, $lanOwner) = ''; + } elsif ( $ipLine =~ /adapter_status=/ ) { + ($adapterStatus) = $ipLine =~ /adapter_status=(.*)/; + $adapter{$adapterAddr}{'status'} = $adapterStatus; + } elsif ( $ipLine =~ /^lan_name=/ ) { + ($lanName) = $ipLine =~ /lan_name=(.*)/; + #} elsif ( $ipLine =~ /^lan_owner=/ ) { + # ($lanOwner) = $ipLine =~ /lan_owner=(.*)/; + } elsif ( $ipLine =~ /^mac_address=/ ) { + ($macAddr) = $ipLine =~ /mac_address=(.*)/; + ($ipVersion, $ipAddr) = ''; + $adapter{$adapterAddr}{'macCnt'} += 1; + } elsif ( $ipLine =~ /^mac_ip_address=/ ) { + ($ipAddr) = $ipLine =~ /mac_ip_address=(.*)/; + } elsif ( $ipLine =~ /^mac_ip_version=/ ) { + ($ipVersion) = $ipLine =~ /mac_ip_version=(.*)/; + } + if ( $ipVersion ne '' and $ipAddr ne '' ) { + $ips{$ipAddr}{'adapterAddr'} = $adapterAddr; + $ips{$ipAddr}{'ipVersion'} = $ipVersion; + $ips{$ipAddr}{'lanName'} = $lanName if $lanName; + #$ips{$ipAddr}{'lanOwner'} = $lanOwner if $lanOwner; + $ips{$ipAddr}{'macAddr'} = $macAddr; + $adapter{$adapterAddr}{'ipCnt'} += 1; + } + } + + my $adapterCnt = keys %adapter; + push @{$rsp->{data}}, "For userid $activeSystem, $adapterCnt adapters were detected." if ( $args{'verbose'} == 1 ); + foreach $adapterAddr ( keys %adapter ) { + if ( $adapter{$adapterAddr}{'macCnt'} > 0 ) { + if ( $adapter{$adapterAddr}{'ipCnt'} != 0 ) { + push @{$rsp->{data}}, " Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs with $adapter{$adapterAddr}{'ipCnt'} associated IP address(es)" if ( $args{'verbose'} == 1 ); + } else { + push @{$rsp->{data}}, " Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs but no associated IP addresses" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs but no associated IP addresses"; + } + } elsif ( $adapter{$adapterAddr}{'status'} eq '00' ) { + push @{$rsp->{data}}, " Adapter $adapterAddr: Not coupled" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: Not coupled"; + } elsif ( $adapter{$adapterAddr}{'status'} eq '01' ) { + push @{$rsp->{data}}, " Adapter $adapterAddr: Not active" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: Not active"; + } else { + push @{$rsp->{data}}, " Adapter $adapterAddr: No MACs with associated IP addresses" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: No MACs with associated IP addresses"; + } + } + xCAT::MsgUtils->message( "I", $rsp, $callback ) if ( $args{'verbose'} == 1 ); + + if ( !%ips ) { + if ( keys %adapter eq 0 ) { + push @failureInfo, "No adapters were found"; + } else { + push @failureInfo, "No IP addresses were detected"; + } + + goto FINISH_findAccessIP; + } + + # Contact the IPs to see which one, if any, lets us in. + foreach $ipAddr ( keys %ips ) { + $rc = 0; + if ( $ips{$ipAddr}{'ipVersion'} eq '6' ) { + # IPv6 is not currently supported. + next; + } + + if ( $args{'ipfilter'} ) { + if ( $ipAddr !~ m/$args{'ipfilter'}/i ) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, filtered out IP: $ipAddr"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + push @failureInfo, "$ipAddr - filtered out by the specified IP filter"; + next; + } + } + + # Ping the address to see if it is responsive. + if ( $ips{$ipAddr}{'ipVersion'} eq '4' ) { + $out = `ping -c1 $ipAddr`; + $rc = $?; + } elsif ( $ips{$ipAddr}{'ipVersion'} eq '6' ) { + # IPv6 is not currently supported. + $rc = 3; + #$out = `ping6 -c1 $ipAddr`; + #$rc = $?; + next; + } else { + push @{$rsp->{data}}, "Userid $activeSystem, IP address: $ipAddr has an unsupported IP version: $ips{$ipAddr}"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + next; + } + if ( $rc != 0 or $out !~ / 0% packet loss,/ ) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, ping failed for $ipAddr"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + push @failureInfo, "$ipAddr - Unable to ping, rc: $rc"; + next; + } + + # SSH into the system to verify access. + my $line = `ssh -q $ipAddr pwd 2>/dev/null`; + $rc = $? >> 8; + if ( $rc == 255 ) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, Unable to ssh into: $ipAddr"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + push @failureInfo, "$ipAddr - Unable to ssh into system"; + next; + } + + # Determine the fully qualified host name to use. + my $fqdn = ''; + + # Attempt to get it from the system's OS using one of a number of commands. + # If we can't get a fully qualified DNS names (with periods) then accept the short name. + my $shortName = ''; + foreach my $cmd ( @hostnameCmds ) { + my $hostname = `ssh -q $ipAddr $cmd 2>/dev/null`; + my $rc = $? >> 8; + if ( $rc == 255 ) { + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "For userid $activeSystem, Unable to ssh into: $ipAddr"; + xCAT::MsgUtils->message("I", $rsp, $callback); + push @failureInfo, "$ipAddr - Unable to ssh into system"; + last; + } + last; + } elsif ( $rc == 0 ) { + # verify the hostname is a fully qualified name. + chomp $hostname; + if ( $hostname =~ /\./ ) { + $fqdn = $hostname; + last; + } else { + $hostname =~ s/^\s+//; + if (( $hostname ne '' ) && ( $hostname !~ /\s/ )) { + # Single word returned without periods. Must be a short name. + $shortName = $hostname; + } + # Keep looking for a long name but we will remember the short name + # in case we can't find a long name. + } + } else { + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "For userid $activeSystem, \'$cmd\' returned, rc: $rc."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + } + if (( $shortName eq '' ) && ( $rc != 255 )) { + push @failureInfo, "$ipAddr - hostname query commands failed to return required information"; + } + + if (( $fqdn eq '' ) && ( $shortName ne '' )) { + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "For userid $activeSystem, Unable to determine the fully qualified domain name but found a short name. The short name will be used."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + $fqdn = $shortName; + } + + # Found the last piece of info needed. + # Note: If hostname is empty because we could not find it, the ultimate response will + # be a failing return code. + $sysInfoRef->{$activeSystem}{'adapterAddr'} = $ips{$ipAddr}{'adapterAddr'};; + $sysInfoRef->{$activeSystem}{'ipAddr'} = $ipAddr; + $sysInfoRef->{$activeSystem}{'ipVersion'} = $ips{$ipAddr}; + $sysInfoRef->{$activeSystem}{'macAddr'} = $ips{$ipAddr}{'macAddr'}; + $sysInfoRef->{$activeSystem}{'switch'} = $ips{$ipAddr}{'lanName'}; + $sysInfoRef->{$activeSystem}{'hostname'} = $fqdn; + last; + } + +FINISH_findAccessIP: + if ( $sysInfoRef->{$activeSystem}{'hostname'} ) { + $rc = 0; + } else { + $rc = 1; + if ( @failureInfo ) { + my $rsp; + my $failureString = join( ',\n', @failureInfo ); + push @{$rsp->{data}}, "Unable to access $activeSystem for the following reasons:\n$failureString"; + xCAT::MsgUtils->message("I", $rsp, $callback); + + $failureString = join( ', ', @failureInfo ); + xCAT::zvmUtils->printSyslog( "findAccessIP() Unable to access $activeSystem for the following reasons: $failureString" ); + } + } + return $rc; +} + + #------------------------------------------------------- =head3 smapi4xcat @@ -2128,12 +3172,12 @@ sub getMaxMemory { Returns : 0 EXEC not found 1 EXEC found Example : my $out = xCAT::zvmUtils->smapi4xcat($user, $hcp); - + =cut #------------------------------------------------------- sub smapi4xcat { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -2151,7 +3195,7 @@ sub smapi4xcat { # Levels 621 and greater support SMAPI EXEC my $out = `ssh $user\@$hcp "$sudo $dir/smcli Query_API_Functional_Level -T $hcpUserId"`; $out = xCAT::zvmUtils->trimStr($out); - if (!($out =~ m/V6.2/i || $out =~ m/V6.1/i || $out =~ m/V5.4/i)) { + if ( !($out =~ m/V6.2/i || $out =~ m/V6.1/i || $out =~ m/V5.4/i) ) { return 1; } @@ -2159,7 +3203,7 @@ sub smapi4xcat { # EXEC found if RC = 8 and RS = 3002 $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c ''"`; $out = xCAT::zvmUtils->trimStr($out); - if ($out =~ m/Return Code: 8/i && $out =~ m/Reason Code: 3002/i) { + if ( $out =~ m/Return Code: 8/i && $out =~ m/Reason Code: 3002/i ) { return 1; } @@ -2179,12 +3223,12 @@ sub smapi4xcat { Cpu Returns : If successful, return file path. Otherwise, return -1 Example : my $out = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount); - + =cut #------------------------------------------------------- sub generateUserEntryFile { - my ($class, $userId, $password, $memorySize, $privilege, $profileName, $cpuCount) = @_; + my ( $class, $userId, $password, $memorySize, $privilege, $profileName, $cpuCount, $ipl) = @_; # If a file of this name already exists, just override it my $file = "/tmp/$userId.txt"; @@ -2192,8 +3236,13 @@ sub generateUserEntryFile { # Add additional CPUs my $i; - for ($i = 1 ; $i < $cpuCount ; $i++) { - $content = $content . sprintf("CPU %02X\n", $i) + for ( $i = 1; $i < $cpuCount; $i++ ) { + $content = $content.sprintf("CPU %02X\n", $i); + } + + if ( $ipl != "") { + # the caller need validate this $ipl param + $content = $content.sprintf("IPL %04s\n", $ipl); } unless (open(FILE, ">$file")) { @@ -2215,12 +3264,12 @@ sub generateUserEntryFile { zHCP Returns : SSI cluster name Example : my $out = xCAT::zvmUtils->querySSI($user, $hcp); - + =cut #------------------------------------------------------- sub querySSI { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -2248,25 +3297,27 @@ sub querySSI { Command to execute Returns : Output returned from executing command Example : my $out = xCAT::zvmUtils->rExecute($user, $node, $cmd); - + =cut #------------------------------------------------------- sub rExecute { - my ($class, $user, $node, $cmd) = @_; + my ( $class, $user, $node, $cmd ) = @_; my $out; my $sudo = "sudo"; - if ($user eq "root") { + if ($user eq "root") { # Just execute the command if root - $out = `ssh $user\@$node "$cmd"`; + #$out = `ssh $user\@$node "$cmd"`; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return $out; } - # Encapsulate command in single quotes $cmd = "'" . $cmd . "'"; - $out = `ssh $user\@$node "$sudo sh -c $cmd"`; + #$out = `ssh $user\@$node "$sudo sh -c $cmd"`; + $cmd = "$sudo sh -c $cmd"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return $out; } @@ -2279,12 +3330,12 @@ sub rExecute { zHCP Returns : List of known FCP devices Example : my %devices = xCAT::zvmUtils->getUsedFcpDevices($user, $zhcp); - + =cut #------------------------------------------------------- sub getUsedFcpDevices { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; # Directory where zFCP pools are my $pool = "/var/opt/zhcp/zfcp"; @@ -2310,7 +3361,7 @@ sub getUsedFcpDevices { # Push used or allocated devices into hash if ($args[6]) { - $usedDevices{ uc($args[6]) } = 1; + $usedDevices{uc($args[6])} = 1; } } @@ -2325,58 +3376,70 @@ sub getUsedFcpDevices { Arguments : Sudoer user name Sudo keyword zHCP hostname + Install root directory Local directory to remotely mount Mount access ('ro' for read only, 'rw' for read write) Directory as known to zHCP (out) Returns : 0 - Mounted, or zHCP and MN are on the same system 1 - Mount failed - Example : establishMount( $callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$provMethod", "ro", \$remoteDeployDir ); - + Example : establishMount( $callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $provMethod, "ro", \$remoteDeployDir ); + =cut #------------------------------------------------------- sub establishMount { - # Get inputs - my ($class, $callback, $sudoer, $sudo, $hcp, $localDir, $access, $mountedPt) = @_; + my ($class, $callback, $sudoer, $sudo, $hcp, $installRoot, $localDir, $access, $mountedPt) = @_; my $out; # If the target system is not on this system then establish the NFS mount point. - my $hcpIP = xCAT::NetworkUtils->getipaddr($hcp); - if (!defined $hcpIP) { - xCAT::zvmUtils->printLn($callback, "(Error) Unable to obtain the IP address of the hcp node"); + my $hcpIP = xCAT::NetworkUtils->getipaddr( $hcp ); + if (! defined $hcpIP) { + xCAT::zvmUtils->printLn( $callback, "(Error) Unable to obtain the IP address of the hcp node" ); return 1; } - my $masterIp = xCAT::TableUtils->get_site_Master(); + # Get internal master IP if xcat and zhcp are on a 10. network + my $masterIp = xCAT::TableUtils->get_site_attribute("internalmaster"); + + # Use "internalmaster", if it is set. Otherwise, look at "master" property. if (!defined $masterIp) { - xCAT::zvmUtils->printLn($callback, "$hcp: (Error) Unable to obtain the management node IP address from the site table"); - return 1; + + $masterIp = xCAT::TableUtils->get_site_attribute("master"); + if (! defined $masterIp) { + xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to obtain the management node IP address from the site table" ); + return 1; + } } if ($masterIp eq $hcpIP) { - # xCAT MN and zHCP are on the same box and will use the same directory without the need for an NFS mount. - $$mountedPt = $localDir; + $$mountedPt = "$installRoot/$localDir"; } else { - # Determine the hostname for this management node my $masterHostname = Sys::Hostname::hostname(); - if (!defined $masterHostname) { - + if (! defined $masterHostname) { # For some reason, the xCAT MN's hostname is not known. We pass along the IP address instead. $masterHostname = $masterIp; } - xCAT::zvmUtils->printSyslog("establishMount() Preparing the NFS mount point on zHCP ($hcpIP) to xCAT MN $masterHostname($masterIp) for $localDir"); + $$mountedPt = "/mnt/$masterHostname$installRoot/$localDir"; + + # If the mount point already exists then return because we are done. + my $rc = `ssh $sudoer\@$hcp "$sudo mount | grep $$mountedPt > /dev/null; echo \\\$?"`; + if ($rc == 0) { + return 0; + } + + xCAT::zvmUtils->printSyslog( "establishMount() Preparing the NFS mount point on zHCP ($hcpIP) to xCAT MN $masterHostname($masterIp) for $localDir" ); # Prepare the staging mount point on zHCP, if they need to be established - $$mountedPt = "/mnt/$masterHostname$localDir"; - my $rc = `ssh $sudoer\@$hcp "$sudo mkdir -p $$mountedPt && mount -t nfs -o $access $masterIp:$localDir $$mountedPt; echo \\\$?"`; + $rc = `ssh $sudoer\@$hcp "$sudo mkdir -p $$mountedPt && mount -t nfs -o $access $masterIp:/$localDir $$mountedPt; echo \\\$?"`; - # Return code = 0 (mount succeeded) or 32 (mount already exists) - if ($rc != '0' && $rc != '32') { - xCAT::zvmUtils->printLn($callback, "$hcp: (Error) Unable to establish zHCP mount point: $$mountedPt"); + # Return code = 0 (mount succeeded) + if ($rc != '0') { + xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to establish zHCP mount point: $$mountedPt" ); + xCAT::zvmUtils->printSyslog( "establishMount() Unable to establish zHCP mount point: $$mountedPt, rc: $rc" ); return 1; } } @@ -2388,16 +3451,18 @@ sub establishMount { =head3 getFreeRepoSpace - Description : Get the free space of image repository under /install + Description : Get the free space of image repository under /install. Arguments : Node - Returns : The available space for /install + Returns : The available space for /install (e.g. "2.1G "). + The value is returned as a perl string (e.g. "0 ") to + avoid perl returning null instead of "0" in the case + of no space available. Example : my $free = getFreeRepoSpace($callback, $node); - + =cut #------------------------------------------------------- sub getFreeRepoSpace { - # Get inputs my ($class, $user, $node) = @_; @@ -2408,18 +3473,22 @@ sub getFreeRepoSpace { # Check if node is the management node my @entries = xCAT::TableUtils->get_site_attribute("master"); - my $master = xCAT::zvmUtils->trimStr($entries[0]); - my $ip = xCAT::NetworkUtils->getipaddr($node); + my $master = xCAT::zvmUtils->trimStr($entries[0]); + my $ip = xCAT::NetworkUtils->getipaddr($node); $ip = xCAT::zvmUtils->trimStr($ip); my $mn = 0; if ($master eq $ip) { - # If the master IP and node IP match, then it is the management node my $out = `$sudo /bin/df -h /install | sed 1d`; - # causing problems on other platforms $out =~ s/\h+/ /g;$out =~ s/\h+/ /g; + # Comment out the horizontal whitespace escape, it was causing "Restarting xCATd Unrecognized escape" + # $out =~ s/\h+/ /g; + my @results = split(' ', $out); - return ($results[3]); + if ( $results[3] eq "0" ) { + $results[3] = "0M"; + } + return $results[3]; } return; @@ -2442,19 +3511,18 @@ sub getFreeRepoSpace { - LUN - Size requested - Owner - - Tag + - Tag Returns : Results hash including: - Return code (0 = Success, -1 = Failure) - zFCP device (if one is requested) - WWPN - LUN Example : my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $header, $user, $hcp, $pool, $criteriaRef); - + =cut #------------------------------------------------------- sub findAndUpdatezFcpPool { - # Get inputs my ($class, $callback, $header, $user, $hcp, $pool, $criteriaRef) = @_; @@ -2470,22 +3538,22 @@ sub findAndUpdatezFcpPool { # Directory where FCP disk pools are on zHCP my $zfcpDir = "/var/opt/zhcp/zfcp"; - my %results = ('rc' => -1); # Default to error + my %results = ('rc' => -1); # Default to error # Extract criteria - my %criteria = %$criteriaRef; - my $status = defined($criteria{status}) ? $criteria{status} : ""; + my %criteria = %$criteriaRef; + my $status = defined($criteria{status}) ? $criteria{status} : ""; my $fcpDevice = defined($criteria{fcp}) ? $criteria{fcp} : ""; - my $wwpn = defined($criteria{wwpn}) ? $criteria{wwpn} : ""; - my $lun = defined($criteria{lun}) ? $criteria{lun} : ""; - my $size = defined($criteria{size}) ? $criteria{size} : ""; - my $owner = defined($criteria{owner}) ? $criteria{owner} : ""; - my $tag = defined($criteria{tag}) ? $criteria{tag} : ""; + my $wwpn = defined($criteria{wwpn}) ? $criteria{wwpn} : ""; + my $lun = defined($criteria{lun}) ? $criteria{lun} : ""; + my $size = defined($criteria{size}) ? $criteria{size} : ""; + my $owner = defined($criteria{owner}) ? $criteria{owner} : ""; + my $tag = defined($criteria{tag}) ? $criteria{tag} : ""; # Check required arguments: pool, status # If you do not know what to update, why update! if (!$pool && !$status) { - return \%results; + return \%results; } # Check status @@ -2495,17 +3563,17 @@ sub findAndUpdatezFcpPool { } # Check FCP device syntax - if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f]/i)) { + if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f;]/i)) { xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid FCP channel address $fcpDevice."); return \%results; } - # Check WWPN and LUN syntax - if ($wwpn && ($wwpn =~ /[^0-9a-f;"]/i)) { - xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid world wide portname $wwpn."); + # Owner must be specified if status is used + if ($status =~ m/used/i && !$owner) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Owner must be specified if status is used." ); return \%results; - } if ($lun && ($lun =~ /[^0-9a-f]/i)) { - xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid logical unit number $lun."); + } elsif ($status =~ m/free/i && $owner) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Owner must not be specified if status is free." ); return \%results; } @@ -2513,15 +3581,13 @@ sub findAndUpdatezFcpPool { my $originSize = $size; if ($size) { if ($size =~ m/G/i) { - # Convert to MegaBytes $size =~ s/\D//g; $size = int($size) * 1024 } elsif ($size =~ m/M/i || !$size) { - # Do nothing } else { - xCAT::zvmUtils->printLn($callback, "$header: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)."); + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)." ); return \%results; } } @@ -2535,20 +3601,27 @@ sub findAndUpdatezFcpPool { # Make sure WWPN and LUN do not have 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + + # Check WWPN and LUN syntax + if ( $wwpn && ($wwpn =~ /[^0-9a-f;]/i) ) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid world wide portname $wwpn." ); + return \%results; + } if ( $lun && ($lun =~ /[^0-9a-f]/i) ) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid logical unit number $lun." ); + return \%results; + } } # Find disk pool (create one if non-existent) my $out; if (!(`ssh $user\@$hcp "$sudo test -d $zfcpDir && echo Exists"`)) { - # Create pool directory $out = `ssh $user\@$hcp "$sudo mkdir -p $zfcpDir"`; } # Find if disk pool exists if (!(`ssh $user\@$hcp "$sudo test -e $zfcpDir/$pool.conf && echo Exists"`)) { - # Return if xCAT is expected to find a FCP device, but no disk pool exists. xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP storage pool does not exist"); return \%results; @@ -2556,11 +3629,10 @@ sub findAndUpdatezFcpPool { # Find a free disk in the pool # FCP devices are contained in /var/opt/zhcp/zfcp/.conf - my $range = ""; + my $range = ""; my $sizeFound = "*"; my @info; if (!$useWwpnLun) { - # Find a suitable pair of WWPN and LUN in device pool based on requested size # Sample pool configuration file: # #status,wwpn,lun,size,range,owner,channel,tag @@ -2574,12 +3646,10 @@ sub findAndUpdatezFcpPool { # Check if the size is sufficient. Convert size into MB. if ($info[3] =~ m/G/i) { - # Convert to MegaBytes $info[3] =~ s/\D//g; $info[3] = int($info[3]) * 1024 } elsif ($info[3] =~ m/M/i) { - # Do nothing $info[3] =~ s/\D//g; } else { @@ -2589,42 +3659,41 @@ sub findAndUpdatezFcpPool { # Find optimal disk based on requested size if ($sizeFound && $info[3] >= $size && $info[3] < $sizeFound) { $sizeFound = $info[3]; - $wwpn = $info[1]; - $lun = $info[2]; - $range = $info[4]; + $wwpn = $info[1]; + $lun = $info[2]; + $range = $info[4]; } elsif (!$sizeFound && $info[3] >= $size) { $sizeFound = $info[3]; - $wwpn = $info[1]; - $lun = $info[2]; - $range = $info[4]; + $wwpn = $info[1]; + $lun = $info[2]; + $range = $info[4]; } } # Do not continue if no devices can be found - if (!$wwpn && !$lun) { + if (!$wwpn || !$lun) { xCAT::zvmUtils->printLn($callback, "$header: (Error) A suitable device of $size" . "M or larger could not be found"); return \%results; } } else { - # Find given WWPN and LUN. Do not continue if device is used my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$wwpn,$lun"`; chomp($select); + if (!$select) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) zFCP device 0x$wwpn/0x$lun could not be found in zFCP pool $pool"); + return \%results; + } @info = split(',', $select); if ($size) { if ($info[3] =~ m/G/i) { - # Convert to MegaBytes $info[3] =~ s/\D//g; $info[3] = int($info[3]) * 1024 - } elsif ($info[3] =~ m/M/i) { - + } else { # Do nothing $info[3] =~ s/\D//g; - } else { - next; } # Do not continue if specified device does not have enough capacity @@ -2638,15 +3707,98 @@ sub findAndUpdatezFcpPool { $range = $info[4]; } - # If there are multiple paths, take the 1st one - # Handle multi-pathing in postscript because autoyast/kickstart does not support it. - my $origWwpn = $wwpn; - if ($wwpn =~ m/;/i) { - @info = split(';', $wwpn); - $wwpn = xCAT::zvmUtils->trimStr($info[0]); + xCAT::zvmUtils->printLn($callback, "$header: Found FCP device 0x$wwpn/0x$lun"); + + if ( ($status =~ m/used/i) && ($fcpDevice =~ /^auto/i) ) { + # select an eligible FCP device + $fcpDevice = xCAT::zvmUtils->selectFcpDevice($callback, $header, $user, $hcp, $fcpDevice, $range, $owner); + if (!$fcpDevice) { + return \%results; + } + } elsif ($status =~ m/free/i) { + # Owner and FCP channel make no sense when status is free + $fcpDevice = ""; + $owner = ""; } - xCAT::zvmUtils->printLn($callback, "$header: Found FCP device 0x$wwpn/0x$lun"); + # Mark WWPN and LUN as used, free, or reserved and set the owner/channel appropriately + # This config file keeps track of the owner of each device, which is useful in nodeset + $size = $size . "M"; + my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$lun"`; + chomp($select); + if ($select) { + @info = split(',', $select); + + if (!$info[3]) { + $info[3] = $size; + } + + # Do not update if WWPN/LUN pair is specified but the pair does not exist + if (!($info[1] =~ m/$wwpn/i)) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun does not exists"); + return \%results; + } + + # Entry order: status,wwpn,lun,size,range,owner,channel,tag + # The following are never updated: wwpn, lun, size, and range + my $update = "$status,$info[1],$info[2],$info[3],$info[4],$owner,$fcpDevice,$tag"; + my $expression = "'s#" . $select . "#" .$update . "#i'"; + $out = `ssh $user\@$hcp "$sudo sed --in-place -e $expression $zfcpDir/$pool.conf"`; + } else { + # Insert device entry into file + $out = `ssh $user\@$hcp "$sudo echo \"$status,$wwpn,$lun,$size,,$owner,$fcpDevice,$tag\" >> $zfcpDir/$pool.conf"`; + } + + # Generate results hash + %results = ( + 'rc' => 0, + 'fcp' => $fcpDevice, + 'wwpn' => $wwpn, + 'lun' => $lun + ); + return \%results; +} + +#------------------------------------------------------- + +=head3 selectFcpDevice + + Description : Select an eligible FCP device for attaching a zFCP device to a node + Arguments : Message header + User (root or non-root) + zHCP + candidate FCP devices or auto + FCP device range + zFCP device owner + Returns : selected FCP device or empty if no one is selected + Example : my $fcpDevice = xCAT::zvmUtils->selectFcpDevice($callback, $header, $user, $hcp, $fcpDevice, $range, $owner); + +=cut + +#------------------------------------------------------- +sub selectFcpDevice { + # Get inputs + my ($class, $callback, $header, $user, $hcp, $fcpDevice, $range, $owner) = @_; + + # Determine if sudo is used + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where executables are on zHCP + my $dir = "/opt/zhcp/bin"; + + # Directory where FCP disk pools are on zHCP + my $zfcpDir = "/var/opt/zhcp/zfcp"; + + my %results = ('rc' => -1); # Default to error + + # Check FCP device syntax + if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f]/i)) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid FCP channel address $fcpDevice."); + return; + } # Find a free FCP device based on the given range if ($fcpDevice =~ m/^auto/i) { @@ -2662,18 +3814,16 @@ sub findAndUpdatezFcpPool { } if (!$found) { - # If the node has an eligible FCP device, use it my @deviceList = xCAT::zvmUtils->getDedicates($callback, $user, $owner); foreach (@deviceList) { - # Check if this devide is eligible (among the range specified for disk $lun) - @info = split(' ', $_); + my @info = split(' ', $_); my $candidate = $info[2]; foreach (@ranges) { ($min, $max) = split('-', $_); if (hex($candidate) >= hex($min) && hex($candidate) <= hex($max)) { - $found = 1; + $found = 1; $fcpDevice = uc($candidate); last; @@ -2688,7 +3838,6 @@ sub findAndUpdatezFcpPool { } if (!$found) { - # If the node has no eligible FCP device, find a free one for it. my %usedDevices = xCAT::zvmUtils->getUsedFcpDevices($user, $hcp); @@ -2696,10 +3845,9 @@ sub findAndUpdatezFcpPool { $hcpUserId =~ tr/a-z/A-Z/; # Find a free FCP channel - $out = `ssh $user\@$hcp "$sudo $dir/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; - my @devices = split("\n", $out); - for (my $i = 0 ; $i < @devices ; $i++) { - + my $out = `ssh $user\@$hcp "$sudo $dir/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; + my @devices = split( "\n", $out ); + for (my $i = 0; $i < @devices; $i++) { # Extract the device number and status $fcpDevice = $devices[$i]; $fcpDevice =~ s/^FCP device number:(.*)/$1/; @@ -2714,7 +3862,6 @@ sub findAndUpdatezFcpPool { # Only look at free FCP devices if ($fcpStatus =~ m/free/i) { - # If the device number is within the specified range, exit out of loop # Range: 3B00-3C00;4B00-4C00;5E12-5E12 foreach (@ranges) { @@ -2722,7 +3869,7 @@ sub findAndUpdatezFcpPool { if (hex($fcpDevice) >= hex($min) && hex($fcpDevice) <= hex($max)) { $fcpDevice = uc($fcpDevice); - # Used found FCP channel if not in use or allocated + # Use found FCP channel if not in use or allocated if (!$usedDevices{$fcpDevice}) { $found = 1; last; @@ -2742,14 +3889,14 @@ sub findAndUpdatezFcpPool { # Do not continue if no FCP channel is found if (!$found) { xCAT::zvmUtils->printLn($callback, "$header: (Error) A suitable FCP channel could not be found"); - return \%results; + return; } } # If there are multiple devices (multipathing), take the 1st one if ($fcpDevice) { if ($fcpDevice =~ m/;/i) { - @info = split(';', $fcpDevice); + my @info = split(';', $fcpDevice); $fcpDevice = xCAT::zvmUtils->trimStr($info[0]); } @@ -2759,43 +3906,7 @@ sub findAndUpdatezFcpPool { } } - # Mark WWPN and LUN as used, free, or reserved and set the owner/channel appropriately - # This config file keeps track of the owner of each device, which is useful in nodeset - $size = $size . "M"; - my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$lun" | grep -i "$wwpn"`; - chomp($select); - if ($select) { - @info = split(',', $select); - - if (!$info[3]) { - $info[3] = $size; - } - - # Do not update if WWPN/LUN pair is specified but the pair does not exist - if (!($info[1] =~ m/$wwpn/i)) { - xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun does not exists"); - return \%results; - } - - # Entry order: status,wwpn,lun,size,range,owner,channel,tag - # The following are never updated: wwpn, lun, size, and range - my $update = "$status,$info[1],$info[2],$info[3],$info[4],$owner,$fcpDevice,$tag"; - my $expression = "'s#" . $select . "#" . $update . "#i'"; - $out = `ssh $user\@$hcp "$sudo sed --in-place -e $expression $zfcpDir/$pool.conf"`; - } else { - - # Insert device entry into file - $out = `ssh $user\@$hcp "$sudo echo \"$status,$origWwpn,$lun,$size,,$owner,$fcpDevice,$tag\" >> $zfcpDir/$pool.conf"`; - } - - # Generate results hash - %results = ( - 'rc' => 0, - 'fcp' => $fcpDevice, - 'wwpn' => $wwpn, - 'lun' => $lun - ); - return \%results; + return $fcpDevice; } #------------------------------------------------------- @@ -2809,14 +3920,14 @@ sub findAndUpdatezFcpPool { LUN Returns : Storage pool where zFCP device resides Example : my $pool = xCAT::zvmUtils->findzFcpDevicePool($user, $hcp, $wwpn, $lun); - + =cut #------------------------------------------------------- sub findzFcpDevicePool { # Get inputs - my ($class, $user, $hcp, $wwpn, $lun) = @_; + my ( $class, $user, $hcp, $wwpn, $lun ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -2827,11 +3938,11 @@ sub findzFcpDevicePool { my $zfcpDir = "/var/opt/zhcp/zfcp"; # Find the pool that contains the SCSI/FCP device - my @pools = split("\n", `ssh $user\@$hcp "$sudo grep -i -l \"$wwpn,$lun\" $zfcpDir/*.conf"`); + my @pools = split("\n", `ssh $user\@$hcp "$sudo grep -i -l \\\"$wwpn,$lun\\\" $zfcpDir/*.conf"`); my $pool = ""; if (scalar(@pools)) { $pool = basename($pools[0]); - $pool =~ s/\.[^.]+$//; # Do not use extension + $pool =~ s/\.[^.]+$//; # Do not use extension } return $pool; @@ -2848,15 +3959,15 @@ sub findzFcpDevicePool { WWPN LUN Returns : Architecture of node - Example : my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($user, $hcp, $wwpn, $lun); - + Example : my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($user, $hcp, $pool, $wwpn, $lun); + =cut #------------------------------------------------------- sub findzFcpDeviceAttr { # Get inputs - my ($class, $user, $hcp, $pool, $wwpn, $lun) = @_; + my ( $class, $user, $hcp, $pool, $wwpn, $lun ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -2868,7 +3979,7 @@ sub findzFcpDeviceAttr { # Find the SCSI/FCP device # Entry order: status,wwpn,lun,size,range,owner,channel,tag - my @info = split("\n", `ssh $user\@$hcp "$sudo grep \"$wwpn,$lun\" $zfcpDir/$pool.conf"`); + my @info = split("\n", `ssh $user\@$hcp "$sudo grep -i \"$wwpn,$lun\" $zfcpDir/$pool.conf"`); my $entry = $info[0]; chomp($entry); @@ -2881,13 +3992,13 @@ sub findzFcpDeviceAttr { @info = split(',', $entry); %attrs = ( 'status' => $info[0], - 'wwpn' => $info[1], - 'lun' => $info[2], - 'size' => $info[3], - 'range' => $info[4], - 'owner' => $info[5], - 'fcp' => $info[6], - 'tag' => $info[7] + 'wwpn' => $info[1], + 'lun' => $info[2], + 'size' => $info[3], + 'range' => $info[4], + 'owner' => $info[5], + 'fcp' => $info[6], + 'tag' => $info[7] ); return \%attrs; @@ -2904,22 +4015,21 @@ sub findzFcpDeviceAttr { DHCP is used or not (0 or 1) Returns : NIC, device channel, and layer (2 or 3) Example : my ($nic, $channel, $layer) = xCAT::zvmUtils->findUsablezHcpNetwork($user, $hcp, $userId, $dhcp); - + =cut #------------------------------------------------------- sub findUsablezHcpNetwork { - - # Get inputs - my ($class, $user, $hcp, $userId, $dhcp) = @_; + # Get inputs + my ( $class, $user, $hcp, $userId, $dhcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $nic = ''; # Usuable NIC on zHCP - my $channel = ''; # Device channel where NIC is attached + my $nic = ''; # Usuable NIC on zHCP + my $channel = ''; # Device channel where NIC is attached my $layer; my $i; my @words; @@ -2936,26 +4046,23 @@ sub findUsablezHcpNetwork { my @lines = split('\n', $out); # Go through each line - for ($i = 0 ; $i < @lines ; $i++) { - + for ($i = 0; $i < @lines; $i++) { # Go through each network device attached to zHCP foreach (@hcpNetworks) { # If network device is found if ($lines[$i] =~ m/ $_/i) { - # Get network layer $layer = xCAT::zvmCPUtils->getNetworkLayer($user, $hcp, $_); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() NIC:$_ layer:$layer"); # If template using DHCP, layer must be 2 if ((!$dhcp && $layer != 2) || (!$dhcp && $layer == 2) || ($dhcp && $layer == 2)) { - # Save network name $nic = $_; # Get network virtual address - @words = split(' ', $lines[$i]); + @words = split(' ', $lines[$i]); # Get virtual address (channel) # Convert subchannel to decimal @@ -2964,7 +4071,6 @@ sub findUsablezHcpNetwork { xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() Candidate found NIC:$nic channel:$channel layer:$layer"); return ($nic, $channel, $layer); } else { - # Go to next network available $nic = ''; } @@ -2974,7 +4080,6 @@ sub findUsablezHcpNetwork { # If network device is not found if (!$nic) { - # Check for user profile my $profileName = `echo "$userEntry" | grep "INCLUDE"`; if ($profileName) { @@ -2984,31 +4089,28 @@ sub findUsablezHcpNetwork { my $userProfile = xCAT::zvmUtils->getUserProfile($user, $hcp, $words[1]); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() $userProfile"); - # Get the NICDEF statement containing the HCP network + # Get the NICDEF statement $out = `echo "$userProfile" | grep "NICDEF"`; @lines = split('\n', $out); # Go through each line - for ($i = 0 ; $i < @lines ; $i++) { - + for ($i = 0; $i < @lines; $i++) { # Go through each network device attached to zHCP foreach (@hcpNetworks) { # If network device is found if ($lines[$i] =~ m/ $_/i) { - # Get network layer $layer = xCAT::zvmCPUtils->getNetworkLayer($user, $hcp, $_); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() NIC:$_ layer:$layer"); # If template using DHCP, layer must be 2 if ((!$dhcp && $layer != 2) || (!$dhcp && $layer == 2) || ($dhcp && $layer == 2)) { - # Save network name $nic = $_; # Get network virtual address - @words = split(' ', $lines[$i]); + @words = split(' ', $lines[$i]); # Get virtual address (channel) # Convert subchannel to decimal @@ -3017,15 +4119,859 @@ sub findUsablezHcpNetwork { xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() Candidate found NIC:$nic channel:$channel layer:$layer"); return ($nic, $channel, $layer); } else { - # Go to next network available $nic = ''; } } - } # End of foreach - } # End of for - } # End of if + } # End of foreach + } # End of for + } # End of if } return; } + +#------------------------------------------------------- + +=head3 printInfo + + Description : Print a long string to stdout as information without checking anything + Arguments : String + Returns : Nothing + Example : xCAT::zvmUtils->printInfo($callback, $str); + +=cut + +#------------------------------------------------------- +sub printInfo { + + # Get inputs + my ( $class, $callback, $str ) = @_; + + # Print string + my $rsp; + + $rsp->{data}->[0] = "$str"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + + return; +} + +#------------------------------------------------------- + +=head3 getSpecialCloneInfo + + Description : Look in the /var/opt/xcat/doclone.txt file (if exists) and return a + hash of the keys and values found that match the image name parameter + Arguments : User friendly image name + + Returns : hash of keys and values found or empty hash + Example : my %cloneinfo = xCAT::zvmUtils->getSpecialCloneInfo($callback, $user, $node); + if (%cloneinfo) { + %cloneinfo has at least one key + } else { + %cloneinfo empty, no keys + } + +=cut + +#------------------------------------------------------- +sub getSpecialCloneInfo { + + # Get inputs + my ( $class, $imagename ) = @_; + my %cloneInfoHash = (); # create empty hash + + # Directory where doclone.txt is + my $dir = '/var/opt/xcat/'; + my $clonefile = 'doclone.txt'; + my $out; + + # Does the file exist? If so read and look for this image name + if (-e "$dir$clonefile") { + # look for this image name and ignore case + $out = `cat $dir$clonefile | grep -v '^\\s*/[*]'| grep -v '^\\s*[*]'| grep -E -i -w "IMAGE_NAME[[:blank:]]*=[[:blank:]]*$imagename"`; + + my @lines = split( '\n', $out ); + my $count = @lines; + + # loop for any lines found + for (my $i=0; $i < $count; $i++) { + # Break out each key=value; item + my @parms = split( ';', $lines[$i]); + my $parmcount = @parms; + # get the key and value for this item, store in hash + for (my $j=0; $j < $parmcount; $j++) { + my @keyvalue = split('=', $parms[$j]); + my $key = $keyvalue[0]; + $key =~ s/^\s+|\s+$//g; # get rid of leading and trailing blanks + next if ( length( $key ) == 0 ); # Skip incorrect key=value data + + my $value = $keyvalue[1]; + $value =~ s/^\s+|\s+$//g; + next if ( length( $value ) == 0 ); # Skip incorrect key=value data + #uppercase both key and value; + $key = uc $key; + $value = uc $value; + $cloneInfoHash{ $key } = $value; + } + } + } + return (%cloneInfoHash); +} + +#------------------------------------------------------- + +=head3 pingNode + + Description : Execute a Perl ping for this node + Arguments : Node name + + Returns : "ping" if found; or "noping" (if not found) + Example : my $out = xCAT::zvmUtils->pingNode($node); + +=cut + +#------------------------------------------------------- +sub pingNode { + + # Get input node + my ( $class, $node ) = @_; + + my $timeout = 2; # how many seconds to wait for response. Default was 5 + # call system ping and max count of pings 2 + my $out = `ping -W $timeout -c 2 -q $node`; + if ($? != 0) { + # Ping failed, try to get result with execcmdonVM. + my $result = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, 'date'); + if (xCAT::zvmUtils->checkOutput( $result ) == -1) { + return $result; + } + if ($result) { + return ("ping"); + } + return ("noping"); + } + return ("ping"); +} + +#------------------------------------------------------- + +=head3 onlineZhcpPunch + + Description : Online punch device and load VMCP module on zHCP + Arguments : User (root or non-root) + zHCP + Returns : Operation results (Done/Failed) + Example : my $out = xCAT::zvmUtils->onlineZhcpPunch($user, $hcp); + +=cut + +#------------------------------------------------------- +sub onlineZhcpPunch { + + # Get input node + my ( $class, $user, $hcp ) = @_; + + my $out = ""; + my $subResp = ""; + my $rc = ""; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Online zHCP's punch + $out = `ssh $user\@$hcp "$sudo cat /sys/bus/ccw/drivers/vmur/0.0.000d/online" 2>&1`; + $rc = $? >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. + $subResp = "Failed to communicate with the zHCP system to get the punch device status"; + } elsif ( $rc != 0 ) { + # Generic failure of the command. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "onlineZhcpPunch() Failed to get the punch device status on zHCP rc: $rc, out: $out" ); + $subResp = "Failed to online the punch device on zHCP rc: $rc, out: $out"; + } + + if ( $subResp eq "" ) { + if ($out != 1) { + chomp( $out = `ssh $user\@$hcp "$sudo /sbin/cio_ignore -r 000d; /sbin/chccwdev -e 000d"`); + $rc = $? >> 8; + if ( $rc == 0 ) { + $subResp = "Done"; + } elsif ( $rc == 255 ) { + # SSH failure to communicate with zHCP. + $subResp = "Failed to communicate with the zHCP system to online the punch device"; + } else { + if ( !( $out =~ m/Done$/i ) ) { + xCAT::zvmUtils->printSyslog("onlineZhcpPunch() failed to online the zHCP's punch, cmd output: $out."); + $subResp = "Failed to online the zHCP's punch rc: $rc, out: $out"; + } + } + `ssh $user\@$hcp "$sudo which udevadm &> /dev/null && udevadm settle || udevsettle"`; + } else { + $subResp = "Done"; + } + } + return $subResp +} + +#------------------------------------------------------- +=head3 genCfgdrive + + Description : Generate a final config drive to punch + Arguments : Configure file directory + + Returns : Generated config drive file path + Example : my $out = xCAT::zvmUtils->genCfgdrive($path); + +=cut + +#------------------------------------------------------- +sub genCfgdrive { + + # Get input node + my ( $class, $cfgpath ) = @_; + my $node = basename($cfgpath); + + my $out = xCAT::zvmUtils->injectMNKey($cfgpath); + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog("genCfgdrive() Failed to generate the final cfgdrive.tgz for target node: $node, out: $out"); + return ""; + } else { + xCAT::zvmUtils->printSyslog("genCfgdrive() Successfully generated the final cfgdrive.tgz for target node: $node"); + return "$cfgpath/cfgdrive.tgz"; + } +} + +#------------------------------------------------------- +=head3 injectMNKey + + Description : Inject xCAT MN's public key to the meta_data.json for target vm + Arguments : Configure file directory + + Returns : A message indicate whether the MN's key in injected success or not + Example : my $out = xCAT::zvmUtils->injectMNKey($path); + +=cut + +#------------------------------------------------------- + +sub injectMNKey { + + # Get input node + my ( $class, $cfgpath ) = @_; + my $subResp = ""; + + if ( -e "$cfgpath/cfgdrive.tgz" ) { + system("tar -zxf $cfgpath/cfgdrive.tgz -C $cfgpath "); + } else { + $subResp = "injectMNKey() Failed to find the cfgdrive.tgz under $cfgpath for target node"; + return $subResp; + } + + # Get xcat key, store it to a hash var for later use + open(my $keyFile, '<', "/root/.ssh/id_rsa.pub"); + my $mnKey = <$keyFile>; + close($keyFile); + my @set = ('0' ..'9', 'A' .. 'F'); + my $mnKeyName = join '' => map $set[rand @set], 1 .. 8; + my %mnKeyHash = ("name" => $mnKeyName, "type" => "ssh", "data" => $mnKey,); + + # Read the file content to a variable named md_json,and close the source file + my $jsonText; + my $MDfile; + if(open($MDfile, '<', "$cfgpath/openstack/latest/meta_data.json")) { + while(<$MDfile>) { + $jsonText .= "$_"; + } + } else { + $subResp = "injectMNKey() Failed to open the meta data file for processing"; + close($MDfile); + return $subResp; + } + close($MDfile); + + # Get the public_keys from meta_data.json, if it not exist, add xCAT's key to meta_data.json directly, + # if already exist, compare if the xCAT's key is same or not with existing one, append xCAT key if not same + my $md_json = decode_json($jsonText); + if (exists $md_json->{"public_keys"}) { + my $publicKeys = $md_json->{"public_keys"}; + # Check if xCAT key already exist , append it if not exist. + foreach my $pubkey ( keys %$publicKeys ) { + if ( $publicKeys->{$pubkey} eq $mnKey ) { + last; + } + $publicKeys->{$mnKeyName} = $mnKey; + my @tkeys = $md_json->{"keys"}; + push @tkeys, {%mnKeyHash}; + #push $md_json->{"keys"}, {%mnKeyHash}; + } + } else { + # Set the public_keys and keys with xCAT's key info in meta_data.json + $md_json->{"public_keys"}->{$mnKeyName} = $mnKey; + $md_json->{"keys"}[0] = {%mnKeyHash}; + } + + # Save the changed meta_data.json to new file + open my $fh, ">", "$cfgpath/meta_data.json"; + print $fh encode_json($md_json); + close $fh; + + # Replace the meta_data.json file in original config drive with the modified one + system( "find $cfgpath/openstack -name meta_data.json -print | xargs -i cp $cfgpath/meta_data.json {}"); + `rm -f $cfgpath/meta_data.json`; + + # Tar the file generate the final one + my $oldpath=cwd(); + chdir($cfgpath); + system ( "tar -zcf cfgdrive.tgz openstack ec2"); + chdir($oldpath); + + $subResp = "Done"; + return $subResp; +} + +#------------------------------------------------------- + +=head3 execcmdthroughIUCV + + Description : Execute a command to node with IUCV client. + Arguments : User (root or non-root). + zHCP (opencloud user) + VM's userid + command [parms..] the comands and parms with the command which need to execute. + callback + + Returns : command result, if success. + if an error: + and $callback then $callback gets error message + returns with string containing (Error) and message + Example : my $out = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $command); + +=cut + +#------------------------------------------------------- +sub execcmdthroughIUCV { + my ($class, $user, $hcp, $userid, $commandwithparm, $callback) = @_; + my $result = ''; + my $rsp; + my $msg; + my $iucvpath = '/opt/zhcp/bin/IUCV'; + my $isCallback = 0; + if (defined $callback) { + $isCallback = 1; + } + $result= `ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid "\'$commandwithparm\'" 2>&1`; + + my $rc = $? >> 8; + $result = xCAT::zvmUtils->trimStr( $result ); + if ( $isCallback || $rc == 0 ){ + xCAT::zvmUtils->printSyslog("$userid: IUCV command: ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid $commandwithparm. return $rc\n $result"); + } else { + xCAT::zvmUtils->printSyslog("$userid: IUCV command: ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid $commandwithparm."); + } + if ( $rc == 0 ) { + if ($result eq ''){ + return "Done"; + } + return $result; + } elsif ( $rc == 1 ) { + $msg = "IUCV authorized error, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 2 ) { + $msg = "parameter to iucvclient error, $result"; + push @{$rsp->{data}}, $msg + } elsif ( $rc == 4 ) { + $msg = "IUCV socket error, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 8 ) { + $msg = "Command executed failed, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 16 ) { + $msg = "File Transport failed, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 32 ) { + $msg = "File Transport failed, error details $result"; + push @{$rsp->{data}}, $msg; + } + + # Error occurred + if ($isCallback){ + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + return "(Error) $msg"; +} + +#------------------------------------------------------- + +=head3 cleanIUCV + + Description : rollback IUCV to clean all the files that copy to it. + Arguments : User (root or non-root). + VM's node + VM's system + + Returns : + Example : xCAT::zvmUtils->cleanIUCV( $user, $hcp, $userid); + +=cut + +#------------------------------------------------------- +sub cleanIUCV { + my ($class, $user, $node, $os) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + my $result = ''; + my $outmsg = ''; + my $cmd = ''; + my $rc; + my $trgtiucvpath = "/usr/bin/iucvserv"; + my $trgtiucvservicepath_rh6_sl11 = "/etc/init.d/iucvserd"; + my $trgtiucvservicepath_rh7 = "/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_ubuntu16 = "/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_sl12 = "/usr/lib/systemd/system/iucvserd.service"; + + #clean iucv server file + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + xCAT::zvmUtils->printSyslog("$node: SSH to VM to clean iucv server file failed. $result"); + # Continue processing even if an error. + } + + #clean iucv server service file + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ) { + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh6_sl11 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh6_sl11 2>&1`; + } + elsif ( $os =~ m/sles12/i ) { + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_sl12 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_sl12 2>&1`; + } + elsif ( $os =~ m/rhel7/i){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh7 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh7 2>&1`; + } elsif ( $os =~ m/ubuntu16/i){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_ubuntu16 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_ubuntu16 2>&1`; + } + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + xCAT::zvmUtils->printSyslog("$node: SSH to VM to clean iucv server serivce file failed. $result"); + # Continue processing even if an error. + } + + #clean iucv server authorized file + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + xCAT::zvmUtils->printSyslog("$node: SSH to VM to clean iucv server authorized file failed. $result"); + # Continue processing even if an error. + } + + #clean iucv server service start + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"chkconfig --del iucvserd && service iucvserd stop 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node "chkconfig --del iucvserd && service iucvserd stop 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + xCAT::zvmUtils->printSyslog("$node: SSH to VM to clean iucv server serivce start failed. $result"); + # Continue processing even if an error. + } + }else{ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"systemctl disable iucvserd.service && systemctl stop iucvserd.service 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node "systemctl disable iucvserd.service && systemctl stop iucvserd.service 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + xCAT::zvmUtils->printSyslog("$node: SSH to VM to clean iucv server serivce start failed. $result"); + # Continue processing even if an error. + } + } +} + +#------------------------------------------------------- + +=head3 setsshforvm + + Description : If IUCV communication failed, try to use ssh to make communication. + Arguments : User (root or non-root). + VM's node + VM's linux system type + command [parms..] the comands and parms with the command which need to execute. + error message which is got in setup IUCV + current VM's status in zvm table + callback + + Returns : command result, if success. + if an error: + and $callback then $callback gets error message and routine returns with 1 + if no $callback then routine returns with string containing Error: and message + Example : my $out = xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + +=cut + +#------------------------------------------------------- + +sub setsshforvm { + my ($class, $user, $node, $os, $commandwithparm, $msg, $status, $callback) = @_; + my $result =''; + my $rsp; + my $isCallback = 0; + my $outmsg = ''; + my $rc; + if (defined $callback) { + $isCallback = 1; + } + + #clean IUCV server first. + $result = xCAT::zvmUtils->cleanIUCV($user, $node, $os); + if (xCAT::zvmUtils->checkOutput( $result ) == -1) { + return $result; + } + # check whether the vm can be ping, if so then set ssh to zvm table, + # to indicate that it use ssh. + my $ping = `ping -W 2 -c 2 -q $node`; + if ($? == 0) { + my $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + } + # Continue processing even if an error. + } + + if ($status){ + $status = "$status;SSH=1"; + }else{ + $status = "SSH=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + xCAT::zvmUtils->printSyslog("$node: Set SSH=1 for node $node"); + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: Set SSH=1 for node $node."); + } + return $result; + } + + # Error occurred on ping + if ($callback){ + push @{$rsp->{data}}, $msg; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + xCAT::zvmUtils->printSyslog("$node: $msg"); + return "$msg"; +} + +#------------------------------------------------------- + +=head3 execcmdonVM + + Description : Execute a command to node. + Arguments : User (root or non-root). + VM's node + command [parms..] the comands and parms with the command which need to execute. + callback + + Returns : command result, if success. + if an error: + and $callback then $callback gets error message + routine returns with string containing Error: and message + + Example : my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $commandwithparm, $callback); + +=cut + +#------------------------------------------------------- +sub execcmdonVM { + my ($class, $user, $node, $commandwithparm, $callback) = @_; + + # get HCP and z/VM userid + my @propNames = ( 'hcp', 'userid', 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + my $status = $propVals->{'status'}; + if(!(defined($status))){ + $status = ''; + } + my $hcp = $propVals->{'hcp'}; + my $userid = $propVals->{'userid'}; + my $isCallback = 0; + if (defined $callback) { + $isCallback = 1; + } + + my $result = ''; + my $outmsg = ''; + my $rsp; + my $rc; + my $msg = ''; + my $cmd = ''; + my $opnclouduserid='OPNCLOUD'; + + # Create path string + my $dest = "$user\@$node"; + my $srciucvpath = '/opt/zhcp/bin/IUCV'; + my $trgtiucvpath = "/usr/bin/iucvserv"; + my $trgtiucvservicepath_rh6_sl11 = "/etc/init.d/iucvserd"; + my $trgtiucvservicepath_rh7 = "/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_sl12 = "/usr/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_ubuntu16 = "/lib/systemd/system/iucvserd.service"; + my $authorizedfilepath = "/etc/iucv_authorized_userid"; + my $xcatuserid = `vmcp q userid | awk '{print \$1}'`; + chomp($xcatuserid); + + # Add escape for IUCV and SSH commands. + if ($commandwithparm =~ '\\\"'){ + $commandwithparm =~ s/"/\\"/g; + } + $commandwithparm =~ s/"/\\"/g; + + # For not xcat deployed or cloning node, use SSH to make communication. + if (!(defined($userid)) || !(defined($hcp)) || ($status =~ /CLONE_ONLY=1/)){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + xCAT::zvmUtils->printSyslog("$node: $cmd $outmsg"); + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + } + } + # Remove IUCV=1 if it has been set + if ($status =~ /IUCV=1/){ + $status =~ s/IUCV=1/SSH=1/g; + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } + return $result; + } + + $userid =~ tr/a-z/A-Z/; + # For normal managed nodes, ask zhcp to query the power state + if (($userid ne $xcatuserid) && !($hcp =~ /$node/) && ($hcp ne '')){ + # Get VM's power stat first, if power stat is off, return error. + my $max = 0; + while ( !$result && $max < 10 ) { + $cmd = "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q user $userid 2>/dev/null\" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/'"; + $result = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userid 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/'`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + } + return $outmsg; + } + $max++; + } + #xCAT::zvmUtils->printSyslog("$node: ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q user $userid 2>/dev/null\" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/' ##$result##"); + if ("off" =~ $result) { + my $msgText = "$node: (Error) VM $userid is powered off"; + xCAT::zvmUtils->printSyslog("$msgText"); + if ($isCallback) { + xCAT::zvmUtils->printLn( $callback, "$msgText"); + } + return "$msgText"; + } + + if (!($status =~ /SSH=1/) && !($status =~ /IUCV=1/)){ + # if zhcp direct entry does set "IUCV ANY", will set to SSH directly. + my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); + @propNames = ( 'status' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $hcp, @propNames ); + my $iucvanystatus = $propVals->{'status'}; + if (!($iucvanystatus =~ m/IUCVANY/i)){ + xCAT::zvmUtils->printSyslog("$node: zhcp's IUCVANY status is not set"); + my $out = `ssh $user\@$hcp "$::SUDO /opt/zhcp/bin/smcli Image_Query_DM -T $hcpUserId"| egrep -i "IUCV ANY"`; + if ( $out =~ m/IUCV ANY/i){ + if ($iucvanystatus){ + $iucvanystatus = "$status;IUCVANY=1"; + }else{ + $iucvanystatus = "IUCVANY=1"; + } + }else{ + if ($iucvanystatus){ + $iucvanystatus = "$status;IUCVANY=0"; + }else{ + $iucvanystatus = "IUCVANY=0"; + } + } + xCAT::zvmUtils->setNodeProp( 'zvm', $hcp, 'status', $iucvanystatus ); + xCAT::zvmUtils->printSyslog("$node: zhcp's status is $iucvanystatus"); + } + if ($iucvanystatus =~ m/IUCVANY=0/i){ + xCAT::zvmUtils->printSyslog("$node: zhcp doesn't support to make communication with IUCV, set SSH=1 for $node"); + if ($status){ + $status = "$status;SSH=1"; + }else{ + $status = "SSH=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } + } + } + + # If node userid is xcat or zhcp, only use SSH. + if (($status =~ /SSH=1/) || ($userid eq $xcatuserid) || ($hcp =~ /$node/) || ($hcp eq '')){ + # SSH=1, Use ssh to make communication. + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc == -1) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + xCAT::zvmUtils->printSyslog("$node: $cmd $outmsg"); + } + } + return $result; + }elsif ($status =~ /IUCV=1/){ + #xCAT::zvmUtils->printSyslog("$node: IUCV command: $commandwithparm"); + # IUCV=1, Use IUCV to make communication. + $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); + return $result; + } + + xCAT::zvmUtils->printSyslog("$node: VM $userid doesn't set communicate type, will set it first." ); + my $releaseInfo = `ssh -qo ConnectTimeout=2 $user\@$node "$::SUDO ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; + if (xCAT::zvmUtils->checkOutput( $releaseInfo ) == -1) { + return $releaseInfo; + } + my $os = buildOsVersion( $callback, $releaseInfo, 'all' ); + + # For the existed VMs which are deployed with SSH, will try to copy IUCV files to them. + # These VMs are not set communication type, try IUCV first and set the type after communication. + # if IUCV server doesn't exist on xcat /var/lib/sspmod, first to copy from OPNCLOUD. + if ( not (-e "$srciucvpath/iucvserv" and -e "$srciucvpath/iucvserd" + and -e "$srciucvpath/iucvserd.service" )) { + $result= `mkdir -p $srciucvpath && scp -p $user\@$hcp:$srciucvpath/iucvser* $srciucvpath 2>&1`; + $rc = $? >>8 ; + xCAT::zvmUtils->printSyslog("$node: IUCV server files doesn't exist on xcat $srciucvpath, copy from OPNCLOUD.return $rc, $result"); + if ($rc != 0) { + $msg = "Failed to copy $user\@$hcp:$srciucvpath/iucvser* to $srciucvpath. $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } + # Check whether IUCV server is installed. + $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: try to execute command through IUCV. $result return $?" ); + if ( $rc != 0 ){ + #IUCV server doesn't exist on node, copy file to it and restart service + if ($result =~ "ERROR connecting socket") { + xCAT::zvmUtils->printSyslog("$node: start to set iucv, first to copy iucv server files and start iucv server service" ); + #copy IUCV server files. + $result = `/usr/bin/scp -p $srciucvpath/iucvserv $dest:$trgtiucvpath 2>&1`; + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: /usr/bin/scp -p $srciucvpath/iucvserv $dest:$trgtiucvpath 2>&1 return $rc\n $result"); + if ($rc != 0) { + $msg = "Failed to copy $srciucvpath/iucvserv to $dest:$trgtiucvpath. $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ) { + $result = `/usr/bin/scp -p $srciucvpath/iucvserd $dest:$trgtiucvservicepath_rh6_sl11 2>&1`; + } + elsif ( $os =~ m/sles12/i ) { + $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_sl12 2>&1`; + } + elsif ( $os =~ m/rhel7/i){ + $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_rh7 2>&1`; + } elsif ( $os =~ m/ubuntu16/i){ + # Note: we should not encounter this line as we don't have ubuntu support before IUCV enablement + $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_ubuntu16 2>&1`; + } + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: /usr/bin/scp -p iucv service file return $rc $result"); + if ($rc != 0) { + $msg = "Failed to copy iucvservice file. $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + $opnclouduserid = xCAT::zvmCPUtils->getUserId($user, $hcp); + $opnclouduserid =~ tr/a-z/A-Z/; + if ($rc !=0) { + $msg = "failed to get OPNCLOUD userid. return $? \n$result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"echo -n $opnclouduserid >$authorizedfilepath\" 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node "echo -n $opnclouduserid >$authorizedfilepath" 2>&1`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc != 0) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + } + $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + + # Start service of IUCV server + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"chkconfig --add iucvserd && service iucvserd start 2>&1\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "chkconfig --add iucvserd && service iucvserd start 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc != 0) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + } + $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + }else{ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"systemctl enable iucvserd.service && systemctl start iucvserd.service 2>&1\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "systemctl enable iucvserd.service && systemctl start iucvserd.service 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSHOutput( $?, "$cmd" ); + if ($rc != 0) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: $outmsg"); + } + $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: start iucvserver service return $rc. $result"); + if ($rc == 0) { + $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); + if ($? == 0){ + xCAT::zvmUtils->printSyslog("$node: successfully initialized IUCV, Set IUCV=1 for $user"); + if ($callback){ + xCAT::zvmUtils->printLn( $callback, "$node: successfully initialized IUCV, Set IUCV=1 for $user"); + } + if ($status){ + $status = "$status;IUCV=1"; + }else{ + $status = "IUCV=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + return $result; + }else{ + $msg = "$node: Failed to start iucvserver, result is $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } else { + $msg = "$node: Failed to start iucvserver, result is $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } else { + $msg = "$node: IUCV server on VM doesn't start well, result is $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } else { + $msg = "IUCV has worked well, set IUCV=1 for $user ."; + if ($status) { + $status = "$status;IUCV=1"; + } else { + $status = "IUCV=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + return $result; + } + +}