From 5a83bf5e63cb0c8badcb65c79dbaee1ed2eb06de Mon Sep 17 00:00:00 2001 From: Chuck Brazie Date: Thu, 2 Feb 2017 14:31:27 -0500 Subject: [PATCH] Merge our code with global xCAT team except for xcatws.cgi Change-Id: I945c430685fa2febd914be09f6a6f976783abeec --- xCAT-server/lib/perl/xCAT/xcatd.pm | 35 +- xCAT-server/lib/xcat/plugins/imgcapture.pm | 11 +- xCAT-server/lib/xcat/plugins/imgport.pm | 18 +- xCAT-server/lib/xcat/plugins/seqdiscovery.pm | 152 +- xCAT-server/lib/xcat/plugins/xdsh.pm | 10 +- xCAT-server/lib/xcat/plugins/zvm.pm | 7751 ++++++++++++----- .../lib/xcat/plugins/zvmdiagnostics.pm | 306 + xCAT-server/lib/xcat/plugins/zvmdiscovery.pm | 2310 +++++ .../xcat/install/rh/compute.rhel5.s390x.tmpl | 4 +- .../xcat/install/rh/compute.rhels6.s390x.tmpl | 4 +- .../xcat/install/rh/zfcp.rhel5.s390x.tmpl | 2 +- .../xcat/install/rh/zfcp.rhels6.s390x.tmpl | 2 +- .../xcat/install/scripts/post.rhel5.s390x | 19 +- .../xcat/install/scripts/post.rhel6.s390x | 19 +- .../xcat/install/scripts/post.sles10.s390x | 16 +- .../xcat/install/scripts/post.sles11.s390x | 16 +- .../install/sles/compute.sles10.s390x.tmpl | 10 +- .../install/sles/compute.sles11.s390x.tmpl | 10 +- .../xcat/install/sles/zfcp.sles10.s390x.tmpl | 9 +- .../xcat/install/sles/zfcp.sles11.s390x.tmpl | 9 +- .../netboot/rh/compute.rhel5.s390x.pkglist | 3 - .../netboot/rh/compute.rhel6.s390x.pkglist | 3 - xCAT-server/share/xcat/scripts/setupDisk | 579 ++ xCAT-server/share/xcat/scripts/xcatconf4z | 159 +- .../share/xcat/scripts/xcatconf4z.service | 14 + xCAT-server/share/xcat/tools/zvm/README | 13 + .../xcat/tools/zvm/prep_zxcatIVP_HAVANA.pl | 938 ++ .../xcat/tools/zvm/prep_zxcatIVP_ICEHOUSE.pl | 1096 +++ .../xcat/tools/zvm/prep_zxcatIVP_JUNO.pl | 1531 ++++ .../xcat/tools/zvm/prep_zxcatIVP_KILO.pl | 1532 ++++ .../xcat/tools/zvm/prep_zxcatIVP_LIBERTY.pl | 1738 ++++ .../xcat/tools/zvm/prep_zxcatIVP_NEWTON.pl | 2383 +++++ xCAT-server/xCAT-server.spec | 51 +- xCAT-server/xCAT-wsapi/xcat-ws.conf.apache2 | 13 + xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 | 6 +- xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 | 4 +- xCAT-server/xCAT-wsapi/xcat-ws.conf.httpd | 4 + 37 files changed, 18240 insertions(+), 2540 deletions(-) create mode 100644 xCAT-server/lib/xcat/plugins/zvmdiagnostics.pm create mode 100644 xCAT-server/lib/xcat/plugins/zvmdiscovery.pm create mode 100644 xCAT-server/share/xcat/scripts/setupDisk create mode 100644 xCAT-server/share/xcat/scripts/xcatconf4z.service create mode 100644 xCAT-server/share/xcat/tools/zvm/README create mode 100644 xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_HAVANA.pl create mode 100644 xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_ICEHOUSE.pl create mode 100644 xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_JUNO.pl create mode 100644 xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_KILO.pl create mode 100644 xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_LIBERTY.pl create mode 100644 xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_NEWTON.pl create mode 100644 xCAT-server/xCAT-wsapi/xcat-ws.conf.apache2 create mode 100644 xCAT-server/xCAT-wsapi/xcat-ws.conf.httpd diff --git a/xCAT-server/lib/perl/xCAT/xcatd.pm b/xCAT-server/lib/perl/xCAT/xcatd.pm index 69de2fd25..8dc8c8d8c 100644 --- a/xCAT-server/lib/perl/xCAT/xcatd.pm +++ b/xCAT-server/lib/perl/xCAT/xcatd.pm @@ -31,7 +31,7 @@ This program module file, is a set of utilities used by xCAT daemon. Here is where we check if $peername is allowed to do $request in policy tbl. $peername, if set signifies client has a cert that the xCAT CA accepted. Logs to syslog and auditlog table all user commands, see site.auditskipcmds - attribute and auditnosyslog attribute. + attribute and auditnosyslog attribute. Arguments: @@ -94,7 +94,7 @@ sub validate { $req_noderange_info{leftnodes} = \@tmpn; } } - + RULE: foreach $rule (@sortedpolicies) { if ($rule->{name} and $rule->{name} ne '*') { @@ -163,7 +163,7 @@ sub validate { push @non_hit_nodes, $_; } } - + if($hitnum == 0){ next RULE; }elsif($hitnum && $hitnum != $req_noderange_info{leftnodenum}){ @@ -227,7 +227,32 @@ sub validate { $arglist .= " " . $argument; } - if ($arglist) { $logst .= $arglist; } + my $saveArglist = $arglist; + + # If this is mkvm check for --password or -w + if ($request->{command}->[0] eq "mkvm") { + + my $first; + my $restcommand; + my $passw = index ($saveArglist, '--password'); + if ($passw > -1) { + $passw = $passw + 11; + my $first = substr($saveArglist,0,$passw). "******** "; + my $restcommand = substr($saveArglist,$passw); + $restcommand =~ s/^\S+\s*//; + $saveArglist = "$first$restcommand"; + } + # now check for -w with password + $passw = index ($saveArglist, '-w'); + if ($passw > -1) { + $passw = $passw + 3; + $first = substr($saveArglist,0,$passw). "******** "; + $restcommand = substr($saveArglist,$passw); + $restcommand =~ s/^\S+\s*//; + $saveArglist = "$first$restcommand"; + } + } + if ($arglist) { $logst .= $saveArglist; } if ($peername) { $logst .= " for " . $request->{username}->[0] } if ($peerhost) { $logst .= " from " . $peerhost } @@ -319,7 +344,7 @@ sub validate { if($req_noderange_info{leftnodenum}){ my $leftnodes = join(",", @{$req_noderange_info{leftnodes}}); - xCAT::MsgUtils->message("S", "Request matched no policy rule: peername=$peername, peerhost=$peerhost $request->{command}->[0] to $leftnodes"); + xCAT::MsgUtils->message("S", "Request matched no policy rule: peername=$peername, peerhost=$peerhost $request->{command}->[0] to $leftnodes"); }else{ xCAT::MsgUtils->message("S", "Request matched no policy rule: peername=$peername, peerhost=$peerhost " . $request->{command}->[0]); } diff --git a/xCAT-server/lib/xcat/plugins/imgcapture.pm b/xCAT-server/lib/xcat/plugins/imgcapture.pm index 8502123fe..280fd2abe 100755 --- a/xCAT-server/lib/xcat/plugins/imgcapture.pm +++ b/xCAT-server/lib/xcat/plugins/imgcapture.pm @@ -46,10 +46,16 @@ sub process_request { @ARGV = @{ $request->{arg} } if (defined $request->{arg}); my $argc = scalar @ARGV; - my $usage = "Usage:\n imgcapture -t|--type {diskless|sysclone} -o|--osimage [-V | --verbose] \n imgcapture [-h|--help] \n imgcapture [-v|--version]"; + my $usage = "Usage: imgcapture -t|--type diskless [-p | --profile ] " . + "[-o|--osimage ] [-i ] [-n ] " . + "[-d | --device ] [-c | --compress ] [-V | --verbose] \n\n" . + "imgcapture -t|--type sysclone -o|--osimage [-V | --verbose] \n" . + "imgcapture [-h|--help] \n" . + "imgcapture [-v|--version]"; my $os; my $arch; + my $compression; # Supported by the s390x architecture my $device; my $profile; my $bootif; @@ -65,6 +71,7 @@ sub process_request { 'n=s' => \$netdriver, 'osimage|o=s' => \$osimg, "device|d=s" => \$device, + "compress|c=s" => \$compression, "help|h" => \$help, "version|v" => \$version, "verbose|V" => \$verbose, @@ -123,7 +130,7 @@ sub process_request { # Handle image capture separately for s390x if ($arch eq 's390x') { eval { require xCAT_plugin::zvm; }; # Load z/VM plugin dynamically - xCAT_plugin::zvm->imageCapture($callback, $node, $os, $arch, $profile, $osimg, $device); + xCAT_plugin::zvm->imageCapture($callback, $node, $os, $arch, $type, $profile, $osimg, $device, $compression); return; } diff --git a/xCAT-server/lib/xcat/plugins/imgport.pm b/xCAT-server/lib/xcat/plugins/imgport.pm index 0eb2f5b37..4c0c2fe84 100644 --- a/xCAT-server/lib/xcat/plugins/imgport.pm +++ b/xCAT-server/lib/xcat/plugins/imgport.pm @@ -99,13 +99,14 @@ sub ximport { my $nodes; my $new_profile; my $remoteHost; + my $nozip = 0; my $xusage = sub { my $ec = shift; push @{ $rsp{data} }, "imgimport: Takes in an xCAT image bundle and defines it to xCAT so you can use it"; push @{ $rsp{data} }, "Usage: "; push @{ $rsp{data} }, "\timgimport [-h|--help]"; - push @{ $rsp{data} }, "\timgimport [-p|--postscripts ] [-f|--profile ] [-R|--remotehost ] [-v]"; + push@{ $rsp{data} }, "\timgimport [-p|--postscripts ] [-f|--profile ] [-R|--remotehost ] [-n|--nozip] [-v]"; if ($ec) { $rsp{errorcode} = $ec; } $callback->(\%rsp); }; @@ -122,6 +123,7 @@ sub ximport { 'R|remotehost=s' => \$remoteHost, 'p|postscripts=s' => \$nodes, 'f|profile=s' => \$new_profile, + 'n|nozip' => \$nozip ); if ($help) { @@ -130,7 +132,7 @@ sub ximport { } # first extract the bundle - extract_bundle($request, $callback, $nodes, $new_profile, $remoteHost); + extract_bundle($request, $callback, $nodes, $new_profile, $remoteHost, $nozip ); } @@ -608,7 +610,7 @@ sub get_files { if (-f "$rootimgdir/kernel") { $kernel = "$rootimgdir/kernel"; } - + my $compressedrootimg=xCAT::SvrUtils->searchcompressedrootimg("$rootimgdir"); $rootimg = "$rootimgdir/$compressedrootimg"; @@ -1115,6 +1117,7 @@ sub extract_bundle { my $nodes = shift; my $new_profile = shift; my $remoteHost = shift; + my $nozip = shift; @ARGV = @{ $request->{arg} }; my $xml; @@ -1188,12 +1191,21 @@ sub extract_bundle { $callback->({ data => ["Unbundling image..."] }); my $rc; + if ($nozip) { if ($::VERBOSE) { + $callback->({data=>["tar xvf $bundle -C $tpath"]}); + $rc = system("tar xvf $bundle -C $tpath"); + } else { + $rc = system("tar xf $bundle -C $tpath"); + } + } else { + if ($::VERBOSE) { $callback->({ data => ["tar zxvf $bundle -C $tpath"] }); $rc = system("tar zxvf $bundle -C $tpath"); } else { $rc = system("tar zxf $bundle -C $tpath"); } + } if ($rc) { $callback->({ error => ["Failed to extract bundle $bundle"], errorcode => [1] }); diff --git a/xCAT-server/lib/xcat/plugins/seqdiscovery.pm b/xCAT-server/lib/xcat/plugins/seqdiscovery.pm index 3bc482215..4f4bf1de9 100755 --- a/xCAT-server/lib/xcat/plugins/seqdiscovery.pm +++ b/xCAT-server/lib/xcat/plugins/seqdiscovery.pm @@ -55,6 +55,7 @@ sub findme { my @SEQdiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover"); my @PCMdiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + my @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover"); if (defined($request->{discoverymethod}) and defined($request->{discoverymethod}->[0]) and ($request->{discoverymethod}->[0] ne 'undef')) { @@ -62,6 +63,14 @@ sub findme { return; } unless ($SEQdiscover[0]) { + if ( $PCMdiscover[0] or $ZVMDiscover[0] ) { + + # profile or z/VM discovery is running, then just return to make the other discovery handle it + return; + } + # update the discoverydata table to have an undefined node + $request->{discoverymethod}->[0] = 'undef'; + xCAT::DiscoveryUtils->update_discovery_data($request); return; } @@ -436,7 +445,7 @@ sub nodediscoverstart { xCAT::MsgUtils->message("E", $rsp, $cb, 1); } - my $usageinfo = "nodediscoverstart: Start a discovery process: Sequential or Profile. + my $usageinfo = "nodediscoverstart: Start a discovery process: Sequential, Profile or z/VM. Usage: Common: nodediscoverstart [-h|--help|-v|--version|-V|--verbose] @@ -444,12 +453,16 @@ Usage: nodediscoverstart noderange= [hostiprange=] [bmciprange=] [groups=] [rack=] [chassis=] [height=] [unit=] [osimage=] [-n|--dns] [-s|--skipbmcsetup] [-V|--verbose] Profile Discovery: nodediscoverstart networkprofile= imageprofile= hostnameformat= [hardwareprofile=] [groups=] [rack=] [chassis=] [height=] [unit=] [rank=rank-num]"; + z/VM Discovery: + nodediscoverstart zvmhost= [defineto=both] [groups=] [ipfilter=] [openstackoperands=] [useridfilter=] + nodediscoverstart zvmhost= defineto=xcatonly [groups=] [ipfilter=] [nodenameformat=] [useridfilter=] + nodediscoverstart zvmhost= defineto=openstackonly [openstackoperands=]"; $rsp = (); push @{ $rsp->{data} }, $usageinfo; xCAT::MsgUtils->message("I", $rsp, $cb); }; - # valid attributes for deqdiscovery + # valid attributes for seqdiscovery my %validargs = ( 'noderange' => 1, 'hostiprange' => 1, @@ -492,7 +505,13 @@ Usage: $orgargs{$name} = $value; } - # Check the noderage=has been specified which is the flag that this is for sequential discovery + # Check the zvmhost= has been specified which is the flag that this is for z/VM discovery. + # Otherwise, fall thru to handle either sequential or profile discovery. + if ( defined( $orgargs{zvmhost} ) ) { + return; + } + + # Check the noderange= has been specified which is the flag that this is for sequential discovery # Otherwise try to check the whether the networkprofile || hardwareprofile || imageprofile # has been passed, if yes, return to profile discovery unless (defined($orgargs{noderange})) { @@ -502,6 +521,7 @@ Usage: return; } else { $usage->($callback, "For sequential discovery, the \'noderange\' option must be specified."); + $usage->($callback, "For z/VM discovery, the \'zvmhost\' option must be specified."); return; } } @@ -567,7 +587,7 @@ Usage: $sitetab->setAttribs({ "key" => "__SEQDiscover" }, { "value" => "$textparam" }); $sitetab->close(); - # Clean the entries which discovery method is 'sequential' from the discoverdata table + # Clean the entries which discovery method is 'sequential' from the discoverydata table my $distab = xCAT::Table->new("discoverydata"); $distab->delEntries({ method => 'sequential' }); $distab->commit(); @@ -689,9 +709,12 @@ sub nodediscoverstop { xCAT::MsgUtils->message("E", $rsp, $cb, 1); } - my $usageinfo = "nodediscoverstop: Stop the running discovery: Sequential and Profile. + my $usageinfo = "nodediscoverstop: Stop the running discovery: Profile, Sequential and z/VM. Usage: - nodediscoverstop [-h|--help|-v|--version] "; + Common: + nodediscoverstop [-h|--help|-v|--version] + z/VM discovery: + nodediscoverstop [-z|--zvmhost ]"; $rsp = (); push @{ $rsp->{data} }, $usageinfo; xCAT::MsgUtils->message("I", $rsp, $cb); @@ -721,9 +744,10 @@ Usage: # Check the running of sequential discovery my @SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover"); my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); - if ($PCMDiscover[0]) { + my @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover"); + if ($PCMDiscover[0] or $ZVMDiscover[0]) { - # return directly that profile discover will cover it + # return directly that the other discovery will handle the stop function. return; } elsif (!$SEQDiscover[0]) { @@ -731,12 +755,13 @@ Usage: my $rsp; push @{ $rsp->{data} }, "Sequential Discovery is stopped."; push @{ $rsp->{data} }, "Profile Discovery is stopped."; + push @{$rsp->{data}}, "z/VM Discovery is stopped."; xCAT::MsgUtils->message("E", $rsp, $callback, 1); return; } my $DBname = xCAT::Utils->get_DBName; # support for DB2 - # Go thought discoverydata table and display the sequential disocvery entries + # Go through discoverydata table and display the sequential discovery entries my $distab = xCAT::Table->new('discoverydata'); unless ($distab) { my $rsp; @@ -779,7 +804,7 @@ Usage: } =head3 nodediscoverls - Display the discovered nodes + Display the discovered nodes. This supports sequential and z/VM and partially Profile discovery. =cut sub nodediscoverls { @@ -797,12 +822,14 @@ sub nodediscoverls { } my $usageinfo = "nodediscoverls: list the discovered nodes. -Usage: +Usage: + Common: nodediscoverls nodediscoverls [-h|--help|-v|--version] - nodediscoverls [-t seq|profile|switch|blade|manual|undef|all] [-l] + nodediscoverls [-t seq|profile|switch|blade|manual|undef|zvm|all] [-l] nodediscoverls [-u uuid] [-l] - "; + z/VM: + nodediscoverls [-t zvm][-z|--zvmhost ] [-l]"; $rsp = (); push @{ $rsp->{data} }, $usageinfo; xCAT::MsgUtils->message("I", $rsp, $cb); @@ -811,14 +838,15 @@ Usage: if ($args) { @ARGV = @$args; } - my ($type, $uuid, $long, $help, $ver); + my ($type, $uuid, $long, $help, $ver, $zvmHost ); if (!GetOptions( 't=s' => \$type, 'u=s' => \$uuid, 'l' => \$long, 'h|help' => \$help, 'V|verbose' => \$::VERBOSE, - 'v|version' => \$ver)) { + 'v|version' => \$ver, + 'z|zvmhost=s' => \$zvmHost )) { $usage->($callback); return; } @@ -833,9 +861,9 @@ Usage: } # If the type is specified, display the corresponding type of nodes - my @SEQDiscover; + my ( @SEQDiscover, @ZVMDiscover ); if ($type) { - if ($type !~ /^(seq|profile|switch|blade|manual|undef|all)$/) { + if ($type !~ /^(seq|profile|switch|blade|manual|undef|zvm|all)$/) { $usage->($callback, "The discovery type \'$type\' is not supported."); return; } @@ -844,24 +872,31 @@ Usage: # Check the running of sequential discovery @SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover"); + @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover"); if ($SEQDiscover[0]) { $type = "seq"; - } else { - my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); - if ($PCMDiscover[0]) { + } elsif ($PCMDiscover[0]) { - #return directly if my type of discover is not running. + #return directly if profile discovery is running. + return; + } elsif ( $ZVMDiscover[0] ) { + # zvmdiscovery handles requests for a running z/VM discovery. return; } else { - # no type, no seq and no profile, then just diaplay all + # no type, no seq and no profile, then just display all $type = "all"; } } - } - my $DBname = xCAT::Utils->get_DBName; # support for DB2 - # Go thought discoverydata table and display the disocvery entries + # If a zvmHost was specified then let zvmdiscovery handle it. + # Specifying '-u' will keep processing within seqdiscovery. + if ( !$uuid && ( $zvmHost || ( $type && $type eq 'zvm' )) ) { + # zvmdiscovery handles request specific to z/VM. + return; + } + + # Go through discoverydata table and display the discovery entries my $distab = xCAT::Table->new('discoverydata'); unless ($distab) { my $rsp; @@ -914,7 +949,8 @@ Usage: } my $rsp; - if ($SEQDiscover[0] && $type eq "sequential") { + if (($SEQDiscover[0] && $type eq "sequential" ) or + ( $type && $type eq "all" )) { push @{ $rsp->{data} }, "Discovered $discoverednum node."; } if (@discoverednodes) { @@ -949,8 +985,11 @@ sub nodediscoverstatus { } my $usageinfo = "nodediscoverstatus: Display the discovery process status. -Usage: - nodediscoverstatus [-h|--help|-v|--version] "; +Usage: + Common: + nodediscoverstatus [-h|--help|-v|--version] + z/VM + nodediscoverstatus [-z|--zvmhost ]"; $rsp = (); push @{ $rsp->{data} }, $usageinfo; xCAT::MsgUtils->message("I", $rsp, $cb); @@ -980,6 +1019,7 @@ Usage: # Check the running of sequential discovery my @SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover"); my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); + my @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover"); if ($SEQDiscover[0]) { my $rsp; push @{ $rsp->{data} }, "Sequential discovery is running."; @@ -989,10 +1029,13 @@ Usage: my $rsp; push @{ $rsp->{data} }, "Node discovery for all nodes using profiles is running"; xCAT::MsgUtils->message("I", $rsp, $callback); + } elsif ( $ZVMDiscover[0] ) { + # z/VM discovery is a more complex response so we let its handler return the response. } else { my $rsp; push @{ $rsp->{data} }, "Sequential Discovery is stopped."; push @{ $rsp->{data} }, "Profile Discovery is stopped."; + push @{$rsp->{data}}, "z/VM Discovery is stopped."; xCAT::MsgUtils->message("I", $rsp, $callback); } @@ -1008,6 +1051,9 @@ sub nodediscoverdef { my $subreq = shift; my $args = shift; + my @inputZvmHosts; # Input list of z/VM host nodes to stop + my $zvmHost; # Small scope variable to temporarily hold a z/VM host node value + # The subroutine used to display the usage message my $usage = sub { my $cb = shift; @@ -1021,10 +1067,13 @@ sub nodediscoverdef { my $usageinfo = "nodediscoverdef: Define the undefined discovery request, or clean the discovery entries in the discoverydata table (Which can be displayed by nodediscoverls command). Usage: + Common: nodediscoverdef -u uuid -n node nodediscoverdef -r -u uuid - nodediscoverdef -r -t {seq|profile|switch|blade|manual|undef|all} - nodediscoverdef [-h|--help|-v|--version]"; + nodediscoverdef -r -t {seq|profile|switch|blade|manual|undef|zvm|all} + nodediscoverdef [-h|--help|-v|--version] + z/VM: + nodediscoverdef -r -t zvm [-z|--zvmhost noderange]"; $rsp = (); push @{ $rsp->{data} }, $usageinfo; xCAT::MsgUtils->message("I", $rsp, $cb); @@ -1042,7 +1091,8 @@ Usage: 'r' => \$remove, 'h|help' => \$help, 'V|verbose' => \$::VERBOSE, - 'v|version' => \$ver)) { + 'v|version' => \$ver, + 'z|zvmhost=s' => \$zvmHost )) { $usage->($callback); return; } @@ -1057,6 +1107,29 @@ Usage: } my $DBname = xCAT::Utils->get_DBName; # support for DB2 + # Put any specified zvmhosts into an array for later use. + if ( $zvmHost ) { + $type = 'zvm' if ( !$type ); + if ( $type ne 'zvm' ) { + xCAT::MsgUtils->message("E", {data=>["Discovery Error: Type must be 'zvm' when '-z' or '--zvmhost' is specified."]}, $callback); + return; + } + if ( index( $zvmHost, ',' ) != -1 ) { + # Must have specified multiple host node names + my @hosts = split( /,/, $zvmHost ); + foreach $zvmHost ( @hosts ) { + if ( !$zvmHost ) { + # Tolerate zvmhost value beginning with a comma. + # It is wrong but not worth an error message. + next; + } + push( @inputZvmHosts, $zvmHost ); + } + } else { + push( @inputZvmHosts, $zvmHost ); + } + } + # open the discoverydata table for the subsequent using my $distab = xCAT::Table->new("discoverydata"); unless ($distab) { @@ -1095,7 +1168,7 @@ Usage: } elsif ($type) { # handle the -r -t <...> - if ($type !~ /^(seq|profile|switch|blade|manual|undef|all)$/) { + if ($type !~ /^(seq|profile|switch|blade|manual|undef|zvm|all)$/) { $usage->($callback, "The discovery type \'$type\' is not supported."); return; } @@ -1131,9 +1204,24 @@ Usage: if ($type =~ /^seq/) { $type = "sequential"; } + # If a noderange of z/VM hosts was specified then delete + # all z/VM discovered systems for those hosts. 'zvm' type + # must be used when z/VM hosts are specified. + if ( @inputZvmHosts ) { + my %keyhash; + $keyhash{'method'} = 'zvm'; + foreach $zvmHost ( @inputZvmHosts ) { + $keyhash{'otherdata'} = "zvmhost." . $zvmHost; + $distab->delEntries( \%keyhash ); + } + $distab->commit(); + } else { + # Otherwise, Delete all systems discovered using the specified method. $distab->delEntries({ method => $type }); $distab->commit(); } + + } } xCAT::MsgUtils->message("I", { data => ["Removing discovery entries finished."] }, $callback); } elsif ($uuid) { diff --git a/xCAT-server/lib/xcat/plugins/xdsh.pm b/xCAT-server/lib/xcat/plugins/xdsh.pm index f3e1bd229..d32196854 100644 --- a/xCAT-server/lib/xcat/plugins/xdsh.pm +++ b/xCAT-server/lib/xcat/plugins/xdsh.pm @@ -303,6 +303,8 @@ sub parse_xdcp_cmd 'F|File=s' => \$options{'File'}, 'h|help' => \$options{'help'}, 'i|rootimg=s' => \$options{'rootimg'}, + 'ip=s' => \$options{'ip'}, + 'show=s' => \$options{'show'}, 'l|user=s' => \$options{'user'}, 'n|nodes=s' => \$options{'nodes'}, 'o|node-options=s' => \$options{'node-options'}, @@ -459,6 +461,8 @@ sub parse_xdsh_cmd 'q|show-config' => \$options{'show-config'}, 'r|node-rsh=s' => \$options{'node-rsh'}, 'i|rootimg=s' => \$options{'rootimg'}, + 'ip=s' => \$options{'ip'}, + 'show=s' => \$options{'show'}, 's|stream' => \$options{'streaming'}, 't|timeout=i' => \$options{'timeout'}, 'v|verify' => \$options{'verify'}, @@ -1070,11 +1074,11 @@ sub process_nodes #------------------------------------------------------- =head3 syncSNZoneKeys - Build the xdcp command to send the zone keys to the service nodes - Return an array of servicenodes that do not have errors + Build the xdcp command to send the zone keys to the service nodes + Return an array of servicenodes that do not have errors Returns error code: if = 0, good return continue to process the - nodes. + nodes. if = 1, global error need to quit =cut diff --git a/xCAT-server/lib/xcat/plugins/zvm.pm b/xCAT-server/lib/xcat/plugins/zvm.pm index ee74b4fa0..9c327eb1c 100644 --- a/xCAT-server/lib/xcat/plugins/zvm.pm +++ b/xCAT-server/lib/xcat/plugins/zvm.pm @@ -1,10 +1,10 @@ -# 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 xCAT plugin to support z/VM (s390x) - + =cut #------------------------------------------------------- @@ -23,12 +23,18 @@ use XML::Simple; use File::Basename; use File::Copy; use File::Path; +use File::Temp; use Time::HiRes; use POSIX; use Getopt::Long; use strict; +use warnings; +use Cwd; +# builtin should be set to 1 if this is xcat built into z/VM +my $builtin; +$builtin = 1; -# If the following line is not included, you get: +# If the following line ("1;")is not included, you get: # /opt/xcat/lib/perl/xCAT_plugin/zvm.pm did not return a true value 1; @@ -43,20 +49,21 @@ use strict; #------------------------------------------------------- sub handled_commands { return { - rpower => 'nodehm:power,mgt', - rinv => 'nodehm:mgt', - mkvm => 'nodehm:mgt', - rmvm => 'nodehm:mgt', - lsvm => 'nodehm:mgt', - chvm => 'nodehm:mgt', - rscan => 'nodehm:mgt', - nodeset => 'noderes:netboot', - getmacs => 'nodehm:getmac,mgt', - rnetboot => 'nodehm:mgt', - rmigrate => 'nodehm:mgt', - chhypervisor => [ 'hypervisor:type', 'nodetype:os=(zvm.*)' ], - revacuate => 'hypervisor:type', - reventlog => 'nodehm:mgt', + rpower => 'nodehm:power,mgt', + rinv => 'nodehm:mgt', + mkvm => 'nodehm:mgt', + rmvm => 'nodehm:mgt', + lsvm => 'nodehm:mgt', + chvm => 'nodehm:mgt', + rscan => 'nodehm:mgt', + execcmdonvm => 'nodehm:mgt', + nodeset => 'noderes:netboot', + getmacs => 'nodehm:getmac,mgt', + rnetboot => 'nodehm:mgt', + rmigrate => 'nodehm:mgt', + chhypervisor => ['hypervisor:type', 'nodetype:os=(zvm.*)'], + revacuate => 'hypervisor:type', + reventlog => 'nodehm:mgt', }; } @@ -83,7 +90,7 @@ sub preprocess_request { my @requests; # If already preprocessed, go straight to request - if ($req->{_xcatpreprocessed}->[0] == 1) { + if ( $req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; } my $nodes = $req->{node}; @@ -92,10 +99,10 @@ sub preprocess_request { # Find service nodes for requested nodes # Build an individual request for each service node if ($nodes) { - $sn = xCAT::ServiceNodeUtils->get_ServiceNode($nodes, $service, "MN"); + $sn = xCAT::ServiceNodeUtils->get_ServiceNode( $nodes, $service, "MN" ); # Build each request for each service node - foreach my $snkey (keys %$sn) { + foreach my $snkey ( keys %$sn ) { my $n = $sn->{$snkey}; print "snkey=$snkey, nodes=@$n\n"; my $reqcopy = {%$req}; @@ -112,8 +119,8 @@ sub preprocess_request { # Input error my %rsp; my $rsp; - $rsp->{data}->[0] = "Input noderange missing. Useage: zvm \n"; - xCAT::MsgUtils->message("I", $rsp, $callback, 0); + $rsp->{data}->[0] = "Input noderange missing. Usage: zvm \n"; + xCAT::MsgUtils->message( "I", $rsp, $callback, 0 ); return 1; } } @@ -134,7 +141,7 @@ sub process_request { my $command = $request->{command}->[0]; my $args = $request->{arg}; my $envs = $request->{env}; - $::STDIN = $request->{stdin}->[0]; + $::STDIN = $request->{stdin}->[0]; my %rsp; my $rsp; my @nodes = @$nodes; @@ -157,21 +164,21 @@ sub process_request { my $pid; # Child process IDs - my @children; + my @children = (); #*** Power on or off a node *** - if ($command eq "rpower") { + if ( $command eq "rpower" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - powerVM($callback, $_, $args); + elsif ( $pid == 0 ) { + powerVM( $callback, $_, $args ); # Exit process exit(0); @@ -183,11 +190,11 @@ sub process_request { } # Handle 10 nodes at a time, else you will get errors - if (!(@children % 10)) { + if ( !( @children % 10 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -197,21 +204,21 @@ sub process_request { } # End of case #*** Hardware and software inventory *** - elsif ($command eq "rinv") { + elsif ( $command eq "rinv" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { + elsif ( $pid == 0 ) { if (xCAT::zvmUtils->isHypervisor($_)) { - inventoryHypervisor($callback, $_, $args); + inventoryHypervisor( $callback, $_, $args ); } else { - inventoryVM($callback, $_, $args); + inventoryVM( $callback, $_, $args ); } # Exit process @@ -227,18 +234,18 @@ sub process_request { } # End of case #*** Migrate a virtual machine *** - elsif ($command eq "rmigrate") { + elsif ( $command eq "rmigrate" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - migrateVM($callback, $_, $args); + elsif ( $pid == 0 ) { + migrateVM( $callback, $_, $args ); # Exit process exit(0); @@ -253,18 +260,18 @@ sub process_request { } # End of case #*** Evacuate all virtual machines off a hypervisor *** - elsif ($command eq "revacuate") { + elsif ( $command eq "revacuate" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - evacuate($callback, $_, $args); + elsif ( $pid == 0 ) { + evacuate( $callback, $_, $args ); # Exit process exit(0); @@ -279,17 +286,89 @@ sub process_request { } # End of case #*** Create a virtual server *** - elsif ($command eq "mkvm") { + elsif ( $command eq "mkvm" ) { # Determine if the argument is a node my $clone = 0; - if ($args->[0]) { - $clone = xCAT::zvmUtils->isZvmNode($args->[0]); + my %cloneInfoHash = (); # create empty hash + if ( $args->[0] ) { + $clone = xCAT::zvmUtils->isZvmNode( $args->[0] ); + } + + # Loop through all the arguments looking for "-imagename" + # if an image name is found and it matches what is in the + # doclone.txt then this must be a specialcloneVM call. + my $argsSize = @{$args}; + my $imagename = ''; + + for ( my $i = 0 ; $i < $argsSize ; $i++ ) { + my $parm = $args->[$i]; + #xCAT::zvmUtils->printSyslog("Args[$i] =<$parm>\n"); + if ( index( $parm, "--imagename" ) != -1 ) { + if ( ($i+1) < $argsSize) { + $imagename = $args->[$i+1]; + } + + if ( !length($imagename) ) { + xCAT::zvmUtils->printSyslog("(Error) image name value missing\n"); + xCAT::zvmUtils->printLn( $callback, "$nodes: (Error) image name value missing\n" ); + return; + } + + xCAT::zvmUtils->printSyslog("mkvm for (@nodes). Parm --imagename found with value ($imagename). Check if this is special case.\n"); + + %cloneInfoHash = xCAT::zvmUtils->getSpecialCloneInfo($imagename); + if (%cloneInfoHash) { + xCAT::zvmUtils->printSyslog("Image found in doclone.txt for creating (@nodes)\n"); + # call special clonevm processing + specialcloneVM( $callback, \@nodes, $args, \%cloneInfoHash); + return; + } + } + } + + # looking for --osimage, if an osimage name is found, check the image's + # comments, if the comments indicate it's an non-xcatconf4z image, update + # the zvm table for the nodes to set the flag to be xcatconf4z=0 in + # comments colume + my $osimage = ''; + for ( my $i = 0 ; $i < $argsSize ; $i++ ) { + my $parm = $args->[$i]; + if ( index( $parm, "--osimage" ) != -1 ) { + if ( ($i+1) < $argsSize) { + $osimage = $args->[$i+1]; + } + + if ( !length($osimage) ) { + xCAT::zvmUtils->printSyslog("(Error) osimage value missing\n"); + xCAT::zvmUtils->printLn( $callback, "$nodes: (Error) osimage value missing\n" ); + return; + } + + xCAT::zvmUtils->printSyslog("mkvm for (@nodes). Parm --osimage found with value ($osimage). Set the node flag to indicate if it will be deployed by using xcatconf4z image or not.\n"); + + # Update the zvm table comments colume to indicate the xcatconf4z type image + my @propNames = ('comments'); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $osimage, @propNames ); + if ( $propVals->{'comments'} =~ /xcatconf4z=0/ ) { + foreach (@nodes) { + @propNames = ( 'status' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames ); + my $status = $propVals->{'status'}; + if ( !$status ) { + $status = "XCATCONF4Z=0"; + } else { + $status = "$status;XCATCONF4Z=0"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $_, 'status', $status ); + } + } + } } #*** Clone virtual server *** - if ($clone) { - cloneVM($callback, \@nodes, $args); + if ( $clone ) { + cloneVM( $callback, \@nodes, $args ); } #*** Create user entry *** @@ -301,13 +380,13 @@ sub process_request { # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { + elsif ( $pid == 0 ) { - makeVM($callback, $_, $args); + makeVM( $callback, $_, $args ); # Exit process exit(0); @@ -322,18 +401,18 @@ sub process_request { } # End of case #*** Remove a virtual server *** - elsif ($command eq "rmvm") { + elsif ( $command eq "rmvm" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - removeVM($callback, $_); + elsif ( $pid == 0 ) { + removeVM( $callback, $_, $args ); # Exit process exit(0); @@ -345,11 +424,11 @@ sub process_request { } # Handle 10 nodes at a time, else you will get errors - if (!(@children % 10)) { + if ( !( @children % 10 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -359,18 +438,18 @@ sub process_request { } # End of case #*** Print the user entry *** - elsif ($command eq "lsvm") { + elsif ( $command eq "lsvm" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - listVM($callback, $_, $args); + elsif ( $pid == 0 ) { + listVM( $callback, $_, $args ); # Exit process exit(0); @@ -382,11 +461,11 @@ sub process_request { } # Handle 10 nodes at a time, else you will get errors - if (!(@children % 10)) { + if ( !( @children % 10 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -396,18 +475,18 @@ sub process_request { } # End of case #*** Change the user entry *** - elsif ($command eq "chvm") { + elsif ( $command eq "chvm" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - changeVM($callback, $_, $args); + elsif ( $pid == 0 ) { + changeVM( $callback, $_, $args ); # Exit process exit(0); @@ -419,11 +498,11 @@ sub process_request { } # Handle 10 nodes at a time, else you will get errors - if (!(@children % 10)) { + if ( !( @children % 10 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -433,18 +512,18 @@ sub process_request { } # End of case #*** Collect node information from zHCP *** - elsif ($command eq "rscan") { + elsif ( $command eq "rscan" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - scanVM($callback, $_, $args); + elsif ( $pid == 0 ) { + scanVM( $callback, $_, $args ); # Exit process exit(0); @@ -459,29 +538,29 @@ sub process_request { } # End of case #*** Set the boot state for a node *** - elsif ($command eq "nodeset") { + elsif ( $command eq "nodeset" ) { foreach (@nodes) { # Only one file can be punched to reader at a time # Forking this process is not possible - nodeSet($callback, $_, $args); + nodeSet( $callback, $_, $args ); } # End of foreach } # End of case #*** Get the MAC address of a node *** - elsif ($command eq "getmacs") { + elsif ( $command eq "getmacs" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - getMacs($callback, $_, $args); + elsif ( $pid == 0 ) { + getMacs( $callback, $_, $args ); # Exit process exit(0); @@ -496,18 +575,18 @@ sub process_request { } # End of case #*** Boot from network *** - elsif ($command eq "rnetboot") { + elsif ( $command eq "rnetboot" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - netBoot($callback, $_, $args); + elsif ( $pid == 0 ) { + netBoot( $callback, $_, $args ); # Exit process exit(0); @@ -519,11 +598,11 @@ sub process_request { } # Handle 10 nodes at a time, else you will get errors - if (!(@children % 10)) { + if ( !( @children % 10 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -533,18 +612,18 @@ sub process_request { } # End of case #*** Configure the virtualization hosts *** - elsif ($command eq "chhypervisor") { + elsif ( $command eq "chhypervisor" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - changeHypervisor($callback, $_, $args); + elsif ( $pid == 0 ) { + changeHypervisor( $callback, $_, $args ); # Exit process exit(0); @@ -556,11 +635,11 @@ sub process_request { } # Handle 10 nodes at a time, else you will get errors - if (!(@children % 10)) { + if ( !( @children % 10 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -570,18 +649,18 @@ sub process_request { } # End of case #*** Retrieve or clear event logs *** - elsif ($command eq "reventlog") { + elsif ( $command eq "reventlog" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - eventLog($callback, $_, $args); + elsif ( $pid == 0 ) { + eventLog( $callback, $_, $args ); # Exit process exit(0); @@ -596,18 +675,18 @@ sub process_request { } # End of case #*** Update the node (no longer supported) *** - elsif ($command eq "updatenode") { + elsif ( $command eq "updatenode" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { - updateNode($callback, $_, $args); + elsif ( $pid == 0 ) { + updateNode( $callback, $_, $args ); # Exit process exit(0); @@ -621,9 +700,35 @@ sub process_request { } # End of foreach } # End of case + + #*** Execute a command on VM *** + elsif ( $command eq "execcmdonvm" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + xCAT::zvmUtils->execcmdonVM($::SUDOER, $_, $args->[0]); + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } return; @@ -635,96 +740,168 @@ sub process_request { Description : Delete the user from user directory Arguments : Node to remove + Upstream instance ID (Optional) + Upstream request ID (Optional) Returns : Nothing Example : removeVM($callback, $node); - + =cut #------------------------------------------------------- sub removeVM { # Get inputs - my ($callback, $node) = @_; + my ( $callback, $node , $args ) = @_; + my $rc; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid', 'discovered' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); + my $out; - # Power off user ID - my $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId -f IMMED"`; - xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId -f IMMED"); + my $requestId = "NoUpstreamRequestID"; # Default is still visible in the log + my $objectId = "NoUpstreamObjectID"; # Default is still visible in the log + if ($args) { + @ARGV = @$args; - # Delete user entry - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Delete_DM -T $userId -e 1"`; - xCAT::zvmUtils->printSyslog("smcli Image_Delete_DM -T $userId -e 1"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); - - # Check for errors - my $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - return; + # Parse options + GetOptions( + 'q|requestid=s' => \$requestId # Optional + , 'j|objectid=s' => \$objectId # Optional + ); } - # Go through each pool and free zFCP devices belonging to node - my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`); - my $pool; - my @luns; - my $update; - my $expression; - foreach (@pools) { - $pool = xCAT::zvmUtils->replaceStr($_, ".conf", ""); - - @luns = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -i $node`); - foreach (@luns) { - - # Update entry: status,wwpn,lun,size,range,owner,channel,tag - my @info = split(',', $_); - $update = "free,$info[1],$info[2],$info[3],$info[4],,,"; - $expression = "'s#" . $_ . "#" . $update . "#i'"; - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e $expression $::ZFCPPOOL/$pool.conf"`; + # If node is a not a discovered node then remove the userid and its related resources. + my $discovered = $propVals->{'discovered'}; + if ( !$discovered || $discovered == 0 ) { + # System was not discovered so we can destroy the virtual machine and + # its resources. First, get any vswitches in directory. + xCAT::zvmUtils->printSyslog("Calling getVswitchIdsFromDirectory $::SUDOER, $hcp, $userId"); + my @vswitch = xCAT::zvmUtils->getVswitchIdsFromDirectory( $::SUDOER, $hcp, $userId); + my %vswitchhash; + # For each vswitch revoke the userid vswitch authority + foreach (@vswitch) { + if (!(length $_)) {next;} + # skip revoke if we already did one for this vswitch + if (exists $vswitchhash{$_}) { + xCAT::zvmUtils->printSyslog("removeVM. Skipping duplicate vswitch remove grant from: $_"); + } + else { + xCAT::zvmUtils->printSyslog("removeVM. Found vswitch to remove grant from: $_"); + $out = xCAT::zvmCPUtils->revokeVSwitch( $callback, $::SUDOER, $hcp, $userId, $_); + $vswitchhash{$_} = '1'; + #caller logs any errors, so just continue. + } } - if (@luns) { - xCAT::zvmUtils->printLn($callback, "$node: Updating FCP device pool $pool... Done"); + # Power off user ID + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId -f IMMED"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId -f IMMED"`; + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + xCAT::zvmUtils->printSyslog("$userId already logged off."); + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + xCAT::zvmUtils->printSyslog("$userId in process of logging off."); + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + return; + } + + # Delete user entry + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Delete_DM -T $userId -e 0"`; + $rc = $? >> 8; + if ($rc == 255) { # Adding "Failed" to message will cause zhcp error dialog to be displayed to user + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; + } + xCAT::zvmUtils->printSyslog("smcli Image_Delete_DM -T $userId -e 0 $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Check for errors + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + return; + } + + # Go through each pool and free zFCP devices belonging to node + my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`); + my $pool; + my @luns; + my $update; + my $expression; + foreach (@pools) { + if (!(length $_)) {next;} + $pool = xCAT::zvmUtils->replaceStr( $_, ".conf", "" ); + + @luns = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -a -i $node`); + foreach (@luns) { + if (!(length $_)) {next;} + # Update entry: status,wwpn,lun,size,range,owner,channel,tag + my @info = split(',', $_); + $update = "free,$info[1],$info[2],$info[3],$info[4],,,"; + $expression = "'s#" . $_ . "#" .$update . "#i'"; + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e $expression $::ZFCPPOOL/$pool.conf"`; + } + + if (@luns) { + xCAT::zvmUtils->printLn($callback, "$node: Updating FCP device pool $pool... Done"); + } + } + + # Check for errors + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + return; + } + } else { + xCAT::zvmUtils->printLn($callback, "$node: 'discovered' property in zvm table is 1. Node is removed only from xCAT. Virtual machine was not deleted."); } - # Check for errors - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - return; - } - - # Remove node from 'zvm', 'nodelist', 'nodetype', 'noderes', and 'nodehm' tables + # Remove node from 'zvm', 'nodelist', 'nodetype', 'noderes', 'nodehm', 'ppc', 'switch' tables # Save node entry in 'mac' table - xCAT::zvmUtils->delTabEntry('zvm', 'node', $node); - xCAT::zvmUtils->delTabEntry('hosts', 'node', $node); - xCAT::zvmUtils->delTabEntry('nodelist', 'node', $node); - xCAT::zvmUtils->delTabEntry('nodetype', 'node', $node); - xCAT::zvmUtils->delTabEntry('noderes', 'node', $node); - xCAT::zvmUtils->delTabEntry('nodehm', 'node', $node); + xCAT::zvmUtils->delTabEntry( 'zvm', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'hosts', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'nodelist', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'nodetype', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'noderes', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'nodehm', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'ppc', 'node', $node ); + xCAT::zvmUtils->delTabEntry( 'switch', 'node', $node ); - # Erase old hostname from known_hosts - $out = `ssh-keygen -R $node`; + # Erase old hostname from known_hosts,all hostname are recorded in lower-case. + my $lowernode = lc($node); + $out = `ssh-keygen -R $lowernode`; # Erase hostname from /etc/hosts $out = `sed -i /$node./d /etc/hosts`; @@ -738,53 +915,61 @@ sub removeVM { Description : Change a virtual machine's configuration Arguments : Node - Option + Option Returns : Nothing Example : changeVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub changeVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid', 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # If the node is being actively cloned then return. + if ( $propVals->{'status'} =~ /CLONING=1/ and $propVals->{'status'} =~ /CLONE_ONLY=1/ ) { + return; + } # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); - return; - } - # Capitalize user ID - $userId =~ tr/a-z/A-Z/; + # add page or spool does not need a userid, but flag any others + if ( $args->[0] ne "--addpagespool" ) { + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); + return; + } + # Capitalize user ID + $userId =~ tr/a-z/A-Z/; + } # Get zHCP user ID my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); $hcpUserId =~ tr/a-z/A-Z/; - xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); + xCAT::zvmUtils->printSyslog("changeVM() node:$node userid:$userId subCmd:$args->[0] zHCP:$hcp sudoer:$::SUDOER sudo:$::SUDO"); # Output string my $out = ""; - # add3390 [disk pool] [device address] [size] [mode] [read password (optional)] [write password (optional)] [multi password (optional)] - if ($args->[0] eq "--add3390") { - my $pool = $args->[1]; - my $addr = $args->[2]; - my $cyl = $args->[3]; + # add3390 [disk pool] [device address] [size] [mode] [read password (optional)] [write password (optional)] [multi password (optional)] [fstype (optional)] + if ( $args->[0] eq "--add3390" ) { + my $pool = $args->[1]; + my $addr = $args->[2]; + my $cyl = $args->[3]; # If the user specifies auto as the device address, then find a free device address @@ -797,7 +982,7 @@ sub changeVM { $mode = $args->[4]; } - my $readPw = "''"; + my $readPw = "''"; if ($args->[5]) { $readPw = $args->[5]; } @@ -812,6 +997,11 @@ sub changeVM { $multiPw = $args->[7]; } + my $fstype = ''; + if ($args->[8]) { + $fstype = $args->[8]; + } + # Convert to cylinders if size is given as M or G # Otherwise, assume size is given in cylinders # Note this is for a 4096 block size ECKD disk, where 737280 bytes = 1 cylinder @@ -819,47 +1009,127 @@ sub changeVM { $cyl =~ s/M//g; $cyl = xCAT::zvmUtils->trimStr($cyl); $cyl = sprintf("%.4f", $cyl); - $cyl = ($cyl * 1024 * 1024) / 737280; + $cyl = ($cyl * 1024 * 1024)/737280; $cyl = ceil($cyl); } elsif ($cyl =~ m/G/i) { $cyl =~ s/G//g; $cyl = xCAT::zvmUtils->trimStr($cyl); $cyl = sprintf("%.4f", $cyl); - $cyl = ($cyl * 1024 * 1024 * 1024) / 737280; + $cyl = ($cyl * 1024 * 1024 * 1024)/737280; $cyl = ceil($cyl); } elsif ($cyl =~ m/[a-zA-Z]/) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Size can be Megabytes (M), Gigabytes (G), or number of cylinders"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size can be Megabytes (M), Gigabytes (G), or number of cylinders" ); return; } # Add to directory entry + my $error = 0; $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $userId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"); + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + if ($error) { + xCAT::zvmUtils->printLn( $callback, "$node: Error on Image_Disk_Create_DM $out" ); + xCAT::zvmUtils->printSyslog( $callback, "$node: Error on Image_Disk_Create_DM $out" ); + return; + } + + if ( $fstype && ( $fstype !~ /(ext2|ext3|ext4|xfs)/i ) ) { + $out = "(Warning) File system type can only be ext2, ext3, ext4 or xfs" . "\n"; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + $error = 1; + } + + if ( $fstype && !$error ) { # Format the disk before making it active. + # link the disk + my $retry = 3; + my $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' ); + while ( $retry > 0 ) { + # wait 2 seconds for disk creation complete + sleep(2); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp LINK TO $userId $addr AS $vdev M 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + + if ($error) { + $retry -= 1; + $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' ); + } else { + last; + } + } + + # make the disk online and get it's device name + my $device = 0; + if ( !$error ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r $vdev &> /dev/null"`; + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $vdev ); + my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat /proc/dasd/devices | grep -a 0.0.$vdev"`; + chomp( $select ); + # A sample entry: + # 0.0.0101(ECKD) at ( 94: 0) is dasda : active at blocksize: 4096, 600840 blocks, 2347 MB + if ( $select ) { + my @info = split( ' ', $select ); + $device = "/dev/" . $info[6]; + } + $error = !$device; + } + + # format the disk + if ( !$error ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -y -b 4096 -d cdl -f $device 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + } + if ( !$error ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdasd -a $device 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + } + if ( !$error ) { + $device .= '1'; + if ( $fstype =~ m/xfs/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs.xfs $device 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs -t $fstype $device 2>&1"`; + $error = !($out =~ m/done/); + } + } + if ( $error ) { + xCAT::zvmUtils->printSyslog("$out"); + $out = "(Warning) Can not format disk with fstype $fstype" . "\n"; + } else { + $out = ""; + } + + xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $vdev ); + `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null"`; + xCAT::zvmUtils->printSyslog("vmcp DETACH $vdev"); + } # Add to active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { - $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; + $out .= "Adding 3390 disk for $node as $addr ... Done"; + my $power = `/opt/xcat/bin/rpower $node stat`; + if ($power =~ m/: on/i) { + $out .= "\n" . `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); } - $out = xCAT::zvmUtils->appendHostname($node, $out); + + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # add3390active [device address] [mode] - elsif ($args->[0] eq "--add3390active") { + elsif ( $args->[0] eq "--add3390active" ) { my $addr = $args->[1]; my $mode = $args->[2]; $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } - # add9336 [disk pool] [virtual device address] [size] [mode] [read password (optional)] [write password (optional)] [multi password (optional)] - elsif ($args->[0] eq "--add9336") { - my $pool = $args->[1]; - my $addr = $args->[2]; - my $blks = $args->[3]; + # add9336 [disk pool] [virtual device address] [size] [mode] [read password (optional)] [write password (optional)] [multi password (optional)] [fstype (optional)] + elsif ( $args->[0] eq "--add9336" ) { + my $pool = $args->[1]; + my $addr = $args->[2]; + my $blks = $args->[3]; # If the user specifies auto as the device address, then find a free device address if ($addr eq "auto") { @@ -871,7 +1141,7 @@ sub changeVM { $mode = $args->[4]; } - my $readPw = "''"; + my $readPw = "''"; if ($args->[5]) { $readPw = $args->[5]; } @@ -886,6 +1156,11 @@ sub changeVM { $multiPw = $args->[7]; } + my $fstype = ''; + if ($args->[8]) { + $fstype = $args->[8]; + } + # Convert to blocks if size is given as M or G # Otherwise, assume size is given in blocks # Note this is for a 4096 block size ECKD disk, where 737280 bytes = 1 cylinder @@ -893,174 +1168,316 @@ sub changeVM { $blks =~ s/M//g; $blks = xCAT::zvmUtils->trimStr($blks); $blks = sprintf("%.4f", $blks); - $blks = ($blks * 1024 * 1024) / 512; + $blks = ($blks * 1024 * 1024)/512; $blks = ceil($blks); } elsif ($blks =~ m/G/i) { $blks =~ s/G//g; $blks = xCAT::zvmUtils->trimStr($blks); $blks = sprintf("%.4f", $blks); - $blks = ($blks * 1024 * 1024 * 1024) / 512; + $blks = ($blks * 1024 * 1024 * 1024)/512; $blks = ceil($blks); } elsif ($blks =~ m/[a-zA-Z]/) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Size can be Megabytes (M), Gigabytes (G), or number of blocks"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size can be Megabytes (M), Gigabytes (G), or number of blocks" ); return; } - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 9336 -a AUTOG -r $pool -u 2 -z $blks -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"`; + # Add to directory entry + my $error = 0; + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t 9336 -a AUTOG -r $pool -u 2 -z $blks -m $mode -f 1 -R $readPw -W $writePw -M $multiPw 2>&1"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $userId -v $addr -t 9336 -a AUTOG -r $pool -u 2 -z $blks -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"); + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + if ($error) { + xCAT::zvmUtils->printLn( $callback, "$node: Error on Image_Disk_Create_DM $out" ); + xCAT::zvmUtils->printSyslog( $callback, "$node: Error on Image_Disk_Create_DM $out" ); + return; + } + + if ( $fstype && ( $fstype !~ /(ext2|ext3|ext4|xfs)/i ) ) { + $out = "(Warning) File system type can only be ext2, ext3, ext4 or xfs" . "\n"; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + $error = 1; + } + + if ( $fstype && !$error ) { # Format the disk before making it active. + # link the disk + my $retry = 3; + my $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' ); + while ( $retry > 0 ) { + # wait 2 seconds for disk creation complete + sleep(2); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp LINK TO $userId $addr AS $vdev M 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + + if ($error) { + $retry -= 1; + $vdev = xCAT::zvmUtils->getFreeAddress( $::SUDOER, $hcp, 'vmcp' ); + } else { + last; + } + } + + # make the disk online and get it's device name + my $device = 0; + if ( !$error ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r $vdev &> /dev/null"`; + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $vdev ); + my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat /proc/dasd/devices | grep -a 0.0.$vdev"`; + chomp( $select ); + # A sample entry: + # 0.0.1300(FBA ) at ( 94: 56) is dasdo : active at blocksize: 512, 7291441 blocks, 3560 MB + if ( $select ) { + my @info = split( ' ', $select ); + $device = "/dev/" . $info[7]; + } + $error = !$device; + } + + #Delete the existing partition in case the disk already has partition on it + if ( !$error ) { + $out = +`ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk $device << EOF +d +w +EOF"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + } + + # Create one partition to use the entire disk space + if ( !$error ) { + $out = +`ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk $device << EOF +n +p +1 + + +w +EOF"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + } + if ( !$error ) { + $device .= '1'; + if ( $fstype =~ m/xfs/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs.xfs $device 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs -t $fstype $device 2>&1"`; + $error = !($out =~ m/done/); + } + } + if ( $error ) { + xCAT::zvmUtils->printSyslog("$out"); + $out = "(Warning) Can not format disk with fstype $fstype" . "\n"; + } else { + $out = ""; + } + + xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $vdev ); + `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null"`; + xCAT::zvmUtils->printSyslog("vmcp DETACH $vdev"); + } # Add to active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { - $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; + $out .= "Adding 9336 disk for $node as $addr ... Done"; + my $power = `/opt/xcat/bin/rpower $node stat`; + if ($power =~ m/: on/i) { + $out .= "\n" . `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); } - $out = xCAT::zvmUtils->appendHostname($node, $out); + + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # adddisk2pool [function] [region] [volume] [group] - elsif ($args->[0] eq "--adddisk2pool") { - + elsif ( $args->[0] eq "--adddisk2pool" ) { # This is no longer supported in chvm. Using chhypervisor instead. - changeHypervisor($callback, $node, $args); + changeHypervisor( $callback, $node, $args ); } # addzfcp2pool [pool] [status] [wwpn] [lun] [size] [owner (optional)] - elsif ($args->[0] eq "--addzfcp2pool") { - + elsif ( $args->[0] eq "--addzfcp2pool" ) { # This is no longer supported in chvm. Using chhypervisor instead. - changeHypervisor($callback, $node, $args); + changeHypervisor( $callback, $node, $args ); } # addnic [address] [type] [device count] - elsif ($args->[0] eq "--addnic") { + elsif ( $args->[0] eq "--addnic" ) { my $addr = $args->[1]; my $type = $args->[2]; my $devcount = $args->[3]; + my $allgood = 1; - # Add to active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { - $out = `ssh $::SUDOER\@$node "/sbin/vmcp define nic $addr type $type"`; + # Add to active configuration if possible + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "ping") { + #$out = `ssh $::SUDOER\@$node "/sbin/vmcp define nic $addr type $type"`; + my $cmd = "$::SUDO /sbin/vmcp define nic $addr type $type"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + # Check if error. + if ( $out =~ m/not created/i || $out =~ m/conflicting/i ) { + $allgood = 0; + xCAT::zvmUtils->printLn( $callback, "(Error) Failed in: define nic $addr type $type
" ); + } } + if ($allgood == 1) { + # Translate QDIO or Hipersocket into correct type + if ($type =~m/QDIO/i) { + $type = 2; + } elsif ($type =~m/HIPER/i) { + $type = 1; + } - # Translate QDIO or Hipersocket into correct type - if ($type =~ m/QDIO/i) { - $type = 2; - } elsif ($type =~ m/HIPER/i) { - $type = 1; + # Add to directory entry + $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Create_DM -T $userId -v $addr -a $type -n $devcount"`; + xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Create_DM -T $userId -v $addr -a $type -n $devcount"); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } - - # Add to directory entry - $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Create_DM -T $userId -v $addr -a $type -n $devcount"`; - xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Create_DM -T $userId -v $addr -a $type -n $devcount"); - $out = xCAT::zvmUtils->appendHostname($node, $out); } # addpagespool [vol_addr] [volume_label] [volume_use] [system_config_name (optional)] [system_config_type (optional)] [parm_disk_owner (optional)] [parm_disk_number (optional)] [parm_disk_password (optional)] - elsif ($args->[0] eq "--addpagespool") { + elsif ( $args->[0] eq "--addpagespool" ) { my $argsSize = @{$args}; my $i; my @options = ("", "vol_addr=", "volume_label=", "volume_use=", "system_config_name=", "system_config_type=", "parm_disk_owner=", "parm_disk_number=", "parm_disk_password="); my $argStr = ""; - foreach $i (1 .. $argsSize) { - if ($args->[$i]) { - $argStr .= " -k $args->[$i]"; + foreach $i ( 1 .. $argsSize ) { + if ( $args->[$i] ) { + $argStr .= " -k \"$options[$i]$args->[$i]\""; } } # Add a full volume page or spool disk to the system - $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Page_or_Spool_Volume_Add -T $userId $argStr"`; - xCAT::zvmUtils->printSyslog("smcli Page_or_Spool_Volume_Add -T $userId $argStr"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Page_or_Spool_Volume_Add -T $hcpUserId $argStr"`; + xCAT::zvmUtils->printSyslog("smcli Page_or_Spool_Volume_Add -T $hcpUserId $argStr"); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addprocessor [address] - elsif ($args->[0] eq "--addprocessor") { + elsif ( $args->[0] eq "--addprocessor" ) { my $addr = $args->[1]; $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_CPU_Define_DM -T $userId -v $addr -b 0 -d 1 -y 0"`; xCAT::zvmUtils->printSyslog("smcli Image_CPU_Define_DM -T $userId -v $addr -b 0 -d 1 -y 0"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addprocessoractive [address] [type] - elsif ($args->[0] eq "--addprocessoractive") { + elsif ( $args->[0] eq "--addprocessoractive" ) { my $addr = $args->[1]; my $type = $args->[2]; - $out = xCAT::zvmCPUtils->defineCpu($::SUDOER, $node, $addr, $type); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmCPUtils->defineCpu( $::SUDOER, $node, $addr, $type ); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addvdisk [device address] [size] - elsif ($args->[0] eq "--addvdisk") { + elsif ( $args->[0] eq "--addvdisk" ) { my $addr = $args->[1]; my $size = $args->[2]; my $mode = $args->[3]; + my $error = 0; + xCAT::zvmUtils->printSyslog("$node: smcli Image_Disk_Create_DM -T $userId -v $addr -t FB-512 -a V-DISK -r NONE -u 2 -z $size -m $mode -f 0"); $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $userId -v $addr -t FB-512 -a V-DISK -r NONE -u 2 -z $size -m $mode -f 0"`; - $out = xCAT::zvmUtils->appendHostname($node, $out); + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + if ($error) { + xCAT::zvmUtils->printLn( $callback, "$node: Error on Image_Disk_Create_DM $out" ); + xCAT::zvmUtils->printSyslog( $callback, "$node: Error on Image_Disk_Create_DM $out" ); + return; + } + + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addzfcp [pool] [device address (or auto)] [loaddev (0 or 1)] [size] [tag (optional)] [wwpn (optional)] [lun (optional)] - elsif ($args->[0] eq "--addzfcp") { + elsif ( $args->[0] eq "--addzfcp" ) { my $argsSize = @{$args}; - if (($argsSize != 5) && ($argsSize != 6) && ($argsSize != 8)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + if ( ($argsSize != 5) && ($argsSize != 6) && ($argsSize != 8) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - my $pool = lc($args->[1]); - my $device = $args->[2]; + my $pool = lc($args->[1]); + my $device = $args->[2]; my $loaddev = int($args->[3]); if ($loaddev != 0 && $loaddev != 1) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) The loaddev can be 0 or 1"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) The loaddev can be 0 or 1" ); return; } my $size = $args->[4]; - # Tag specifies what to replace in the autoyast/kickstart template, e.g. $root_device$ # This argument is optional my $tag = $args->[5]; # Check if WWPN and LUN are given # WWPN can be given as a semi-colon separated list - my $wwpn = ""; - my $lun = ""; + my $wwpn = ""; + my $lun = ""; my $useWwpnLun = 0; if ($argsSize == 8) { $useWwpnLun = 1; - $wwpn = $args->[6]; - $lun = $args->[7]; + $wwpn = $args->[6]; + $lun = $args->[7]; + if ($wwpn =~ m/;/) { + # It's not supported to ipl from a multipath device + if ($loaddev) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) It's not supported to ipl from a multipath device"); + return; + } + } } - # Find a suitable SCSI/FCP device in the zFCP storage pool - my %criteria; + my %zFCP; # Store zFCP device's original attributes here + my %criteria; # Store zFCP device's new attributes here my $resultsRef; if ($useWwpnLun) { + # Store current attributes of the SCSI/FCP device in case need to roll back when something goes wrong + my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun); + %zFCP = %$deviceRef; + + # Check current status of the FCP device + if ('used' eq $zFCP{'status'}) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) FCP device 0x$wwpn/0x$lun is in use."); + return; + } + %criteria = ( - 'status' => 'used', - 'fcp' => $device, - 'wwpn' => $wwpn, - 'lun' => $lun, - 'size' => $size, - 'owner' => $node, - 'tag' => $tag + 'status' => 'used', + 'fcp' => $device, + 'wwpn' => $wwpn, + 'lun' => $lun, + 'size' => $size, + 'owner' => $node, + 'tag' => $tag ); $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); } else { - # Do not know the WWPN or LUN in this case %criteria = ( - 'status' => 'used', - 'fcp' => $device, - 'size' => $size, - 'owner' => $node, - 'tag' => $tag + 'status' => 'used', + 'fcp' => $device, + 'size' => $size, + 'owner' => $node, + 'tag' => $tag ); $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + + # Store original attributes of the SCSI/FCP device in case need to roll back when something goes wrong + my %results = %$resultsRef; + %zFCP = ( + 'status' => 'free', + 'wwpn' => $results{'wwpn'}, + 'lun' => $results{'lun'}, + 'fcp' => '', + 'size' => $size, + 'owner' => '', + 'tag' => '' + ); } my %results = %$resultsRef; @@ -1071,109 +1488,150 @@ sub changeVM { # Obtain the device assigned by xCAT $device = $results{'fcp'}; - $wwpn = $results{'wwpn'}; - $lun = $results{'lun'}; + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; # Get user directory entry my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; - # Find DEDICATE statement in the entry (dedicate one if one does not exist) - my $dedicate = `echo "$userEntry" | egrep -i "DEDICATE $device"`; - if (!$dedicate) { - $out = `/opt/xcat/bin/chvm $node --dedicatedevice $device $device 0`; - xCAT::zvmUtils->printLn($callback, "$out"); - if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { + # Get source node OS + my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node); - # Exit if dedicate failed - return; + my @device_list = split(';', $device); + foreach (@device_list) { + # Find DEDICATE statement in the entry (dedicate one if one does not exist) + my $cur_device = $_; + if (!$cur_device) { next; } + my $dedicate = `echo "$userEntry" | egrep -a -i "DEDICATE $cur_device"`; + if (!$dedicate) { + # Remove FCP device address from CIO device blacklist + my $cmd = "$::SUDO /sbin/cio_ignore -r $cur_device"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + # (to-do) Add check for command fail. + + $out = `/opt/xcat/bin/chvm $node --dedicatedevice $cur_device $cur_device 0 2>&1`; + xCAT::zvmUtils->printLn($callback, "$out"); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + # Roll back. Undedicate FCP device and restore all attributes of the zFCP device + foreach (@device_list) { + `/opt/xcat/bin/chvm $node --undedicatedevice $_`; + } + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%zFCP); + # Exit if dedicate failed + return; + } + if ( $os =~ m/sles/i ) { + my $cmd = "$::SUDO /sbin/zfcp_host_configure 0.0.$cur_device 1"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + } elsif ( $os =~ m/ubuntu/i ) { + my $cmd = "$::SUDO /sbin/chzdev zfcp-host $cur_device -e"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + } } } # Configure native SCSI/FCP inside node (if online) - my $cmd; - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "ping") { + foreach (@device_list) { + my $cur_device = $_; + if (!$cur_device) { next; } + if ($os =~ m/ubuntu/i) { next; } - # Add the dedicated device to the active config - # Ignore any errors since it might be already dedicated - $out = `ssh $::SUDOER\@$node "$::SUDO $::DIR/smcli Image_Device_Dedicate -T $userId -v $device -r $device -R MR"`; - xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate -T $userId -v $device -r $device -R MR"); - - # Online device - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $node, "-e", "0.0." . $device); - if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { - xCAT::zvmUtils->printLn($callback, "$node: $out"); - return; + # Online device + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $node, "-e", "0.0." . $cur_device); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + xCAT::zvmUtils->printLn($callback, "$node: $out"); + # Roll back. Undedicate FCP device and restore all attributes of the zFCP device + foreach (@device_list) { + `/opt/xcat/bin/chvm $node --undedicatedevice $_`; + } + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%zFCP); + return; + } } # Set WWPN and LUN in sysfs - $device = lc($device); - $wwpn = lc($wwpn); + foreach (@device_list) { + my $cur_device = lc($_); + if (!$cur_device) { next; } + $wwpn = lc($wwpn); - # For the version above RHEL6 or SLES11, the port_add is removed - # Keep the code here for lower editions, of course, ignore the potential errors - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$wwpn > /sys/bus/ccw/drivers/zfcp/0.0.$device/port_add"); - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$device/0x$wwpn/unit_add"); + my @wwpn_list = split(";", $wwpn); + foreach (@wwpn_list) { + my $cur_wwpn = $_; + if (!$cur_wwpn) { next; } - # Get source node OS - my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node); + # For versions below RHEL6 or SLES11, they are not supported any more. + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$cur_device/0x$cur_wwpn/unit_add"); - # Set WWPN and LUN in configuration files - # RHEL: /etc/zfcp.conf - # SLES 10: /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-* - # SLES 11: /etc/udev/rules.d/51-zfcp* - my $tmp; - if ($os =~ m/sles10/i) { - $out = `ssh $::SUDOER\@$node "$::SUDO /sbin/zfcp_host_configure 0.0.$device 1"`; - if ($out) { - xCAT::zvmUtils->printLn($callback, "$node: $out"); + # Set WWPN and LUN in configuration files + my $tmp; + if ( $os =~ m/sles1[12]/i ) { + # SLES 11&12: /etc/udev/rules.d/51-zfcp* + my $cmd = "$::SUDO /sbin/zfcp_disk_configure 0.0.$cur_device $cur_wwpn $lun 1"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + # Check if the config file already exists and contains the zFCP channel + $cmd = $::SUDO . ' cat /etc/udev/rules.d/51-zfcp-0.0.' . $cur_device . '.rules | egrep -a -i ccw/0.0.' . $cur_device. ']online'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + # Configure zFCP device to be persistent + $cmd = "$::SUDO touch /etc/udev/rules.d/51-zfcp-0.0.$cur_device.rules"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + # Not configured before, do configuration, will not check for errors here + $tmp = 'ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.' . $cur_device . '\", IMPORT{program}=\"collect 0.0.' . $cur_device . ' \%k 0.0.' . $cur_device . ' zfcp\"'; + $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + + $tmp = 'ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.' . $cur_device . ' \%k 0.0.' . $cur_device . 'zfcp\"'; + $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + + $tmp = 'ACTION==\"add\", ENV{COLLECT_0.0.' . $cur_device . '}==\"0\", ATTR{[ccw/0.0.' . $cur_device . ']online}=\"1\"'; + $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + } + + # Will not check for errors here + $tmp = 'ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x' . $cur_wwpn . '\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.' . $cur_device . '\", ATTR{[ccw/0.0.' . $cur_device .']0x' . $cur_wwpn . '/unit_add}=\"0x' . $lun . '\"'; + $cmd = 'echo '. $tmp . '>> /etc/udev/rules.d/51-zfcp-0.0.'. $cur_device .'.rules'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + + } elsif ( $os =~ m/rhel/i ) { + # RHEL: /etc/zfcp.conf + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo \"0.0.$cur_device 0x$cur_wwpn 0x$lun\" >> /etc/zfcp.conf"); + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo add > /sys/bus/ccw/devices/0.0.$cur_device/uevent"); + + } elsif ( $os =~ m/ubuntu/i ) { + # Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -e + my $cmd = "$::SUDO /sbin/chzdev zfcp-lun 0.0.$cur_device:0x$cur_wwpn:0x$lun -e"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + } } + } - $out = `ssh $::SUDOER\@$node "$::SUDO /sbin/zfcp_disk_configure 0.0.$device $wwpn $lun 1"`; - if ($out) { - xCAT::zvmUtils->printLn($callback, "$node: $out"); - } - - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$wwpn:0x$lun >> /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$device"); - } elsif ($os =~ m/sles/i) { - $out = `ssh $::SUDOER\@$node "$::SUDO /sbin/zfcp_host_configure 0.0.$device 1"`; - if ($out) { - xCAT::zvmUtils->printLn($callback, "$node: $out"); - } - - $out = `ssh $::SUDOER\@$node "$::SUDO /sbin/zfcp_disk_configure 0.0.$device $wwpn $lun 1"`; - if ($out) { - xCAT::zvmUtils->printLn($callback, "$node: $out"); - } - - # Configure zFCP device to be persistent - $out = `ssh $::SUDOER\@$node "$::SUDO touch /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - - # Check if the file already contains the zFCP channel - $out = `ssh $::SUDOER\@$node "$::SUDO cat /etc/udev/rules.d/51-zfcp-0.0.$device.rules" | egrep -i "ccw/0.0.$device]online"`; - if (!$out) { - $tmp = "'ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.$device\", IMPORT{program}=\"collect 0.0.$device \%k 0.0.$device zfcp\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - - $tmp = "'ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.$device \%k 0.0.$device zfcp\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - - $tmp = "'ACTION==\"add\", ENV{COLLECT_0.0.$device}==\"0\", ATTR{[ccw/0.0.$device]online}=\"1\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - } - - $tmp = "'ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x$wwpn\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.$device\", ATTR{[ccw/0.0.$device]0x$wwpn/unit_add}=\"0x$lun\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - } elsif ($os =~ m/rhel/i) { - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo \"0.0.$device 0x$wwpn 0x$lun\" >> /etc/zfcp.conf"); - - if ($os =~ m/rhel6/i) { - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo add > /sys/bus/ccw/devices/0.0.$device/uevent"); - } + my $cmd = "$::SUDO /sbin/multipath -r"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if ($out) { + # not a fatal error, print a message and continue + xCAT::zvmUtils->printLn($callback, "$node: $out"); } xCAT::zvmUtils->printLn($callback, "$node: Configuring FCP device to be persistent... Done"); @@ -1184,7 +1642,7 @@ sub changeVM { if ($loaddev) { $out = `/opt/xcat/bin/chvm $node --setloaddev $wwpn $lun`; xCAT::zvmUtils->printLn($callback, "$out"); - if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to set LOADDEV statement in the directory entry"); return; } @@ -1195,48 +1653,55 @@ sub changeVM { } # connectnic2guestlan [address] [lan] [owner] - elsif ($args->[0] eq "--connectnic2guestlan") { + elsif ( $args->[0] eq "--connectnic2guestlan" ) { my $addr = $args->[1]; my $lan = $args->[2]; my $owner = $args->[3]; # Connect to LAN in active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { + my $power = `/opt/xcat/bin/rpower $node stat`; + if ($power =~ m/: on/i) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_LAN -T $userId -v $addr -l $lan -o $owner"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_LAN -T $userId -v $addr -l $lan -o $owner"); } $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_LAN_DM -T $userId -v $addr -n $lan -o $owner"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_LAN_DM -T $userId -v $addr -n $lan -o $owner"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # connectnic2vswitch [address] [vSwitch] - elsif ($args->[0] eq "--connectnic2vswitch") { + elsif ( $args->[0] eq "--connectnic2vswitch" ) { my $addr = $args->[1]; my $vswitch = $args->[2]; + my $vswitchPortType = ''; + my $vswitchLanId = ''; + my $argsSize = @{$args}; + if ($argsSize > 3 ) { + $vswitchPortType = $args->[3]; + $vswitchLanId = $args->[4]; + } # Grant access to VSWITCH for Linux user - $out = xCAT::zvmCPUtils->grantVSwitch($callback, $::SUDOER, $hcp, $userId, $vswitch); - xCAT::zvmUtils->printLn($callback, "$node: Granting VSwitch ($vswitch) access for $userId... $out"); + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $vswitch, $vswitchPortType, $vswitchLanId ); + xCAT::zvmUtils->printLn( $callback, "$node: Granting VSwitch ($vswitch $vswitchPortType $vswitchLanId) access for $userId... $out" ); # Connect to VSwitch in directory entry $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_Vswitch_DM -T $userId -v $addr -n $vswitch"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_Vswitch_DM -T $userId -v $addr -n $vswitch"); # Connect to VSwitch in active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { + my $power = `/opt/xcat/bin/rpower $node stat`; + if ($power =~ m/: on/i) { $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_Vswitch -T $userId -v $addr -n $vswitch"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_Vswitch -T $userId -v $addr -n $vswitch"); } - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # copydisk [target address] [source node] [source address] - elsif ($args->[0] eq "--copydisk") { + elsif ( $args->[0] eq "--copydisk" ) { my $tgtNode = $node; my $tgtUserId = $userId; my $tgtAddr = $args->[1]; @@ -1244,21 +1709,21 @@ sub changeVM { my $srcAddr = $args->[3]; # Get source userID - @propNames = ('hcp', 'userid'); - $propVals = xCAT::zvmUtils->getNodeProps('zvm', $srcNode, @propNames); + @propNames = ( 'hcp', 'userid' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $srcNode, @propNames ); my $sourceId = $propVals->{'userid'}; # Assume flashcopy is supported (via SMAPI) - xCAT::zvmUtils->printLn($callback, "$tgtNode: Copying $sourceId disk ($srcAddr) to $tgtUserId disk ($srcAddr) using FLASHCOPY"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying $sourceId disk ($srcAddr) to $tgtUserId disk ($srcAddr) using FLASHCOPY" ); if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) { - $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $srcAddr, $tgtUserId, $srcAddr); - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $srcAddr, $tgtUserId, $srcAddr); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); - # Exit if flashcopy completed successfully - # Otherwsie, try CP FLASHCOPY - if ($out =~ m/Done/i) { - return; - } + # Exit if flashcopy completed successfully + # Otherwise try CP FLASHCOPY + if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) { + return; + } } #*** Link and copy disk *** @@ -1270,31 +1735,30 @@ sub changeVM { # Link source disk to HCP my $srcLinkAddr; $try = 5; - while ($try > 0) { - + while ( $try > 0 ) { # New disk address $srcLinkAddr = $srcAddr + 1000; # Check if new disk address is used (source) - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $srcLinkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $srcLinkAddr ); # If disk address is used (source) - while ($rc == 0) { + while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $srcLinkAddr = $srcLinkAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $srcLinkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $srcLinkAddr ); } # Link source disk # Because the zHCP has LNKNOPAS, no disk password is required - xCAT::zvmUtils->printLn($callback, "$tgtNode: Linking source disk ($srcAddr) as ($srcLinkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking source disk ($srcAddr) as ($srcLinkAddr)" ); $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $sourceId $srcAddr $srcLinkAddr RR"`; # If link fails - if ($out =~ m/not linked/i) { + if ( $out =~ m/not linked/i ) { # Wait before trying again sleep(5); @@ -1306,9 +1770,9 @@ sub changeVM { } # End of while ( $try > 0 ) # If source disk is not linked - if ($out =~ m/not linked/i) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Failed to link source disk ($srcAddr)"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Failed"); + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link source disk ($srcAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Exit return; @@ -1317,31 +1781,31 @@ sub changeVM { # Link target disk to HCP my $tgtLinkAddr; $try = 5; - while ($try > 0) { + while ( $try > 0 ) { # New disk address $tgtLinkAddr = $tgtAddr + 2000; # Check if new disk address is used (target) - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $tgtLinkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr ); # If disk address is used (target) - while ($rc == 0) { + while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $tgtLinkAddr = $tgtLinkAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $tgtLinkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr ); } # Link target disk # Because the zHCP has LNKNOPAS, no disk password is required - xCAT::zvmUtils->printLn($callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" ); $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $tgtAddr $tgtLinkAddr MR"`; # If link fails - if ($out =~ m/not linked/i) { + if ( $out =~ m/not linked/i ) { # Wait before trying again sleep(5); @@ -1353,9 +1817,9 @@ sub changeVM { } # End of while ( $try > 0 ) # If target disk is not linked - if ($out =~ m/not linked/i) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Failed"); + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Detatch disks from HCP $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`; @@ -1371,7 +1835,7 @@ sub changeVM { # Check for CP flashcopy lock my $wait = 0; - while (`ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"` && $wait < 90) { + while ( `ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"` && $wait < 90 ) { # Wait until the lock dissappears # 90 seconds wait limit @@ -1386,8 +1850,8 @@ sub changeVM { $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`; - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Flashcopy lock is enabled"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Solution) Remove lock by deleting /tmp/.flashcopy_lock on the zHCP. Use caution!"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Flashcopy lock is enabled" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Remove lock by deleting /tmp/.flashcopy_lock on the zHCP. Use caution!" ); return; } else { @@ -1395,10 +1859,10 @@ sub changeVM { $out = `ssh $::SUDOER\@$hcp "$::SUDO touch /tmp/.flashcopy_lock"`; # Flashcopy source disk - $out = xCAT::zvmCPUtils->flashCopy($::SUDOER, $hcp, $srcLinkAddr, $tgtLinkAddr); - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + $out = xCAT::zvmCPUtils->flashCopy( $::SUDOER, $hcp, $srcLinkAddr, $tgtLinkAddr ); + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); # Try using Linux DD $ddCopy = 1; @@ -1413,13 +1877,12 @@ sub changeVM { # Flashcopy not supported, use Linux dd if ($ddCopy) { - #*** Use Linux dd to copy *** - xCAT::zvmUtils->printLn($callback, "$tgtNode: FLASHCOPY not working. Using Linux DD"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not working. Using Linux DD" ); # Enable disks - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", $tgtLinkAddr); - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", $srcLinkAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtLinkAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $srcLinkAddr ); # Determine source device node $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcLinkAddr); @@ -1428,13 +1891,13 @@ sub changeVM { $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtLinkAddr); # Format target disk - xCAT::zvmUtils->printLn($callback, "$tgtNode: Formating target disk ($tgtDevNode)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; # Check for errors - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); # Detatch disks from HCP $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; @@ -1447,21 +1910,21 @@ sub changeVM { sleep(2); # Automatically create a partition using the entire disk - xCAT::zvmUtils->printLn($callback, "$tgtNode: Creating a partition using the entire disk ($tgtDevNode)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating a partition using the entire disk ($tgtDevNode)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdasd -a /dev/$tgtDevNode"`; # Copy source disk to target disk (4096 block size) - xCAT::zvmUtils->printLn($callback, "$tgtNode: Copying source disk ($srcDevNode) to target disk ($tgtDevNode)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcDevNode) to target disk ($tgtDevNode)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync"`; # Disable disks - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-d", $tgtLinkAddr); - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-d", $srcLinkAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtLinkAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $srcLinkAddr ); # Check for error - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); # Detatch disks from HCP $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; @@ -1475,8 +1938,8 @@ sub changeVM { } # Detatch disks from HCP - xCAT::zvmUtils->printLn($callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Detatching source disk ($srcLinkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching source disk ($srcLinkAddr)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $srcLinkAddr"`; @@ -1484,7 +1947,7 @@ sub changeVM { } # createfilesysnode [source file] [target file] - elsif ($args->[0] eq "--createfilesysnode") { + elsif ( $args->[0] eq "--createfilesysnode" ) { my $srcFile = $args->[1]; my $tgtFile = $args->[2]; @@ -1494,58 +1957,196 @@ sub changeVM { return; } - $out = `ssh $::SUDOER\@$node "$::SUDO /usr/bin/stat --printf=%n $tgtFile"`; + # Obtain corresponding WWPN and LUN of source file. + # A sample url is '/dev/disk/by-path/ccw-0.0.1fb3-zfcp-0x5005076801102991:0x0021000000000000'. + # (On Ubuntu it's '/dev/disk/by-path/ccw-0.0.1fb3-fc-0x5005076801102991-lun-33') + # If it's multipath, it could be '0x5005076801102991;5005076801102992;5005076801102993' instead of '0x5005076801102991'. + # Please note the url is not an actual device path. It's a parameter used to handle devices and the parameter + # appears like a device path. + # For the pattern '-zfcp-([\w;]+):(\w+)', '-zfcp-' is used to locate, ([\w;]+) will capture wwpn and store it into + # variable $1, ':' is the delimiter and (\w+) will capture lun and store it into variable $2. The pattern for wwpn + # and lun is different because wwpn may appear in multipath format but lun could only be one number. + + # Get source node OS + my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node); + if (xCAT::zvmUtils->checkOutput($os) == -1) { + return; + } + + my ($devices, $wwpn, $lun); + if ( $os =~ m/ubuntu/i ) { + $srcFile =~ m/ccw-0.0.([\w;]+)-fc-([\w;]+)-lun-(\w+)/; + ($devices, $wwpn, $lun) = ($1, $2, $3); + } else { + $srcFile =~ m/ccw-0.0.([\w;]+)-zfcp-([\w;]+):(\w+)/; + ($devices, $wwpn, $lun) = ($1, $2, $3); + } + + my $multipath = 0; + if ($wwpn =~ m/;/) { $multipath = 1; } + my @deviceList = split(";", $devices); + + my $cmd = "$::SUDO /usr/bin/stat --printf=%n $tgtFile"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); if ($out eq $tgtFile) { xCAT::zvmUtils->printLn($callback, "$node: (Error) $tgtFile already exists"); return; } - $out = `ssh $::SUDOER\@$node "$::SUDO /usr/bin/stat --printf=%n $srcFile"`; - if ($out ne $srcFile) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) $srcFile does not exist"); + # Use udev tools to create mountpoint. If it's signal path device, its devices path + # is enough for udev. If it's multipath device, multipath device path are involved, + # so we have to figure out its WWID and use the WWID to create mountpoint. + my $wwid = ''; + if ($multipath) { + # Find the name of the multipath device by arbitrary one path in the set + my @wwpnList = split(";", $wwpn); + my $curWwpn = ''; + foreach (@wwpnList) { + if ($_ =~ m/^0x/i) { + $curWwpn = $_; + } else { + $curWwpn = "0x$_"; + } + # Try to get WWID by current WWPN. + foreach (@deviceList) { + my $cur_device = $_; + if ( $os =~ m/ubuntu/i ) { + $srcFile =~ s/ccw-0.0.[0-9a-f;]+-fc-0x[0-9a-f;]+-lun/ccw-0.0.$cur_device-fc-$curWwpn-lun/i; + } else { + $srcFile =~ s/ccw-0.0.[0-9a-f;]+-zfcp-0x[0-9a-f;]+:/ccw-0.0.$cur_device-zfcp-$curWwpn:/i; + } + my $cmd = "$::SUDO /usr/bin/stat --printf=%n $srcFile"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if ($out ne $srcFile) { + xCAT::zvmUtils->printLn($callback, "$node: (Warning) $srcFile does not exist"); + next; + } + + $cmd = $::SUDO . ' /sbin/udevadm info --query=all --name=' . $srcFile . ' | grep -a "ID_SERIAL="'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + $out =~ m/ID_SERIAL=(\w+)\s*$/; + $wwid = $1; + if ($wwid) { last; } + } + if ($wwid) { last; } + } + if (!$wwid) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) zfcp device $wwpn:$lun not found in OS"); + return; + } + } else { + my $isFound = 0; + foreach (@deviceList) { + my $cur_device = $_; + if ( $os =~ m/ubuntu/i ) { + $srcFile =~ s/ccw-0.0.[0-9a-f;]+-fc/ccw-0.0.$cur_device-fc/i; + } else { + $srcFile =~ s/ccw-0.0.[0-9a-f;]+-zfcp/ccw-0.0.$cur_device-zfcp/i; + } + + my $cmd = "$::SUDO /usr/bin/stat --printf=%n $srcFile"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if ($out ne $srcFile) { + xCAT::zvmUtils->printLn($callback, "$node: (warning) $srcFile does not exist"); + } else { + $isFound = 1; + } + } + if (!$isFound) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) zfcp device $wwpn:$lun not found in OS"); + return; + } + } + + # Create udev config file if not exist + my $configFile = '/etc/udev/rules.d/56-zfcp.rules'; + $cmd = "$::SUDO test -e $configFile && echo Exists"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if (!($out)) { + $cmd = "$::SUDO touch $configFile"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + if ( $os =~ m/rhel/i ) { + # will not check for errors here + my $zfcp_rule = 'KERNEL==\"zfcp\", RUN+=\"/sbin/zfcpconf.sh\"'; + my $multipath_rule = 'KERNEL==\"zfcp\", RUN+=\"/sbin/multipath -r\"'; + $cmd = $::SUDO . ' echo '. $zfcp_rule . ' >> ' . $configFile; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + $cmd = $::SUDO . ' echo ' . $multipath_rule . ' >> ' . $configFile; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + } + } + + # Add the entry into udev config file + my ( $create_symlink_cmd, $reload_cmd, $update_rule_cmd ); + my $tgtFileName = $tgtFile; + $tgtFileName =~ s/\/dev\///; + if ($multipath) { + my $linkItem = 'KERNEL==\"dm-*\", ENV{DM_UUID}==\"mpath-' . $wwid . '\", SYMLINK+=\"' . $tgtFileName . '\"'; + $update_rule_cmd = $::SUDO . ' echo '. $linkItem . '>>' . $configFile; + $reload_cmd = "$::SUDO udevadm control --reload"; + $create_symlink_cmd = "$::SUDO udevadm trigger --sysname-match=dm-*"; + } else { + my $linkItem = 'KERNEL==\"sd*\", ATTRS{wwpn}==\"' . $wwpn . '\", ATTRS{fcp_lun}==\"' . $lun . '\", SYMLINK+=\"' . $tgtFileName . '\%n\"'; + $update_rule_cmd = $::SUDO . ' echo '. $linkItem . '>>' . $configFile; + $reload_cmd = "$::SUDO udevadm control --reload"; + $create_symlink_cmd = "$::SUDO udevadm trigger --sysname-match=sd*"; + } + + $cmd = "$update_rule_cmd ; $reload_cmd ; $create_symlink_cmd"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return; } - $out = `ssh $::SUDOER\@$node "$::SUDO /usr/bin/stat -L --printf=%t:%T $srcFile"`; - if ($out != '') { - my @device = split(":", $out); - my $major = sprintf("%d", hex($device[0])); - my $minor = sprintf("%d", hex($device[1])); - $out = `ssh $::SUDOER\@$node "$::SUDO /bin/mknod $tgtFile b $major $minor "`; - } + xCAT::zvmUtils->printLn($callback, "$node: Creating file system node $tgtFile... Done"); } # dedicatedevice [virtual device] [real device] [mode (1 or 0)] - elsif ($args->[0] eq "--dedicatedevice") { + elsif ( $args->[0] eq "--dedicatedevice" ) { my $vaddr = $args->[1]; my $raddr = $args->[2]; my $mode = $args->[3]; + my $argsSize = @{$args}; + if ($argsSize != 4) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + return; + } + my $doActive = 1; + # Dedicate device to directory entry $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Dedicate_DM -T $userId -v $vaddr -r $raddr -R $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate_DM -T $userId -v $vaddr -r $raddr -R $mode"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + if (($out =~ m/Return Code: 404/i) and ($out =~ m/Reason Code: 4/i)) { + $out = "Dedicating device $raddr to $userId" . "'s directory entry... Done"; + $doActive = 0; # Have already been defined before, no need to make active in this case. + } + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Dedicate device to active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { + my $power = `/opt/xcat/bin/rpower $node stat`; + if (($power =~ m/: on/i) and $doActive) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Dedicate -T $userId -v $vaddr -r $raddr -R $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate -T $userId -v $vaddr -r $raddr -R $mode"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } $out = ""; } # deleteipl - elsif ($args->[0] eq "--deleteipl") { + elsif ( $args->[0] eq "--deleteipl" ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_IPL_Delete_DM -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_IPL_Delete_DM -T $userId"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # formatdisk [address] [multi password] - elsif ($args->[0] eq "--formatdisk") { + elsif ( $args->[0] eq "--formatdisk" ) { my $tgtNode = $node; my $tgtUserId = $userId; my $tgtAddr = $args->[1]; @@ -1558,33 +2159,32 @@ sub changeVM { # Link target disk to zHCP my $tgtLinkAddr; $try = 5; - while ($try > 0) { + while ( $try > 0 ) { # New disk address $tgtLinkAddr = $tgtAddr + 1000; # Check if new disk address is used (target) - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $tgtLinkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr ); # If disk address is used (target) - while ($rc == 0) { + while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $tgtLinkAddr = $tgtLinkAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $tgtLinkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtLinkAddr ); } # Link target disk - xCAT::zvmUtils->printLn($callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" ); $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $tgtAddr $tgtLinkAddr MR"`; # If link fails - if ($out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i) { - + if ( $out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i ) { # Detatch link because only linked as R/O -`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; + `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; # Wait before trying again sleep(5); @@ -1596,9 +2196,9 @@ sub changeVM { } # End of while ( $try > 0 ) # If target disk is not linked - if ($out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Failed"); + if ( $out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Detatch link because only linked as R/O `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; @@ -1609,78 +2209,86 @@ sub changeVM { #*** Format disk *** my @words; - if ($rc == -1) { + if ( $rc == -1 ) { # Enable disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", $tgtLinkAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtLinkAddr ); # Determine target device node $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtLinkAddr); # Format target disk - xCAT::zvmUtils->printLn($callback, "$tgtNode: Formating target disk ($tgtDevNode)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; # Check for errors - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } } # Disable disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-d", $tgtLinkAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtLinkAddr ); # Detatch disk from HCP - xCAT::zvmUtils->printLn($callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; $out = "$tgtNode: Done"; } # grantvswitch [VSwitch] - elsif ($args->[0] eq "--grantvswitch") { + elsif ( $args->[0] eq "--grantvswitch" ) { my $vsw = $args->[1]; + my $vswitchPortType = ''; + my $vswitchLanId = ''; - $out = xCAT::zvmCPUtils->grantVSwitch($callback, $::SUDOER, $hcp, $userId, $vsw); - $out = xCAT::zvmUtils->appendHostname($node, "Granting VSwitch ($vsw) access for $userId... $out"); + my $argsSize = @{$args}; + if ($argsSize > 2 ) { + $vswitchPortType = $args->[2]; + $vswitchLanId = $args->[3]; + } + + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $vsw, $vswitchPortType, $vswitchLanId ); + $out = xCAT::zvmUtils->appendHostname( $node, "Granting VSwitch ($vsw) access for $userId... $out" ); } # disconnectnic [address] - elsif ($args->[0] eq "--disconnectnic") { + elsif ( $args->[0] eq "--disconnectnic" ) { my $addr = $args->[1]; $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Disconnect_DM -T $userId -v $addr"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Disconnect_DM -T $userId -v $addr"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # punchfile [file path] [class (optional)] [remote host (optional)] - elsif ($args->[0] eq "--punchfile") { - + elsif ( $args->[0] eq "--punchfile" ) { # Punch a file to a the node reader my $argsSize = @{$args}; if (($argsSize < 2) || ($argsSize > 4)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } my $filePath = $args->[1]; - my $class = "A"; # Default spool class should be A + my $class = "A"; # Default spool class should be A my $remoteHost; if ($argsSize > 2) { $class = $args->[2]; - } if ($argsSize > 3) { - $remoteHost = $args->[3]; # Must be specified as user@host } - + if ($argsSize > 3) { + $remoteHost = $args->[3]; # Must be specified as user@host + } # Obtain file name my $fileName = basename($filePath); + my $trunkFile = "/tmp/$node-$fileName"; # Validate class if ($class !~ /^[a-zA-Z0-9]$/) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid spool class: $class. It should be 1-character alphanumeric"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid spool class: $class. It should be 1-character alphanumeric" ); return; } @@ -1688,139 +2296,164 @@ sub changeVM { # The xCAT public SSH key must have been already setup if this is to work my $rc; if (defined $remoteHost) { - $rc = `/usr/bin/scp $remoteHost:$filePath /tmp/$fileName 2>/dev/null; echo $?`; + $rc = `/usr/bin/scp $remoteHost:$filePath $trunkFile 2>/dev/null; echo $?`; } else { - $rc = `/bin/cp $filePath /tmp/$fileName 2>/dev/null; echo $?`; + $rc = `/bin/cp $filePath $trunkFile 2>/dev/null; echo $?`; } if ($rc != '0') { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to copy over source file"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source file" ); return; } - # Set up punch device and class - $rc = `ssh $::SUDOER\@$hcp "$::SUDO cio_ignore -r d"`; - xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d"); - $rc = `ssh $::SUDOER\@$hcp "$::SUDO vmcp spool punch class $class"`; + # Check the node flag, if the node has xcatconf4z flag set, punch it directly. + # Otherwise, put files into a temp directory for later use. + my $nodeFlag = ''; + my $cfgTrunkDir = "/tmp/configdrive/$node/"; + my @propNames = ('status'); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames ); + $nodeFlag = $propVals->{'status'}; + if ($nodeFlag =~ /XCATCONF4Z=0/) { + if (!-d $cfgTrunkDir) { + mkpath($cfgTrunkDir); + } + $rc = `/bin/cp $trunkFile $cfgTrunkDir/$fileName 2>/dev/null; echo $?`; + `rm -rf $trunkFile`; + if ($rc != '0') { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source file $trunkFile to directory $cfgTrunkDir, please check if xCAT is running out of space" ); + return; + } - # Send over file to zHCP and punch it to the node reader - $filePath = "/tmp/$fileName"; - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $filePath, $filePath); - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $filePath, $fileName, ""); + } else { + # Set up punch device + $rc = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r d"`; + xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d"); - # No extra steps are needed if the punch succeeded or failed, just output the results - xCAT::zvmUtils->printLn($callback, "$node: Punching $fileName to reader... $out"); + # Send over file to zHCP and punch it to the node's reader + xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $trunkFile, $trunkFile); + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $trunkFile, $fileName, "", $class ); - # Remove temporary file and restore punch class - `rm -rf $filePath`; - `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/$fileName"`; - `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp spool punch class A"`; - $out = ""; + # No extra steps are needed if the punch succeeded or failed, just output the results + xCAT::zvmUtils->printLn( $callback, "$node: Punching $fileName to reader... $out" ); + + # Remove temporary file + `rm -rf $trunkFile`; + `ssh $::SUDOER\@$hcp "$::SUDO rm -f $trunkFile"`; + $out = ""; + } } # purgerdr - elsif ($args->[0] eq "--purgerdr") { - + elsif ( $args->[0] eq "--purgerdr" ) { # Purge the reader of node $out = xCAT::zvmCPUtils->purgeReader($::SUDOER, $hcp, $userId); - $out = xCAT::zvmUtils->appendHostname($node, "$out"); + $out = xCAT::zvmUtils->appendHostname( $node, "$out" ); } # removediskfrompool [function] [region] [group] - elsif ($args->[0] eq "--removediskfrompool") { - + elsif ( $args->[0] eq "--removediskfrompool" ) { # This is no longer supported in chvm. Using chhypervisor instead. - changeHypervisor($callback, $node, $args); + changeHypervisor( $callback, $node, $args ); } # removezfcpfrompool [pool] [lun] - elsif ($args->[0] eq "--removezfcpfrompool") { - + elsif ( $args->[0] eq "--removezfcpfrompool" ) { # This is no longer supported in chvm. Using chhypervisor instead. - changeHypervisor($callback, $node, $args); + changeHypervisor( $callback, $node, $args ); } # removedisk [virtual address] - elsif ($args->[0] eq "--removedisk") { + elsif ( $args->[0] eq "--removedisk" ) { my $addr = $args->[1]; # Remove from active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $node, "-d", $addr); + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "ping") { + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $node, "-d", $addr ); $out = `ssh $node "/sbin/vmcp det $addr"`; } # Remove from user directory entry $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Delete_DM -T $userId -v $addr -e 0"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Delete_DM -T $userId -v $addr -e 0"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # removefilesysnode [target file] - elsif ($args->[0] eq "--removefilesysnode") { + elsif ( $args->[0] eq "--removefilesysnode" ) { my $tgtFile = $args->[1]; my $argsSize = @{$args}; if ($argsSize != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - # Unmount this disk, but ignore the output - $out = `ssh $::SUDOER\@$node "$::SUDO umount $tgtFile"`; - $out = `ssh $::SUDOER\@$node "$::SUDO rm -f $tgtFile"`; + # Unmount this disk and remove this disk from udev config file, but ignore the output + my $configFile = '/etc/udev/rules.d/56-zfcp.rules'; + my $tgtFileName = $tgtFile; + $tgtFileName =~ s/\/dev\///; + my $update_rule_cmd_sd = $::SUDO . ' sed -i -e /SYMLINK+=\"' . $tgtFileName . '\%n\"/d ' . $configFile; # For single device + my $update_rule_cmd_dm = $::SUDO . ' sed -i -e /SYMLINK+=\"' . $tgtFileName . '\"/d ' . $configFile; # For multipath + my $reload_cmd = "$::SUDO udevadm control --reload"; + my $create_symlink_cmd_sd = "$::SUDO udevadm trigger --sysname-match=sd*"; # For single device + my $create_symlink_cmd_dm = "$::SUDO udevadm trigger --sysname-match=dm-*"; # For multipath + + my $cmd = "$update_rule_cmd_sd ; $update_rule_cmd_dm ; $reload_cmd ; $create_symlink_cmd_sd ; $create_symlink_cmd_dm"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } xCAT::zvmUtils->printLn($callback, "$node: Removing file system node $tgtFile... Done"); } # removenic [address] - elsif ($args->[0] eq "--removenic") { + elsif ( $args->[0] eq "--removenic" ) { my $addr = $args->[1]; # Remove from active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "ping") { $out = `ssh $node "/sbin/vmcp det nic $addr"`; } # Remove from user directory entry $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Delete_DM -T $userId -v $addr"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Delete_DM -T $userId -v $addr"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # removeprocessor [address] - elsif ($args->[0] eq "--removeprocessor") { + elsif ( $args->[0] eq "--removeprocessor" ) { my $addr = $args->[1]; # Remove from user directory entry $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_CPU_Delete_DM -T $userId -v $addr"`; xCAT::zvmUtils->printSyslog("smcli Image_CPU_Delete_DM -T $userId -v $addr"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # removeloaddev [wwpn] [lun] - elsif ($args->[0] eq "--removeloaddev") { + elsif ( $args->[0] eq "--removeloaddev" ) { my $wwpn = $args->[1]; - my $lun = $args->[2]; + my $lun = $args->[2]; xCAT::zvmUtils->printLn($callback, "$node: Removing LOADDEV directory statements"); # Make sure WWPN and LUN do not have 0x prefix - $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $wwpn = xCAT::zvmUtils->replaceStr( $wwpn, "0x", "" ); + $lun = xCAT::zvmUtils->replaceStr( $lun, "0x", "" ); # Get user directory entry - my $updateEntry = 0; + my $updateEntry = 0; my $userEntryFile = "/tmp/$node.txt"; my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'"); chomp($userEntry); if (!$wwpn && !$lun) { - # If no WWPN or LUN is provided, delete all LOADDEV statements - `echo "$userEntry" | grep -v "LOADDEV" > $userEntryFile`; + `echo "$userEntry" | grep -a -v "LOADDEV" > $userEntryFile`; $updateEntry = 1; } else { @@ -1828,9 +2461,9 @@ sub changeVM { `rm -rf $userEntryFile`; # Remove LOADDEV PORTNAME and LUN statements in directory entry - my @lines = split('\n', $userEntry); + my @lines = split( '\n', $userEntry ); foreach (@lines) { - + if (!(length $_)) {next;} # Check if LOADDEV PORTNAME and LUN statements are in the directory entry if ($_ =~ m/LOADDEV PORTNAME $wwpn/i) { $updateEntry = 1; @@ -1839,7 +2472,6 @@ sub changeVM { $updateEntry = 1; next; } else { - # Write directory entry to file `echo "$_" >> $userEntryFile`; } @@ -1848,7 +2480,7 @@ sub changeVM { # Replace user directory entry (if necessary) if ($updateEntry) { - $out = `/opt/xcat/bin/chvm $node --replacevs $userEntryFile`; + $out = `/opt/xcat/bin/chvm $node --replacevs $userEntryFile 2>&1`; xCAT::zvmUtils->printLn($callback, "$out"); # Delete directory entry file @@ -1860,16 +2492,16 @@ sub changeVM { $out = ""; } - # removezfcp [device address] [wwpn] [lun] [persist (0 or 1)] - elsif ($args->[0] eq "--removezfcp") { - my $device = $args->[1]; - my $wwpn = $args->[2]; - my $lun = $args->[3]; - my $persist = "0"; # Optional + # removezfcp [device address] [wwpn] [lun] [persist (0 or 1) (optional)] + elsif ( $args->[0] eq "--removezfcp" ) { + my $device = $args->[1]; + my $wwpn = $args->[2]; + my $lun = $args->[3]; + my $persist = "0"; # Optional # Delete 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); my $argsSize = @{$args}; if ($argsSize != 4 && $argsSize != 5) { @@ -1891,36 +2523,37 @@ sub changeVM { # Find the pool that contains the SCSI/FCP device my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun); if (!$pool) { - # Continue to try and remove the SCSI/FCP device even when it is not found in a storage pool - xCAT::zvmUtils->printLn($callback, "$node: Could not find FCP device in any FCP storage pool"); + xCAT::zvmUtils->printLn( $callback, "$node: Could not find FCP device in any FCP storage pool" ); } else { - xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool"); + xCAT::zvmUtils->printLn( $callback, "$node: Found FCP device in $pool" ); + my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | grep -a -i "$wwpn,$lun"`; + chomp($select); + my @info = split(',', $select); + + # A node can only remove a zFCP device that belongs to itself + if ( ('used' eq $info[0]) && ($node ne $info[5]) ) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) The zFCP device 0x$wwpn/0x$lun does not belong to the node $node."); + return; + } # If the device is not known, try to find it in the storage pool - if ($device !~ /^[0-9a-f]/i) { - my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | grep -i "$wwpn,$lun"`; - chomp($select); - my @info = split(',', $select); - if ($device) { - $device = $info[6]; - } + if ($device && $device !~ /^[0-9a-f;]/i) { + $device = $info[6]; } my $status = "free"; - my $owner = ""; + my $owner = ""; if ($persist) { - # Keep the device reserved if persist = 1 $status = "reserved"; - $owner = $node; } my %criteria = ( - 'status' => $status, - 'wwpn' => $wwpn, - 'lun' => $lun, - 'owner' => $owner, + 'status' => $status, + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $owner, ); my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); my %results = %$resultsRef; @@ -1932,39 +2565,66 @@ sub changeVM { # Obtain the device assigned by xCAT $wwpn = $results{'wwpn'}; - $lun = $results{'lun'}; + $lun = $results{'lun'}; } # De-configure SCSI over FCP inside node (if online) - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { - + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "ping") { # Delete WWPN and LUN from sysfs $device = lc($device); - $wwpn = lc($wwpn); + $wwpn = lc($wwpn); - # unit_remove does not exist on SLES 10! - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$device/0x$wwpn/unit_remove"); + my @device_list = split(';', $device); + foreach (@device_list) { + my $cur_device = $_; + if (!$cur_device) { next; } + my @wwpnList = split(";", $wwpn); + foreach (@wwpnList) { + my $cur_wwpn = $_; + if (!$cur_wwpn) { next; } - # Get source node OS - my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node); + # unit_remove does not exist on SLES 10! + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$cur_device/0x$cur_wwpn/unit_remove"); - # Delete WWPN and LUN from configuration files - # RHEL: /etc/zfcp.conf - # SLES 10: /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-* - # SLES 11: /etc/udev/rules.d/51-zfcp* - my $expression = ""; - if ($os =~ m/sles/i) { - $expression = "/$lun/d"; - if ($os =~ m/sles10/i) { - $out = `ssh $::SUDOER\@$node "$::SUDO sed -i -e $expression /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$device"`; - } else { - $out = `ssh $::SUDOER\@$node "$::SUDO sed -i -e $expression /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; + # Get source node OS + my $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node); + + # Delete WWPN and LUN from configuration files + my $expression = ""; + if ( $os =~ m/sles1[12]/i ) { + # SLES 11&12: /etc/udev/rules.d/51-zfcp* + $expression = "/$lun/d"; + my $cmd = "$::SUDO sed -i -e $expression /etc/udev/rules.d/51-zfcp-0.0.$cur_device.rules"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + } elsif ( $os =~ m/rhel/i ) { + # RHEL: /etc/zfcp.conf + $expression = "/$lun/d"; + my $cmd = "$::SUDO sed -i -e $expression /etc/zfcp.conf"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + } elsif ( $os =~ m/ubuntu/i ) { + # Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -d + my $cmd = "$::SUDO /sbin/chzdev zfcp-lun 0.0.$cur_device:0x$cur_wwpn:0x$lun -d"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + } } - } elsif ($os =~ m/rhel/i) { - $expression = "/$lun/d"; - $out = `ssh $::SUDOER\@$node "$::SUDO sed -i -e $expression /etc/zfcp.conf"`; } + # will not check for errors here + my $cmd = "$::SUDO /sbin/multipath -W"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + $cmd = "$::SUDO /sbin/multipath -r"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); xCAT::zvmUtils->printLn($callback, "$node: De-configuring FCP device on host... Done"); } @@ -1973,7 +2633,7 @@ sub changeVM { } # replacevs [file] - elsif ($args->[0] eq "--replacevs") { + elsif ( $args->[0] eq "--replacevs" ) { my $argsSize = @{$args}; my $file; if ($argsSize == 2) { @@ -1982,7 +2642,6 @@ sub changeVM { if ($file) { if (-e $file) { - # Target system (zHCP), e.g. root@gpok2.endicott.ibm.com my $target = "$::SUDOER@"; $target .= $hcp; @@ -1991,7 +2650,7 @@ sub changeVM { $out = `scp $file $target:$file`; # Lock image -`ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Lock_DM -T $userId"`; + `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Lock_DM -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Lock_DM -T $userId"); # Replace user directory entry @@ -1999,17 +2658,16 @@ sub changeVM { xCAT::zvmUtils->printSyslog("smcli Image_Replace_DM -T $userId -f $file"); # Unlock image -`ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`; + `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Unlock_DM -T $userId"); # Delete file on zHCP `ssh $::SUDOER\@$hcp "rm -rf $file"`; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) File does not exist"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) File does not exist" ); return; } } elsif ($::STDIN) { - # Create a temporary file to contain directory on zHCP $file = "/tmp/" . $node . ".direct"; my @lines = split("\n", $::STDIN); @@ -2020,6 +2678,7 @@ sub changeVM { # Write directory entry into temporary file # because directory entry cannot be remotely echoed into stdin foreach (@lines) { + if (!(length $_)) {next;} if ($_) { $_ = "'" . $_ . "'"; `ssh $::SUDOER\@$hcp "echo $_ >> $file"`; @@ -2027,7 +2686,7 @@ sub changeVM { } # Lock image -`ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Lock_DM -T $userId"`; + `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Lock_DM -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Lock_DM -T $userId"); # Replace user directory entry @@ -2035,29 +2694,28 @@ sub changeVM { xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp cat $file | $::SUDO $::DIR/smcli Image_Replace_DM -T $userId -s"); # Unlock image -`ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`; + `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Unlock_DM -T $userId"); # Delete created file on zHCP `ssh $::SUDOER\@$hcp "rm -rf $file"`; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) No directory entry file specified"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Specify a text file containing the updated directory entry"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) No directory entry file specified" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify a text file containing the updated directory entry" ); return; } - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # resetsmapi - elsif ($args->[0] eq "--resetsmapi") { - + elsif ( $args->[0] eq "--resetsmapi" ) { # This is no longer supported in chvm. Using chhypervisor instead. - changeHypervisor($callback, $node, $args); + changeHypervisor( $callback, $node, $args ); } # setipl [ipl target] [load parms] [parms] - elsif ($args->[0] eq "--setipl") { + elsif ( $args->[0] eq "--setipl" ) { my $trgt = $args->[1]; my $loadparms = "''"; @@ -2072,181 +2730,191 @@ sub changeVM { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_IPL_Set_DM -T $userId -s $trgt -l $loadparms -p $parms"`; xCAT::zvmUtils->printSyslog("smcli Image_IPL_Set_DM -T $userId -s $trgt -l $loadparms -p $parms"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # setpassword [password] - elsif ($args->[0] eq "--setpassword") { + elsif ( $args->[0] eq "--setpassword" ) { my $pw = $args->[1]; $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Password_Set_DM -T $userId -p $pw"`; xCAT::zvmUtils->printSyslog("smcli Image_Password_Set_DM -T $userId -p $pw"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } - # setloaddev [wwpn] [lun] - elsif ($args->[0] eq "--setloaddev") { - my $wwpn = $args->[1]; - my $lun = $args->[2]; - - if (!$wwpn || !$lun) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + # setloaddev [wwpn] [lun] [isHex (0 or 1, optional)] [scpdata (optional)] + elsif ( $args->[0] eq "--setloaddev" ) { + my $argsSize = @{$args}; + if (($argsSize != 3) && ($argsSize != 5)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } + my $updateScpdata = 0; + if ($argsSize > 3) { + $updateScpdata = 1; + } + + my $wwpn = $args->[1]; + my $lun = $args->[2]; + # isHex and scpdata must appear or disappear concurrently. + my $isHex = 0; + my $scpdata = ''; + my $scptype = ''; + if ($updateScpdata) { + $isHex = $args->[3]; + $scpdata = $args->[4]; + if ($isHex){ + $scptype = '3'; + } else { + $scptype = '2'; + # If the scpdata is not in HEX form, it may contain white space. + # So wrap the parameter with quote marks. + $scpdata = "'".$scpdata."'"; + } + } + # Make sure WWPN and LUN do not have 0x prefix - $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $wwpn = xCAT::zvmUtils->replaceStr( $wwpn, "0x", "" ); + $lun = xCAT::zvmUtils->replaceStr( $lun, "0x", "" ); xCAT::zvmUtils->printLn($callback, "$node: Setting LOADDEV directory statements"); - # Get user directory entry - my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; - xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'"); - - # Delete old directory entry file - my $userEntryFile = "/tmp/$node.txt"; - `rm -rf $userEntryFile`; - - # Append LOADDEV PORTNAME and LUN statements in directory entry - # These statements go before DEDICATE statements - my $containsPortname = 0; - my $containsLun = 0; - my $updateEntry = 0; - my @lines = split('\n', $userEntry); - foreach (@lines) { - - # Check if LOADDEV PORTNAME and LUN statements are in the directory entry - # This should be hit before any DEDICATE statements - if ($_ =~ m/LOADDEV PORTNAME $wwpn/i) { - $containsPortname = 1; - } if ($_ =~ m/LOADDEV LUN $lun/i) { - $containsLun = 1; - } - - if ($_ =~ m/DEDICATE/i) { - - # Append LOADDEV PORTNAME statement - if (!$containsPortname) { - `echo "LOADDEV PORTNAME $wwpn" >> $userEntryFile`; - $containsPortname = 1; - $updateEntry = 1; - } - - # Append LOADDEV LUN statement - if (!$containsLun) { - `echo "LOADDEV LUN $lun" >> $userEntryFile`; - $containsLun = 1; - $updateEntry = 1; - } - } - - # Write directory entry to file - `echo "$_" >> $userEntryFile`; - } - - # Replace user directory entry (if necessary) - if ($updateEntry) { - $out = `/opt/xcat/bin/chvm $node --replacevs $userEntryFile`; - xCAT::zvmUtils->printLn($callback, "$out"); - - # Delete directory entry file - `rm -rf $userEntryFile`; - } else { - xCAT::zvmUtils->printLn($callback, "$node: No changes required in the directory entry"); - } + # Change SCSI definitions + `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_SCSI_Characteristics_Define_DM -T $userId -b '' -k '' -l $lun -p $wwpn -s $scptype -d $scpdata"`; + xCAT::zvmUtils->printSyslog("smcli Image_SCSI_Characteristics_Define_DM -T $userId -b '' -k '' -l $lun -p $wwpn -s $scptype -d $scpdata"); $out = ""; } + # smcli [smcli command] + # 'smcli command' is the smcli command less the 'smcli' command name. The command will + # normally have a %userid% substring within it which indicates the location in the + # command that should be replaced with the z/VM userid for the target node. This can + # occur multiple times within the command string. + # e.g. chvm gpok168 --smcli 'Virtual_Network_Adapter_Query_Extended -T "%userid%" -k image_device_number=*' + # chvm gpok168 --smcli 'Image_Definition_Update_DM -h' + # The output from the smcli invocation is returned. If we cannot SSH into the + # zhcp server then an error message indicating this failure is returned. + elsif ( $args->[0] eq "--smcli" ) { + my @smcliCmd; + my $useridKeyword = '\%userid\%'; + + @smcliCmd = @{$args}; + shift @smcliCmd; + if ( !@smcliCmd ) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + return; + } + + my $smcliCmdStr = join( ' ', @smcliCmd ); + if ( $smcliCmdStr =~ /$useridKeyword/ ) { + # Replace userid keyword with the userid for the node. + $smcliCmdStr =~ s/$useridKeyword/$userId/g; + } + + $out = `ssh $::SUDOER\@$hcp "$::DIR/smcli $smcliCmdStr"`; + my $rc = $? >> 8; + + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + return; + } + } + # undedicatedevice [virtual device] - elsif ($args->[0] eq "--undedicatedevice") { + elsif ( $args->[0] eq "--undedicatedevice" ) { my $vaddr = $args->[1]; + my $argsSize = @{$args}; + if ($argsSize != 2) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + return; + } + # Undedicate device in directory entry $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Undedicate_DM -T $userId -v $vaddr"`; xCAT::zvmUtils->printSyslog("smcli Image_Device_Undedicate_DM -T $userId -v $vaddr"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Undedicate device in active configuration - my $ping = `/opt/xcat/bin/pping $node`; - if (!($ping =~ m/noping/i)) { + my $power = `/opt/xcat/bin/rpower $node stat`; + if ($power =~ m/: on/i) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Undedicate -T $userId -v $vaddr"`; xCAT::zvmUtils->printSyslog("smcli Image_Device_Undedicate -T $userId -v $vaddr"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } $out = ""; } # sharevolume [vol_addr] [share_enable (YES or NO)] - elsif ($args->[0] eq "--sharevolume") { + elsif ( $args->[0] eq "--sharevolume" ) { my $volAddr = $args->[1]; - my $share = $args->[2]; + my $share = $args->[2]; # Add disk to running system $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Share -T $userId -k img_vol_addr=$volAddr -k share_enable=$share"`; xCAT::zvmUtils->printSyslog("smcli Image_Volume_Share -T $userId -k img_vol_addr=$volAddr -k share_enable=$share"); - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # setprocessor [count] - elsif ($args->[0] eq "--setprocessor") { + elsif($args->[0] eq "--setprocessor") { my $cpuCount = $args->[1]; my @allCpu; my $count = 0; my $newAddr; my $cpu; - my @allValidAddr = ('00', '01', '02', '03', '04', '05', '06', '07', '09', '09', '0A', '0B', '0C', '0D', '0E', '0F', -'10', '11', '12', '13', '14', '15', '16', '17', '19', '19', '1A', '1B', '1C', '1D', '1E', '1F', -'20', '21', '22', '23', '24', '25', '26', '27', '29', '29', '2A', '2B', '2C', '2D', '2E', '2F', -'30', '31', '32', '33', '34', '35', '36', '37', '39', '39', '3A', '3B', '3C', '3D', '3E', '3F'); + my @allValidAddr = ('00','01','02','03','04','05','06','07','09','09','0A','0B','0C','0D','0E','0F', + '10','11','12','13','14','15','16','17','19','19','1A','1B','1C','1D','1E','1F', + '20','21','22','23','24','25','26','27','29','29','2A','2B','2C','2D','2E','2F', + '30','31','32','33','34','35','36','37','39','39','3A','3B','3C','3D','3E','3F'); # Get current CPU count and address - my $proc = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $userId -k CPU" | grep CPU=`; - xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $userId -k CPU | grep CPU="); - while (index($proc, "CPUADDR") != -1) { + my $proc = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $userId -k CPU" | grep -a CPU=`; + xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $userId -k CPU | grep -a CPU="); + while ( index( $proc, "CPUADDR" ) != -1) { my $position = index($proc, "CPUADDR"); my $address = substr($proc, $position + 8, 2); - push(@allCpu, $address); - $proc = substr($proc, $position + 10); + push( @allCpu, $address ); + $proc = substr( $proc, $position + 10 ); } # Find free valid CPU address - my %allCpu = map { $_ => 1 } @allCpu; - my @addrLeft = grep (!defined $allCpu{$_}, @allValidAddr); + my %allCpu = map { $_=>1 } @allCpu; + my @addrLeft = grep( !defined $allCpu{$_}, @allValidAddr ); # Add new CPUs - if ($cpuCount > @allCpu) { + if ( $cpuCount > @allCpu ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k CPU_MAXIMUM=COUNT=$cpuCount -k TYPE=ESA"`; xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k CPU_MAXIMUM=COUNT=$cpuCount -k TYPE=ESA"); - while ($count < $cpuCount - @allCpu) { + while ( $count < $cpuCount - @allCpu ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k CPU=CPUADDR=$addrLeft[$count]"`; xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k CPU=CPUADDR=$addrLeft[$count]"); $count++; } - - # Remove CPUs + # Remove CPUs } else { - while ($count <= @allCpu - $cpuCount) { + while ( $count <= @allCpu - $cpuCount ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_CPU_Delete_DM -T $userId -v $allCpu[@allCpu-$count]"`; xCAT::zvmUtils->printSyslog("smcli Image_CPU_Delete_DM -T $userId -v $allCpu[@allCpu-$count]"); $count++; } } - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); $out = ""; } # setmemory [size] elsif ($args->[0] eq "--setmemory") { - # Memory hotplug not supported, just change memory size in user directory my $size = $args->[1]; if (!($size =~ m/G/i || $size =~ m/M/i)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Size can be Megabytes (M) or Gigabytes (G)"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size can be Megabytes (M) or Gigabytes (G)" ); return; } @@ -2258,18 +2926,115 @@ sub changeVM { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Update_DM -T $userId -k STORAGE_INITIAL=$size -k STORAGE_MAXIMUM=$size"`; xCAT::zvmUtils->printSyslog("smcli Image_Definition_Update_DM -T $userId -k STORAGE_INITIAL=$size -k STORAGE_MAXIMUM=$size"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); $out = ""; } + # setconsole [start|stop] + elsif ($args->[0] eq "--setconsole") { + # Start or stop spool console + my $action = $args->[1]; + my $consoleCmd; + + # Start console spooling + if ( $action eq 'start' ) { + $consoleCmd = "spool console start"; + $out = xCAT::zvmCPUtils->sendCPCmd( $::SUDOER, $hcp, $userId, $consoleCmd ); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + # Stop console + elsif ( $action eq 'stop' ) { + $consoleCmd = "spool console stop"; + $out = xCAT::zvmCPUtils->sendCPCmd( $::SUDOER, $hcp, $userId, $consoleCmd ); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); + } + } + + # aemod [function] [parm1 (optional)] [parm2 (optional)].. + elsif ($args->[0] eq "--aemod") { + my $parm = ""; + my $conf = ""; + my $i = 0; + my $invokescript = "invokeScript.sh"; + my $workerscript = $args->[1]; + my $argsSize = @{$args}; + my $trunkFile = "aemod.doscript"; + my $class = "X"; + my $tempDir = `/bin/mktemp -d /tmp/aemod.XXXXXXXX`; + chomp($tempDir); + + $conf .= sprintf("%s\n", "#!/bin/bash"); + $parm = "/bin/bash $workerscript"; + for ( $i = 2 ; $i < $argsSize ; $i++ ) { + $parm .= ' '; + $parm .= $args->[$i]; + } + $conf .= sprintf("%s\n", $parm); + + open(FILE, ">$tempDir/$invokescript"); + print FILE ("$conf"); + close(FILE); + + if (-e "/opt/xcat/share/xcat/scripts/$workerscript") { + # Generate the tar package for punch + my $oldpath=cwd(); + system("cp /opt/xcat/share/xcat/scripts/$workerscript $tempDir"); + chdir($tempDir); + system("tar cvf $trunkFile $invokescript $workerscript"); + + # Check the node, if node status contains XCATCONF4Z=0, store it in a tmp directory for later use. + # Otherwise, punch it directly. + my $nodeFlag = ''; + my $cfgTrunkDir = "/tmp/configdrive/$node/"; + my @propNames = ('status'); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames ); + $nodeFlag = $propVals->{'status'}; + if ($nodeFlag =~ /XCATCONF4Z=0/) { + if (!-d $cfgTrunkDir) { + mkpath($cfgTrunkDir, 0, 0750); + } + my $rc = `/bin/cp -r $tempDir $cfgTrunkDir/ 2>/dev/null; echo $?`; + `rm -rf $tempDir`; + if ($rc != '0') { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source directory $tempDir to directory $cfgTrunkDir, please check if xCAT is running out of space" ); + rmtree "$cfgTrunkDir"; + return; + } + } else { + # Online zHCP's punch device + $out = xCAT::zvmUtils->onlineZhcpPunch($::SUDOER, $hcp); + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printLn( $callback, "$node: Online zHCP's punch device... $out" ); + `rm -rf $tempDir`; + return; + } + + # Punch file to reader + xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $trunkFile, $trunkFile); + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $trunkFile, $trunkFile, "", $class ); + chdir($oldpath); + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printLn( $callback, "$node: Punching file to reader... $out" ); + `rm -rf $tempDir`; + return; + } + } + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) The worker script $workerscript does not exist on xCAT server" ); + rmdir($tempDir); + return; + } + } + # Otherwise, print out error else { - $out = "$node: (Error) Option not supported"; - } - - # Only print if there is content - if ($out) { - xCAT::zvmUtils->printLn($callback, "$out"); + $out = "$node: (Error) Option '$args->[0]' not supported on chvm"; + xCAT::zvmUtils->printLn( $callback, "$out" ); } return; } @@ -2280,153 +3045,401 @@ sub changeVM { Description : Power on or off a given node Arguments : Node - Option [on|off|reboot|reset|stat] + Option [on|off|reboot|reset|stat|isreachable] Returns : Nothing Example : powerVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub powerVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid', 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + my $status = $propVals->{'status'}; # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); - return; + if ( !$userId ) { + # This may be a stat query to a zVM host hypervisor, if so return on/off for the zhcp node name + # Look in the hosts table for this zhcp to get the node name then look back in zvm table + # to get the userid to check the power on. + if ( $args->[0] eq 'stat') { + my @propNames2 = ( 'node' ); + my $propVals2 = xCAT::zvmUtils->getTabPropsByKey('hosts', 'hostnames', $hcp, @propNames2); + if ( !$propVals2->{'node'} ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to look up zhcp node for $hcp" ); + return; + } + # Now we have a node name for this zhcp, get the userid so we can check the power. + my $node2 = $propVals2->{'node'}; + $propVals2 = xCAT::zvmUtils->getNodeProps( 'zvm', $node2, @propNames ); + $userId = $propVals2->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID on zhcp node $node2" ); + return; + } + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); + return; + } } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; - xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); + xCAT::zvmUtils->printSyslog("powerVM() node:$node userid:$userId zHCP:$hcp sudoer:$::SUDOER sudo:$::SUDO"); # Output string my $out; # Power on virtual server - if ($args->[0] eq 'on') { - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $userId"`; + if ( $args->[0] eq 'on' ) { + # Check the node flag, if it contain XCATCONF4Z=0, it indicate that this node will be deployed by using non-xcatconf4z type + # image, it will call the reconstructor to generate a final punched file, and punched to reader. otherwise, power on the vm directly. + my $nodeFlag = ''; + my $cfgTrunkDir = "/tmp/configdrive/$node"; + my @propNames = ('status'); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames ); + my $cfgdrive = ''; + my $destCfgdrive = ''; + my $class = "X"; + $nodeFlag = $propVals->{'status'}; + # When start the non-xcatconf4z cloud image at first time, we need to modify cfgdrive.tgz to append xCAT key, so adding SSH and IUCV + # check to ensure it is the first start, since after vm is started SSH and IUCV flag is set. + if ( $nodeFlag =~ /XCATCONF4Z=0/ && $nodeFlag !~ /SSH=1/ && $nodeFlag != /IUCV=1/ ) { + # Call constructor to generate a final configdrive for target vm + $cfgdrive = xCAT::zvmUtils->genCfgdrive($cfgTrunkDir); + if ( -e $cfgdrive ) { + # Purge reader + $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId ); + xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); + + # Online zHCP's punch device + $out = xCAT::zvmUtils->onlineZhcpPunch($::SUDOER, $hcp); + if ( $out =~ m/Failed/i ) { + `rm -rf $cfgTrunkDir`; + xCAT::zvmUtils->printLn( $callback, "$node: Online zHCP's punch device... $out" ); + return; + } + + $destCfgdrive = "/tmp/$node-" . basename($cfgdrive); + # Punch file to reader + xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $cfgdrive, $destCfgdrive); + `/bin/rm -rf $cfgTrunkDir`; + + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $destCfgdrive, basename($cfgdrive), "", $class ); + `ssh $::SUDOER\@$hcp "$::SUDO /bin/rm $destCfgdrive"`; + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printLn( $callback, "$node: Punching final config drive to reader... $out" ); + return; + } + } else { + xCAT::zvmUtils->printLn( $callback, "$node: Failed to generate the final cfgdrive for target vm" ); + return; + } + } + xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $userId"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $userId"`; + my $rc = $? >> 8; + $out = xCAT::zvmUtils->trimStr( $out ); + if ( $rc == 255 ) { + xCAT::zvmUtils->printSyslog( "$node: (Error) Failed to communicate with the zhcp system: $hcp output:$out" ); + return; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + my $delscriptpath = "/var/lib/sspmod/portdelete"; + # If user doesn't use NIC which is record in switch table. When power on the instance, delete + # the port at the same time. Use file "portdelete" to delete the ports, it exists on CMA appliance. + # If the script of "portdelete" doesn't exist, the function is skipped, ports will not be deleted. + if ( -e $delscriptpath and $status =~ /CLONING=1/ and $status =~ /CLONE_ONLY=1/ ) { + xCAT::zvmUtils->printSyslog( "$node: Delete the port that is NOT used for Alternate Deploy Provisioning."); + # Get the list from switch table. + my $switchTab = xCAT::Table->new('switch'); + if ( !$switchTab ) { + xCAT::zvmUtils->printSyslog( "$node: (Error) Could not open table: switch."); + return; + } + my @portData = $switchTab->getAllAttribsWhere( "node='".lc($userId)."'", 'port' ); + $switchTab->close; + my $ports = ''; + foreach ( @portData ) { + $ports = $ports.' '.$_->{'port'}; + } + my $out = `$delscriptpath $ports`; + $rc = $?; + $out = xCAT::zvmUtils->trimStr( $out ); + if ( $rc !=0 ) { + xCAT::zvmUtils->printSyslog( "$node:(Error) Failed to delete port output:$out" ); + return; + }else { + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + } + + # If we were cloning the server then turn off cloning flag. + if ( $status =~ /CLONING=1/ ) { + $status =~ s/CLONING=1/CLONING=0/g; + } + + if ( $status =~ /CLONE_ONLY=1/ ) { + # Indicate node is being powered up so that we will confirm the IP address on the nodestat. + if ( $status =~ /POWER_UP=/ ) { + $status =~ s/POWER_UP=0/POWER_UP=1/g; + } else { + $status = "$status;POWER_UP=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } } # Power off virtual server - elsif ($args->[0] eq 'off') { + elsif ( $args->[0] eq 'off' ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId -f IMMED"); $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId -f IMMED"`; - xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + my $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + xCAT::zvmUtils->printSyslog("$userId already logged off."); + $out = "$userId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + xCAT::zvmUtils->printSyslog("$userId in process of logging off."); + $out = "$userId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + return; + } + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Power off virtual server (gracefully) - elsif ($args->[0] eq 'softoff') { - if (`/opt/xcat/bin/pping $node` !~ m/noping/i) { - $out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -h now"`; - sleep(15); # Wait 15 seconds before logging user off + elsif ( $args->[0] eq 'softoff' ) { + my $ping = xCAT::zvmUtils->pingNode($node); + my $sleepseconds = 15; + if ($ping eq "ping") { + #$out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -h now"`; + my $cmd = "$::SUDO shutdown -h now"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + sleep($sleepseconds); # Wait 15 seconds before logging user off } - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`; + my $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + xCAT::zvmUtils->printSyslog("$userId already logged off."); + $out = "$userId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + xCAT::zvmUtils->printSyslog("$userId in process of logging off."); + $out = "$userId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + return; + } + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Get the status (on|off) - elsif ($args->[0] eq 'stat') { - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userId.*/on/'`; - - # Wait for output - my $max = 0; - while (!$out && $max < 10) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userId.*/on/'`; - $max++; + elsif ( $args->[0] eq 'stat' ) { + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/HCPCQU361E.*/off/' | sed 's/$userId.*/on/'`; + my $rc = $? >> 8; + if ($rc != 0) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $out, rc is $rc" ); } - - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Reset a virtual server - elsif ($args->[0] eq 'reset') { + elsif ( $args->[0] eq 'reset' ) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`; + my $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + xCAT::zvmUtils->printSyslog("$userId already logged off."); + $out = "$userId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + xCAT::zvmUtils->printSyslog("$userId in process of logging off."); + $out = "$userId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $userId output: $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + return; + } + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Wait for output - while (`ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/Done/'` != "Done") { - - # Do nothing + while ( `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/Done/'` !~ "Done" ) { + sleep(5); } $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $userId"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + if ( $status =~ /CLONE_ONLY=1/ ) { + # Indicate node is being powered up so that we will confirm the IP address on the nodestat. + if ( $status =~ /POWER_UP=/ ) { + $status =~ s/POWER_UP=0/POWER_UP=1/g; + } else { + $status = "$status;POWER_UP=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } } # Reboot a virtual server - elsif ($args->[0] eq 'reboot') { + elsif ( $args->[0] eq 'reboot' ) { my $timeout = 0; - $out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -r now &>/dev/null && echo Done"`; - if (!($out =~ m/Done/)) { - xCAT::zvmUtils->printLn($callback, "$node: Connecting to $node... Failed\n"); - return; - } + #$out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -r now &>/dev/null"`; + my $cmd = "$::SUDO shutdown -r now &>/dev/null"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); # Wait until node is down or 180 seconds - while ((`/opt/xcat/bin/pping $node` !~ m/noping/i) && $timeout < 180) { - sleep(1); - $timeout++; + while ($timeout < 180) { + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "ping") { + sleep(1); + $timeout++; + } else { + last; + } } if ($timeout >= 180) { - xCAT::zvmUtils->printLn($callback, "$node: Shuting down $userId... Failed\n"); + xCAT::zvmUtils->printLn( $callback, "$node: Shutting down $userId... Failed\n" ); return; } - xCAT::zvmUtils->printLn($callback, "$node: Shuting down $userId... Done\n"); + xCAT::zvmUtils->printLn( $callback, "$node: Shutting down $userId... Done\n" ); # Wait until node is up or 180 seconds $timeout = 0; - while ((`/opt/xcat/bin/pping $node` =~ m/noping/i) && $timeout < 180) { - sleep(1); - $timeout++; + while ($timeout < 180) { + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "noping") { + sleep(1); + $timeout++; + } else { + last; + } } if ($timeout >= 180) { - xCAT::zvmUtils->printLn($callback, "$node: Rebooting $userId... Failed\n"); + xCAT::zvmUtils->printLn( $callback, "$node: Rebooting $userId... Failed\n" ); return; } - xCAT::zvmUtils->printLn($callback, "$node: Rebooting $userId... Done\n"); + if ( $status =~ /CLONE_ONLY=1/ ) { + # Indicate node is being powered up so that we will confirm the IP address on the nodestat. + if ( $status =~ /POWER_UP=/ ) { + $status =~ s/POWER_UP=0/POWER_UP=1/g; + } else { + $status = "$status;POWER_UP=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } + + xCAT::zvmUtils->printLn( $callback, "$node: Rebooting $userId... Done\n" ); } # Pause a virtual server - elsif ($args->[0] eq 'pause') { + elsif ( $args->[0] eq 'pause' ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Pause -T $userId -k PAUSE=YES"`; xCAT::zvmUtils->printSyslog("smcli Image_Pause -T $userId -k PAUSE=YES"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Unpause a virtual server - elsif ($args->[0] eq 'unpause') { + elsif ( $args->[0] eq 'unpause' ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Pause -T $userId -k PAUSE=NO"`; xCAT::zvmUtils->printSyslog("smcli Image_Pause -T $userId -k PAUSE=NO"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + #Check VM reachable status + elsif ( $args->[0] eq 'isreachable' ) { + if ($status =~ /CLONE_ONLY=1/ and $status =~ /POWER_UP=1/){ + # Special test and handling for 's390x' architecture nodes which are in the nodetype + # and zvm table and are marked as being powered up. + my %generalArgs; + $generalArgs{'verbose'} = 0; + my $nodes = [$node]; + xCAT::zvmUtils->handlePowerUp( $callback, $nodes, \%generalArgs ); + my $oldstatus = $status; + $status =~ s/IUCV=1/SSH=1/g; + if (!($status =~ /SSH=1/)){ + $status = "$status;SSH=1"; + } + if ($status != $oldstatus){ + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } + } + # Check vm's status + xCAT::zvmUtils->printSyslog("check $node isreachable"); + my $cmd = "$::SUDO date"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$node: unreachable"); + return; + } + + + # Create output string + if ($out) { + xCAT::zvmUtils->printLn( $callback, "$node: reachable"); + } else { + xCAT::zvmUtils->printLn( $callback, "$node: unreachable"); + } } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Option not supported"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); } return; } @@ -2439,48 +3452,47 @@ sub powerVM { Arguments : zHCP Returns : Nothing Example : scanVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub scanVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; my $write2db = ''; if ($args) { @ARGV = @$args; # Parse options - GetOptions('w' => \$write2db); + GetOptions( 'w' => \$write2db ); } # 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 zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); # Exit if node is not a HCP - if (!($hcp =~ m/$node/i)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) $node is not a hardware control point"); + if ( !( $hcp =~ m/$node/i ) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) $node is not a hardware control point" ); return; } @@ -2506,11 +3518,11 @@ sub scanVM { # Get nodes managed by this zHCP # Look in 'zvm' table - my $tab = xCAT::Table->new('zvm', -create => 1, -autocommit => 0); - my @entries = $tab->getAllAttribsWhere("hcp like '%" . $hcp . "%'", 'node', 'userid'); + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); + my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $hcp . "%'", 'node', 'userid' ); my $out; - my $node; + my $node2; my $id; my $os; my $arch; @@ -2522,104 +3534,106 @@ sub scanVM { my $sysinfo = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO cat /proc/sysinfo"`; # Get node CEC - my $cec = `echo "$sysinfo" | grep "Sequence Code"`; - my @args = split(':', $cec); - + my $cec = `echo "$sysinfo" | grep -a "Sequence Code"`; + my @args = split( ':', $cec ); # Remove leading spaces and zeros $args[1] =~ s/^\s*0*//; $cec = xCAT::zvmUtils->trimStr($args[1]); # Get node LPAR - my $lpar = `echo "$sysinfo" | grep "LPAR Name"`; - @args = split(':', $lpar); + my $lpar = `echo "$sysinfo" | grep -a "LPAR Name"`; + @args = split( ':', $lpar ); $lpar = xCAT::zvmUtils->trimStr($args[1]); # Save CEC, LPAR, and zVM to 'zvm' table my %propHash; if ($write2db) { - # Save CEC to 'zvm' table %propHash = ( - 'nodetype' => 'cec', - 'parent' => '' + 'nodetype' => 'cec', + 'parent' => '' ); - xCAT::zvmUtils->setNodeProps('zvm', $cec, \%propHash); + xCAT::zvmUtils->setNodeProps( 'zvm', $cec, \%propHash ); # Save LPAR to 'zvm' table %propHash = ( - 'nodetype' => 'lpar', - 'parent' => $cec + 'nodetype' => 'lpar', + 'parent' => $cec ); - xCAT::zvmUtils->setNodeProps('zvm', $lpar, \%propHash); + xCAT::zvmUtils->setNodeProps( 'zvm', $lpar, \%propHash ); # Save zVM to 'zvm' table %propHash = ( - 'nodetype' => 'zvm', - 'parent' => $lpar + 'nodetype' => 'zvm', + 'parent' => $lpar ); - xCAT::zvmUtils->setNodeProps('zvm', lc($host), \%propHash); + xCAT::zvmUtils->setNodeProps( 'zvm', lc($host), \%propHash ); } - # Search for nodes managed by given zHCP + # Search for s managed by given zHCP # Get 'node' and 'userid' properties %propHash = (); foreach (@entries) { - $node = $_->{'node'}; + $node2 = $_->{'node'}; # Get groups @propNames = ('groups'); - $propVals = xCAT::zvmUtils->getNodeProps('nodelist', $node, @propNames); - $groups = $propVals->{'groups'}; + $propVals = xCAT::zvmUtils->getNodeProps( 'nodelist', $node2, @propNames ); + $groups = $propVals->{'groups'}; # Load VMCP module - xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node); + xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node2); # Get user ID @propNames = ('userid'); - $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); - $id = $propVals->{'userid'}; + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node2, @propNames ); + $id = $propVals->{'userid'}; if (!$id) { - $id = xCAT::zvmCPUtils->getUserId($::SUDOER, $node); + $id = xCAT::zvmCPUtils->getUserId($::SUDOER, $node2); } # Get architecture - $arch = `ssh -o ConnectTimeout=2 $::SUDOER\@$node "uname -p"`; - $arch = xCAT::zvmUtils->trimStr($arch); - if (!$arch) { + #$arch = `ssh -o ConnectTimeout=2 $::SUDOER\@$node2 "uname -p"`; + my $cmd = "$::SUDO uname -p"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node2, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + $arch = xCAT::zvmUtils->trimStr($out); + if (!$out) { # Assume arch is s390x $arch = 's390x'; } # Get OS - $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node); + $os = xCAT::zvmUtils->getOsVersion($::SUDOER, $node2); # Save node attributes if ($write2db) { # Do not save if node = host - if (!(lc($host) eq lc($node))) { - + if (!(lc($host) eq lc($node2))) { # Save to 'zvm' table %propHash = ( - 'hcp' => $hcp, - 'userid' => $id, - 'nodetype' => 'vm', - 'parent' => lc($host) + 'hcp' => $hcp, + 'userid' => $id, + 'nodetype' => 'vm', + 'parent' => lc($host) ); - xCAT::zvmUtils->setNodeProps('zvm', $node, \%propHash); + xCAT::zvmUtils->setNodeProps( 'zvm', $node2, \%propHash ); # Save to 'nodetype' table %propHash = ( - 'arch' => $arch, - 'os' => $os + 'arch' => $arch, + 'os' => $os ); - xCAT::zvmUtils->setNodeProps('nodetype', $node, \%propHash); + xCAT::zvmUtils->setNodeProps( 'nodetype', $node2, \%propHash ); } } # Create output string - $str .= "$node:\n"; + $str .= "$node2:\n"; $str .= " objtype=node\n"; $str .= " arch=$arch\n"; $str .= " os=$os\n"; @@ -2631,7 +3645,7 @@ sub scanVM { $str .= " mgt=zvm\n\n"; } - xCAT::zvmUtils->printLn($callback, "$str"); + xCAT::zvmUtils->printLn( $callback, "$str" ); return; } @@ -2641,56 +3655,68 @@ sub scanVM { Description : Get hardware and software inventory of a given node Arguments : Node - Type of inventory (config|all) + Type of inventory (all|config|console [logsize]|cpumem|cpumempowerstat|--freerepospace) Returns : Nothing Example : inventoryVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub inventoryVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Output string my $str = ""; # Check if node is pingable - if (`/opt/xcat/bin/pping $node | egrep -i "noping"`) { - $str = "$node: (Error) Host is unreachable"; - xCAT::zvmUtils->printLn($callback, "$str"); + if (($args->[0] ne '--consoleoutput') and ($args->[0] ne 'cpumempowerstat')){ + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "noping") { + $str = "$node: (Error) Host is unreachable"; + xCAT::zvmUtils->printLn( $callback, "$str" ); return; + } } # 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 zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); + # Get zvm system node name. nodetype should be zvm. + my $tab2 = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); + my @results2 = $tab2->getAllAttribsWhere( "nodetype='zvm'", 'hcp', 'node' ); + my $hypervisornode = "unknown"; + foreach (@results2) { + if ( $_->{'hcp'} eq $hcp ) { + $hypervisornode = $_->{'node'}; + } + } + # Load VMCP module xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node); # Get configuration - if ($args->[0] eq 'config') { + if ( $args->[0] eq 'config' ) { # Get z/VM host for specified node my $host = xCAT::zvmCPUtils->getHost($::SUDOER, $node); @@ -2708,7 +3734,7 @@ sub inventoryVM { my $memory = xCAT::zvmCPUtils->getMemory($::SUDOER, $node); # Get max memory - my $maxMem = xCAT::zvmUtils->getMaxMemory($::SUDOER, $hcp, $node); + my $maxMem = xCAT::zvmUtils->getMaxMemory($::SUDOER, $hcp , $node); # Get processors configuration my $proc = xCAT::zvmCPUtils->getCpu($::SUDOER, $node); @@ -2722,7 +3748,86 @@ sub inventoryVM { $str .= "Total Memory: $memory\n"; $str .= "Max Memory: $maxMem\n"; $str .= "Processors: \n$proc\n"; - } elsif ($args->[0] eq 'all') { + $str .= "xCAT Hypervisor Node: $hypervisornode\n"; # new field for GUI + + } elsif ( $args->[0] eq 'cpumem' ) { + + # Get memory configuration + my $memory = xCAT::zvmCPUtils->getMemory($::SUDOER, $node); + + # Get processors configuration + my $proc = xCAT::zvmCPUtils->getCpu($::SUDOER, $node); + + # Get instance CPU used time + my $cputime = xCAT::zvmUtils->getUsedCpuTime($::SUDOER, $hcp , $node); + + $str .= "Total Memory: $memory\n"; + $str .= "Processors: \n$proc\n"; + $str .= "CPU Used Time: $cputime\n"; + + } elsif ( $args->[0] eq 'cpumempowerstat' ) { + # This option will check power stat then based on the power stat, use + # SMAPI to query the cpu, mem and uptime. so all info is done in one + # SMAPI call will help the performance enhancement. + my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userId 2>/dev/null"`; + xCAT::zvmUtils->printSyslog( "$node: power stat query return: $out"); + if ( $out =~ 'HCPCQU045E' or $out =~ 'HCPCQU361E' ) { + $out = 'off'; + } elsif ( $out =~ $userId) { + $out = 'on'; + } else { + # should not be here + xCAT::zvmUtils->printLn( $callback, "$node: (Error) power stat query return not parsable, the result is $out"); + return + } + + if ( $out eq 'off') { + # upper layer should check power off state first + $str .= "Power state: off\n"; + $str .= "Total Memory: 0M\n"; + $str .= "Processors: 0\n"; + $str .= "CPU Used Time: 0 sec\n"; + } else { + # This is 'on' branch, we should be able to query info + xCAT::zvmUtils->printSyslog( "$node: calling smcli Image_Performance_Query -T $userId -c 1" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Performance_Query -T $userId -c 1"`; + my $rc = $? >> 8; + + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + return; + } + if ( $rc ) { + xCAT::zvmUtils->printSyslog( "$node: (Error) calling smcli Image_Performance_Query -T $userId" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) calling smcli Image_Performance_Query -T $userId" ); + return; + } + # In order to save SMAPI call effort, we didn't set them into separated function + # just get SMAPI output and parse the output + my $time = `echo -e "$out" | egrep -a -i "Used CPU time:"`; + $time =~ s/^Used CPU time:(.*)/$1/; + my @timearray = split(' ', $time); + # Get value is us , need make it seconds + my $usedtime = $timearray[0]/1000000; + + my $cpus = `echo -e "$out" | egrep -a -i "Guest CPUs:"`; + $cpus =~ s/^Guest CPUs:(.*)/$1/; + my @cpuarray = split(' ', $cpus); + my $totalcpu = $cpuarray[0]; + + # This is the used memory, not max mem defined in user dirct, it's in KB + my $mem = `echo -e "$out" | egrep -a -i "Max memory:"`; + $mem =~ s/^Max memory:(.*)/$1/; + my @memarry = split(' ', $mem); + my $totalmem = $memarry[0]/1024; + + $str .= "Power state: on\n"; + $str .= "Total Memory: $totalmem"."M\n"; + $str .= "Processors: $totalcpu\n"; + $str .= "CPU Used Time: $usedtime"." sec\n"; + } + } elsif ( $args->[0] eq 'all' ) { # Get z/VM host for specified node my $host = xCAT::zvmCPUtils->getHost($::SUDOER, $node); @@ -2739,8 +3844,8 @@ sub inventoryVM { # Get memory configuration my $memory = xCAT::zvmCPUtils->getMemory($::SUDOER, $node); - # Get max memory - my $maxMem = xCAT::zvmUtils->getMaxMemory($::SUDOER, $hcp, $node); + # Get max memory + my $maxMem = xCAT::zvmUtils->getMaxMemory($::SUDOER, $hcp , $node); # Get processors configuration my $proc = xCAT::zvmCPUtils->getCpu($::SUDOER, $node); @@ -2758,7 +3863,7 @@ sub inventoryVM { my $uptime = xCAT::zvmUtils->getUpTime($::SUDOER, $node); # Get instance CPU used time - my $cputime = xCAT::zvmUtils->getUsedCpuTime($::SUDOER, $hcp, $node); + my $cputime = xCAT::zvmUtils->getUsedCpuTime($::SUDOER, $hcp , $node); # Create output string $str .= "z/VM UserID: $userId\n"; @@ -2777,7 +3882,9 @@ sub inventoryVM { $str .= "zFCP: \n$zfcp\n"; } $str .= "NICs: \n$nic\n"; - } elsif ($args->[0] eq '--freerepospace') { + $str .= "xCAT Hypervisor Node: $hypervisornode\n"; # new field for GUI + + } elsif ( $args->[0] eq '--freerepospace' ) { # Get /install available disk size my $freespace = xCAT::zvmUtils->getFreeRepoSpace($::SUDOER, $node); @@ -2788,16 +3895,77 @@ sub inventoryVM { } else { return; } + + # Get console output + } elsif ( $args->[0] eq '--consoleoutput' ) { + + my $argsSize = @{$args}; + + # Let SMAPI execution on ZHCP to punch the console log to the caller + my $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Console_Get -T $userId"`; + xCAT::zvmUtils->printSyslog("$::SUDOER\@$hcp $::SUDO $::DIR/smcli Image_Console_Get -T $userId"); + + my $out; + chomp( $out = `ssh $::SUDOER\@$hcp "$::SUDO cat /sys/bus/ccw/drivers/vmur/0.0.000c/online"` ); + if ($out != 1) { + chomp( $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/cio_ignore -r 000c; /sbin/chccwdev -e 000c"`); + if ( !( $out =~ m/Done$/i ) ) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to online the zHCP's reader, cmd output: $out."); + xCAT::zvmUtils->printSyslog( "inventoryVM() Failed to online the zHCP's reader, cmd output: $out." ); + return; + } + $out = `ssh $::SUDOER\@$hcp "$::SUDO which udevadm &> /dev/null && udevadm settle || udevsettle"`; + } + # we need set class otherwise we will get error like: + # vmur: Reader device class does not match spool file class. + $out = `ssh $::SUDOER\@$hcp "$::SUDO vmcp spool c class \\*"`; + xCAT::zvmUtils->printSyslog( "vmcp spool c class return: $out" ); + + # Get console output from zhcp + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/sbin/vmur list | egrep -a -i $userId "`; + my @spoolFiles = sort(split('\n', $out)); + $str = ""; + foreach (@spoolFiles){ + if (!(length $_)) {next;} + my @fileProperty = split(' ', $_); + my $spoolFileId = $fileProperty[1]; + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/sbin/vmur re -t -O $spoolFileId"`; + $str .= $out + } + + # Prepare to output + my $str_length = length($str); + if (!$str_length) { + $str = "(Error) No console log avaiable"; + # Append hostname (e.g. gpok3) in front + $str = xCAT::zvmUtils->appendHostname( $node, $str ); + + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; + } elsif ($argsSize eq 2) { + my $logsize = $args->[1]; + # only output last $logsize bytes of console log + if (($logsize > 0) and ($logsize < $str_length)) { + $str = substr($str, -$logsize); + my $truncatd = $str_length - $logsize; + $str = "Truncated console log, $truncatd bytes ignored\n".$str + } + } + # Append hostname (e.g. gpok3) in front + $str = xCAT::zvmUtils->appendHostname( $node, $str ); + xCAT::zvmUtils->printInfo( $callback, "$str" ); + return; + } else { $str = "$node: (Error) Option not supported"; - xCAT::zvmUtils->printLn($callback, "$str"); + xCAT::zvmUtils->printLn( $callback, "$str" ); return; } # Append hostname (e.g. gpok3) in front - $str = xCAT::zvmUtils->appendHostname($node, $str); + $str = xCAT::zvmUtils->appendHostname( $node, $str ); - xCAT::zvmUtils->printLn($callback, "$str"); + xCAT::zvmUtils->printLn( $callback, "$str" ); return; } @@ -2807,114 +3975,194 @@ sub inventoryVM { Description : Show the info for a given node Arguments : Node - Option + Option Returns : Nothing Example : listVM($callback, $node); - + =cut #------------------------------------------------------- sub listVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Set cache directory my $cache = '/var/opt/zhcp/cache'; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid', 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); my $out; + my $rc; # Get disk pool configuration - if ($args->[0] eq "--diskpool") { - + if ( $args->[0] eq "--diskpool" ) { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); } # Get disk pool names - elsif ($args->[0] eq "--diskpoolnames") { - + elsif ( $args->[0] eq "--diskpoolnames" ) { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); } # Get network names - elsif ($args->[0] eq "--getnetworknames") { - + elsif ( $args->[0] eq "--getnetworknames" ) { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); } # Get network - elsif ($args->[0] eq "--getnetwork") { - + elsif ( $args->[0] eq "--getnetwork" ) { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); } # Get the status of all DASDs accessible to a virtual image - elsif ($args->[0] eq "--querydisk") { + elsif ( $args->[0] eq "--querydisk" ) { my $vdasd = $args->[1]; $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Query -T $userId -k $vdasd"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Query -T $userId -k $vdasd"); } - # Get user profile names - elsif ($args->[0] eq "--userprofilenames") { + # Get the status of all DASDs accessible to a the system + elsif ( $args->[0] eq "--queryalldisks" ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Disk_Query -T MAINT -k dev_num=ALL -k disk_size=YES"`; + xCAT::zvmUtils->printSyslog(" ssh zhcp smcli System_Disk_Query -T MAINT -k dev_num=ALL -k disk_size=YES"); + } + # Get list of PAGE volumes + elsif ( $args->[0] eq "--querypagevolumes" ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Page_Utilization_Query -T MAINT "`; + xCAT::zvmUtils->printSyslog(" ssh zhcp smcli System_Page_Utilization_Query -T MAINT"); + } + + # Get list of SPOOL volumes + elsif ( $args->[0] eq "--queryspoolvolumes" ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_Spool_Utilization_Query -T MAINT "`; + xCAT::zvmUtils->printSyslog(" ssh zhcp smcli System_Spool_Utilization_Query -T MAINT"); + } + + # Get user profile names + elsif ( $args->[0] eq "--userprofilenames" ) { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); } # Get zFCP disk pool configuration - elsif ($args->[0] eq "--zfcppool") { - + elsif ( $args->[0] eq "--zfcppool" ) { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); } # Get zFCP disk pool names - elsif ($args->[0] eq "--zfcppoolnames") { - + elsif ( $args->[0] eq "--zfcppoolnames") { # This is no longer supported in lsvm. Using inventoryHypervisor instead. - inventoryHypervisor($callback, $node, $args); + inventoryHypervisor( $callback, $node, $args ); + } + + # Check whether instance has given NIC + elsif ( $args->[0] eq "--checknics") { + if ($propVals->{'status'} =~ /CLONE_ONLY=1/) { + xCAT::zvmUtils->printSyslog("$node: cloned flag detected, no further check"); + } else { + # Get the directory data without the *DVHOPT line + xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`; + $rc = $? >> 8; + + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + return; + } + if ( $rc ) { + xCAT::zvmUtils->printSyslog( "$node: (Error) calling smcli Image_Query_DM -T $userId" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) calling smcli Image_Query_DM -T $userId" ); + return; + } + $out =~ s/\*DVHOPT(.*)//s; # remove last line with *DVHOPT and newline after it + + my $argsSize = @{$args}; + if ($argsSize != 2) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Nodename and only one NIC address must be input"); + return; + } + # For each NIC given, check whether it exists in the user direct of user + # In case this is a cloned instance, will not check whether it exist or not + # directly return True. + my $i; + my $dev; + + $dev = $args->[1]; + if ($dev =~ m/^[0-9a-fA-F]{1,4}/) { + # add '0' to $dev, e.g 0100 or 100 are both valid, the length check already done above + if ($out =~ m/.*NICDEF [0]*$dev TYPE QDIO LAN SYSTEM .*/i ) { + xCAT::zvmUtils->printSyslog("$node: succeed in find $dev in user direct"); + } else { + # Not return $out to upper layer as it might contain password + xCAT::zvmUtils->printLn( $callback, "$node: (Error) not able to find $dev in user direct"); + + xCAT::zvmUtils->printSyslog("$node: (Error) not able to find $dev in user direct: $out"); + return; + } + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) input NIC param $dev invalid"); + return; + } + } + # ok, NIC we planned to check are found or this is a cloned node. + $out = ''; } # Get user entry - elsif (!$args->[0]) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; + elsif ( !$args->[0] ) { + + # Get the directory data without the *DVHOPT line xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`; + $rc = $? >> 8; + + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + return; + } + if ( $rc ) { + xCAT::zvmUtils->printSyslog( "$node: (Error) calling smcli Image_Query_DM -T $userId" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) calling smcli Image_Query_DM -T $userId" ); + return; + } + $out =~ s/\*DVHOPT(.*)//s; # remove last line with *DVHOPT and newline after it } else { $out = "$node: (Error) Option not supported"; } # Append hostname (e.g. gpok3) in front - $out = xCAT::zvmUtils->appendHostname($node, $out); - xCAT::zvmUtils->printLn($callback, "$out"); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + xCAT::zvmUtils->printLn( $callback, "$out" ); return; } @@ -2927,39 +4175,39 @@ sub listVM { * A unique MAC address will be assigned Arguments : Node Directory entry text file (optional) + Upstream instance ID (optional) + Upstream request ID (optional) Returns : Nothing Example : makeVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub makeVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # 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 zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; - xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); + xCAT::zvmUtils->printSyslog("makeVM for node:$node on zhcp:$hcp"); # Find the number of arguments my $argsSize = @{$args}; @@ -2967,62 +4215,66 @@ sub makeVM { # Create a new user in zVM without user directory entry file my $out; my $stdin; - my $password = ""; - my $memorySize = ""; - my $privilege = ""; + my $password = ""; + my $memorySize = ""; + my $privilege = ""; my $profileName = ""; - my $cpuCount = 1; - my $diskPool = ""; - my $diskSize = ""; - my $diskVdev = ""; + my $cpuCount = 1; + my $diskPool = ""; + my $diskSize = ""; + my $diskVdev = ""; + my $ipl = ""; + my $requestId = "NoUpstreamRequestID"; # Default is still visible in the log + my $objectId = "NoUpstreamObjectID"; # Default is still visible in the log if ($args) { @ARGV = @$args; # Parse options GetOptions( - 's|stdin' => \$stdin, # Directory entry contained in stdin - 'p|profile=s' => \$profileName, + 's|stdin' => \$stdin, # Directory entry contained in stdin + 'p|profile=s' => \$profileName, 'w|password=s' => \$password, - 'c|cpus=i' => \$cpuCount, # Optional - 'm|mem=s' => \$memorySize, + 'c|cpus=i' => \$cpuCount, # Optional + 'm|mem=s' => \$memorySize, 'd|diskpool=s' => \$diskPool, - 'z|size=s' => \$diskSize, - 'v|diskvdev=s' => \$diskVdev, # Optional - 'r|privilege=s' => \$privilege); # Optional + 'z|size=s' => \$diskSize, + 'v|diskvdev=s' => \$diskVdev, # Optional + 'r|privilege=s' => \$privilege, # Optional + 'q|requestid=s' => \$requestId, # Optional + 'j|objectid=s' => \$objectId, # Optional + 'i|ipl=s' => \$ipl); # Optional } - # If one of the options above are given, create the user without a directory entry file if ($profileName || $password || $memorySize) { if (!$profileName || !$password || !$memorySize) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing one or more required parameter(s)"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more required parameter(s)" ); return; } # Default privilege to G if none is given if (!$privilege) { $privilege = 'G'; - } + } # Generate temporary user directory entry file - my $userEntryFile = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount); - if ($userEntryFile == -1) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to generate user directory entry file"); + my $userEntryFile = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount, $ipl); + if ( $userEntryFile == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to generate user directory entry file" ); return; } - # Create a new user in z/VM without disks - $out = `/opt/xcat/bin/mkvm $node $userEntryFile`; - xCAT::zvmUtils->printLn($callback, "$out"); - if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { - + $out = `/opt/xcat/bin/mkvm $node $userEntryFile 2>&1`; + xCAT::zvmUtils->printLn( $callback, "$out"); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { # The error would have already been printed under mkvm + `rm -rf $userEntryFile`; return; } - # If one of the disk operations are given, add disk(s) to this new user if ($diskPool || $diskSize) { if (!$diskPool || !$diskSize) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing one or more required parameter(s) for adding disk"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more required parameter(s) for adding disk" ); + `rm -rf $userEntryFile`; return; } @@ -3031,11 +4283,11 @@ sub makeVM { $diskVdev = "0100"; } - $out = `/opt/xcat/bin/chvm $node --add3390 $diskPool $diskVdev $diskSize`; - xCAT::zvmUtils->printLn($callback, "$out"); - if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { - + $out = `/opt/xcat/bin/chvm $node --add3390 $diskPool $diskVdev $diskSize 2>&1`; + xCAT::zvmUtils->printLn( $callback, "$out"); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { # The error would have already been printed under chvm + `rm -rf $userEntryFile`; return; } } @@ -3055,48 +4307,46 @@ sub makeVM { my $macId; my $generateNew = 1; @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps('mac', $node, @propNames); - + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); # If MAC address exists my @lines; my @words; - if ($propVals->{'mac'}) { + if ( $propVals->{'mac'} ) { # Get MAC suffix (MACID) $macId = $propVals->{'mac'}; - $macId = xCAT::zvmUtils->replaceStr($macId, ":", ""); - $macId = substr($macId, 6); + $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); + $macId = substr( $macId, 6 ); } else { $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; # Get USER Prefix - my $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -i "USER Prefix:"`; + my $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -a -i "USER Prefix:"`; $prefix =~ s/(.*?)USER Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; # Get MACADDR Prefix instead if USER Prefix is not defined if (!$prefix) { - $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -i "MACADDR Prefix:"`; + $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -a -i "MACADDR Prefix:"`; $prefix =~ s/(.*?)MACADDR Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; if (!$prefix) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Could not find the MACADDR/USER prefix of the z/VM system"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Verify that the node's zHCP($hcp) is correct, the node is online, and the SSH keys are setup for the zHCP"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not find the MACADDR/USER prefix of the z/VM system" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the node's zHCP($hcp) is correct, the node is online, and the SSH keys are setup for the zHCP" ); return; } } - # Generate MAC address my $mac; while ($generateNew) { # If no MACID is found, get one $macId = xCAT::zvmUtils->getMacID($::SUDOER, $hcp); - if (!$macId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Could not generate MACID"); + if ( !$macId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not generate MACID" ); return; } @@ -3104,26 +4354,25 @@ sub makeVM { $mac = $prefix . $macId; # 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 ); # Check 'mac' table for MAC address - my $tab = xCAT::Table->new('mac', -create => 1, -autocommit => 0); - my @entries = $tab->getAllAttribsWhere("mac = '" . $mac . "'", 'node'); + my $tab = xCAT::Table->new( 'mac', -create => 1, -autocommit => 0 ); + my @entries = $tab->getAllAttribsWhere( "mac = '" . $mac . "'", 'node' ); # If MAC address exists if (@entries) { - # Generate new MACID $out = xCAT::zvmUtils->generateMacId($::SUDOER, $hcp); $generateNew = 1; @@ -3131,12 +4380,12 @@ sub makeVM { $generateNew = 0; # Save MAC address in 'mac' table - xCAT::zvmUtils->setNodeProp('mac', $node, 'mac', $mac); + xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac ); # Generate new MACID $out = xCAT::zvmUtils->generateMacId($::SUDOER, $hcp); } - } # End of while ($generateNew) + } # End of while ($generateNew) } # Create virtual server @@ -3149,16 +4398,14 @@ sub makeVM { my $rc; my @vswId; my $target = "$::SUDOER\@$hcp"; - if ($userEntry) { - # Copy user entry - $out = `cp $userEntry /tmp/$node.txt`; + $out = `cp $userEntry /tmp/$node.txt`; $userEntry = "/tmp/$node.txt"; # If the directory entry contains a NICDEF statement, append MACID to the end # User must select the right one (layer) based on template chosen - $out = `cat $userEntry | egrep -i "NICDEF"`; + $out = `cat $userEntry | egrep -a -i "NICDEF"`; if ($out) { # Get the networks used by the zHCP @@ -3166,14 +4413,14 @@ sub makeVM { # Search user entry for network name foreach (@hcpNets) { - if ($out =~ m/ $_/i) { + if ( $out =~ m/ $_/i ) { $netName = $_; last; } } # Find NICDEF statement - $oldNicDef = `cat $userEntry | egrep -i "NICDEF" | egrep -i "$netName"`; + $oldNicDef = `cat $userEntry | egrep -a -i "NICDEF" | egrep -a -i "$netName"`; if ($oldNicDef) { $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef); $nicDef = xCAT::zvmUtils->replaceStr($oldNicDef, $netName, "$netName MACID $macId"); @@ -3182,39 +4429,60 @@ sub makeVM { $out = `sed -i -e "s,$oldNicDef,$nicDef,i" $userEntry`; } } - # Open user entry $out = `cat $userEntry`; - @lines = split('\n', $out); + @lines = split( '\n', $out ); # Get the userID in user entry - $line = xCAT::zvmUtils->trimStr($lines[0]); - @words = split(' ', $line); - $id = $words[1]; + $line = xCAT::zvmUtils->trimStr( $lines[0] ); + @words = split( ' ', $line ); + $id = $words[1]; # Change userID in user entry to match userID defined in xCAT $out = `sed -i -e "s,$id,$userId,i" $userEntry`; # SCP file over to zHCP $out = `scp $userEntry $target:$userEntry`; - # Create virtual server $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Create_DM -T $userId -f $userEntry"`; xCAT::zvmUtils->printSyslog("smcli Image_Create_DM -T $userId -f $userEntry"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Check output - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == 0) { + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == 0 ) { # Get VSwitch of zHCP (if any) @vswId = xCAT::zvmCPUtils->getVswitchId($::SUDOER, $hcp); + # Is there an internal vswitch for xcat and zhcp? If so do not grant to that. + my $internalVswitch = '-'; + if (open my $input_fh, "; # read first line, should be just one token + close $input_fh; + chomp($internalVswitch); + } + # Grant access to VSwitch for Linux user # GuestLan do not need permissions + # skip any duplicates + my %vswitchhash; foreach (@vswId) { - $out = xCAT::zvmCPUtils->grantVSwitch($callback, $::SUDOER, $hcp, $userId, $_); - xCAT::zvmUtils->printLn($callback, "$node: Granting VSwitch ($_) access for $userId... $out"); + if (!(length $_)) {next;} + # skip grant if we already did one for this vswitch + if (exists $vswitchhash{$_}) { + xCAT::zvmUtils->printSyslog("makeVM. Skipping duplicate vswitch grant from: $_"); + } + else { + if ($_ ne $internalVswitch) { + xCAT::zvmUtils->printSyslog("makeVM. Found vswitch to grant: $_"); + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $_ ,'', ''); # Don't have porttype or vlan + xCAT::zvmUtils->printLn( $callback, "$node: Granting VSwitch ($_) access for $userId... $out" ); + $vswitchhash{$_} = '1'; + } else { + xCAT::zvmUtils->printSyslog("makeVM. Skipping grant for internal vswitch: $_"); + } + } } # Remove user entry file (on zHCP) @@ -3224,35 +4492,32 @@ sub makeVM { # Remove user entry on xCAT $out = `rm -rf $userEntry`; } elsif ($stdin) { - # Take directory entry from stdin $stdin = $::STDIN; # If the directory entry contains a NICDEF statement, append MACID to the end # User must select the right one (layer) based on template chosen - $out = `echo -e "$stdin" | egrep -i "NICDEF"`; + $out = `echo -e "$stdin" | egrep -a -i "NICDEF"`; if ($out) { - # Get the networks used by the zHCP @hcpNets = xCAT::zvmCPUtils->getNetworkNamesArray($::SUDOER, $hcp); # Search user entry for network name $netName = ''; foreach (@hcpNets) { - if ($out =~ m/ $_/i) { + if ( $out =~ m/ $_/i ) { $netName = $_; last; } } # Find NICDEF statement - $oldNicDef = `echo -e "$stdin" | egrep -i "NICDEF" | egrep -i "$netName"`; + $oldNicDef = `echo -e "$stdin" | egrep -a -i "NICDEF" | egrep -a -i "$netName"`; if ($oldNicDef) { $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef); # Append MACID at the end - $nicDef = xCAT::zvmUtils->replaceStr($oldNicDef, $netName, "$netName MACID $macId"); - + $nicDef = xCAT::zvmUtils->replaceStr( $oldNicDef, $netName, "$netName MACID $macId" ); # Update stdin $stdin =~ s/$oldNicDef/$nicDef/g; } @@ -3268,6 +4533,7 @@ sub makeVM { # Write directory entry into temporary file # because directory entry cannot be remotely echoed into stdin foreach (@lines) { + if (!(length $_)) {next;} if ($_) { $_ = "'" . $_ . "'"; `ssh $::SUDOER\@$hcp "echo $_ >> $file"`; @@ -3277,11 +4543,11 @@ sub makeVM { # Create virtual server $out = `ssh $::SUDOER\@$hcp "cat $file | $::SUDO $::DIR/smcli Image_Create_DM -T $userId -s"`; xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp cat $file | $::SUDO $::DIR/smcli Image_Create_DM -T $userId -s"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Check output - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == 0) { + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == 0 ) { # Get VSwitch of zHCP (if any) @vswId = xCAT::zvmCPUtils->getVswitchId($::SUDOER, $hcp); @@ -3289,8 +4555,8 @@ sub makeVM { # Grant access to VSwitch for Linux user # GuestLan do not need permissions foreach (@vswId) { - $out = xCAT::zvmCPUtils->grantVSwitch($callback, $::SUDOER, $hcp, $userId, $_); - xCAT::zvmUtils->printLn($callback, "$node: Granting VSwitch ($_) access for $userId... $out"); + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $userId, $_, '', ''); + xCAT::zvmUtils->printLn( $callback, "$node: Granting VSwitch ($_) access for $userId... $out" ); } # Delete created file on zHCP @@ -3300,7 +4566,7 @@ sub makeVM { # Create NOLOG virtual server $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/createvs $userId"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } return; @@ -3314,16 +4580,17 @@ sub makeVM { Arguments : Node Disk pool Disk password + clone info hash, can be empty Returns : Nothing Example : cloneVM($callback, $targetNode, $args); - + =cut #------------------------------------------------------- sub cloneVM { # Get inputs - my ($callback, $nodes, $args) = @_; + my ( $callback, $nodes, $args ) = @_; # Get nodes my @nodes = @$nodes; @@ -3340,35 +4607,39 @@ sub cloneVM { # Get source node my $sourceNode = $args->[0]; - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $sourceNode, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); # Get zHCP my $srcHcp = $propVals->{'hcp'}; # Get node user ID my $sourceId = $propVals->{'userid'}; - # Capitalize user ID $sourceId =~ tr/a-z/A-Z/; # Get operating system, e.g. sles11sp2 or rhel6.2 - @propNames = ('os'); - $propVals = xCAT::zvmUtils->getNodeProps('nodetype', $sourceNode, @propNames); + @propNames = ( 'os' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'nodetype', $sourceNode, @propNames ); my $srcOs = $propVals->{'os'}; # Set IP address my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); + my @dedicates = xCAT::zvmUtils->getDedicates( $callback, $::SUDOER, $sourceNode ); + if (scalar(@dedicates)) { + xCAT::zvmUtils->printLn( $callback, "$sourceNode: (Error) Dedicate statements found in source directory." ); + return; + } + # Get networks in 'networks' table my $netEntries = xCAT::zvmUtils->getAllTabEntries('networks'); my $srcNetwork = ""; my $srcMask; foreach (@$netEntries) { - # Get source network and mask $srcNetwork = $_->{'net'}; - $srcMask = $_->{'mask'}; + $srcMask = $_->{'mask'}; # If the host IP address is in this subnet, return if (xCAT::NetworkUtils->ishostinsubnet($sourceIp, $srcMask, $srcNetwork)) { @@ -3382,69 +4653,69 @@ sub cloneVM { xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$srcHcp sudo:$::SUDO"); xCAT::zvmUtils->printSyslog("srcHcp:$srcHcp sourceId:$sourceId srcOs:$srcOs srcNetwork:$srcNetwork srcMask:$srcMask"); + xCAT::zvmUtils->printSyslog("nodes:@nodes"); foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Cloning $sourceNode"); + xCAT::zvmUtils->printLn( $callback, "$_: Cloning $sourceNode" ); # Exit if missing source node - if (!$sourceNode) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing source node"); + if ( !$sourceNode ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node" ); return; } # Exit if missing source HCP - if (!$srcHcp) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing source node HCP"); + if ( !$srcHcp ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node HCP" ); return; } # Exit if missing source user ID - if (!$sourceId) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing source user ID"); + if ( !$sourceId ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source user ID" ); return; } # Exit if missing source operating system - if (!$srcOs) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing source operating system"); + if ( !$srcOs ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source operating system" ); return; } # Exit if missing source operating system - if (!$sourceIp || !$srcNetwork || !$srcMask) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing source IP, network, or mask"); + if ( !$sourceIp || !$srcNetwork || !$srcMask ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source IP, network, or mask" ); return; } # Get target node - @propNames = ('hcp', 'userid'); - $propVals = xCAT::zvmUtils->getNodeProps('zvm', $_, @propNames); + @propNames = ( 'hcp', 'userid' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames ); # Get target HCP my $tgtHcp = $propVals->{'hcp'}; # Get node userID my $tgtId = $propVals->{'userid'}; - # Capitalize userID $tgtId =~ tr/a-z/A-Z/; # Exit if missing target zHCP - if (!$tgtHcp) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing target node HCP"); + if ( !$tgtHcp ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing target node HCP" ); return; } # Exit if missing target user ID - if (!$tgtId) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Missing target user ID"); + if ( !$tgtId ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing target user ID" ); return; } # Exit if source and target zHCP are not equal - if ($srcHcp ne $tgtHcp) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Source and target HCP are not equal"); - xCAT::zvmUtils->printLn($callback, "$_: (Solution) Set the source and target HCP appropriately in the zvm table"); + if ( $srcHcp ne $tgtHcp ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Source and target HCP are not equal" ); + xCAT::zvmUtils->printLn( $callback, "$_: (Solution) Set the source and target HCP appropriately in the zvm table" ); return; } @@ -3453,21 +4724,21 @@ sub cloneVM { my $macId; my $generateNew = 0; # Flag to generate new MACID @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps('mac', $_, @propNames); - if (!$propVals->{'mac'}) { + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $_, @propNames ); + if ( !$propVals->{'mac'} ) { # If no MACID is found, get one $macId = xCAT::zvmUtils->getMacID($::SUDOER, $tgtHcp); - if (!$macId) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) Could not generate MACID"); + if ( !$macId ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Could not generate MACID" ); return; } # Create MAC address (target) - $targetMac = xCAT::zvmUtils->createMacAddr($::SUDOER, $_, $macId); + $targetMac = xCAT::zvmUtils->createMacAddr( $::SUDOER, $_, $macId ); # Save MAC address in 'mac' table - xCAT::zvmUtils->setNodeProp('mac', $_, 'mac', $targetMac); + xCAT::zvmUtils->setNodeProp( 'mac', $_, 'mac', $targetMac ); # Generate new MACID $out = xCAT::zvmUtils->generateMacId($::SUDOER, $tgtHcp); @@ -3492,7 +4763,7 @@ sub cloneVM { # Hash table of source disk type # $srcLinkAddr[$addr] = $type my %srcDiskType; - my @srcDisks = xCAT::zvmUtils->getMdisks($callback, $::SUDOER, $sourceNode); + my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $sourceNode ); # Get details about source disks # Output is similar to: @@ -3500,13 +4771,14 @@ sub cloneVM { $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $sourceId -k MDISK"`; xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $sourceId -k MDISK"); xCAT::zvmUtils->printSyslog("$out"); + xCAT::zvmUtils->printSyslog("srcDisks:@srcDisks"); my $srcDiskDet = xCAT::zvmUtils->trimStr($out); foreach (@srcDisks) { # Get disk address - @words = split(' ', $_); - $addr = $words[1]; - $type = $words[2]; + @words = split( ' ', $_ ); + $addr = $words[1]; + $type = $words[2]; # Add 0 in front if address length is less than 4 while (length($addr) < 4) { @@ -3518,52 +4790,54 @@ sub cloneVM { # Get disk size (cylinders or blocks) # ECKD or FBA disk - if ($type eq '3390' || $type eq '9336') { - my @lines = split('\n', $srcDiskDet); + if ( $type eq '3390' || $type eq '9336' ) { + my @lines = split( '\n', $srcDiskDet ); # Loop through each line - for ($i = 0 ; $i < @lines ; $i++) { + for ( $i = 0 ; $i < @lines ; $i++ ) { + # remove the MDISK= from the line $lines[$i] =~ s/MDISK=//g; - # Extract NIC address + # Extract vdev address + # search for = signs, capture what is after = but not whitespace @words = ($lines[$i] =~ m/=(\S+)/g); my $srcDiskAddr = $words[0]; - - $srcDiskSize{$srcDiskAddr} = $words[3]; - xCAT::zvmUtils->printSyslog("addr:$addr type:$type srcDiskAddr:$srcDiskAddr srcDiskSize:$words[3]"); + if ($srcDiskAddr eq $addr) { + $srcDiskSize{$srcDiskAddr} = $words[3]; + xCAT::zvmUtils->printSyslog("addr:$addr type:$type srcDiskAddr:$srcDiskAddr srcDiskSize:$words[3]"); + } } } # If source disk is not linked my $try = 5; - while ($try > 0) { + while ( $try > 0 ) { # New disk address $linkAddr = $addr + 1000; # Check if new disk address is used (source) - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $srcHcp, $linkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $srcHcp, $linkAddr ); # If disk address is used (source) - while ($rc == 0) { + while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $linkAddr = $linkAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $srcHcp, $linkAddr); + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $srcHcp, $linkAddr ); } $srcLinkAddr{$addr} = $linkAddr; # Link source disk to HCP foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Linking source disk ($addr) as ($linkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$_: Linking source disk ($addr) as ($linkAddr)" ); } $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$srcHcp "$::SUDO /sbin/vmcp link $sourceId $addr $linkAddr RR"`; - if ($out =~ m/not linked/i) { - + if ( $out =~ m/not linked/i ) { # Do nothing } else { last; @@ -3573,12 +4847,12 @@ sub cloneVM { # Wait before next try sleep(5); - } # End of while ( $try > 0 ) + } # End of while ( $try > 0 ) # If source disk is not linked - if ($out =~ m/not linked/i) { + if ( $out =~ m/not linked/i ) { foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Failed"); + xCAT::zvmUtils->printLn( $callback, "$_: Failed" ); } # Exit @@ -3586,8 +4860,8 @@ sub cloneVM { } # Enable source disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $srcHcp, "-e", $linkAddr); - } # End of foreach (@srcDisks) + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $srcHcp, "-e", $linkAddr ); + } # End of foreach (@srcDisks) # Get the networks the HCP is on my @hcpNets = xCAT::zvmCPUtils->getNetworkNamesArray($::SUDOER, $srcHcp); @@ -3602,24 +4876,20 @@ sub cloneVM { $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $sourceId -k NICDEF"`; xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $sourceId -k NICDEF"); xCAT::zvmUtils->printSyslog("$out"); - # Output is similar to: # NICDEF_PROFILE=VDEV=0800 TYPE=QDIO LAN=SYSTEM SWITCHNAME=VSW2 # NICDEF=VDEV=0900 TYPE=QDIO DEVICES=3 LAN=SYSTEM SWITCHNAME=GLAN1 # NICDEF=VDEV=0A00 TYPE=QDIO DEVICES=3 LAN=SYSTEM SWITCHNAME=VSW2 - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); # Loop through each line my $line; - for ($i = 0 ; $i < @lines ; $i++) { - + for ( $i = 0 ; $i < @lines ; $i++ ) { # Loop through each network name foreach (@hcpNets) { - # If the network is found - if ($lines[$i] =~ m/SWITCHNAME=$_/i) { - + if ( $lines[$i] =~ m/SWITCHNAME=$_/i ) { # Save network name $hcpNetName = $_; @@ -3627,7 +4897,7 @@ sub cloneVM { $lines[$i] =~ s/NICDEF=//g; # Extract NIC address - @words = ($lines[$i] =~ m/=(\S+)/g); + @words = ($lines[$i] =~ m/=(\S+)/g); $srcNicAddr = $words[0]; xCAT::zvmUtils->printSyslog("hcpNetName:$hcpNetName srcNicAddr:$srcNicAddr"); @@ -3639,22 +4909,21 @@ sub cloneVM { # If no network name is found, exit if (!$hcpNetName || !$srcNicAddr) { - #*** Detatch source disks *** - for $addr (keys %srcLinkAddr) { + for $addr ( keys %srcLinkAddr ) { $linkAddr = $srcLinkAddr{$addr}; # Disable and detatch source disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $srcHcp, "-d", $linkAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $srcHcp, "-d", $linkAddr ); $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$srcHcp "$::SUDO /sbin/vmcp det $linkAddr"`; foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Detatching source disk ($addr) at ($linkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$_: Detatching source disk ($addr) at ($linkAddr)" ); } } foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: (Error) No suitable network device found in user directory entry"); - xCAT::zvmUtils->printLn($callback, "$_: (Solution) Verify that the node has one of the following network devices: @hcpNets"); + xCAT::zvmUtils->printLn( $callback, "$_: (Error) No suitable network device found in user directory entry" ); + xCAT::zvmUtils->printLn( $callback, "$_: (Solution) Verify that the node has one of the following network devices: @hcpNets" ); } return; @@ -3666,36 +4935,58 @@ sub cloneVM { # Get source MAC address in 'mac' table my $srcMac; @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps('mac', $sourceNode, @propNames); - if ($propVals->{'mac'}) { + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $sourceNode, @propNames ); + if ( $propVals->{'mac'} ) { # Get MAC address $srcMac = $propVals->{'mac'}; } - # Get user entry of source node + # Get user entry of source node without any mdisk statements my $srcUserEntry = "/tmp/$sourceNode.txt"; $out = `rm $srcUserEntry`; - $out = xCAT::zvmUtils->getUserEntryWODisk($callback, $::SUDOER, $sourceNode, $srcUserEntry); + $out = xCAT::zvmUtils->getUserEntryWODisk( $callback, $::SUDOER, $sourceNode, $srcUserEntry ); # Check if user entry is valid $out = `cat $srcUserEntry`; # If output contains USER LINUX123, then user entry is good - if ($out =~ m/USER $sourceId/i) { + if ( $out =~ m/USER $sourceId/i ) { # Turn off source node - if (`/opt/xcat/bin/pping $sourceNode` =~ m/ ping/i) { + my $ping = xCAT::zvmUtils->pingNode($sourceNode); + if ($ping eq "ping") { $out = `ssh -o ConnectTimeout=10 $sourceNode "shutdown -h now"`; sleep(90); # Wait 1.5 minutes before logging user off foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Shutting down $sourceNode"); + xCAT::zvmUtils->printLn( $callback, "$_: Shutting down $sourceNode" ); } } - $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Deactivate -T $sourceId"`; xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $sourceId"); + $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Deactivate -T $sourceId"`; + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $srcHcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $srcHcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + $out = "$sourceId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + $out = "$sourceId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $sourceId output: $out" ); + xCAT::zvmUtils->printLn( $callback, "$out"); + return; + } xCAT::zvmUtils->printSyslog("$out"); #*** Clone source node *** @@ -3706,11 +4997,11 @@ sub cloneVM { # Parent process if ($pid) { - push(@children, $pid); + push( @children, $pid ); } # Child process - elsif ($pid == 0) { + elsif ( $pid == 0 ) { clone( $callback, $_, $args, \@srcDisks, \%srcLinkAddr, \%srcDiskSize, \%srcDiskType, $srcNicAddr, $hcpNetName, \@srcVswitch, $srcOs, $srcMac, $netEntries, $sourceIp, $srcNetwork, $srcMask @@ -3729,11 +5020,11 @@ sub cloneVM { # Clone 4 nodes at a time # If you handle more than this, some nodes will not be cloned # You will get errors because SMAPI cannot handle many nodes - if (!(@children % 4)) { + if ( !( @children % 4 ) ) { # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Clear children @@ -3744,7 +5035,7 @@ sub cloneVM { # Handle the remaining nodes # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Remove source user entry @@ -3752,21 +5043,21 @@ sub cloneVM { } # End of if #*** Detatch source disks *** - for $addr (keys %srcLinkAddr) { + for $addr ( keys %srcLinkAddr ) { $linkAddr = $srcLinkAddr{$addr}; # Disable and detatch source disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $srcHcp, "-d", $linkAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $srcHcp, "-d", $linkAddr ); $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$srcHcp "$::SUDO /sbin/vmcp det $linkAddr"`; foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Detatching source disk ($addr) at ($linkAddr)"); + xCAT::zvmUtils->printLn( $callback, "$_: Detatching source disk ($addr) at ($linkAddr)" ); } } #*** Done *** foreach (@nodes) { - xCAT::zvmUtils->printLn($callback, "$_: Done"); + xCAT::zvmUtils->printLn( $callback, "$_: Done" ); } return; @@ -3792,10 +5083,10 @@ sub cloneVM { Path to network configuration file Path to hardware configuration file (SUSE only) Returns : Nothing - Example : clone($callback, $_, $args, \@srcDisks, \%srcLinkAddr, \%srcDiskSize, - $srcNicAddr, $hcpNetName, \@srcVswitch, $srcOs, $srcMac, $netEntries, + Example : clone($callback, $_, $args, \@srcDisks, \%srcLinkAddr, \%srcDiskSize, + $srcNicAddr, $hcpNetName, \@srcVswitch, $srcOs, $srcMac, $netEntries, $sourceIp, $srcNetwork, $srcMask); - + =cut #------------------------------------------------------- @@ -3810,15 +5101,14 @@ sub clone { # Get source node properties from 'zvm' table my $sourceNode = $args->[0]; - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $sourceNode, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); # Get zHCP my $srcHcp = $propVals->{'hcp'}; # Get node user ID my $sourceId = $propVals->{'userid'}; - # Capitalize user ID $sourceId =~ tr/a-z/A-Z/; @@ -3833,30 +5123,38 @@ sub clone { my $rc; # Get node properties from 'zvm' table - @propNames = ('hcp', 'userid'); - $propVals = xCAT::zvmUtils->getNodeProps('zvm', $tgtNode, @propNames); + @propNames = ( 'hcp', 'userid' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames ); # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node HCP" ); return; } + # Get zHCP user ID + my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + if ( !$hcpUserId ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing zHCP user ID" ); + return; + } + # Capitalize user ID + $hcpUserId =~ tr/a-z/A-Z/; + # Get node user ID my $tgtUserId = $propVals->{'userid'}; - if (!$tgtUserId) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Missing user ID"); + if ( !$tgtUserId ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing user ID" ); return; } - # Capitalize user ID $tgtUserId =~ tr/a-z/A-Z/; # Exit if source node HCP is not the same as target node HCP - if (!($srcHcp eq $hcp)) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Source node HCP ($srcHcp) is not the same as target node HCP ($hcp)"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Solution) Set the source and target HCP appropriately in the zvm table"); + if ( !( $srcHcp eq $hcp ) ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Source node HCP ($srcHcp) is not the same as target node HCP ($hcp)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Set the source and target HCP appropriately in the zvm table" ); return; } @@ -3864,9 +5162,9 @@ sub clone { `makehosts`; sleep(5); my $targetIp = xCAT::zvmUtils->getIp($tgtNode); - if (!$targetIp) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Missing IP for $tgtNode in /etc/hosts"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Solution) Verify that the node's IP address is specified in the hosts table and then run makehosts"); + if ( !$targetIp ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing IP for $tgtNode in /etc/hosts" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify that the node's IP address is specified in the hosts table and then run makehosts" ); return; } xCAT::zvmUtils->printSyslog("hcp:$hcp tgtUserId:$tgtUserId targetIp:$targetIp"); @@ -3878,11 +5176,11 @@ sub clone { # Get disk pool and multi password my $i; my %inputs; - foreach $i (1 .. 2) { - if ($args->[$i]) { + foreach $i ( 1 .. 2 ) { + if ( $args->[$i] ) { # Split parameters by '=' - @words = split("=", $args->[$i]); + @words = split( "=", $args->[$i] ); # Create hash array $inputs{ $words[0] } = $words[1]; @@ -3891,8 +5189,8 @@ sub clone { # Get disk pool my $pool = $inputs{"pool"}; - if (!$pool) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Missing disk pool. Please specify one."); + if ( !$pool ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing disk pool. Please specify one." ); return; } xCAT::zvmUtils->printSyslog("pool:$pool"); @@ -3924,21 +5222,21 @@ sub clone { my $macId; my $generateNew = 0; # Flag to generate new MACID @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps('mac', $tgtNode, @propNames); + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $tgtNode, @propNames ); if ($propVals) { # Get MACID $targetMac = $propVals->{'mac'}; $macId = $propVals->{'mac'}; - $macId = xCAT::zvmUtils->replaceStr($macId, ":", ""); - $macId = substr($macId, 6); + $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); + $macId = substr( $macId, 6 ); } else { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Missing target MAC address"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target MAC address" ); return; } # If the user entry contains a NICDEF statement - $out = `cat $userEntry | egrep -i "NICDEF"`; + $out = `cat $userEntry | egrep -a -i "NICDEF"`; if ($out) { # Get the networks used by the zHCP @@ -3947,17 +5245,17 @@ sub clone { # Search user entry for network name my $hcpNetName = ''; foreach (@hcpNets) { - if ($out =~ m/ $_/i) { + if ( $out =~ m/ $_/i ) { $hcpNetName = $_; last; } } # If the user entry contains a MACID - $out = `cat $userEntry | egrep -i "MACID"`; + $out = `cat $userEntry | egrep -a -i "MACID"`; if ($out) { - my $pos = rindex($out, "MACID"); - my $oldMacId = substr($out, $pos + 6, 12); + my $pos = rindex( $out, "MACID" ); + my $oldMacId = substr( $out, $pos + 6, 12 ); $oldMacId = xCAT::zvmUtils->trimStr($oldMacId); # Replace old MACID @@ -3965,9 +5263,9 @@ sub clone { } else { # Find NICDEF statement - my $oldNicDef = `cat $userEntry | egrep -i "NICDEF" | egrep -i "$hcpNetName"`; + my $oldNicDef = `cat $userEntry | egrep -a -i "NICDEF" | egrep -a -i "$hcpNetName"`; $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef); - my $nicDef = xCAT::zvmUtils->replaceStr($oldNicDef, $hcpNetName, "$hcpNetName MACID $macId"); + my $nicDef = xCAT::zvmUtils->replaceStr( $oldNicDef, $hcpNetName, "$hcpNetName MACID $macId" ); # Append MACID at the end $out = `sed -i -e "s,$oldNicDef,$nicDef,i" $userEntry`; @@ -3975,15 +5273,15 @@ sub clone { } # SCP user entry file over to HCP - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $userEntry, $userEntry); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $userEntry, $userEntry ); #*** Create new virtual server *** my $try = 5; - while ($try > 0) { - if ($try > 4) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Creating user directory entry"); + while ( $try > 0 ) { + if ( $try > 4 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" ); } else { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Trying again ($try) to create user directory entry"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to create user directory entry" ); } $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Create_DM -T $tgtUserId -f $userEntry"`; xCAT::zvmUtils->printSyslog("smcli Image_Create_DM -T $tgtUserId -f $userEntry"); @@ -3993,9 +5291,9 @@ sub clone { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId" | sed '\$d'`; xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId | sed '\$d'"); xCAT::zvmUtils->printSyslog("$out"); - $rc = xCAT::zvmUtils->checkOutput($callback, $out); + $rc = xCAT::zvmUtils->checkOutput( $out ); - if ($rc == -1) { + if ( $rc == -1 ) { # Wait before trying again sleep(5); @@ -4011,9 +5309,9 @@ sub clone { $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $userEntry"`; # Exit on bad output - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Could not create user entry"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Solution) Verify that the node's zHCP and its zVM's SMAPI are both online"); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify that the node's zHCP and its zVM's SMAPI are both online" ); return; } @@ -4022,18 +5320,27 @@ sub clone { # Grant access to VSwitch for Linux user # GuestLan do not need permissions + my %vswitchhash; foreach (@srcVswitch) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Granting VSwitch ($_) access for $tgtUserId"); - $out = xCAT::zvmCPUtils->grantVSwitch($callback, $::SUDOER, $hcp, $tgtUserId, $_); + # skip grant if we already did one for this vswitch + if (exists $vswitchhash{$_}) { + xCAT::zvmUtils->printSyslog("clone. Skipping duplicate vswitch grant from: $_"); + } + else { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Granting VSwitch ($_) access for $tgtUserId" ); + # If this is one of our recent provisions the directory of the source should contain any vlan id grants also + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $::SUDOER, $hcp, $tgtUserId, $_, '', '' ); - # Check for errors - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { + # Check for errors + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { - # Exit on bad output - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); - return; - } + # Exit on bad output + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + return; + } + $vswitchhash{$_} = '1'; + } } # End of foreach (@vswitchId) #*** Add MDisk to target user entry *** @@ -4045,11 +5352,11 @@ sub clone { foreach (@srcDisks) { # Get disk address - @words = split(' ', $_); + @words = split( ' ', $_ ); $addr = $words[1]; - push(@tgtDisks, $addr); - $type = $words[2]; - $mode = $words[6]; + push( @tgtDisks, $addr ); + $type = $words[2]; + $mode = $words[6]; if (!$mode) { $mode = "MR"; } @@ -4060,27 +5367,28 @@ sub clone { } # Add ECKD disk - if ($type eq '3390') { + if ( $type eq '3390' ) { # Get disk size (cylinders) $cyl = $srcDiskSize{$addr}; $try = 5; - while ($try > 0) { + while ( $try > 0 ) { # Add ECKD disk - if ($try > 4) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Adding minidisk ($addr)"); + if ( $try > 4 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" ); } else { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" ); } $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"); xCAT::zvmUtils->printSyslog("$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); # Check output - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { # Wait before trying again sleep(5); @@ -4095,35 +5403,36 @@ sub clone { } # End of while ( $try > 0 ) # Exit on bad output - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Could not add minidisk ($addr)"); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" ); return; } } # End of if ( $type eq '3390' ) # Add FBA disk - elsif ($type eq '9336') { + elsif ( $type eq '9336' ) { # Get disk size (blocks) my $blkSize = '512'; my $blks = $srcDiskSize{$addr}; $try = 10; - while ($try > 0) { + while ( $try > 0 ) { # Add FBA disk - if ($try > 9) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Adding minidisk ($addr)"); + if ( $try > 9 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" ); } else { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" ); } $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $pool -u 1 -z $blks -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $pool -u 1 -z $blks -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"); xCAT::zvmUtils->printSyslog("$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); # Check output - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { # Wait before trying again sleep(5); @@ -4138,8 +5447,8 @@ sub clone { } # End of while ( $try > 0 ) # Exit on bad output - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Could not add minidisk ($addr)"); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" ); return; } } # End of elsif ( $type eq '9336' ) @@ -4149,16 +5458,16 @@ sub clone { # is equal to the number of disks added my @disks; $try = 10; - while ($try > 0) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks added (@tgtDisks). Checking directory for those disks..." ); + while ( $try > 0 ) { # Get disks within user entry - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId" | sed '\$d' | grep "MDISK"`; - xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId | grep MDISK"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId" | sed '\$d' | grep -a "MDISK"`; + xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId | grep -a MDISK"); xCAT::zvmUtils->printSyslog("$out"); - @disks = split('\n', $out); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Disks added (" . @tgtDisks . "). Disks in user entry (" . @disks . ")"); + @disks = split( '\n', $out ); - if (@disks != @tgtDisks) { + if ( @disks != @tgtDisks ) { $try = $try - 1; # Wait before trying again @@ -4169,236 +5478,305 @@ sub clone { } # Exit if all disks are not present - if (@disks != @tgtDisks) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Disks not present in user entry"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Solution) Verify disk pool($pool) has free disks"); + if ( @disks != @tgtDisks ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." ); + xCAT::zvmUtils->printSyslog( $callback, "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." ); + + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks found in $sourceId source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" ); + xCAT::zvmUtils->printSyslog( $callback, "$tgtNode: Disks found in $sourceId + source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" ); + + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify disk pool($pool) has free disks and that directory updates are working" ); return; } - #*** Link, format, and copy source disks *** + #** * Link, format, and copy source disks *** my $srcAddr; + my $directoryMdiskAddr; my $tgtAddr; my $srcDevNode; my $tgtDevNode; my $tgtDiskType; + foreach (@tgtDisks) { - #*** Link target disk *** - $try = 10; - while ($try > 0) { - - # Add 0 in front if address length is less than 4 - while (length($_) < 4) { - $_ = '0' . $_; - } - - # New disk address - $srcAddr = $srcLinkAddr{$_}; - $tgtAddr = $_ + 2000; - - # Check if new disk address is used (target) - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $tgtAddr); - - # If disk address is used (target) - while ($rc == 0) { - - # Generate a new disk address - # Sleep 5 seconds to let existing disk appear - sleep(5); - $tgtAddr = $tgtAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed($::SUDOER, $hcp, $tgtAddr); - } - - # Link target disk - xCAT::zvmUtils->printLn($callback, "$tgtNode: Linking target disk ($_) as ($tgtAddr)"); - $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $_ $tgtAddr MR $tgtPw"`; - - # If link fails - if ($out =~ m/not linked/i || $out =~ m/not write-enabled/i) { - - # Wait before trying again - sleep(5); - - $try = $try - 1; - } else { - last; - } - } # End of while ( $try > 0 ) - - # If target disk is not linked - if ($out =~ m/not linked/i) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Failed to link target disk ($_)"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Failed"); - - # Exit - return; - } - # Get disk type (3390 or 9336) $tgtDiskType = $srcDiskType{$_}; - #*** Use flashcopy *** - # Flashcopy only supports ECKD volumes - # Assume flashcopy is supported and use Linux DD on failure - my $ddCopy = 0; + #*** Try to use SMAPI flashcopy first if ECKD *** + # Otherwise link the target disks and if ECKD, try CP Flashcopy. If + # CP flashcopy does not work or not ECKD; use Linux DD + my $ddCopy = 0; my $cpFlashcopy = 1; - if ($tgtDiskType eq '3390') { + my $smapiFlashCopyDone = 0; + $directoryMdiskAddr = $_; - # Use SMAPI FLASHCOPY - xCAT::zvmUtils->printLn($callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($srcAddr) using FLASHCOPY"); + if ($tgtDiskType eq '3390') { + # Try SMAPI FLASHCOPY if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) { - $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $srcAddr, $tgtUserId, $srcAddr); - xCAT::zvmUtils->printSyslog("smapiFlashCopy: $out"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($directoryMdiskAddr) to target disk ($directoryMdiskAddr) using FLASHCOPY" ); + xCAT::zvmUtils->printSyslog( "$tgtNode: Doing SMAPI flashcopy source disk ($sourceId $directoryMdiskAddr) to target disk ($tgtUserId $directoryMdiskAddr) using FLASHCOPY" ); + $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $directoryMdiskAddr, $tgtUserId, $directoryMdiskAddr); # Exit if flashcopy completed successfully - # Otherwsie, try CP FLASHCOPY - if ($out =~ m/Done/i) { + # Otherwise, if not built in xCAT, try CP FLASHCOPY + if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) { + xCAT::zvmUtils->printSyslog( "$tgtNode: SMAPI flashcopy done. output:<$out>" ); $cpFlashcopy = 0; - } - } + $smapiFlashCopyDone = 1; - # Use CP FLASHCOPY - if ($cpFlashcopy) { + # now link the disk in zhcp for further tailoring + $try = 10; + while ( $try > 0 ) { - # Check for CP flashcopy lock - my $wait = 0; - while (`ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"` && $wait < 90) { + # Add 0 in front if address length is less than 4 + while (length($directoryMdiskAddr) < 4) { + $directoryMdiskAddr = '0' . $directoryMdiskAddr; + } + # New disk address + $tgtAddr = $directoryMdiskAddr + 2000; - # Wait until the lock dissappears - # 90 seconds wait limit - sleep(2); - $wait = $wait + 2; - } + # Check if new disk address is used (target) + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr ); - # If flashcopy locks still exists - if (`ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"`) { + # If disk address is used (target) + while ( $rc == 0 ) { - # Detatch disks from HCP - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Error) Flashcopy lock is enabled"); - xCAT::zvmUtils->printLn($callback, "$tgtNode: (Solution) Remove lock by deleting /tmp/.flashcopy_lock on the zHCP. Use caution!"); - return; - } else { + # Generate a new disk address + # Sleep 5 seconds to let existing disk appear + sleep(5); + $tgtAddr = $tgtAddr + 1; + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr ); + } - # Enable lock - $out = `ssh $::SUDOER\@$hcp "$::SUDO touch /tmp/.flashcopy_lock"`; + # Link target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($directoryMdiskAddr) as ($tgtAddr)" ); + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $directoryMdiskAddr $tgtAddr MR $tgtPw"`; - # Flashcopy source disk - $out = xCAT::zvmCPUtils->flashCopy($::SUDOER, $hcp, $srcAddr, $tgtAddr); - xCAT::zvmUtils->printSyslog("flashCopy: $out"); - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + # If link fails + if ( $out =~ m/not linked/i || $out =~ m/not write-enabled/i ) { - # Try Linux dd - $ddCopy = 1; + # Wait before trying again + sleep(5); + + $try = $try - 1; + } else { + last; + } + } # End of while ( $try > 0 ) + + # If target disk is not linked + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($directoryMdiskAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); + + # Exit + return; } - # Wait a while for flashcopy to completely finish - sleep(10); - - # Remove lock - $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/.flashcopy_lock"`; + } else { + xCAT::zvmUtils->printLn( $callback, "$out" ); + xCAT::zvmUtils->printSyslog("$tgtNode: SMAPI Flashcopy error, trying CP Flashcopy. SMAPI output: $out" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: SMAPI Flashcopy error, trying CP Flashcopy." ); } } - } else { - $ddCopy = 1; } - # Flashcopy not supported, use Linux dd - if ($ddCopy) { + # If SMAPI flashcopy did not work or this is not an ECKD, then link the target disks in write mode + if ( !$smapiFlashCopyDone ) { - #*** Use Linux dd to copy *** - xCAT::zvmUtils->printLn($callback, "$tgtNode: FLASHCOPY not working. Using Linux DD"); + #*** Link target disk *** + $try = 10; + while ( $try > 0 ) { - # Enable target disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", $tgtAddr); + # Add 0 in front if address length is less than 4 + while (length($_) < 4) { + $_ = '0' . $_; + } + # New disk address + $srcAddr = $srcLinkAddr{$_}; + $tgtAddr = $_ + 2000; - # Determine source device node - $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcAddr); + # Check if new disk address is used (target) + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr ); - # Determine target device node - $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr); + # If disk address is used (target) + while ( $rc == 0 ) { - # Format target disk - # Only ECKD disks need to be formated - if ($tgtDiskType eq '3390') { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Formating target disk ($tgtAddr)"); - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; - xCAT::zvmUtils->printSyslog("dasdfmt -b 4096 -y -f /dev/$tgtDevNode"); + # Generate a new disk address + # Sleep 5 seconds to let existing disk appear + sleep(5); + $tgtAddr = $tgtAddr + 1; + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr ); + } - # Check for errors - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + # Link target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($_) as ($tgtAddr)" ); + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $_ $tgtAddr MR $tgtPw"`; - # Detatch disks from HCP - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + # If link fails + if ( $out =~ m/not linked/i || $out =~ m/not write-enabled/i ) { - return; - } + # Wait before trying again + sleep(5); - # Sleep 2 seconds to let the system settle - sleep(2); + $try = $try - 1; + } else { + last; + } + } # End of while ( $try > 0 ) - # Copy source disk to target disk - xCAT::zvmUtils->printLn($callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)"); - $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync && $::SUDO echo $?"`; - $out = xCAT::zvmUtils->trimStr($out); - if (int($out) != 0) { + # If target disk is not linked + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($_)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); - # If $? is not 0 then there was an error during Linux dd - $out = "(Error) Failed to copy /dev/$srcDevNode"; - } + # Exit + return; + } + if ($tgtDiskType eq '3390') { - xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync"); - xCAT::zvmUtils->printSyslog("$out"); - } else { - # Copy source disk to target disk - # Block size = 512 - xCAT::zvmUtils->printLn($callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)"); - $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync && $::SUDO echo $?"`; - $out = xCAT::zvmUtils->trimStr($out); - if (int($out) != 0) { + # Use CP FLASHCOPY + if ($cpFlashcopy) { + # Check for CP flashcopy lock + my $wait = 0; + while ( `ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"` && $wait < 90 ) { - # If $? is not 0 then there was an error during Linux dd - $out = "(Error) Failed to copy /dev/$srcDevNode"; - } + # Wait until the lock dissappears + # 90 seconds wait limit + sleep(2); + $wait = $wait + 2; + } - xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync"); - xCAT::zvmUtils->printSyslog("$out"); + # If flashcopy locks still exists + if (`ssh $::SUDOER\@$hcp "$::SUDO ls /tmp/.flashcopy_lock"`) { - # Force Linux to re-read partition table - xCAT::zvmUtils->printLn($callback, "$tgtNode: Forcing Linux to re-read partition table"); - $out = -`ssh $::SUDOER\@$hcp "$::SUDO cat<printLn( $callback, "$tgtNode: (Error) Flashcopy lock is enabled" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Remove lock by deleting /tmp/.flashcopy_lock on the zHCP. Use caution!" ); + return; + } else { - # Check for error - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + # Enable lock + $out = `ssh $::SUDOER\@$hcp "$::SUDO touch /tmp/.flashcopy_lock"`; - # Disable disks - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-d", $tgtAddr); + # Flashcopy source disk + $out = xCAT::zvmCPUtils->flashCopy( $::SUDOER, $hcp, $srcAddr, $tgtAddr ); + xCAT::zvmUtils->printSyslog("flashCopy: $out"); + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + xCAT::zvmUtils->printSyslog("$tgtNode: CP Flashcopy error, trying Linux DD next." ); - # Detatch disks from zHCP - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + # Try Linux dd + $ddCopy = 1; + } - return; - } + # Wait a while for flashcopy to completely finish + sleep(10); + + # Remove lock + $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/.flashcopy_lock"`; + } + } + } else { + $ddCopy = 1; + } + + # Flashcopy not supported, use Linux dd + if ($ddCopy) { + + #*** Use Linux dd to copy *** + xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not working. Using Linux DD" ); + + # Enable target disk + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr ); + + # Determine source device node + $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcAddr); + + # Determine target device node + $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr); + + # Format target disk + # Only ECKD disks need to be formated + if ($tgtDiskType eq '3390') { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; + xCAT::zvmUtils->printSyslog("dasdfmt -b 4096 -y -f /dev/$tgtDevNode"); + + # Check for errors + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + + # Detatch disks from HCP + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + + # Copy source disk to target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync && $::SUDO echo $?"`; + $out = xCAT::zvmUtils->trimStr($out); + if (int($out) != 0) { + # If $? is not 0 then there was an error during Linux dd + $out = "(Error) Failed to copy /dev/$srcDevNode"; + } + + xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync"); + xCAT::zvmUtils->printSyslog("$out"); + } else { + # Copy source disk to target disk + # Block size = 512 + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync && $::SUDO echo $?"`; + $out = xCAT::zvmUtils->trimStr($out); + if (int($out) != 0) { + # If $? is not 0 then there was an error during Linux dd + $out = "(Error) Failed to copy /dev/$srcDevNode"; + } + + xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync"); + xCAT::zvmUtils->printSyslog("$out"); + + # Force Linux to re-read partition table + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Forcing Linux to re-read partition table" ); + $out = + `ssh $::SUDOER\@$hcp "$::SUDO cat<checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr ); + + # Detatch disks from zHCP + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + } # end if ddcopy + } # end if SMAPI flashcopy did not complete - # Sleep 2 seconds to let the system settle - sleep(2); - } # Disable and enable target disk - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-d", $tgtAddr); - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", $tgtAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr ); # Determine target device node (it might have changed) $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr); @@ -4423,15 +5801,14 @@ EOM"`; $out = ""; $try = 5; while (!$out && $try > 0) { - # Check if /usr/bin/file is available if (`ssh $::SUDOER\@$hcp "$::SUDO test -f /usr/bin/file && echo Exists"`) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*" | grep -v swap | grep -o "$tgtDevNode\[1-9\]"`; + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*" | grep -a -v swap | grep -a -o "$tgtDevNode\[1-9\]"`; } else { - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode* | grep -v swap | grep -o $tgtDevNode\[1-9\]"`; + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode* | grep -a -v swap | grep -a -o $tgtDevNode\[1-9\]"`; } $out = xCAT::zvmUtils->trimStr($out); - xCAT::zvmUtils->printSyslog("fdisk -l /dev/$tgtDevNode* | grep -v swap | grep -o $tgtDevNode\[1-9\]"); + xCAT::zvmUtils->printSyslog("fdisk -l /dev/$tgtDevNode* | grep -a -v swap | grep -a -o $tgtDevNode\[1-9\]"); xCAT::zvmUtils->printSyslog("$out"); # Wait before trying again @@ -4439,15 +5816,16 @@ EOM"`; $try = $try - 1; } - my @tgtDevNodes = split("\n", $out); + my @tgtDevNodes = split( "\n", $out ); my $iTgtDevNode = 0; $tgtDevNode = xCAT::zvmUtils->trimStr($tgtDevNodes[$iTgtDevNode]); - xCAT::zvmUtils->printLn($callback, "$tgtNode: Mounting /dev/$tgtDevNode to $cloneMntPt"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Mounting /dev/$tgtDevNode to $cloneMntPt" ); + xCAT::zvmUtils->printSyslog("tgtDevNodes:@tgtDevNodes"); # Check the disk is mounted $try = 5; - while (!(`ssh $::SUDOER\@$hcp "$::SUDO ls $cloneMntPt"`) && $try > 0) { + while ( !(`ssh $::SUDOER\@$hcp "$::SUDO ls $cloneMntPt"`) && $try > 0 ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $cloneMntPt"`; $out = `ssh $::SUDOER\@$hcp "$::SUDO mount /dev/$tgtDevNode $cloneMntPt"`; xCAT::zvmUtils->printSyslog("mount /dev/$tgtDevNode $cloneMntPt"); @@ -4464,26 +5842,31 @@ EOM"`; } if (!(`ssh $::SUDOER\@$hcp "$::SUDO ls $cloneMntPt"`)) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: Failed to mount /dev/$tgtDevNode. Skipping device."); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed to mount /dev/$tgtDevNode. Skipping device." ); } # Is this the partition containing /etc? if (`ssh $::SUDOER\@$hcp "$::SUDO test -d $cloneMntPt/etc && echo Exists"`) { - #*** Set network configuration *** # Set hostname - xCAT::zvmUtils->printLn($callback, "$tgtNode: Setting network configuration"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Setting network configuration" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/HOSTNAME"`; - xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/HOSTNAME"); + $rc = $? >> 8; + if ($rc == 255) { # Adding "Failed" to message will cause zhcp error dialog to be displayed to user + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp rc:$?" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp rc:$?" ); + return; + } + xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/HOSTNAME output:$out"); # If Red Hat - Set hostname in /etc/sysconfig/network - if ($srcOs =~ m/rhel/i) { + if ( $srcOs =~ m/rhel/i ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/sysconfig/network"`; xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/sysconfig/network"); } # Get network layer - my $layer = xCAT::zvmCPUtils->getNetworkLayer($::SUDOER, $hcp, $hcpNetName); + my $layer = xCAT::zvmCPUtils->getNetworkLayer( $::SUDOER, $hcp, $hcpNetName ); xCAT::zvmUtils->printSyslog("hcp:$hcp hcpNetName:$hcpNetName layer:$layer"); # Get network configuration file @@ -4492,32 +5875,32 @@ EOM"`; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts my @files; - if ($srcOs =~ m/rhel/i) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"`; - xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"); + if ( $srcOs =~ m/rhel/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts | grep -a -i 'ifcfg-eth' "`; + xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"); xCAT::zvmUtils->printSyslog("$out"); @files = split('\n', $out); - @words = split(':', $files[0]); + @words = split( ':', $files[0] ); $srcIfcfg = $words[0]; } # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($srcOs =~ m/sles10/i) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"`; - xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"); + elsif ( $srcOs =~ m/sles10/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"`; + xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"); xCAT::zvmUtils->printSyslog("$out"); @files = split('\n', $out); - @words = split(':', $files[0]); + @words = split( ':', $files[0] ); $srcIfcfg = $words[0]; } # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($srcOs =~ m/sles/i) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"`; - xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"); + elsif ( $srcOs =~ m/sles11/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"`; + xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"); xCAT::zvmUtils->printSyslog("$out"); @files = split('\n', $out); - @words = split(':', $files[0]); + @words = split( ':', $files[0] ); $srcIfcfg = $words[0]; } @@ -4531,7 +5914,7 @@ EOM"`; # Get network and mask $tgtNetwork = $_->{'net'}; - $tgtMask = $_->{'mask'}; + $tgtMask = $_->{'mask'}; # If the host IP address is in this subnet, return if (xCAT::NetworkUtils->ishostinsubnet($targetIp, $tgtMask, $tgtNetwork)) { @@ -4542,10 +5925,11 @@ EOM"`; $tgtNetwork = ""; } } - - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" \ -e \"s/$sourceIp/$targetIp/i\" $cloneMntPt/etc/hosts"`; + # Make sure we change all host name occurrances: long and short. Change old IP to new IP + # add in a duplicate change in case they put in two space delimited tokens + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \'s/$sourceIp/$targetIp/i\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode\\\./ $tgtNode\\\./gi\' \ -e \'s/ $sourceNode\\\$/ $tgtNode/gi\' \ $cloneMntPt/etc/hosts"`; $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceIp/$targetIp/i\" \ -e \"s/$sourceNode/$tgtNode/i\" $ifcfgPath"`; - xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i \ -e s/$sourceIp/$targetIp/i $cloneMntPt/etc/hosts"); + xCAT::zvmUtils->printSyslog("sed -i -e \'s/$sourceIp/$targetIp/i\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode / $tgtNode /gi\' \ -e \'s/ $sourceNode\\\./ $tgtNode\\\./gi\' \ -e \'s/ $sourceNode\\\$/ $tgtNode/gi\' \ $cloneMntPt/etc/hosts"); xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceIp/$targetIp/i \ -e s/$sourceNode/$tgtNode/i $ifcfgPath"); if ($tgtNetwork && $tgtMask) { @@ -4556,18 +5940,18 @@ EOM"`; # Set MAC address my $networkFile = $tgtNode . "NetworkConfig"; my $config; - if ($srcOs =~ m/rhel/i) { + if ( $srcOs =~ m/rhel/i ) { # Red Hat only - $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath" | grep -v "MACADDR"`; + $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath" | grep -a -v "MACADDR"`; $config .= "MACADDR='" . $targetMac . "'\n"; } else { # SUSE only - $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath" | grep -v "LLADDR" | grep -v "UNIQUE"`; + $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath" | grep -a -v "LLADDR" | grep -a -v "UNIQUE"`; # Set to MAC address (only for layer 2) - if ($layer == 2) { + if ( $layer == 2 ) { $config .= "LLADDR='" . $targetMac . "'\n"; $config .= "UNIQUE=''\n"; } @@ -4584,16 +5968,14 @@ EOM"`; $out = `rm -rf /tmp/$networkFile`; # Set to hardware configuration (only for layer 2) - if ($layer == 2) { - if ($srcOs =~ m/rhel/i && $srcMac) { - + if ( $layer == 2 ) { + if ( $srcOs =~ m/rhel/i && $srcMac ) { #*** Red Hat Linux *** # Set MAC address $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$srcMac/$targetMac/i\" $ifcfgPath"`; xCAT::zvmUtils->printSyslog("sed -i -e s/$srcMac/$targetMac/i $ifcfgPath"); } else { - #*** SuSE Linux *** # Get hardware configuration @@ -4601,9 +5983,9 @@ EOM"`; my $hwcfgPath = $cloneMntPt . "/etc/sysconfig/hardware/hwcfg-qeth-bus-ccw-0.0.$srcNicAddr"; xCAT::zvmUtils->printSyslog("hwcfgPath=$hwcfgPath"); my $hardwareFile = $tgtNode . "HardwareConfig"; - $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $hwcfgPath" | grep -v "QETH_LAYER2_SUPPORT" > /tmp/$hardwareFile`; + $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $hwcfgPath" | grep -a -v "QETH_LAYER2_SUPPORT" > /tmp/$hardwareFile`; $out = `echo "QETH_LAYER2_SUPPORT='1'" >> /tmp/$hardwareFile`; - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, "/tmp/$hardwareFile", $hwcfgPath); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, "/tmp/$hardwareFile", $hwcfgPath ); # Remove hardware file from /tmp $out = `rm /tmp/$hardwareFile`; @@ -4624,7 +6006,7 @@ EOM"`; $out = `ssh $::SUDOER\@$hcp "$::SUDO rm -rf $cloneMntPt"`; # Disable disks - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-d", $tgtAddr); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr ); # Detatch disks from HCP $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; @@ -4639,14 +6021,14 @@ EOM"`; } # Power on target virtual server - xCAT::zvmUtils->printLn($callback, "$tgtNode: Powering on"); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Powering on" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Activate -T $tgtUserId"`; xCAT::zvmUtils->printSyslog("smcli Image_Activate -T $tgtUserId"); # Check for error - $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc == -1) { - xCAT::zvmUtils->printLn($callback, "$tgtNode: $out"); + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } } @@ -4661,33 +6043,37 @@ EOM"`; Arguments : Node Returns : Nothing Example : nodeSet($callback, $node, $args); - + =cut #------------------------------------------------------- sub nodeSet { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid', 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # If the node is being actively cloned then return. + if ( $propVals->{'status'} =~ /CLONING=1/ and $propVals->{'status'} =~ /CLONE_ONLY=1/ ) { + return; + } # Get zHCP my $hcp = $propVals->{'hcp'}; if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; @@ -4699,8 +6085,9 @@ sub nodeSet { my $transport; my $device; my $action; + my $rc; - foreach my $arg (@$args) { + foreach my $arg ( @$args ) { if ($arg =~ m/^osimage=/i) { $osImg = $arg; $osImg =~ s/^osimage=//; @@ -4714,7 +6101,6 @@ sub nodeSet { $transport = $arg; $transport =~ s/transport=//; } else { - # If not a recognized operand with a value then it must be an action $action = $arg; } @@ -4731,30 +6117,29 @@ sub nodeSet { $osImg =~ s/\s+$//; @propNames = ('profile', 'provmethod', 'osvers', 'osarch'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('osimage', 'imagename', $osImg, @propNames); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $osImg, @propNames ); # Update nodetype table with os, arch, and profile based on osimage - if (!$propVals->{'profile'} || !$propVals->{'provmethod'} || !$propVals->{'osvers'} || !$propVals->{'osarch'}) { - + if ( !$propVals->{'profile'} || !$propVals->{'provmethod'} || !$propVals->{'osvers'} || !$propVals->{'osarch'} ) { # Exit - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing profile, provmethod, osvers, or osarch for osimage"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Provide profile, provmethod, osvers, and osarch in the osimage definition"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile, provmethod, osvers, or osarch for osimage" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Provide profile, provmethod, osvers, and osarch in the osimage definition" ); return; } # Update nodetype table with osimage attributes for node my %propHash = ( - 'os' => $propVals->{'osvers'}, - 'arch' => $propVals->{'osarch'}, - 'profile' => $propVals->{'profile'}, + 'os' => $propVals->{'osvers'}, + 'arch' => $propVals->{'osarch'}, + 'profile' => $propVals->{'profile'}, 'provmethod' => $propVals->{'provmethod'} ); - xCAT::zvmUtils->setNodeProps('nodetype', $node, \%propHash); + xCAT::zvmUtils->setNodeProps( 'nodetype', $node, \%propHash ); $action = $propVals->{'provmethod'}; } # Get install directory and domain from site table - my @entries = xCAT::TableUtils->get_site_attribute("installdir"); + my @entries = xCAT::TableUtils->get_site_attribute("installdir"); my $installDir = $entries[0]; @entries = xCAT::TableUtils->get_site_attribute("domain"); my $domain = $entries[0]; @@ -4764,8 +6149,8 @@ sub nodeSet { my $xcatdPort = $entries[0]; # Get node OS, arch, and profile from 'nodetype' table - @propNames = ('os', 'arch', 'profile'); - $propVals = xCAT::zvmUtils->getNodeProps('nodetype', $node, @propNames); + @propNames = ( 'os', 'arch', 'profile' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'nodetype', $node, @propNames ); $os = $propVals->{'os'}; $arch = $propVals->{'arch'}; @@ -4775,41 +6160,41 @@ sub nodeSet { if (!$os || !$arch || !$profile) { # Exit - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node OS, arch, and profile in nodetype table"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node OS, arch, and profile in nodetype table" ); return; } # Get action my $out; - if ($action eq "install") { + if ( $action eq "install" ) { # Get node root password @propNames = ('password'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('passwd', 'key', 'system', @propNames); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'passwd', 'key', 'system', @propNames ); my $passwd = $propVals->{'password'}; - if (!$passwd) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing root password for this node"); + if ( !$passwd ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing root password for this node" ); return; } # Get node OS base my @tmp; - if ($os =~ m/sp/i) { - @tmp = split(/sp/, $os); + if ( $os =~ m/sp/i ) { + @tmp = split( /sp/, $os ); } else { - @tmp = split(/\./, $os); + @tmp = split( /\./, $os ); } my $osBase = $tmp[0]; # Get node distro my $distro = ""; - if ($os =~ m/sles/i) { + if ( $os =~ m/sles/i ) { $distro = "sles"; - } elsif ($os =~ m/rhel/i) { + } elsif ( $os =~ m/rhel/i ) { $distro = "rh"; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to determine node Linux distribution"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Verify the node Linux distribution is either sles* or rh*"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to determine node Linux distribution" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify the node Linux distribution is either sles* or rh*" ); return; } @@ -4817,62 +6202,57 @@ sub nodeSet { my $tmpl; # Check for $profile.$os.$arch.tmpl - if (-e "$installDir/custom/install/$distro/$profile.$os.$arch.tmpl") { + if ( -e "$installDir/custom/install/$distro/$profile.$os.$arch.tmpl" ) { $tmpl = "$profile.$os.$arch.tmpl"; } - # Check for $profile.$osBase.$arch.tmpl - elsif (-e "$installDir/custom/install/$distro/$profile.$osBase.$arch.tmpl") { + elsif ( -e "$installDir/custom/install/$distro/$profile.$osBase.$arch.tmpl" ) { $tmpl = "$profile.$osBase.$arch.tmpl"; } - # Check for $profile.$arch.tmpl - elsif (-e "$installDir/custom/install/$distro/$profile.$arch.tmpl") { + elsif ( -e "$installDir/custom/install/$distro/$profile.$arch.tmpl" ) { $tmpl = "$profile.$arch.tmpl"; } - # Check for $profile.tmpl second - elsif (-e "$installDir/custom/install/$distro/$profile.tmpl") { + elsif ( -e "$installDir/custom/install/$distro/$profile.tmpl" ) { $tmpl = "$profile.tmpl"; } else { # No template exists - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing autoyast/kickstart template"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Create a template under $installDir/custom/install/$distro/"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing autoyast/kickstart template: $installDir/custom/install/$distro/$profile.$os.$arch.tmpl" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Create a template under $installDir/custom/install/$distro/" ); return; } # Get host IP and hostname from /etc/hosts - $out = `cat /etc/hosts | egrep -i "$node |$node."`; - my @words = split(' ', $out); + $out = `cat /etc/hosts | egrep -a -i "$node |$node."`; + my @words = split( ' ', $out ); my $hostIP = $words[0]; my $hostname = $words[2]; if (!($hostname =~ m/./i)) { $hostname = $words[1]; } - if (!$hostIP || !$hostname) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing IP for $node in /etc/hosts"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Verify that the nodes IP address is specified in the hosts table and then run makehosts"); + if ( !$hostIP || !$hostname ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the nodes IP address is specified in the hosts table and then run makehosts" ); return; } # Check template if DHCP is used my $dhcp = 0; if ($distro eq "sles") { - # Check autoyast template - if (-e "$installDir/custom/install/sles/$tmpl") { - $out = `cat $installDir/custom/install/sles/$tmpl | egrep -i ""`; + if ( -e "$installDir/custom/install/sles/$tmpl" ) { + $out = `cat $installDir/custom/install/sles/$tmpl | egrep -a -i ""`; if ($out =~ m/dhcp/i) { $dhcp = 1; } } } elsif ($distro eq "rh") { - # Check kickstart template - if (-e "$installDir/custom/install/rh/$tmpl") { - $out = `cat $installDir/custom/install/rh/$tmpl | egrep -ie "--bootproto dhcp"`; + if ( -e "$installDir/custom/install/rh/$tmpl" ) { + $out = `cat $installDir/custom/install/rh/$tmpl | egrep -a -ie "--bootproto dhcp"`; if ($out =~ m/dhcp/i) { $dhcp = 1; } @@ -4884,12 +6264,12 @@ sub nodeSet { my $layer; my $i; - @propNames = ('primarynic', 'nfsserver', 'xcatmaster'); - $propVals = xCAT::zvmUtils->getNodeProps('noderes', $node, @propNames); + @propNames = ( 'primarynic', 'nfsserver', 'xcatmaster' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames ); - my $repo = $propVals->{'nfsserver'}; # Repository containing Linux ISO + my $repo = $propVals->{'nfsserver'}; # Repository containing Linux ISO my $xcatmaster = $propVals->{'xcatmaster'}; - my $primaryNic = $propVals->{'primarynic'}; # NIC to use for OS installation + my $primaryNic = $propVals->{'primarynic'}; # NIC to use for OS installation # If noderes.primarynic is not specified, find an acceptable NIC shared with the zHCP if ($primaryNic) { @@ -4897,18 +6277,18 @@ sub nodeSet { # If DHCP is used and the NIC is not layer 2, then exit if ($dhcp && $layer != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) The template selected uses DHCP. A layer 2 VSWITCH or GLAN is required. None were found."); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Modify the template to use static or --bootproto=static, or change the network device attached to virtual machine"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) The template selected uses DHCP. A layer 2 VSWITCH or GLAN is required. None were found." ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Modify the template to use static or --bootproto=static, or change the network device attached to virtual machine" ); return; } # Find device channel of NIC my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; - $out = `echo "$userEntry" | grep "NICDEF" | grep "$primaryNic"`; + $out = `echo "$userEntry" | grep -a "NICDEF" | grep -a "$primaryNic"`; # Check user profile for device channel if (!$out) { - my $profileName = `echo "$userEntry" | grep "INCLUDE"`; + my $profileName = `echo "$userEntry" | grep -a "INCLUDE"`; if ($profileName) { @words = split(' ', xCAT::zvmUtils->trimStr($profileName)); @@ -4916,52 +6296,49 @@ sub nodeSet { my $userProfile = xCAT::zvmUtils->getUserProfile($::SUDOER, $hcp, $words[1]); # Get the NICDEF statement containing the HCP network - $out = `echo "$userProfile" | grep "NICDEF" | grep "$primaryNic"`; + $out = `echo "$userProfile" | grep -a "NICDEF" | grep -a "$primaryNic"`; } } # Grab the device channel from the NICDEF statement my @lines = split('\n', $out); - @words = split(' ', $lines[0]); + @words = split(' ', $lines[0]); $channel = sprintf('%d', hex($words[1])); } else { - xCAT::zvmUtils->printLn($callback, "$node: Searching for acceptable network device"); + xCAT::zvmUtils->printLn( $callback, "$node: Searching for acceptable network device"); ($primaryNic, $channel, $layer) = xCAT::zvmUtils->findUsablezHcpNetwork($::SUDOER, $hcp, $userId, $dhcp); # If DHCP is used and not layer 2 if ($dhcp && $layer != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) The template selected uses DHCP. A layer 2 VSWITCH or GLAN is required. None were found."); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Modify the template to use static or change the network device attached to virtual machine"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) The template selected uses DHCP. A layer 2 VSWITCH or GLAN is required. None were found." ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Modify the template to use static or change the network device attached to virtual machine" ); return; } } # Exit if no suitable network found if (!$primaryNic || !$channel || !$layer) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) No suitable network device found in user directory entry"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) No suitable network device found in user directory entry" ); return; } - xCAT::zvmUtils->printLn($callback, "$node: Setting up networking on $primaryNic (layer:$layer | DHCP:$dhcp)"); + xCAT::zvmUtils->printLn( $callback, "$node: Setting up networking on $primaryNic (layer:$layer | DHCP:$dhcp)" ); # Generate read, write, and data channels my $readChannel = "0.0." . (sprintf('%X', $channel + 0)); if (length($readChannel) < 8) { - # Prepend a zero $readChannel = "0.0.0" . (sprintf('%X', $channel + 0)); } my $writeChannel = "0.0." . (sprintf('%X', $channel + 1)); if (length($writeChannel) < 8) { - # Prepend a zero $writeChannel = "0.0.0" . (sprintf('%X', $channel + 1)); } my $dataChannel = "0.0." . (sprintf('%X', $channel + 2)); if (length($dataChannel) < 8) { - # Prepend a zero $dataChannel = "0.0.0" . (sprintf('%X', $channel + 2)); } @@ -4974,8 +6351,8 @@ sub nodeSet { # Search 'mac' table for node @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('mac', 'node', $node, @propNames); - $mac = $propVals->{'mac'}; + $propVals = xCAT::zvmUtils->getTabPropsByKey('mac', 'node', $node, @propNames); + $mac = $propVals->{'mac'}; # If no MAC address is found, exit # MAC address should have been assigned to the node upon creation @@ -4995,7 +6372,7 @@ sub nodeSet { # Get network and mask $network = $_->{'net'}; - $mask = $_->{'mask'}; + $mask = $_->{'mask'}; # If the host IP address is in this subnet, return if (xCAT::NetworkUtils->ishostinsubnet($hostIP, $mask, $network)) { @@ -5008,18 +6385,18 @@ sub nodeSet { } # If no network found - if (!$network) { + if ( !$network ) { # Exit - xCAT::zvmUtils->printLn($callback, "$node: (Error) Node does not belong to any network in the networks table"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Specify the subnet in the networks table. The mask, gateway, tftpserver, and nameservers must be specified for the subnet."); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any network in the networks table" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the subnet in the networks table. The mask, gateway, tftpserver, and nameservers must be specified for the subnet." ); return; } - @propNames = ('mask', 'gateway', 'tftpserver', 'nameservers'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('networks', 'net', $network, @propNames); - my $mask = $propVals->{'mask'}; - my $gateway = $propVals->{'gateway'}; + @propNames = ( 'mask', 'gateway', 'tftpserver', 'nameservers' ); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); + $mask = $propVals->{'mask'}; + my $gateway = $propVals->{'gateway'}; # Convert to nameserver IP my $nameserver; @@ -5029,21 +6406,20 @@ sub nodeSet { $nameserver = $propVals->{'nameservers'}; } - if (!$network || !$mask || !$nameserver) { - + if ( !$network || !$mask || !$nameserver ) { # It is acceptable to not have a gateway - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing network information"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Specify the mask, gateway, and nameservers for the subnet in the networks table"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing network information" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the mask, gateway, and nameservers for the subnet in the networks table" ); return; } - @propNames = ('nfsserver', 'xcatmaster'); - $propVals = xCAT::zvmUtils->getNodeProps('noderes', $node, @propNames); - my $repo = $propVals->{'nfsserver'}; # Repository containing Linux ISO - my $xcatmaster = $propVals->{'xcatmaster'}; + @propNames = ( 'nfsserver', 'xcatmaster' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames ); + $repo = $propVals->{'nfsserver'}; # Repository containing Linux ISO + $xcatmaster = $propVals->{'xcatmaster'}; # Use noderes.xcatmaster instead of site.master if it is given - if ($xcatmaster) { + if ( $xcatmaster ) { $master = $xcatmaster; } @@ -5054,11 +6430,11 @@ sub nodeSet { @words = split(/\./, $hostIP); my ($ipUnpack) = unpack("N", pack("C4", @words)); @words = split(/\./, $mask); - my ($maskUnpack) = unpack("N", pack("C4", @words)); + my ($maskUnpack) = unpack("N", pack( "C4", @words )); # Calculate broadcast address by inverting the netmask and do a logical or with network address - my $math = ($ipUnpack & $maskUnpack) + (~$maskUnpack); - @words = unpack("C4", pack("N", $math)); + my $math = ( $ipUnpack & $maskUnpack ) + ( ~ $maskUnpack ); + @words = unpack("C4", pack( "N", $math )) ; my $broadcast = join(".", @words); # Load VMCP module on HCP @@ -5078,7 +6454,7 @@ sub nodeSet { # Default parameters - SUSE my $instNetDev = "osa"; # Only OSA interface type is supported my $osaInterface = "qdio"; # OSA interface = qdio or lcs - my $osaMedium = "eth"; # OSA medium = eth (ethernet) or tr (token ring) + my $osaMedium = "eth"; # OSA medium = eth (ethernet) or tr (token ring) # Default parameters - RHEL my $netType = "qeth"; @@ -5087,16 +6463,16 @@ sub nodeSet { # Get postscript content my $postScript; - if ($os =~ m/sles10/i) { + if ( $os =~ m/sles10/i ) { $postScript = "/opt/xcat/share/xcat/install/scripts/post.sles10.s390x"; - } elsif ($os =~ m/sles11/i) { + } elsif ( $os =~ m/sles11/i ) { $postScript = "/opt/xcat/share/xcat/install/scripts/post.sles11.s390x"; - } elsif ($os =~ m/rhel5/i) { + } elsif ( $os =~ m/rhel5/i ) { $postScript = "/opt/xcat/share/xcat/install/scripts/post.rhel5.s390x"; - } elsif ($os =~ m/rhel6/i) { + } elsif ( $os =~ m/rhel6/i ) { $postScript = "/opt/xcat/share/xcat/install/scripts/post.rhel6.s390x"; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) No postscript available for $os"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) No postscript available for $os" ); return; } @@ -5107,31 +6483,31 @@ sub nodeSet { my $packages = ''; my $postBoot = "$installDir/postscripts/xcatinstallpost"; my $postInit = "$installDir/postscripts/xcatpostinit1"; - if ($os =~ m/sles/i) { + if ( $os =~ m/sles/i ) { # Create directory in FTP root (/install) to hold template $out = `mkdir -p $installDir/custom/install/sles`; # Copy autoyast template $customTmpl = "$installDir/custom/install/sles/" . $node . "." . $profile . ".tmpl"; - if (-e "$installDir/custom/install/sles/$tmpl") { + if ( -e "$installDir/custom/install/sles/$tmpl" ) { $out = `cp $installDir/custom/install/sles/$tmpl $customTmpl`; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) An autoyast template does not exist for $os in $installDir/custom/install/sles/. Please create one."); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) An autoyast template does not exist for $os in $installDir/custom/install/sles/. Please create one." ); return; } # Get pkglist from /install/custom/install/sles/compute.sles11.s390x.otherpkgs.pkglist # Original one is in /opt/xcat/share/xcat/install/sles/compute.sles11.s390x.otherpkgs.pkglist $pkglist = "/install/custom/install/sles/" . $profile . "." . $osBase . "." . $arch . ".pkglist"; - if (!(-e $pkglist)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing package list for $os in /install/custom/install/sles/"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Please create one or copy default one from /opt/xcat/share/xcat/install/sles/"); + if ( !(-e $pkglist) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing package list for $os in /install/custom/install/sles/" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Please create one or copy default one from /opt/xcat/share/xcat/install/sles/" ); return; } # Read in each software pattern or package - open(FILE, $pkglist); + open (FILE, $pkglist); while () { chomp; @@ -5147,7 +6523,7 @@ sub nodeSet { } } - close(FILE); + close (FILE); # Add appropriate software packages or patterns $out = `sed -i -e "s,replace_software_packages,$packages,g" \ -e "s,replace_software_patterns,$patterns,g" $customTmpl`; @@ -5167,17 +6543,72 @@ sub nodeSet { my $device; my $chanIds = "$readChannel $writeChannel $dataChannel"; - # SLES - if ($os =~ m/sles10/i) { - + # SLES 11 + if ( $os =~ m/sles11/i ) { + $device = "eth0"; + } else { # SLES 10 $device = "qeth-bus-ccw-$readChannel"; - } else { - $device = "eth0"; } + # remove any line ends + chomp($hostIP); + chomp($hostname); + chomp($node); + chomp($domain); + chomp($node); + chomp($nameserver); + chomp($broadcast); + chomp($device); + chomp($hostIP); + chomp($mac); + chomp($mask); + chomp($network); + chomp($chanIds); + chomp($gateway); + chomp($passwd); + chomp($readChannel); + chomp($master); - $out = -`sed -i -e "s,replace_host_address,$hostIP,g" \ -e "s,replace_long_name,$hostname,g" \ -e "s,replace_short_name,$node,g" \ -e "s,replace_domain,$domain,g" \ -e "s,replace_hostname,$node,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_broadcast,$broadcast,g" \ -e "s,replace_device,$device,g" \ -e "s,replace_ipaddr,$hostIP,g" \ -e "s,replace_lladdr,$mac,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_network,$network,g" \ -e "s,replace_ccw_chan_ids,$chanIds,g" \ -e "s,replace_ccw_chan_mode,FOOBAR,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_root_password,$passwd,g" \ -e "s,replace_nic_addr,$readChannel,g" \ -e "s,replace_master,$master,g" \ -e "s,replace_install_dir,$installDir,g" $customTmpl`; + # remove any blanks + $hostIP = xCAT::zvmUtils->trimStr($hostIP); + $hostname = xCAT::zvmUtils->trimStr($hostname); + $node = xCAT::zvmUtils->trimStr($node); + $domain = xCAT::zvmUtils->trimStr($domain); + $node = xCAT::zvmUtils->trimStr($node); + $nameserver = xCAT::zvmUtils->trimStr($nameserver); + $broadcast = xCAT::zvmUtils->trimStr($broadcast); + $device = xCAT::zvmUtils->trimStr($device); + $hostIP = xCAT::zvmUtils->trimStr($hostIP); + $mac = xCAT::zvmUtils->trimStr($mac); + $mask = xCAT::zvmUtils->trimStr($mask); + $network = xCAT::zvmUtils->trimStr($network); + $chanIds = xCAT::zvmUtils->trimStr($chanIds); + $gateway = xCAT::zvmUtils->trimStr($gateway); + $passwd = xCAT::zvmUtils->trimStr($passwd); + $readChannel = xCAT::zvmUtils->trimStr($readChannel); + $master = xCAT::zvmUtils->trimStr($master); + + $out =`sed -i -e "s,replace_host_address,$hostIP,g" $customTmpl`; + $out = `sed -i -e "s,replace_long_name,$hostname,g" $customTmpl`; + $out = `sed -i -e "s,replace_short_name,$node,g" $customTmpl`; + $out = `sed -i -e "s,replace_domain,$domain,g" $customTmpl`; + $out = `sed -i -e "s,replace_hostname,$node,g" $customTmpl`; + $out = `sed -i -e "s,replace_nameserver,$nameserver,g" $customTmpl`; + $out = `sed -i -e "s,replace_broadcast,$broadcast,g" $customTmpl`; + $out = `sed -i -e "s,replace_device,$device,g" $customTmpl`; + $out = `sed -i -e "s,replace_ipaddr,$hostIP,g" $customTmpl`; + $out = `sed -i -e "s,replace_lladdr,$mac,g" $customTmpl`; + $out = `sed -i -e "s,replace_netmask,$mask,g" $customTmpl`; + $out = `sed -i -e "s,replace_network,$network,g" $customTmpl`; + $out = `sed -i -e "s,replace_ccw_chan_ids,$chanIds,g" $customTmpl`; + $out = `sed -i -e "s,replace_ccw_chan_mode,FOOBAR,g" $customTmpl`; + $out = `sed -i -e "s,replace_gateway,$gateway,g" $customTmpl`; + $out = `sed -i -e "s,replace_root_password,$passwd,g" $customTmpl`; + $out = `sed -i -e "s,replace_nic_addr,$readChannel,g" $customTmpl`; + $out = `sed -i -e "s,replace_master,$master,g" $customTmpl`; + $out = `sed -i -e "s,replace_install_dir,$installDir,g" $customTmpl`; + + xCAT::zvmUtils->printSyslog("***Provision settings for SLES:replace_host_address,$hostIP replace_long_name,$hostname replace_short_name,$node replace_domain,$domain replace_hostname,$node replace_nameserver,$nameserver replace_broadcast,$broadcast replace_device,$device replace_ipaddr,$hostIP replace_lladdr,$mac replace_netmask,$mask replace_network,$network replace_ccw_chan_ids,$chanIds replace_ccw_chan_mode,FOOBAR replace_gateway,$gateway replace_root_password,$passwd replace_nic_addr,$readChannel replace_master,$master replace_install_dir,$installDir $customTmpl"); # Attach SCSI FCP devices (if any) # Go through each pool @@ -5187,7 +6618,8 @@ sub nodeSet { my $entry; my $zfcpSection = ""; foreach (@pools) { - $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -i ",$node,"`; + if (!(length $_)) {next;} + $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -a -i ",$node,"`; chomp($entry); if (!$entry) { next; @@ -5196,13 +6628,13 @@ sub nodeSet { # Go through each zFCP device my @device = split('\n', $entry); foreach (@device) { - + if (!(length $_)) {next;} # Each entry contains: status,wwpn,lun,size,range,owner,channel,tag @tmp = split(',', $_); - my $wwpn = $tmp[1]; - my $lun = $tmp[2]; + my $wwpn = $tmp[1]; + my $lun = $tmp[2]; my $device = lc($tmp[6]); - my $tag = $tmp[7]; + my $tag = $tmp[7]; # If multiple WWPNs or device channels are specified (multipathing), just take the 1st one if ($wwpn =~ m/;/i) { @@ -5217,7 +6649,7 @@ sub nodeSet { # 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", ""); # Make sure channel has a length of 4 while (length($device) < 4) { @@ -5226,8 +6658,8 @@ sub nodeSet { # zFCP variables must be in lower-case or AutoYast would get confused $device = lc($device); - $wwpn = lc($wwpn); - $lun = lc($lun); + $wwpn = lc($wwpn); + $lun = lc($lun); # Find tag in template and attach SCSI device associated with it $out = `sed -i -e "s#$tag#/dev/disk/by-path/ccw-0.0.$device-zfcp-0x$wwpn:0x$lun#i" $customTmpl`; @@ -5245,9 +6677,8 @@ END } if ($hasZfcp) { - # Insert device list - my $find = 'replace_zfcp'; + my $find = 'replace_zfcp'; my $replace = <\\ END @@ -5255,7 +6686,7 @@ END $replace .= <\\ END - my $expression = "'s#" . $find . "#" . $replace . "#i'"; + my $expression = "'s#" . $find . "#" .$replace . "#i'"; $out = `sed -i -e $expression $customTmpl`; xCAT::zvmUtils->printLn($callback, "$node: Inserting FCP devices into template... Done"); @@ -5263,13 +6694,13 @@ END # Read sample parmfile in /install/sles10.2/s390x/1/boot/s390x/ $sampleParm = "$installDir/$os/s390x/1/boot/s390x/parmfile"; - open(SAMPLEPARM, "<$sampleParm"); + open( SAMPLEPARM, "<$sampleParm" ); # Search parmfile for -- ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb while () { # If the line contains 'ramdisk_size' - if ($_ =~ m/ramdisk_size/i) { + if ( $_ =~ m/ramdisk_size/i ) { $parmHeader = xCAT::zvmUtils->trimStr($_); } } @@ -5296,11 +6727,11 @@ END $parms = $parmHeader . "\n"; $parms = $parms . "AutoYaST=$ay\n"; - $parms = $parms . "HostIP=$hostIP Hostname=$hostname\n"; - $parms = $parms . "Gateway=$gateway Netmask=$mask\n"; + $parms = $parms . "Hostname=$hostname\n"; + $parms = $parms . " HostIP=$hostIP Gateway=$gateway Netmask=$mask\n"; # Set layer in autoyast profile - if ($layer == 2) { + if ( $layer == 2 ) { $parms = $parms . "Broadcast=$broadcast Layer2=1 OSAHWaddr=$mac\n"; } else { $parms = $parms . "Broadcast=$broadcast Layer2=0\n"; @@ -5312,9 +6743,10 @@ END $parms = $parms . "UseVNC=1 VNCPassword=12345678\n"; $parms = $parms . "InstNetDev=$instNetDev OsaInterface=$osaInterface OsaMedium=$osaMedium Manual=0\n"; + xCAT::zvmUtils->printSyslog("***Parm file SLES(should be max 80 cols, 10 lines:\n$parms"); # Write to parmfile $parmFile = "/tmp/" . $node . "Parm"; - open(PARMFILE, ">$parmFile"); + open( PARMFILE, ">$parmFile" ); print PARMFILE "$parms"; close(PARMFILE); @@ -5324,42 +6756,51 @@ END if ($repo) { $out = `/usr/bin/wget $repo/boot/s390x/vmrdr.ikr -O $kernelFile --no-check-certificate`; + xCAT::zvmUtils->printLn( $callback, "Attempting to copy $repo/boot/s390x/vmrdr.ikr to $kernelFile" ); $out = `/usr/bin/wget $repo/boot/s390x/initrd -O $initFile --no-check-certificate`; } else { $out = `cp $installDir/$os/s390x/1/boot/s390x/vmrdr.ikr $kernelFile`; + xCAT::zvmUtils->printLn( $callback, "Attempting to copy $installDir/$os/s390x/1/boot/s390x/vmrdr.ikr to $kernelFile" ); $out = `cp $installDir/$os/s390x/1/boot/s390x/initrd $initFile`; } + $out = `ls $kernelFile 2>1`; + $rc = $? >> 8; + if ($rc) { + xCAT::zvmUtils->printLn( $callback, "(Failed) Did not copy the file. Did you forget to process the ISO?"); + $out = '(Failed) Did not copy the file.'; + return; + } - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $kernelFile, $kernelFile); - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $parmFile, $parmFile); - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $initFile, $initFile); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $kernelFile, $kernelFile ); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $parmFile, $parmFile ); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $initFile, $initFile ); # Set the virtual unit record devices online on HCP - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "c"); - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d"); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "c" ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "d" ); # Purge reader - $out = xCAT::zvmCPUtils->purgeReader($::SUDOER, $hcp, $userId); - xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done"); + $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId ); + xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); # Punch kernel to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $kernelFile, "sles.kernel", ""); - xCAT::zvmUtils->printLn($callback, "$node: Punching kernel to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $kernelFile, "sles.kernel", "", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } # Punch parm to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $parmFile, "sles.parm", "-t"); - xCAT::zvmUtils->printLn($callback, "$node: Punching parm to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $parmFile, "sles.parm", "-t", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } # Punch initrd to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $initFile, "sles.initrd", ""); - xCAT::zvmUtils->printLn($callback, "$node: Punching initrd to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $initFile, "sles.initrd", "", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } @@ -5367,41 +6808,41 @@ END $out = `rm $parmFile $kernelFile $initFile`; $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $parmFile $kernelFile $initFile"`; - xCAT::zvmUtils->printLn($callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot."); + xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot." ); } # RHEL installation - elsif ($os =~ m/rhel/i) { + elsif ( $os =~ m/rhel/i ) { # Create directory in FTP root (/install) to hold template $out = `mkdir -p $installDir/custom/install/rh`; # Copy kickstart template $customTmpl = "$installDir/custom/install/rh/" . $node . "." . $profile . ".tmpl"; - if (-e "$installDir/custom/install/rh/$tmpl") { + if ( -e "$installDir/custom/install/rh/$tmpl" ) { $out = `cp $installDir/custom/install/rh/$tmpl $customTmpl`; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) An kickstart template does not exist for $os in $installDir/custom/install/rh/"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) An kickstart template does not exist for $os in $installDir/custom/install/rh/" ); return; } # Get pkglist from /install/custom/install/rh/compute.rhel6.s390x.otherpkgs.pkglist # Original one is in /opt/xcat/share/xcat/install/rh/compute.rhel6.s390x.otherpkgs.pkglist $pkglist = "/install/custom/install/rh/" . $profile . "." . $osBase . "." . $arch . ".pkglist"; - if (!(-e $pkglist)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing package list for $os in /install/custom/install/rh/"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Please create one or copy default one from /opt/xcat/share/xcat/install/rh/"); + if ( !(-e $pkglist) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing package list for $os in /install/custom/install/rh/" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Please create one or copy default one from /opt/xcat/share/xcat/install/rh/" ); return; } # Read in each software pattern or package - open(FILE, $pkglist); + open (FILE, $pkglist); while () { chomp; $_ = xCAT::zvmUtils->trimStr($_); $packages .= "$_\\n"; } - close(FILE); + close (FILE); # Add appropriate software packages or patterns $out = `sed -i -e "s,replace_software_packages,$packages,g" $customTmpl`; @@ -5421,9 +6862,37 @@ END if (!$repo) { $repo = "http://$nfs/$os/s390x"; } + # remove newlines + chomp($repo); + chomp($hostIP); + chomp($mask); + chomp($gateway); + chomp($nameserver); + chomp($hostname); + chomp($passwd); + chomp($master); - $out = -`sed -i -e "s,replace_url,$repo,g" \ -e "s,replace_ip,$hostIP,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_hostname,$hostname,g" \ -e "s,replace_rootpw,$passwd,g" \ -e "s,replace_master,$master,g" \ -e "s,replace_install_dir,$installDir,g" $customTmpl`; + # trim blanks + $repo = xCAT::zvmUtils->trimStr($repo); + $hostIP = xCAT::zvmUtils->trimStr($hostIP); + $mask = xCAT::zvmUtils->trimStr($mask); + $gateway = xCAT::zvmUtils->trimStr($gateway); + $nameserver = xCAT::zvmUtils->trimStr($nameserver); + $hostname = xCAT::zvmUtils->trimStr($hostname); + $passwd = xCAT::zvmUtils->trimStr($passwd); + $master = xCAT::zvmUtils->trimStr($master); + + $out = `sed -i -e "s,replace_url,$repo,g" $customTmpl`; + $out = `sed -i -e "s,replace_ip,$hostIP,g" $customTmpl`; + $out = `sed -i -e "s,replace_netmask,$mask,g" $customTmpl`; + $out = `sed -i -e "s,replace_gateway,$gateway,g" $customTmpl`; + $out = `sed -i -e "s,replace_nameserver,$nameserver,g" $customTmpl`; + $out = `sed -i -e "s,replace_hostname,$hostname,g" $customTmpl`; + $out = `sed -i -e "s,replace_rootpw,$passwd,g" $customTmpl`; + $out = `sed -i -e "s,replace_master,$master,g" $customTmpl`; + $out = `sed -i -e "s,replace_install_dir,$installDir,g" $customTmpl`; + + xCAT::zvmUtils->printSyslog("***Provision settings for RedHat:replace_url,$repo replace_ip,$hostIP replace_netmask,$mask replace_gateway,$gateway replace_nameserver,$nameserver replace_hostname,$hostname replace_rootpw,$passwd replace_master,$master replace_install_dir,$installDir for file==>$customTmpl"); # Attach SCSI FCP devices (if any) # Go through each pool @@ -5433,7 +6902,8 @@ END my $entry; my $zfcpSection = ""; foreach (@pools) { - $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -i ",$node,"`; + if (!(length $_)) {next;} + $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -a -i ",$node,"`; chomp($entry); if (!$entry) { next; @@ -5442,13 +6912,13 @@ END # Go through each zFCP device my @device = split('\n', $entry); foreach (@device) { - + if (!(length $_)) {next;} # Each entry contains: status,wwpn,lun,size,range,owner,channel,tag @tmp = split(',', $_); - my $wwpn = $tmp[1]; - my $lun = $tmp[2]; + my $wwpn = $tmp[1]; + my $lun = $tmp[2]; my $device = lc($tmp[6]); - my $tag = $tmp[7]; + my $tag = $tmp[7]; # If multiple WWPNs or device channels are specified (multipathing), just take the 1st one if ($wwpn =~ m/;/i) { @@ -5463,7 +6933,7 @@ END # 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", ""); # Make sure channel has a length of 4 while (length($device) < 4) { @@ -5472,14 +6942,14 @@ END # zFCP variables must be in lower-case or AutoYast would get confused. $device = lc($device); - $wwpn = lc($wwpn); - $lun = lc($lun); + $wwpn = lc($wwpn); + $lun = lc($lun); # Create zfcp section $zfcpSection = "zfcp --devnum 0.0.$device --wwpn 0x$wwpn --fcplun 0x$lun" . '\n'; # Look for replace_zfcp keyword in template and replace it - $out = `sed -i -e "s,$tag,$zfcpSection,i" $customTmpl`; + $out = `sed -i -e "s,$tag,$zfcpSection,i" $customTmpl`; $hasZfcp = 1; } } @@ -5490,17 +6960,17 @@ END # Read sample parmfile in /install/rhel5.3/s390x/images $sampleParm = "$installDir/$os/s390x/images/generic.prm"; - open(SAMPLEPARM, "<$sampleParm"); + open( SAMPLEPARM, "<$sampleParm" ); # Search parmfile for -- root=/dev/ram0 ro ip=off ramdisk_size=40000 while () { # If the line contains 'ramdisk_size' - if ($_ =~ m/ramdisk_size/i) { + if ( $_ =~ m/ramdisk_size/i ) { $parmHeader = xCAT::zvmUtils->trimStr($_); # RHEL 6.1 needs cio_ignore in order to install - if (!($os =~ m/rhel6.1/i)) { + if ( !($os =~ m/rhel6.1/i) ) { $parmHeader =~ s/cio_ignore=all,!0.0.0009//g; } } @@ -5510,17 +6980,18 @@ END close(SAMPLEPARM); # Get mdisk virtual address - my @mdisks = xCAT::zvmUtils->getMdisks($callback, $::SUDOER, $node); + my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $node ); @mdisks = sort(@mdisks); - my $dasd = ""; + my $dasd = ""; my $devices = ""; - my $i = 0; + my $i = 0; foreach (@mdisks) { - $i = $i + 1; - @words = split(' ', $_); + if (!(length $_)) {next;} + $i = $i + 1; + @words = split( ' ', $_ ); # Do not put a comma at the end of the last disk address - if ($i == @mdisks) { + if ( $i == @mdisks ) { $dasd = $dasd . "0.0.$words[1]"; } else { $dasd = $dasd . "0.0.$words[1],"; @@ -5529,20 +7000,20 @@ END # Character limit of 50 in parm file for DASD parameter if (length($dasd) > 50) { - @words = split(',', $dasd); - $dasd = $words[0] . "-" . $words[ @words - 1 ]; + @words = split( ',', $dasd ); + $dasd = $words[0] . "-" . $words[@words - 1]; } # Get dedicated virtual address - my @dedicates = xCAT::zvmUtils->getDedicates($callback, $::SUDOER, $node); + my @dedicates = xCAT::zvmUtils->getDedicates( $callback, $::SUDOER, $node ); @dedicates = sort(@dedicates); - $i = 0; + $i = 0; foreach (@dedicates) { $i = $i + 1; - @words = split(' ', $_); + @words = split( ' ', $_ ); # Do not put a comma at the end of the last disk address - if ($i == @dedicates) { + if ( $i == @dedicates ) { $devices = $devices . "0.0.$words[1]"; } else { $devices = $devices . "0.0.$words[1],"; @@ -5551,8 +7022,8 @@ END # Character limit of 50 in parm file for DASD parameter if (length($devices) > 50) { - @words = split(',', $devices); - $devices = $words[0] . "-" . $words[ @words - 1 ]; + @words = split( ',', $devices ); + $devices = $words[0] . "-" . $words[@words - 1]; } # Concat dedicated devices and DASD together @@ -5582,15 +7053,15 @@ END $parms = $parmHeader . "\n"; $parms = $parms . "ks=$ks\n"; $parms = $parms . "RUNKS=1 cmdline\n"; - $parms = $parms . "DASD=$dasd\n"; - $parms = $parms . "HOSTNAME=$hostname NETTYPE=$netType IPADDR=$hostIP\n"; + $parms = $parms . "DASD=$dasd NETTYPE=$netType IPADDR=$hostIP\n"; + $parms = $parms . "HOSTNAME=$hostname\n"; $parms = $parms . "SUBCHANNELS=$readChannel,$writeChannel,$dataChannel\n"; $parms = $parms . "NETWORK=$network NETMASK=$mask\n"; $parms = $parms . "SEARCHDNS=$domain BROADCAST=$broadcast\n"; $parms = $parms . "GATEWAY=$gateway DNS=$nameserver MTU=1500\n"; # Set layer in kickstart profile - if ($layer == 2) { + if ( $layer == 2 ) { $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=1 MACADDR=$mac\n"; } else { $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=0\n"; @@ -5598,9 +7069,10 @@ END $parms = $parms . "vnc vncpassword=12345678\n"; + xCAT::zvmUtils->printSyslog("***Parm file RedHat(should be max 80 cols, 11 lines:\n$parms"); # Write to parmfile $parmFile = "/tmp/" . $node . "Parm"; - open(PARMFILE, ">$parmFile"); + open( PARMFILE, ">$parmFile" ); print PARMFILE "$parms"; close(PARMFILE); @@ -5611,42 +7083,51 @@ END # Copy over kernel, parmfile, conf, and initrd from remote repository if ($repo) { $out = `/usr/bin/wget $repo/images/kernel.img -O $kernelFile --no-check-certificate`; + xCAT::zvmUtils->printLn( $callback, "Attempting to copy $repo/images/kernel.img to $kernelFile" ); $out = `/usr/bin/wget $repo/images/initrd.img -O $initFile --no-check-certificate`; } else { $out = `cp $installDir/$os/s390x/images/kernel.img $kernelFile`; + xCAT::zvmUtils->printLn( $callback, "Attempting to copy $installDir/$os/s390x/images/kernel.img to $kernelFile" ); $out = `cp $installDir/$os/s390x/images/initrd.img $initFile`; } + $out = `ls $kernelFile 2>1`; + $rc = $? >> 8; + if ($rc) { + xCAT::zvmUtils->printLn( $callback, "(Failed) Did not copy the file. Did you forget to process the ISO?"); + $out = '(Failed) Did not copy the file.';; + return; + } - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $kernelFile, $kernelFile); - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $parmFile, $parmFile); - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $initFile, $initFile); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $kernelFile, $kernelFile ); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $parmFile, $parmFile ); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $initFile, $initFile ); # Set the virtual unit record devices online - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "c"); - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d"); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "c" ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "d" ); # Purge reader - $out = xCAT::zvmCPUtils->purgeReader($::SUDOER, $hcp, $userId); - xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done"); + $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId ); + xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); # Punch kernel to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $kernelFile, "rhel.kernel", ""); - xCAT::zvmUtils->printLn($callback, "$node: Punching kernel to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $kernelFile, "rhel.kernel", "", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } # Punch parm to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $parmFile, "rhel.parm", "-t"); - xCAT::zvmUtils->printLn($callback, "$node: Punching parm to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $parmFile, "rhel.parm", "-t", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } # Punch initrd to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $initFile, "rhel.initrd", ""); - xCAT::zvmUtils->printLn($callback, "$node: Punching initrd to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $initFile, "rhel.initrd", "", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } @@ -5654,25 +7135,25 @@ END $out = `rm $parmFile $kernelFile $initFile`; $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $parmFile $kernelFile $initFile"`; - xCAT::zvmUtils->printLn($callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot."); + xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot." ); } - } elsif ($action eq "statelite") { + } elsif ( $action eq "statelite" ) { # Get node group from 'nodelist' table @propNames = ('groups'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('nodelist', 'node', $node, @propNames); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'nodelist', 'node', $node, @propNames ); my $group = $propVals->{'groups'}; # Get node statemnt (statelite mount point) from 'statelite' table @propNames = ('statemnt'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('statelite', 'node', $node, @propNames); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'statelite', 'node', $node, @propNames ); my $stateMnt = $propVals->{'statemnt'}; - if (!$stateMnt) { - $propVals = xCAT::zvmUtils->getTabPropsByKey('statelite', 'node', $group, @propNames); + if ( !$stateMnt ) { + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'statelite', 'node', $group, @propNames ); $stateMnt = $propVals->{'statemnt'}; - if (!$stateMnt) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node statemnt in statelite table. Please specify one."); + if ( !$stateMnt ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node statemnt in statelite table. Please specify one." ); return; } } @@ -5684,42 +7165,38 @@ END my $initFile = "$netbootDir/initrd-statelite.gz"; # If parmfile exists - if (-e $parmFile) { - + if ( -e $parmFile ) { # Do nothing } else { - xCAT::zvmUtils->printLn($callback, "$node: Creating parmfile"); + xCAT::zvmUtils->printLn( $callback, "$node: Creating parmfile" ); my $sampleParm; my $parmHeader; my $parms; - if ($os =~ m/sles/i) { - if (-e "$installDir/$os/s390x/1/boot/s390x/parmfile") { - + if ( $os =~ m/sles/i ) { + if ( -e "$installDir/$os/s390x/1/boot/s390x/parmfile" ) { # Read sample parmfile in /install/sles11.1/s390x/1/boot/s390x/ $sampleParm = "$installDir/$os/s390x/1/boot/s390x/parmfile"; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing $installDir/$os/s390x/1/boot/s390x/parmfile"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing $installDir/$os/s390x/1/boot/s390x/parmfile" ); return; } - } elsif ($os =~ m/rhel/i) { - if (-e "$installDir/$os/s390x/images/generic.prm") { - + } elsif ( $os =~ m/rhel/i ) { + if ( -e "$installDir/$os/s390x/images/generic.prm" ) { # Read sample parmfile in /install/rhel5.3/s390x/images $sampleParm = "$installDir/$os/s390x/images/generic.prm"; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing $installDir/$os/s390x/images/generic.prm"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing $installDir/$os/s390x/images/generic.prm" ); return; } } - open(SAMPLEPARM, "<$sampleParm"); + open( SAMPLEPARM, "<$sampleParm" ); # Search parmfile for -- ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb while () { - # If the line contains 'ramdisk_size' - if ($_ =~ m/ramdisk_size/i) { + if ( $_ =~ m/ramdisk_size/i ) { $parmHeader = xCAT::zvmUtils->trimStr($_); } } @@ -5737,7 +7214,7 @@ END $parms = $parms . "STATEMNT=$stateMnt XCAT=$master:$xcatdPort\n"; # Write to parmfile - open(PARMFILE, ">$parmFile"); + open( PARMFILE, ">$parmFile" ); print PARMFILE "$parms"; close(PARMFILE); } @@ -5747,117 +7224,239 @@ END my $tmpParmFile = "/tmp/$os-parm-statelite"; my $tmpInitFile = "/tmp/$os-initrd-statelite.gz"; - if (`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp" | grep "$os-kernel"`) { - + xCAT::zvmUtils->printLn( $callback, "$node: Looking for kernel $os-kernel." ); + if (`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp" | grep -a "$os-kernel"`) { # Do nothing } else { - # Send kernel to reader to HCP - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $kernelFile, $tmpKernelFile); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $kernelFile, $tmpKernelFile ); + xCAT::zvmUtils->printLn( $callback, "sendfile $kernelFile, $tmpKernelFile" ); } - if (`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp" | grep "$os-parm-statelite"`) { - + if (`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp" | grep -a "$os-parm-statelite"`) { # Do nothing } else { - # Send parmfile to reader to HCP - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $parmFile, $tmpParmFile); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $parmFile, $tmpParmFile ); } - if (`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp" | grep "$os-initrd-statelite.gz"`) { - + if (`ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp" | grep -a "$os-initrd-statelite.gz"`) { # Do nothing } else { - # Send initrd to reader to HCP - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $initFile, $tmpInitFile); + xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $initFile, $tmpInitFile ); } # Set the virtual unit record devices online - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "c"); - $out = xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d"); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "c" ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", "d" ); # Purge reader - $out = xCAT::zvmCPUtils->purgeReader($::SUDOER, $hcp, $userId); - xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done"); + $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId ); + xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); # Kernel, parm, and initrd are in /install/netboot/// # Punch kernel to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $tmpKernelFile, "sles.kernel", ""); - xCAT::zvmUtils->printLn($callback, "$node: Punching kernel to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $tmpKernelFile, "sles.kernel", "", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } # Punch parm to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $tmpParmFile, "sles.parm", "-t"); - xCAT::zvmUtils->printLn($callback, "$node: Punching parm to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $tmpParmFile, "sles.parm", "-t", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } # Punch initrd to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $tmpInitFile, "sles.initrd", ""); - xCAT::zvmUtils->printLn($callback, "$node: Punching initrd to reader... $out"); - if ($out =~ m/Failed/i) { + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $tmpInitFile, "sles.initrd", "", "" ); + xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); + if ( $out =~ m/Failed/i ) { return; } - xCAT::zvmUtils->printLn($callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot."); - } elsif ($action eq "netboot") { + xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot." ); + } elsif (( $action eq "netboot" ) || ( $action eq "sysclone" )) { # Obtain the location of the install root directory my $installRoot = xCAT::TableUtils->getInstallDir(); # Verify the image exists - my $imageFile; my $deployImgDir = "$installRoot/$action/$os/$arch/$profile"; - my @imageFiles = glob "$deployImgDir/*.img"; - if (@imageFiles == 0) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir does not contain image files"); - return; - } elsif (@imageFiles > 1) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir contains more than the expected number of image files"); + my @imageFiles = glob "$deployImgDir/*.img"; + my %imageFileList; + if ( @imageFiles == 0 ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir does not contain image files" ); return; } else { - $imageFile = (split('/', $imageFiles[0]))[-1]; + # Obtain the list of image files and the vaddr to which they relate + foreach my $imageFileFull ( @imageFiles ) { + my $imageFile = (split('/', $imageFileFull))[-1]; + my $vaddr = (split('\.', $imageFile))[0]; + $imageFileList{ $vaddr } = $imageFile; + } } - if (!defined $device) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Image device was not specified"); - return; + # Build the list of image files and their target device addresses + if ( $action eq "netboot" ) { + if ( @imageFiles > 1 ) { + # Can only have one image file for netboot + xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir contains more than the expected number of image files" ); + return; + } + if (! defined $device) { + # A device must be specified + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Image device was not specified" ); + return; + } + # For netboot, image device address is not necessarily the same as the original device name + my $origKey = (keys %imageFileList)[0]; + if ( $origKey ne $device ) { + # file name was different with a different name than the target device. Update to use the target device. + $imageFileList{ $device } = $imageFileList{ $origKey }; + delete $imageFileList{ $origKey }; + } + } else { + # Handle sysclone which can have multiple image files which MUST match each mdisk + # Get the list of mdisks + my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $node ); + + # Verify the list of images and matching disks + my $validArrayDisks = 0; + foreach (@srcDisks) { + # Get disk address + my @words = split( ' ', $_ ); + my $vaddr = $words[1]; + my $diskType = $words[2]; + + if ( $diskType eq 'FB-512' ) { + # We do not do not deploy into vdisks + next; + } + + # Add 0 in front if address length is less than 4 + while (length($vaddr) < 4) { + $vaddr = '0' . $vaddr; + } + + if ( defined $imageFileList{$vaddr} ) { + # We only count disks that have an image file. + $validArrayDisks = $validArrayDisks + 1; + } + } + if ( $validArrayDisks != @imageFiles ) { + # Number of mdisks does not match the number of image files + xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir contains images for devices that do not exist." ); + return; + } } - # Prepare the deployable netboot mount point on zHCP, if they need to be established. + # Ensure the staging directory exists in case we need to create subdirectories in it. + if (!-d "$installRoot/staging") { + mkpath("$installRoot/staging"); + } + + # Prepare the deployable mount point on zHCP, if it needs to be established. my $remoteDeployDir; - my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$action", "ro", \$remoteDeployDir); - if ($rc) { - + my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $action, "ro", \$remoteDeployDir); + if ( $rc ) { # Mount failed return; } - xCAT::zvmUtils->printLn($callback, "$node: Deploying the image using the zHCP node"); + # Drive each device deploy separately. Up to 10 at a time. + # Each deploy request to zHCP is driven from a child process. + # Process ID for xfork() + my $pid; - # Copy the image to the target disk using the zHCP node - xCAT::zvmUtils->printSyslog("nodeset() unpackdiskimage $userId $device $remoteDeployDir/$os/$arch/$profile/$imageFile"); - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/unpackdiskimage $userId $device $remoteDeployDir/$os/$arch/$profile/$imageFile"`; - $rc = $?; + # Child process IDs + my @children; - my $reasonString = ""; - $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); - if ($rc != 0) { - my $reason = "Reason: $reasonString"; - xCAT::zvmUtils->printSyslog("nodeset() unpackdiskimage of $userId $device failed. $reason"); - xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to deploy the image to $userId $device. $reason"); + # Make a temporary directory in case a process needs to communicate a problem. + my $statusDir = mkdtemp("$installRoot/staging/status.$$.XXXXXX"); + + xCAT::zvmUtils->printLn( $callback, "$node: Deploying the image using the zHCP node" ); + my $reason; + for my $vaddr ( keys %imageFileList ) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process. + elsif ( $pid == 0 ) { + # Drive the deploy on the zHCP node + # Copy the image to the target disk using the zHCP node + xCAT::zvmUtils->printSyslog( "nodeset() unpackdiskimage $userId $vaddr $remoteDeployDir/$os/$arch/$profile/$imageFileList{$vaddr}" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/unpackdiskimage $userId $vaddr $remoteDeployDir/$os/$arch/$profile/$imageFileList{$vaddr}"`; + $rc = $?; + + # Check for script errors + my $reasonString = ""; + $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString ); + if ($rc != 0) { + $reason = "Reason: $reasonString"; + xCAT::zvmUtils->printSyslog( "nodeset() unpackdiskimage of $userId $vaddr failed. $reason" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to deploy the image to $userId $vaddr. $reason" ); + # Create a "FAILED" file to indicate the failure. + if ( ! open FILE, '>'."$statusDir/FAILED" ) { + # if we can't open it then we log the problem. + xCAT::zvmUtils->printSyslog( "nodeset() unable to create a 'FAILED' file." ); + } + } + + # Exit the child process + exit(0); + } + + else { + # Ran out of resources + # Create a "FAILED" file to indicate the failure. + if ( ! open FILE, '>'."$statusDir/FAILED" ) { + # if we can't open it then we log the problem. + xCAT::zvmUtils->printSyslog( "nodeset() unable to create a 'FAILED' file." ); + } + + $reason = "Reason: Could not fork\n"; + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to deploy the image to $userId $vaddr. $reason" ); + last; + } + + # Handle 10 nodes at a time, else you will get errors + if ( !( @children % 10 ) ) { + + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + + # Clear children + @children = (); + } + } # End of foreach + + # If any children remain, then wait for them to complete. + foreach $pid ( @children ) { + xCAT::zvmUtils->printSyslog( "nodeset() Waiting for child process $pid to complete" ); + waitpid( $pid, 0 ); + } + + # If the deploy failed then clean up and return + if ( -e "$statusDir/FAILED" ) { + # Failure occurred in one of the child processes. A message was already generated. + rmtree "$statusDir"; return; } + rmtree "$statusDir"; # If the transport file was specified then setup the transport disk. if ($transport) { my $transImgDir = "$installRoot/staging/transport"; - if (!-d $transImgDir) { + if(!-d $transImgDir) { mkpath($transImgDir); } @@ -5865,94 +7464,97 @@ END my $transportDir = `/bin/mktemp -d $installDir/staging/transport/XXXXXX`; chomp($transportDir); if ($remoteHost) { - # Copy the transport file from the remote system to the local transport directory. - xCAT::zvmUtils->printLn($callback, "/usr/bin/scp -B $remoteHost:$transport $transportDir"); + xCAT::zvmUtils->printLn( $callback, "/usr/bin/scp -B $remoteHost:$transport $transportDir" ); $out = `/usr/bin/scp -v -B $remoteHost:$transport $transportDir`; $rc = $?; } else { - # Safely copy the transport file from a local directory. $out = `/bin/cp $transport $transportDir`; - $rc = $?; + $rc = $?; } if ($rc != 0) { - # Copy failed Get rid of the unique directory that was going to receive the copy. rmtree $transportDir; xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to copy the transport file"); return; } - # Purge the target node's reader - $out = xCAT::zvmCPUtils->purgeReader($::SUDOER, $hcp, $userId); - xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done"); - - # Online zHCP's punch - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/chccwdev -e 00d && echo $?"`; - if ($out != '0') { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to online the zHCP's punch"); - return; - } - - # Load VMCP module on HCP - $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; - if ($out != '0') { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to load the vmcp module on the zHCP node"); - return; - } - - # Set the punch to class 'x' - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp spool punch class x"`; - if ($out != '0') { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to spool the punch on the zHCP node"); - return; - } - - # Punch files to node's reader so it could be pulled on boot - # Reader = transport disk - my @files = glob "$transportDir/*"; - foreach (@files) { - my $file = basename($_); - my $filePath = "/tmp/$node-" . $file; - - # Spool file only accepts [A-Za-z] and file name can only be 8-characters long - my @filePortions = split('\.', $file); - if ((@filePortions > 2) || - ($filePortions[0] =~ m/[^a-zA-Z0-9]/) || (length($filePortions[0]) > 8) || (length($filePortions[0]) < 1) || - ($filePortions[1] =~ m/[^a-zA-Z0-9]{1,8}/) || (length($filePortions[1]) > 8)) { - $out = `/bin/rm -rf $transportDir`; - xCAT::zvmUtils->printLn($callback, "$node: (Error) $file contains a file name or file type portion that is longer than 8 characters, or not alphanumeric "); + # Check the zvm table to see if the node flag "XCATCONF4Z=0" is set or not in status column, + # if set put it to a temp dir for later use, otherwise punched the transport file directly to node's reader, + my $nodeFlag = ''; + my $cfgTrunkDir = "/tmp/configdrive/$node/"; + my @propNames = ('status'); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'zvm', 'node', $node, @propNames ); + $nodeFlag = $propVals->{'status'}; + if ($nodeFlag =~ /XCATCONF4Z=0/) { + if (!-d $cfgTrunkDir) { + mkpath($cfgTrunkDir, 0, 0750); + } + $rc = `/bin/cp -r $transportDir/* $cfgTrunkDir/ 2>/dev/null; echo $?`; + `rm -rf $transportDir`; + if ($rc != '0') { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source directory $transportDir to directory $cfgTrunkDir with rc: $rc, please check if xCAT is running out of space" ); + `rm -rf $cfgTrunkDir`; return; } - xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $_, $filePath); + } else { + # Purge the target node's reader + $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId ); + xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done"); - my $punchOpt = ""; - if ($file =~ /.txt/ || $file =~ /.sh/) { - $punchOpt = "-t"; - } - $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $filePath, "$file", $punchOpt); - - # Clean up file - `ssh $::SUDOER\@$hcp "$::SUDO /bin/rm $filePath"`; - - xCAT::zvmUtils->printLn($callback, "$node: Punching $file to reader... $out"); - if ($out =~ m/Failed/i) { - - # Clean up transport directory - $out = `/bin/rm -rf $transportDir`; + # Online zHCP's punch device + $out = xCAT::zvmUtils->onlineZhcpPunch($::SUDOER, $hcp); + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printLn( $callback, "$node: Online zHCP's punch device... $out" ); return; } + + # Punch files to node's reader so it could be pulled on boot + # Reader = transport disk + my @files = glob "$transportDir/*"; + foreach (@files) { + my $file = basename($_); + my $filePath = "/tmp/$node-" . $file; + + # Spool file only accepts [A-Za-z] and file name can only be 8-characters long + my @filePortions = split( '\.', $file ); + if (( @filePortions > 2 ) || + ( $filePortions[0] =~ m/[^a-zA-Z0-9]/ ) || ( length($filePortions[0]) > 8 ) || ( length($filePortions[0]) < 1 ) || + ( $filePortions[1] =~ m/[^a-zA-Z0-9]{1,8}/ ) || ( length($filePortions[1]) > 8 )) { + $out = `/bin/rm -rf $transportDir`; + xCAT::zvmUtils->printLn($callback, "$node: (Error) $file contains a file name or file type portion that is longer than 8 characters, or not alphanumeric "); + return; + } + + xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $_, $filePath); + + my $punchOpt = ""; + if ($file =~ /.txt/ || $file =~ /.sh/) { + $punchOpt = "-t"; + } + $out = xCAT::zvmCPUtils->punch2Reader( $::SUDOER, $hcp, $userId, $filePath, "$file", $punchOpt, "X" ); + + # Clean up file + `ssh $::SUDOER\@$hcp "$::SUDO /bin/rm $filePath"`; + xCAT::zvmUtils->printLn($callback, "$node: Punching $file to reader... $out"); + if ($out =~ m/Failed/i) { + # Clean up transport directory. Message was already generated. + $out = `/bin/rm -rf $transportDir`; + return; + } + } } # Clean up transport directory $out = `/bin/rm -rf $transportDir`; - xCAT::zvmUtils->printLn($callback, "$node: Completed deploying image($os-$arch-netboot-$profile)"); + xCAT::zvmUtils->printLn( $callback, "$node: Completed deploying image($os-$arch-$action-$profile)" ); } + } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Option not supported"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); return; } @@ -5969,52 +7571,51 @@ END Arguments : Node Returns : Nothing Example : getMacs($callback, $node, $args); - + =cut #------------------------------------------------------- sub getMacs { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; my $force = ''; if ($args) { @ARGV = @$args; # Parse options - GetOptions('f' => \$force); + GetOptions( 'f' => \$force ); } # 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 zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; # Get MAC address in 'mac' table @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps('mac', $node, @propNames); + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); my $mac; - if ($propVals->{'mac'} && !$force) { + if ( $propVals->{'mac'} && !$force) { # Get MAC address $mac = $propVals->{'mac'}; - xCAT::zvmUtils->printLn($callback, "$node: $mac"); + xCAT::zvmUtils->printLn( $callback, "$node: $mac" ); return; } @@ -6022,51 +7623,57 @@ sub getMacs { xCAT::zvmCPUtils->loadVmcp($::SUDOER, $node); # Get xCat MN Lan/VSwitch name - my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q v nic" | egrep -i "VSWITCH|LAN"`; - my @lines = split('\n', $out); + my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q v nic" | egrep -a -i "VSWITCH|LAN"`; + my @lines = split( '\n', $out ); my @words; # Go through each line and extract VSwitch and Lan names # and create search string my $searchStr = ""; my $i; - for ($i = 0 ; $i < @lines ; $i++) { + for ( $i = 0 ; $i < @lines ; $i++ ) { # Extract VSwitch name - if ($lines[$i] =~ m/VSWITCH/i) { - @words = split(' ', $lines[$i]); + if ( $lines[$i] =~ m/VSWITCH/i ) { + @words = split( ' ', $lines[$i] ); $searchStr = $searchStr . "$words[4]"; } # Extract Lan name - elsif ($lines[$i] =~ m/LAN/i) { - @words = split(' ', $lines[$i]); + elsif ( $lines[$i] =~ m/LAN/i ) { + @words = split( ' ', $lines[$i] ); $searchStr = $searchStr . "$words[4]"; } - if ($i != (@lines - 1)) { + if ( $i != ( @lines - 1 ) ) { $searchStr = $searchStr . "|"; } } # Get MAC address of node # This node should be on only 1 of the networks that the xCAT MN is on - $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$node "/sbin/vmcp q v nic" | egrep -i "$searchStr"`; - if (!$out) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find MAC address"); + #$out = `ssh -o ConnectTimeout=5 $::SUDOER\@$node "/sbin/vmcp q v nic" | egrep -a -i "$searchStr"`; + my $cmd = $::SUDO . ' /sbin/vmcp q v nic | egrep -a -i "' . $searchStr . '"'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return; } - @lines = split('\n', $out); - @words = split(' ', $lines[0]); + if ( !$out ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find MAC address" ); + return; + } + + @lines = split( '\n', $out ); + @words = split( ' ', $lines[0] ); $mac = $words[1]; # Replace - with : - $mac = xCAT::zvmUtils->replaceStr($mac, "-", ":"); - xCAT::zvmUtils->printLn($callback, "$node: $mac"); + $mac = xCAT::zvmUtils->replaceStr( $mac, "-", ":" ); + xCAT::zvmUtils->printLn( $callback, "$node: $mac" ); # Save MAC address and network interface into 'mac' table - xCAT::zvmUtils->setNodeProp('mac', $node, 'mac', $mac); + xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac ); return; } @@ -6080,42 +7687,41 @@ sub getMacs { Address to IPL from Returns : Nothing Example : netBoot($callback, $node, $args); - + =cut #------------------------------------------------------- sub netBoot { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # 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 zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); # Get IPL - my @ipl = split('=', $args->[0]); - if (!($ipl[0] eq "ipl")) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing IPL"); + my @ipl = split( '=', $args->[0] ); + if ( !( $ipl[0] eq "ipl" ) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IPL" ); return; } @@ -6126,10 +7732,10 @@ sub netBoot { # IPL when virtual server is online sleep(5); - $out = xCAT::zvmCPUtils->sendCPCmd($::SUDOER, $hcp, $userId, "IPL $ipl[1]"); + $out = xCAT::zvmCPUtils->sendCPCmd( $::SUDOER, $hcp, $userId, "IPL $ipl[1]" ); xCAT::zvmUtils->printSyslog("IPL $ipl[1]"); xCAT::zvmUtils->printSyslog("$out"); - xCAT::zvmUtils->printLn($callback, "$node: Booting from $ipl[1]... Done"); + xCAT::zvmUtils->printLn( $callback, "$node: Booting from $ipl[1]... Done" ); return; } @@ -6140,60 +7746,59 @@ sub netBoot { Description : Update node Arguments : Node - Option + Option Returns : Nothing Example : updateNode($callback, $node, $args); - + =cut #------------------------------------------------------- sub updateNode { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # 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 zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; # Get install directory - my @entries = xCAT::TableUtils->get_site_attribute("installdir"); + my @entries = xCAT::TableUtils->get_site_attribute("installdir"); my $installDir = $entries[0]; # Get host IP and hostname from /etc/hosts - my $out = `cat /etc/hosts | egrep -i "$node |$node."`; - my @words = split(' ', $out); + my $out = `cat /etc/hosts | egrep -a -i "$node |$node."`; + my @words = split( ' ', $out ); my $hostIP = $words[0]; my $hostname = $words[2]; if (!($hostname =~ m/./i)) { $hostname = $words[1]; } - if (!$hostIP || !$hostname) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing IP for $node in /etc/hosts"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Verify that the node's IP address is specified in the hosts table and then run makehosts"); + if ( !$hostIP || !$hostname ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the node's IP address is specified in the hosts table and then run makehosts" ); return; } # Get first 3 octets of node IP (IPv4) - @words = split(/\./, $hostIP); + @words = split( /\./, $hostIP ); my $octets = "$words[0].$words[1].$words[2]"; # Get networks in 'networks' table @@ -6207,7 +7812,7 @@ sub updateNode { $network = $_->{'net'}; # If networks contains the first 3 octets of the node IP - if ($network =~ m/$octets/i) { + if ( $network =~ m/$octets/i ) { # Exit loop last; @@ -6217,32 +7822,32 @@ sub updateNode { } # If no network found - if (!$network) { + if ( !$network ) { # Exit - xCAT::zvmUtils->printLn($callback, "$node: (Error) Node does not belong to any network in the networks table"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Specify the subnet in the networks table. The mask, gateway, tftpserver, and nameservers must be specified for the subnet."); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any network in the networks table" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the subnet in the networks table. The mask, gateway, tftpserver, and nameservers must be specified for the subnet." ); return; } # Get FTP server @propNames = ('tftpserver'); - $propVals = xCAT::zvmUtils->getTabPropsByKey('networks', 'net', $network, @propNames); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); my $nfs = $propVals->{'tftpserver'}; - if (!$nfs) { + if ( !$nfs ) { # It is acceptable to not have a gateway - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing FTP server"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Specify the tftpserver for the subnet in the networks table"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing FTP server" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Specify the tftpserver for the subnet in the networks table" ); return; } # Update node operating system - if ($args->[0] eq "--release") { + if ( $args->[0] eq "--release" ) { my $version = $args->[1]; - if (!$version) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing operating system release. Please specify one."); + if ( !$version ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system release. Please specify one." ); return; } @@ -6251,14 +7856,14 @@ sub updateNode { # Check node OS is the same as the version OS given # You do not want to update a SLES with a RHEL - if ((($os =~ m/SUSE/i) && !($version =~ m/sles/i)) || (($os =~ m/Red Hat/i) && !($version =~ m/rhel/i))) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Node operating system is different from the operating system given to upgrade to. Please correct."); + if ( ( ( $os =~ m/SUSE/i ) && !( $version =~ m/sles/i ) ) || ( ( $os =~ m/Red Hat/i ) && !( $version =~ m/rhel/i ) ) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node operating system is different from the operating system given to upgrade to. Please correct." ); return; } # Generate FTP path to operating system image my $path; - if ($version =~ m/sles/i) { + if ( $version =~ m/sles/i ) { # The following only applies to SLES 10 # SLES 11 requires zypper @@ -6267,58 +7872,112 @@ sub updateNode { $path = "http://$nfs/install/$version/s390x/1/"; # Add installation source using rug - $out = `ssh $::SUDOER\@$node "rug sa -t zypp $path $version"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + #$out = `ssh $::SUDOER\@$node "rug sa -t zypp $path $version"`; + my $cmd = "$::SUDO rug sa -t zypp $path $version"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Subscribe to catalog - $out = `ssh $::SUDOER\@$node "rug sub $version"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + #$out = `ssh $::SUDOER\@$node "rug sub $version"`; + $cmd = "$::SUDO rug sub $version"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Refresh services - $out = `ssh $::SUDOER\@$node "rug ref"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + #$out = `ssh $::SUDOER\@$node "rug ref"`; + $cmd = "$::SUDO rug ref"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Update - $out = `ssh $::SUDOER\@$node "rug up -y"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + #$out = `ssh $::SUDOER\@$node "rug up -y"`; + $cmd = "$::SUDO rug up -y"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } else { # Red Hat Enterprise Linux path - ftp://10.0.0.1/rhel5.4/s390x/Server/ $path = "http://$nfs/install/$version/s390x/Server/"; # Check if file.repo already has this repository location - $out = `ssh $::SUDOER\@$node "cat /etc/yum.repos.d/file.repo"`; - if ($out =~ m/[$version]/i) { + #$out = `ssh $::SUDOER\@$node "cat /etc/yum.repos.d/file.repo"`; + my $cmd = "$::SUDO cat /etc/yum.repos.d/file.repo"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + if ( $out =~ m/[$version]/i ) { # Send over release key my $key = "$installDir/$version/s390x/RPM-GPG-KEY-redhat-release"; my $tmp = "/tmp/RPM-GPG-KEY-redhat-release"; - xCAT::zvmUtils->sendFile($::SUDOER, $node, $key, $tmp); + xCAT::zvmUtils->sendFile( $::SUDOER, $node, $key, $tmp ); # Import key - $out = `ssh $::SUDOER\@$node "rpm --import /tmp/$key"`; + #$out = `ssh $::SUDOER\@$node "rpm --import /tmp/$key"`; + $cmd = "$::SUDO rpm --import /tmp/$key"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + # Upgrade - $out = `ssh $::SUDOER\@$node "yum upgrade -y"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + #$out = `ssh $::SUDOER\@$node "yum upgrade -y"`; + $cmd = "$::SUDO yum upgrade -y"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } else { # Create repository - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo [$version] >> /etc/yum.repos.d/file.repo"); - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo baseurl=$path >> /etc/yum.repos.d/file.repo"); - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo enabled=1 >> /etc/yum.repos.d/file.repo"); + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo [$version] >> /etc/yum.repos.d/file.repo"); + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo baseurl=$path >> /etc/yum.repos.d/file.repo"); + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo enabled=1 >> /etc/yum.repos.d/file.repo"); # Send over release key my $key = "$installDir/$version/s390x/RPM-GPG-KEY-redhat-release"; my $tmp = "/tmp/RPM-GPG-KEY-redhat-release"; - xCAT::zvmUtils->sendFile($::SUDOER, $node, $key, $tmp); + xCAT::zvmUtils->sendFile( $::SUDOER, $node, $key, $tmp ); # Import key - $out = `ssh $::SUDOER\@$node "rpm --import $tmp"`; + #$out = `ssh $::SUDOER\@$node "rpm --import $tmp"`; + my $cmd = "$::SUDO rpm --import $tmp"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + # Upgrade - $out = `ssh $::SUDOER\@$node "yum upgrade -y"`; - xCAT::zvmUtils->printLn($callback, "$node: $out"); + #$out = `ssh $::SUDOER\@$node "yum upgrade -y"`; + $cmd = "$::SUDO yum upgrade -y"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } } } @@ -6328,7 +7987,7 @@ sub updateNode { $out = "$node: (Error) Option not supported"; } - xCAT::zvmUtils->printLn($callback, "$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); return; } @@ -6340,14 +7999,14 @@ sub updateNode { Arguments : Node range (zHCP) Returns : Nothing Example : listHierarchy($callback, $nodes, $args); - + =cut #------------------------------------------------------- sub listTree { # Get inputs - my ($callback, $nodes, $args) = @_; + my ( $callback, $nodes, $args ) = @_; my @nodes = @$nodes; # Directory where executables are on zHCP @@ -6369,11 +8028,11 @@ sub listTree { # Create hierachy structure: CEC -> LPAR -> zVM -> VM # Get table - my $tab = xCAT::Table->new('zvm', -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); # Get CEC entries # There should be few of these nodes - my @entries = $tab->getAllAttribsWhere("nodetype = 'cec'", 'node', 'parent'); + my @entries = $tab->getAllAttribsWhere( "nodetype = 'cec'", 'node', 'parent' ); foreach (@entries) { $node = $_->{'node'}; @@ -6383,9 +8042,9 @@ sub listTree { # Get LPAR entries # There should be a couple of these nodes - @entries = $tab->getAllAttribsWhere("nodetype = 'lpar'", 'node', 'parent'); + @entries = $tab->getAllAttribsWhere( "nodetype = 'lpar'", 'node', 'parent' ); foreach (@entries) { - $node = $_->{'node'}; # LPAR + $node = $_->{'node'}; # LPAR $parent = $_->{'parent'}; # CEC # Add LPAR branch @@ -6395,10 +8054,10 @@ sub listTree { # Get zVM entries # There should be a couple of these nodes $found = 0; - @entries = $tab->getAllAttribsWhere("nodetype = 'zvm'", 'node', 'hcp', 'parent'); + @entries = $tab->getAllAttribsWhere( "nodetype = 'zvm'", 'node', 'hcp', 'parent' ); foreach (@entries) { - $node = $_->{'node'}; # zVM - $hcp = $_->{'hcp'}; # zHCP + $node = $_->{'node'}; # zVM + $hcp = $_->{'hcp'}; # zHCP $parent = $_->{'parent'}; # LPAR # Find out if this z/VM belongs to an SSI cluster @@ -6407,10 +8066,9 @@ sub listTree { # Find CEC root based on LPAR # CEC -> LPAR $found = 0; - foreach my $cec (sort keys %tree) { - foreach my $lpar (sort keys %{ $tree{$cec} }) { + foreach my $cec(sort keys %tree) { + foreach my $lpar(sort keys %{$tree{$cec}}) { if ($lpar eq $parent) { - # Add LPAR branch $tree{$cec}{$parent}{$node} = {}; $found = 1; @@ -6418,30 +8076,29 @@ sub listTree { } # Handle second level zVM - foreach my $vm (sort keys %{ $tree{$cec}{$lpar} }) { + foreach my $vm(sort keys %{$tree{$cec}{$lpar}}) { if ($vm eq $parent) { - # Add VM branch $tree{$cec}{$lpar}{$parent}{$node} = {}; $found = 1; last; } - } # End of foreach zVM - } # End of foreach LPAR + } # End of foreach zVM + } # End of foreach LPAR # Exit loop if LPAR branch added if ($found) { last; } - } # End of foreach CEC + } # End of foreach CEC } # Get VM entries # There should be many of these nodes $found = 0; - @entries = $tab->getAllAttribsWhere("nodetype = 'vm'", 'node', 'parent', 'userid'); + @entries = $tab->getAllAttribsWhere( "nodetype = 'vm'", 'node', 'parent', 'userid' ); foreach (@entries) { - $node = $_->{'node'}; # VM + $node = $_->{'node'}; # VM $parent = $_->{'parent'}; # zVM # Skip node if it is not in noderange @@ -6452,11 +8109,10 @@ sub listTree { # Find CEC/LPAR root based on zVM # CEC -> LPAR -> zVM $found = 0; - foreach my $cec (sort keys %tree) { - foreach my $lpar (sort keys %{ $tree{$cec} }) { - foreach my $zvm (sort keys %{ $tree{$cec}{$lpar} }) { + foreach my $cec(sort keys %tree) { + foreach my $lpar(sort keys %{$tree{$cec}}) { + foreach my $zvm(sort keys %{$tree{$cec}{$lpar}}) { if ($zvm eq $parent) { - # Add zVM branch $tree{$cec}{$lpar}{$parent}{$node} = $_->{'userid'}; $found = 1; @@ -6464,68 +8120,66 @@ sub listTree { } # Handle second level zVM - foreach my $vm (sort keys %{ $tree{$cec}{$lpar}{$zvm} }) { + foreach my $vm(sort keys %{$tree{$cec}{$lpar}{$zvm}}) { if ($vm eq $parent) { - # Add VM branch $tree{$cec}{$lpar}{$zvm}{$parent}{$node} = $_->{'userid'}; $found = 1; last; } - } # End of foreach VM - } # End of foreach zVM + } # End of foreach VM + } # End of foreach zVM # Exit loop if zVM branch added if ($found) { last; } - } # End of foreach LPAR + } # End of foreach LPAR # Exit loop if zVM branch added if ($found) { last; } - } # End of foreach CEC - } # End of foreach VM node + } # End of foreach CEC + } # End of foreach VM node # Print tree # Loop through CECs - foreach my $cec (sort keys %tree) { - xCAT::zvmUtils->printLn($callback, "CEC: $cec"); + foreach my $cec(sort keys %tree) { + xCAT::zvmUtils->printLn( $callback, "CEC: $cec" ); # Loop through LPARs - foreach my $lpar (sort keys %{ $tree{$cec} }) { - xCAT::zvmUtils->printLn($callback, "|__LPAR: $lpar"); + foreach my $lpar(sort keys %{$tree{$cec}}) { + xCAT::zvmUtils->printLn( $callback, "|__LPAR: $lpar" ); # Loop through zVMs - foreach my $zvm (sort keys %{ $tree{$cec}{$lpar} }) { + foreach my $zvm(sort keys %{$tree{$cec}{$lpar}}) { if ($ssi{$zvm}) { - xCAT::zvmUtils->printLn($callback, " |__zVM: $zvm ($ssi{$zvm})"); + xCAT::zvmUtils->printLn( $callback, " |__zVM: $zvm ($ssi{$zvm})" ); } else { - xCAT::zvmUtils->printLn($callback, " |__zVM: $zvm"); + xCAT::zvmUtils->printLn( $callback, " |__zVM: $zvm" ); } # Loop through VMs - foreach my $vm (sort keys %{ $tree{$cec}{$lpar}{$zvm} }) { - + foreach my $vm(sort keys %{$tree{$cec}{$lpar}{$zvm}}) { # Handle second level zVM if (ref($tree{$cec}{$lpar}{$zvm}{$vm}) eq 'HASH') { if ($ssi{$zvm}) { - xCAT::zvmUtils->printLn($callback, " |__zVM: $vm ($ssi{$zvm})"); + xCAT::zvmUtils->printLn( $callback, " |__zVM: $vm ($ssi{$zvm})" ); } else { - xCAT::zvmUtils->printLn($callback, " |__zVM: $vm"); + xCAT::zvmUtils->printLn( $callback, " |__zVM: $vm" ); } - foreach my $vm2 (sort keys %{ $tree{$cec}{$lpar}{$zvm}{$vm} }) { - xCAT::zvmUtils->printLn($callback, " |__VM: $vm2 ($tree{$cec}{$lpar}{$zvm}{$vm}{$vm2})"); + foreach my $vm2(sort keys %{$tree{$cec}{$lpar}{$zvm}{$vm}}) { + xCAT::zvmUtils->printLn( $callback, " |__VM: $vm2 ($tree{$cec}{$lpar}{$zvm}{$vm}{$vm2})" ); } } else { - xCAT::zvmUtils->printLn($callback, " |__VM: $vm ($tree{$cec}{$lpar}{$zvm}{$vm})"); + xCAT::zvmUtils->printLn( $callback, " |__VM: $vm ($tree{$cec}{$lpar}{$zvm}{$vm})" ); } - } # End of foreach VM - } # End of foreach zVM - } # End of foreach LPAR - } # End of foreach CEC + } # End of foreach VM + } # End of foreach zVM + } # End of foreach LPAR + } # End of foreach CEC return; } @@ -6538,23 +8192,23 @@ sub listTree { Arguments Returns : Nothing Example : changeHypervisor($callback, $node, $args); - + =cut #------------------------------------------------------- sub changeHypervisor { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table - my @propNames = ('hcp'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } @@ -6562,7 +8216,7 @@ sub changeHypervisor { my $hcpNode = $hcp; if ($hcp =~ /./) { my @tmp = split(/\./, $hcp); - $hcpNode = $tmp[0]; # Short hostname of zHCP + $hcpNode = $tmp[0]; # Short hostname of zHCP } # Get zHCP user ID @@ -6575,40 +8229,40 @@ sub changeHypervisor { my $out = ""; # adddisk2pool [function] [region] [volume] [group] - if ($args->[0] eq "--adddisk2pool") { - my $funct = $args->[1]; - my $region = $args->[2]; - my $volume = ""; - my $group = ""; + if ( $args->[0] eq "--adddisk2pool" ) { + my $funct = $args->[1]; + my $region = $args->[2]; + my $volume = ""; + my $group = ""; # Create an array for regions my @regions; - if ($region =~ m/,/i) { - @regions = split(',', $region); + if ( $region =~ m/,/i ) { + @regions = split( ',', $region ); } else { - push(@regions, $region); + push( @regions, $region ); } my $tmp; foreach (@regions) { + if (!(length $_)) {next;} $_ = xCAT::zvmUtils->trimStr($_); # Define region as full volume and add to group if ($funct eq "4") { $volume = $args->[3]; - # In case multiple regions/volumes are specified, just use the same name if (scalar(@regions) > 1) { $volume = $_; } - $group = $args->[4]; + $group = $args->[4]; $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -v $volume -p $group -y 0"`; xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -v $volume -p $group -y 0"); } # Add existing region to group - elsif ($funct eq "5") { + elsif($funct eq "5") { $group = $args->[3]; $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -p $group -y 0"`; xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Define_DM -T $hcpUserId -f $funct -g $_ -p $group -y 0"); @@ -6618,11 +8272,43 @@ sub changeHypervisor { } } + # addvolume [dev_no] [volser] + elsif ( $args->[0] eq "--addvolume" ) { + my $argsSize = @{$args}; + if ($argsSize != 3) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + my $devNo = $args->[1]; + my $volser = $args->[2]; + + # Add a DASD volume to the z/VM system configuration + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Add -T $hcpUserId -v $devNo -l $volser"`; + xCAT::zvmUtils->printSyslog("smcli Image_Volume_Add -T $hcpUserId -v $devNo -l $volser"); + } + + # removevolume [dev_no] [volser] + elsif ( $args->[0] eq "--removevolume" ) { + my $argsSize = @{$args}; + if ($argsSize != 3) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + my $devNo = $args->[1]; + my $volser = $args->[2]; + + # Remove a DASD volume from the z/VM system configuration + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Delete -T $hcpUserId -v $devNo -l $volser"`; + xCAT::zvmUtils->printSyslog("smcli Image_Volume_Delete -T $hcpUserId -v $devNo -l $volser"); + } + # addeckd [dev_no] - elsif ($args->[0] eq "--addeckd") { + elsif ( $args->[0] eq "--addeckd" ) { my $argsSize = @{$args}; if ($argsSize != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -6634,24 +8320,23 @@ sub changeHypervisor { } # addscsi [dev_no] [dev_path] [option] [persist] - elsif ($args->[0] eq "--addscsi") { - + elsif ( $args->[0] eq "--addscsi" ) { # Sample command would look like: chhypervisor zvm62 --addscsi 12A3 "1,0x123,0x100;2,0x123,0x101" 1 NO my $argsSize = @{$args}; if ($argsSize < 3 && $argsSize > 5) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } # Option can be: (1) Add new SCSI (default), (2) Add new path, or (3) Delete path - if ($args->[3] != 1 && $args->[3] != 2 && $args->[3] != 3) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Options can be one of the following:\n (1) Add new SCSI disk (default)\n (2) Add new path to existing disk\n (3) Delete path from existing disk"); + if ($args->[3] != 1 && $args->[3] !=2 && $args->[3] !=3) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Options can be one of the following:\n (1) Add new SCSI disk (default)\n (2) Add new path to existing disk\n (3) Delete path from existing disk" ); return; } # Persist can be: (YES) SCSI device updated in active and configured system, or (NO) SCSI device updated only in active system if ($argsSize > 3 && $args->[4] ne "YES" && $args->[4] ne "NO") { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Persist can be one of the following:\n (YES) SCSI device updated in active and configured system\n (NO) SCSI device updated only in active system"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Persist can be one of the following:\n (YES) SCSI device updated in active and configured system\n (NO) SCSI device updated only in active system" ); return; } @@ -6661,23 +8346,23 @@ sub changeHypervisor { # e.g. fcp_devno1 fcp_wwpn1 fcp_lun1; fcp_devno2 fcp_wwpn2 fcp_lun2; my @fcps; if ($args->[2] =~ m/;/i) { - @fcps = split(';', $args->[2]); + @fcps = split( ';', $args->[2] ); } else { - push(@fcps, $args->[2]); + push( @fcps, $args->[2] ); } # Append the correct prefix my @fields; my $pathStr = ""; foreach (@fcps) { - @fields = split(',', $_); - $pathStr .= "fcp_dev_num=$fields[0] fcp_wwpn=$fields[1] fcp_lun=$fields[2];"; + @fields = split( ',', $_ ); + $pathStr .= "$fields[0] $fields[1] $fields[2];"; } my $devPath = "dev_path_array='" . $pathStr . "'"; - my $option = "option=" . $args->[3]; + my $option = "option=" . $args->[3]; my $persist = "persist=" . $args->[4]; # Add disk to running system @@ -6686,15 +8371,15 @@ sub changeHypervisor { } # addvlan [name] [owner] [type] [transport] - elsif ($args->[0] eq "--addvlan") { - my $name = $args->[1]; - my $owner = $args->[2]; - my $type = $args->[3]; + elsif ( $args->[0] eq "--addvlan" ) { + my $name = $args->[1]; + my $owner = $args->[2]; + my $type = $args->[3]; my $transport = $args->[4]; my $argsSize = @{$args}; if ($argsSize != 5) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -6703,20 +8388,19 @@ sub changeHypervisor { } # addvswitch [name] [osa_dev_addr] [port_name] [controller] [connect (0, 1, or 2)] [memory_queue] [router] [transport] [vlan_id] [port_type] [update] [gvrp] [native_vlan] - elsif ($args->[0] eq "--addvswitch") { + elsif ( $args->[0] eq "--addvswitch" ) { my $i; my $argStr = ""; my $argsSize = @{$args}; if ($argsSize < 5) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } my @options = ("", "-n", "-r", "-a", "-i", "-c", "-q", "-e", "-t", "-v", "-p", "-u", "-G", "-V"); - foreach $i (1 .. $argsSize) { - if ($args->[$i]) { - + foreach $i ( 1 .. $argsSize ) { + if ( $args->[$i] ) { # Prepend options prefix to argument $argStr .= "$options[$i] $args->[$i] "; } @@ -6727,25 +8411,24 @@ sub changeHypervisor { } # addzfcp2pool [pool] [status] [wwpn] [lun] [size] [range (optional)] [owner (optional)] - elsif ($args->[0] eq "--addzfcp2pool") { - + elsif ( $args->[0] eq "--addzfcp2pool" ) { # zFCP disk pool located on zHCP at /var/opt/zhcp/zfcp/{pool}.conf # Entries contain: status,wwpn,lun,size,range,owner,channel,tag - my $pool = $args->[1]; + # store pool file in lower case + my $pool = lc($args->[1]); my $status = $args->[2]; - my $wwpn = $args->[3]; - my $lun = $args->[4]; - my $size = $args->[5]; + my $wwpn = $args->[3]; + my $lun = $args->[4]; + my $size = $args->[5]; my $argsSize = @{$args}; if ($argsSize < 6 || $argsSize > 8) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } # Size can be M(egabytes) or G(igabytes) if ($size =~ m/G/i || $size =~ m/M/i || !$size) { - # Do nothing } else { xCAT::zvmUtils->printLn($callback, "$node: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)."); @@ -6760,29 +8443,38 @@ sub changeHypervisor { } # Make sure WWPN and LUN do not have 0x prefix - $wwpn = xCAT::zvmUtils->replaceStr($wwpn, '"', ""); # Strip off enclosing quotes + $wwpn = xCAT::zvmUtils->replaceStr($wwpn, '"', ""); # Strip off enclosing quotes $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); - if ($wwpn =~ /[^0-9a-f;"]/i) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid world wide portname $wwpn."); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + + # Validate wwpn and lun values. + # The pattern '[0-9a-f]{16}' means 16 characters in char-set [0-9a-f]. The pattern '(;[0-9a-f]{16})' + # in last half part is used in the case of multipath. It will not appear in the case of signal path + # so * is used to handle both cases. + if ($wwpn !~ m/^[0-9a-f]{16}(;[0-9a-f]{16})*$/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid world wide portname $wwpn." ); return; } - if ($lun =~ /[^0-9a-f]/i) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid logical unit number $lun."); + if ($lun !~ m/^[0-9a-f]{16}$/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid logical unit number $lun." ); return; } # You cannot have a unique SCSI/FCP device in multiple pools - my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO grep -i -l \",$wwpn,$lun\" $::ZFCPPOOL/*.conf"`); - if (scalar(@pools)) { - foreach (@pools) { - my $otherPool = basename($_); - $otherPool =~ s/\.[^.]+$//; # Do not use extension - - xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device $wwpn/$lun already exists in $otherPool."); + my @wwpnList = split(";", $wwpn); + foreach (@wwpnList) { + my $cur_wwpn = $_; + my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO grep -a -i -l \",$cur_wwpn,$lun\" $::ZFCPPOOL/*.conf"`); + if (scalar(@pools)) { + foreach (@pools) { + my $cur_pool = $_; + if (!(length $cur_pool)) {next;} + my $otherPool = basename($cur_pool); + $otherPool =~ s/\.[^.]+$//; # Do not use extension + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $cur_wwpn/$lun already exists in $otherPool." ); + } + return; } - - return; } # Optional parameters @@ -6796,19 +8488,21 @@ sub changeHypervisor { # Verify syntax of FCP channel range if ($range =~ /[^0-9a-f\-;]/i) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid FCP device range. An acceptable range can be specified as 1A80-1B90 or 1A80-1B90;2A80-2B90."); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid FCP device range. An acceptable range can be specified as 1A80-1B90 or 1A80-1B90;2A80-2B90." ); return; } # Owner must be specified if status is used if ($status =~ m/used/i && !$owner) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Owner must be specified if status is used."); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Owner must be specified if status is used." ); + return; + } elsif ($status =~ m/free/i && $owner) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Owner must not be specified if status is free." ); return; } # Find disk pool (create one if non-existent) if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -d $::ZFCPPOOL && echo Exists"`)) { - # Create pool directory $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $::ZFCPPOOL"`; } @@ -6817,33 +8511,32 @@ sub changeHypervisor { if ($::SUDOER ne "root") { my $priv = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/stat -c \"%G:%U\" /var/opt/zhcp"`); if (!($priv =~ m/$::SUDOER:users/i)) { -`ssh $::SUDOER\@$hcp "$::SUDO chown -R $::SUDOER:users /var/opt/zhcp"`; + `ssh $::SUDOER\@$hcp "$::SUDO chown -R $::SUDOER:users /var/opt/zhcp"`; } } if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -e $::ZFCPPOOL/$pool.conf && echo Exists"`)) { - # Create pool configuration file $out = `ssh $::SUDOER\@$hcp "$::SUDO echo '#status,wwpn,lun,size,range,owner,channel,tag' > $::ZFCPPOOL/$pool.conf"`; - xCAT::zvmUtils->printLn($callback, "$node: New zFCP device pool $pool created"); + xCAT::zvmUtils->printLn( $callback, "$node: New zFCP device pool $pool created" ); } # Update file with given WWPN, LUN, size, and owner my $entry = "'" . "$status,$wwpn,$lun,$size,$range,$owner,," . "'"; $out = `ssh $::SUDOER\@$hcp "$::SUDO echo $entry >> $::ZFCPPOOL/$pool.conf"`; - xCAT::zvmUtils->printLn($callback, "$node: Adding zFCP device to $pool pool... Done"); + xCAT::zvmUtils->printLn( $callback, "$node: Adding zFCP device to $pool pool... Done" ); $out = ""; } # copyzfcp [device address (or auto)] [source wwpn] [source lun] [target wwpn (optional)] [target lun (option)] - elsif ($args->[0] eq "--copyzfcp") { + elsif ( $args->[0] eq "--copyzfcp" ) { my $fcpDevice = $args->[1]; - my $srcWwpn = $args->[2]; - my $srcLun = $args->[3]; + my $srcWwpn = $args->[2]; + my $srcLun = $args->[3]; my $argsSize = @{$args}; if ($argsSize != 4 && $argsSize != 6) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -6853,28 +8546,28 @@ sub changeHypervisor { my $tgtLun; if ($argsSize == 6) { $useWwpnLun = 1; - $tgtWwpn = $args->[4]; - $tgtLun = $args->[5]; + $tgtWwpn = $args->[4]; + $tgtLun = $args->[5]; # Make sure WWPN and LUN do not have 0x prefix $tgtWwpn = xCAT::zvmUtils->replaceStr($tgtWwpn, "0x", ""); - $tgtLun = xCAT::zvmUtils->replaceStr($tgtLun, "0x", ""); + $tgtLun = xCAT::zvmUtils->replaceStr($tgtLun, "0x", ""); } # Find the pool that contains the SCSI/FCP device my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $srcWwpn, $srcLun); if (!$pool) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool" ); return; } else { - xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool"); + xCAT::zvmUtils->printLn( $callback, "$node: Found FCP device in $pool" ); } # Get source device's attributes my $srcDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $srcWwpn, $srcLun); my %srcDisk = %$srcDiskRef; if (!defined($srcDisk{'lun'}) && !$srcDisk{'lun'}) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Source zFCP device $srcWwpn/$srcLun does not exists"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Source zFCP device $srcWwpn/$srcLun does not exists" ); return; } my $srcSize = $srcDisk{'size'}; @@ -6885,7 +8578,7 @@ sub changeHypervisor { my $tgtDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $tgtWwpn, $tgtLun); my %tgtDisk = %$tgtDiskRef; if (!defined($tgtDisk{'lun'}) && !$tgtDisk{'lun'}) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun does not exists"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun does not exists" ); return; } $tgtSize = $tgtDisk{'size'}; @@ -6901,7 +8594,7 @@ sub changeHypervisor { } if ($tgtSize < $srcSize) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun is not large enough"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun is not large enough" ); return; } } @@ -6914,20 +8607,19 @@ sub changeHypervisor { } # Obtain source FCP channel - $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/; + $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/i; my $srcFcpDevice = lc($1); # Attach target disk to zHCP my $isTgtAttached = 0; if ($useWwpnLun) { - $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $tgtSize "" $tgtWwpn $tgtLun | sed 1d`; + $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $tgtSize "" $tgtWwpn $tgtLun | sed 1d `; if ($out !~ /Done/) { xCAT::zvmUtils->printLn($callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun cannot be attached"); } else { $isTgtAttached = 1; } } else { - # Try to obtain a target disk automatically if target disk is not specified $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $srcSize | sed 1d`; if ($out !~ /Done/) { @@ -6938,51 +8630,49 @@ sub changeHypervisor { } # Obtain target disk FCP channel, WWPN, and LUN - $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/; + $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/i; my $tgtFcpDevice = lc($1); $tgtWwpn = lc($2); - $tgtLun = lc($3); + $tgtLun = lc($3); if (!$isTgtAttached) { - # Release source disk from zHCP $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $fcpDevice $srcWwpn $srcLun 0`; return; } # Get device node of source disk and target disk + ($srcWwpn, $srcLun, $tgtWwpn, $tgtLun) = (lc($srcWwpn), lc($srcLun), lc($tgtWwpn), lc($tgtLun)); $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/readlink /dev/disk/by-path/ccw-0.0.$srcFcpDevice-zfcp-0x$srcWwpn:0x$srcLun"`; chomp($out); my @srcDiskInfo = split('/', $out); my $srcDiskNode = pop(@srcDiskInfo); chomp($out); - xCAT::zvmUtils->printLn($callback, "$node: Device name of $tgtFcpDevice/$srcWwpn/$srcLun is $srcDiskNode"); + xCAT::zvmUtils->printLn( $callback, "$node: Device name of $tgtFcpDevice/$srcWwpn/$srcLun is $srcDiskNode"); $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/readlink /dev/disk/by-path/ccw-0.0.$tgtFcpDevice-zfcp-0x$tgtWwpn:0x$tgtLun"`; chomp($out); my @tgtDiskInfo = split('/', $out); my $tgtDiskNode = pop(@tgtDiskInfo); chomp($tgtDiskNode); - xCAT::zvmUtils->printLn($callback, "$node: Device name of $tgtFcpDevice/$tgtWwpn/$tgtLun is $tgtDiskNode"); + xCAT::zvmUtils->printLn( $callback, "$node: Device name of $tgtFcpDevice/$tgtWwpn/$tgtLun is $tgtDiskNode"); my $presist = 0; - my $rc = "Failed"; + my $rc = "Failed"; if (!$srcDiskNode || !$tgtDiskNode) { xCAT::zvmUtils->printLn($callback, "$node: (Error) Could not find device nodes for source or target disk."); } else { - # Copy source disk to target disk (512 block size) - xCAT::zvmUtils->printLn($callback, "$node: Copying source disk ($srcDiskNode) to target disk ($tgtDiskNode)"); + xCAT::zvmUtils->printLn( $callback, "$node: Copying source disk ($srcDiskNode) to target disk ($tgtDiskNode)" ); $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDiskNode of=/dev/$tgtDiskNode bs=512 oflag=sync && $::SUDO echo $?"`; $out = xCAT::zvmUtils->trimStr($out); if (int($out) != 0) { - # If $? is not 0 then there was an error during Linux dd xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to copy /dev/$srcDiskNode"); } - $presist = 1; # Keep target device as reserved - $rc = "Done"; + $presist = 1; # Keep target device as reserved + $rc = "Done"; # Sleep 2 seconds to let the system settle sleep(2); @@ -6996,44 +8686,89 @@ sub changeHypervisor { # Restore original source device attributes my %criteria = ( 'status' => $srcDisk{'status'}, - 'wwpn' => $srcDisk{'wwpn'}, - 'lun' => $srcDisk{'lun'}, - 'size' => $srcDisk{'size'}, - 'range' => $srcDisk{'range'}, - 'owner' => $srcDisk{'owner'}, - 'fcp' => $srcDisk{'fcp'}, - 'tag' => $srcDisk{'tag'} + 'wwpn' => $srcDisk{'wwpn'}, + 'lun' => $srcDisk{'lun'}, + 'size' => $srcDisk{'size'}, + 'range' => $srcDisk{'range'}, + 'owner' => $srcDisk{'owner'}, + 'fcp' => $srcDisk{'fcp'}, + 'tag' => $srcDisk{'tag'} ); my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); my %results = %$resultsRef; if ($results{'rc'} == -1) { - # Unable to reserve the volume and FCP channel xCAT::zvmUtils->printLn($callback, "$node: (Error) Source disk attributes cannot be restored in table"); } - xCAT::zvmUtils->printLn($callback, "$node: Copying zFCP device... $rc"); + xCAT::zvmUtils->printLn( $callback, "$node: Copying zFCP device... $rc"); if ($rc eq "Done") { - xCAT::zvmUtils->printLn($callback, "$node: Source disk copied onto zFCP device $tgtWwpn/$tgtLun"); + xCAT::zvmUtils->printLn( $callback, "$node: Source disk copied onto zFCP device $tgtWwpn/$tgtLun"); } $out = ""; } - # capturezfcp [profile] [wwpn] [lun] - elsif ($args->[0] eq "--capturezfcp") { - my $profile = $args->[1]; - my $wwpn = $args->[2]; - my $lun = $args->[3]; + # capturezfcp [profile] [wwpn] [lun] [compression] + elsif ( $args->[0] eq "--capturezfcp" ) { + my $out; + my $rc; + my $compParm = ''; + my $argsSize = @{$args}; + if ( $argsSize < 4 ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + my $profile = $args->[1]; + my $wwpn = $args->[2]; + my $lun = $args->[3]; + if ( $argsSize >= 5 ) { + # Set the compression invocation parameter if compression was specified. + # Note: Some older zHCP do not support compression specification. + + # Determine if zHCP supports the compression property. + $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/creatediskimage -V"`; + $rc = $?; + + if ( $rc == 65280 ) { + xCAT::zvmUtils->printSyslog( "changeHypervisor() Unable to communicate with zHCP agent" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeHypervisor() is unable to communicate with zHCP agent: $hcp" ); + return; + } + + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc != -1 ) { + # No error. It is probably that the zHCP supports compression. + # We will check the version to see if it is high enough. Any error + # or too low of a version means that we should ignore the compression + # operand in the future creatediskimage call. + # Process the version output. + my @outLn = split("\n", $out); + if ( $#outLn == 0 ) { + # Only a single line of output should come back from a compatable zHCP. + my @versionInfo = split( '\.', $out ); + if ( $versionInfo[0] >= 2 ) { + # zHCP supports compression specification. + if (( $args->[4] =~ /[\d]/ ) and ( length($args->[4]) == 1 )) { + $compParm = "--compression $args->[4]"; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) compression property is not a single digit from 0 to 9" ); + return; + } + } + } + } + } # Verify required properties are defined if (!defined($profile) || !defined($wwpn) || !defined($lun)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing one or more of the required parameters: profile, wwpn, or lun"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more of the required parameters: profile, wwpn, or lun" ); return; } # 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", ""); # Obtain the location of the install root directory my $installRoot = xCAT::TableUtils->getInstallDir(); @@ -7041,21 +8776,20 @@ sub changeHypervisor { xCAT::zvmUtils->printSyslog("changeHypervisor() Preparing the staging directory"); # Create the staging area location for the image - my $os = "unknown"; # Since we do not inspect the disk contents nor care - my $provMethod = "raw"; - my $arch = "s390x"; + my $os = "unknown"; # Since we do not inspect the disk contents nor care + my $provMethod = "raw"; + my $arch = "s390x"; my $stagingImgDir = "$installRoot/staging/$os/$arch/$profile"; - if (-d $stagingImgDir) { - unlink $stagingImgDir; + if(-d $stagingImgDir) { + rmtree $stagingImgDir; } mkpath($stagingImgDir); # Prepare the staging mount point on zHCP, if they need to be established. my $remoteStagingDir; - my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/staging", "rw", \$remoteStagingDir); + $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, $installRoot, "staging", "rw", \$remoteStagingDir); if ($rc) { - # Mount failed. rmtree "$stagingImgDir"; return; @@ -7080,21 +8814,20 @@ sub changeHypervisor { # Reserve the volume and associated FCP channel for the zHCP node my %criteria = ( - 'status' => 'used', - 'fcp' => 'auto', - 'wwpn' => $wwpn, - 'lun' => $lun, - 'owner' => $hcpNode + 'status' => 'used', + 'fcp' => 'auto', + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $hcpNode ); my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); my %results = %$resultsRef; my $device = $results{'fcp'}; $wwpn = $results{'wwpn'}; - $lun = $results{'lun'}; + $lun = $results{'lun'}; if ($results{'rc'} == -1) { - # Unable to reserve the volume and FCP channel xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be reserved"); rmtree "$stagingImgDir"; @@ -7104,41 +8837,40 @@ sub changeHypervisor { xCAT::zvmUtils->printLn($callback, "$node: Capturing volume using zHCP node"); # Drive the capture on the zHCP node - xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage $device 0x$wwpn/0x$lun $remoteStagingDir/$os/$arch/$profile/0x${wwpn}_0x${lun}.img"); - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/creatediskimage $device 0x$wwpn 0x$lun $remoteStagingDir/$os/$arch/$profile/${wwpn}_${lun}.img"`; + xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage $device 0x$wwpn/0x$lun $remoteStagingDir/$os/$arch/$profile/0x${wwpn}_0x${lun}.img $compParm"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/creatediskimage $device 0x$wwpn 0x$lun $remoteStagingDir/$os/$arch/$profile/${wwpn}_${lun}.img $compParm"`; $rc = $?; # Check for capture errors my $reasonString = ""; - $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); + $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString ); if ($rc != 0) { my $reason = "Reason: $reasonString"; xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage of volume 0x$wwpn/0x$lun failed. $reason"); xCAT::zvmUtils->printLn($callback, "$node: (Error) Image capture of volume 0x$wwpn/0x$lun failed on the zHCP node. $reason"); - rmtree "$stagingImgDir"; + rmtree "$stagingImgDir" ; return; } # Restore original source device attributes - my %criteria = ( + %criteria = ( 'status' => $srcDisk{'status'}, - 'wwpn' => $srcDisk{'wwpn'}, - 'lun' => $srcDisk{'lun'}, - 'size' => $srcDisk{'size'}, - 'range' => $srcDisk{'range'}, - 'owner' => $srcDisk{'owner'}, - 'fcp' => $srcDisk{'fcp'}, - 'tag' => $srcDisk{'tag'} + 'wwpn' => $srcDisk{'wwpn'}, + 'lun' => $srcDisk{'lun'}, + 'size' => $srcDisk{'size'}, + 'range' => $srcDisk{'range'}, + 'owner' => $srcDisk{'owner'}, + 'fcp' => $srcDisk{'fcp'}, + 'tag' => $srcDisk{'tag'} ); - my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); - my %results = %$resultsRef; + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + %results = %$resultsRef; if ($results{'rc'} == -1) { - # Unable to reserve the volume and FCP channel xCAT::zvmUtils->printLn($callback, "$node: (Error) Source disk attributes cannot be restored in table"); } - my $imageName = "$os-$arch-$provMethod-$profile"; + my $imageName = "$os-$arch-$provMethod-$profile"; my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile"; xCAT::zvmUtils->printLn($callback, "$node: Moving the image files to the deployable directory: $deployImgDir"); @@ -7152,11 +8884,11 @@ sub changeHypervisor { } # Remove the staging directory - rmtree "$stagingImgDir"; + rmtree "$stagingImgDir" ; xCAT::zvmUtils->printSyslog("changeHypervisor() Updating the osimage table"); - my $osTab = xCAT::Table->new('osimage', -create => 1, -autocommit => 0); + my $osTab = xCAT::Table->new('osimage',-create => 1,-autocommit => 0); my %keyHash; unless ($osTab) { @@ -7165,24 +8897,24 @@ sub changeHypervisor { } $keyHash{provmethod} = $provMethod; - $keyHash{profile} = $profile; - $keyHash{osvers} = $os; - $keyHash{osarch} = $arch; - $keyHash{imagetype} = 'linux'; - $keyHash{imagename} = $imageName; + $keyHash{profile} = $profile; + $keyHash{osvers} = $os; + $keyHash{osarch} = $arch; + $keyHash{imagetype} = 'linux'; + $keyHash{imagename} = $imageName; - $osTab->setAttribs({ imagename => $imageName }, \%keyHash); + $osTab->setAttribs({imagename => $imageName }, \%keyHash); $osTab->commit; xCAT::zvmUtils->printSyslog("changeHypervisor() Updating the linuximage table"); - my $linuxTab = xCAT::Table->new('linuximage', -create => 1, -autocommit => 0); + my $linuxTab = xCAT::Table->new('linuximage',-create => 1,-autocommit => 0); - %keyHash = (); - $keyHash{imagename} = $imageName; + %keyHash = (); + $keyHash{imagename} = $imageName; $keyHash{rootimgdir} = $deployImgDir; - $linuxTab->setAttribs({ imagename => $imageName }, \%keyHash); + $linuxTab->setAttribs({imagename => $imageName }, \%keyHash ); $linuxTab->commit; xCAT::zvmUtils->printLn($callback, "$node: Completed capturing the volume. Image($imageName) is stored at $deployImgDir"); @@ -7190,20 +8922,20 @@ sub changeHypervisor { } # deployzfcp [imageName] [wwpn] [lun] - elsif ($args->[0] eq "--deployzfcp") { - my $imageName = $args->[1]; - my $wwpn = $args->[2]; - my $lun = $args->[3]; + elsif ( $args->[0] eq "--deployzfcp" ) { + my $imageName = $args->[1]; + my $wwpn = $args->[2]; + my $lun = $args->[3]; # Verify required properties are defined - if (!defined($imageName) || !defined($wwpn) || !defined($lun)) { + if ( !defined($imageName) || !defined($wwpn) || !defined($lun)) { xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing one or more arguments: image name, wwpn, or lun"); return; } # 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", ""); # Obtain the location of the install root directory my $installRoot = xCAT::TableUtils->getInstallDir(); @@ -7214,10 +8946,10 @@ sub changeHypervisor { xCAT::zvmUtils->printLn($callback, "$node: (Error) The image name is not valid"); return; } - my $profile = $nameParts[3]; - my $os = "unknown"; + my $profile = $nameParts[3]; + my $os = "unknown"; my $provMethod = "raw"; - my $arch = "s390x"; + my $arch = "s390x"; my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile"; @@ -7231,14 +8963,13 @@ sub changeHypervisor { xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir contains more than the expected number of image files"); return; } else { - $imageFile = (split('/', $imageFiles[0]))[-1]; + $imageFile = (split( '/', $imageFiles[0]))[-1]; } # Prepare the deployable netboot mount point on zHCP, if they need to be established. my $remoteDeployDir; - my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$provMethod", "ro", \$remoteDeployDir); + my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $provMethod, "ro", \$remoteDeployDir); if ($rc) { - # Mount failed. return; } @@ -7254,11 +8985,11 @@ sub changeHypervisor { # Reserve the volume and associated FCP channel for the zHCP node. my %criteria = ( - 'status' => 'used', - 'fcp' => 'auto', - 'wwpn' => $wwpn, - 'lun' => $lun, - 'owner' => $hcpNode + 'status' => 'used', + 'fcp' => 'auto', + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $hcpNode ); my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); my %results = %$resultsRef; @@ -7266,10 +8997,9 @@ sub changeHypervisor { # Obtain the device assigned by xCAT my $device = $results{'fcp'}; $wwpn = $results{'wwpn'}; - $lun = $results{'lun'}; + $lun = $results{'lun'}; if ($results{'rc'} == -1) { - # Unable to reserve the volume and FCP channel xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be reserved"); return; @@ -7284,9 +9014,9 @@ sub changeHypervisor { # Release the volume from the zHCP node %criteria = ( - 'status' => 'reserved', - 'wwpn' => $wwpn, - 'lun' => $lun + 'status' => 'reserved', + 'wwpn' => $wwpn, + 'lun' => $lun ); $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); if ($results{'rc'} == -1) { @@ -7295,7 +9025,7 @@ sub changeHypervisor { # Check for deploy errors my $reasonString = ""; - $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); + $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString ); if ($rc != 0) { my $reason = "Reason: $reasonString"; xCAT::zvmUtils->printSyslog("changeHypervisor() unpackdiskimage of volume 0x$wwpn/0x$lun failed. $reason"); @@ -7308,26 +9038,27 @@ sub changeHypervisor { } # removediskfrompool [function] [region] [group] - elsif ($args->[0] eq "--removediskfrompool") { + elsif ( $args->[0] eq "--removediskfrompool" ) { my $funct = $args->[1]; my $region = $args->[2]; my $group = ""; # Create an array for regions my @regions; - if ($region =~ m/,/i) { - @regions = split(',', $region); + if ( $region =~ m/,/i ) { + @regions = split( ',', $region ); } else { - push(@regions, $region); + push( @regions, $region ); } my $tmp; - foreach (@regions) { + foreach ( @regions ) { + if (!(length $_)) {next;} $_ = xCAT::zvmUtils->trimStr($_); # Remove region from group | Remove entire group if ($funct eq "2" || $funct eq "7") { - $group = $args->[3]; + $group = $args->[3]; $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Remove_DM -T $hcpUserId -f $funct -r $_ -g $group"`; xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Remove_DM -T $hcpUserId -f $funct -r $_ -g $group"); } @@ -7343,14 +9074,14 @@ sub changeHypervisor { } # removescsi [device number] [persist (YES or NO)] - elsif ($args->[0] eq "--removescsi") { + elsif ( $args->[0] eq "--removescsi" ) { my $argsSize = @{$args}; if ($argsSize != 3) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - my $devNo = "dev_num=" . $args->[1]; + my $devNo = "dev_num=" . $args->[1]; my $persist = "persist=" . $args->[2]; # Delete a real SCSI disk @@ -7359,8 +9090,8 @@ sub changeHypervisor { } # removevlan [name] [owner] - elsif ($args->[0] eq "--removevlan") { - my $name = $args->[1]; + elsif ( $args->[0] eq "--removevlan" ) { + my $name = $args->[1]; my $owner = $args->[2]; # Delete a virtual network @@ -7369,7 +9100,7 @@ sub changeHypervisor { } # removevswitch [name] - elsif ($args->[0] eq "--removevswitch") { + elsif ( $args->[0] eq "--removevswitch" ) { my $name = $args->[1]; # Delete a VSWITCH @@ -7377,85 +9108,112 @@ sub changeHypervisor { xCAT::zvmUtils->printSyslog("ssh $hcp $::DIR/smcli Virtual_Network_Vswitch_Delete -T $hcpUserId -n $name"); } - # removezfcpfrompool [pool] [lun] [wwpn (optional)] - elsif ($args->[0] eq "--removezfcpfrompool") { - my $pool = $args->[1]; - my $lun = $args->[2]; - - my $wwpn; + # removezfcpfrompool [pool] [lun] [wwpn] + elsif ( $args->[0] eq "--removezfcpfrompool" ) { my $argsSize = @{$args}; - if ($argsSize == 4) { - $wwpn = $args->[3]; - } elsif ($argsSize > 4) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + my $pool = $args->[1]; + my $lun = $args->[2]; + if ($argsSize < 4) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) WWPN is required." ); + return; + } + my $wwpn = $args->[3]; + if ($argsSize > 4) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } # 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", ""); # Verify WWPN and LUN have the correct syntax - if ($wwpn =~ /[^0-9a-f;"]/i) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid world wide port name $wwpn."); + # The pattern '[0-9a-f]{16}' means 16 characters in char-set [0-9a-f]. The pattern '(;[0-9a-f]{16})' + # in last half part is used in the case of multipath. It will not appear in the case of signal path + # so * is used to handle both cases. + if ($wwpn !~ m/^[0-9a-f]{16}(;[0-9a-f]{16})*$/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid world wide port name $wwpn." ); return; } - if ($lun =~ /[^0-9a-f,]/i) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid logical unit number $lun."); + # The pattern '[0-9a-f]{16}' means 16 characters in char-set [0-9a-f]. The pattern '(,[0-9a-f]{16})' + # in last half part is used to deal with lun list. + if ($lun !~ m/^[0-9a-f]{16}(,[0-9a-f]{16})*$/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid logical unit number $lun." ); return; } my @luns; if ($lun =~ m/,/i) { - @luns = split(',', $lun); + @luns = split( ',', $lun ); } else { push(@luns, $lun); } # Find disk pool if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -e $::ZFCPPOOL/$pool.conf && echo Exists"`)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP pool does not exist"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP pool does not exist" ); return; } - # Go through each LUN + # Go through each LUN, look for matches of lun + wwpn (if specified) my $entry; my @args; foreach (@luns) { - + my $cur_lun = $_; + if (!(length $cur_lun)) {next;} # Entry should contain: status, wwpn, lun, size, range, owner, channel, tag - $entry = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -i $_`); - - # Do not update if LUN does not exists + $entry = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -a -i $cur_lun`; + # Do not update if LUN does not exists, stop checking other luns if this one not found if (!$entry) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device $_ does not exist"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $cur_lun does not exist" ); return; } + # process multiple lines if they exist with this lun + my @lines = split("\n", $entry); + my $foundit = 0; + foreach (@lines) { + my $fcpline = $_; + $fcpline = xCAT::zvmUtils->trimStr($fcpline); + if (!(length $fcpline)) {next;} # in case split causes an empty item - # Do not update if WWPN/LUN combo does not exists - @args = split(',', $entry); - if ($wwpn && !($args[1] =~ m/$wwpn/i)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device $wwpn/$_ does not exists"); - return; + # Skip if WWPN specified, and WWPN/LUN combo does not exists for this line + @args = split(',', $fcpline); + if ( (length $wwpn) && !($args[1] =~ m/$wwpn/i)) { + next; + } + + # delete this line in the file with given WWPN and LUN + $foundit = 1; + $fcpline = "'" . $fcpline . "'"; + $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "sed -i -e /$fcpline/d $::ZFCPPOOL/$pool.conf"); + + xCAT::zvmUtils->printLn( $callback, "$node: Removing zFCP device $wwpn/$cur_lun from $pool pool... Done" ); + + # Check if pool is empty, if so delete the pool.conf (If empty it only contains a header line) + $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "cat $::ZFCPPOOL/$pool.conf"); + my @linesLeft = split("\n",$out); + my $lineCount = scalar(@linesLeft); + my $emptyLines = `grep -cavP '\\S' $::ZFCPPOOL/$pool.conf`; # Count "empty" lines + if ($lineCount <= 1 || $lineCount == $emptyLines) { + $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "rm -f $::ZFCPPOOL/$pool.conf"); + xCAT::zvmUtils->printLn( $callback, "$node: Deleting empty zFCP $pool pool." ); + return; + } } - - # Update file with given WWPN and LUN - $entry = "'" . $entry . "'"; - $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "sed -i -e /$entry/d $::ZFCPPOOL/$pool.conf"); - if ($wwpn) { - xCAT::zvmUtils->printLn($callback, "$node: Removing zFCP device $wwpn/$_ from $pool pool... Done"); - } else { - xCAT::zvmUtils->printLn($callback, "$node: Removing zFCP device $_ from $pool pool... Done"); + if ($foundit == 0) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $wwpn/$cur_lun does not exist." ); + return; } } + # clear any data left in out so it does not display on callback $out = ""; } # releasezfcp [pool] [wwpn] [lun] - elsif ($args->[0] eq "--releasezfcp") { + elsif ( $args->[0] eq "--releasezfcp" ) { my $pool = lc($args->[1]); my $wwpn = lc($args->[2]); - my $lun = lc($args->[3]); + my $lun = lc($args->[3]); my $argsSize = @{$args}; if ($argsSize != 4) { @@ -7468,17 +9226,18 @@ sub changeHypervisor { # In case multiple LUNs are given, push LUNs into an array to be processed my @luns; if ($lun =~ m/,/i) { - @luns = split(',', $lun); + @luns = split( ',', $lun ); } else { push(@luns, $lun); } # Go through each LUN foreach (@luns) { + if (!(length $_)) {next;} my %criteria = ( - 'status' => 'free', - 'wwpn' => $wwpn, - 'lun' => $_ + 'status' => 'free', + 'wwpn' => $wwpn, + 'lun' => $_ ); my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); @@ -7493,25 +9252,37 @@ sub changeHypervisor { } # reservezfcp [pool] [status] [owner] [device address (or auto)] [size] [wwpn (optional)] [lun (optional)] - elsif ($args->[0] eq "--reservezfcp") { - my $pool = lc($args->[1]); + elsif ( $args->[0] eq "--reservezfcp" ) { + my $pool = lc($args->[1]); my $status = $args->[2]; - my $owner = $args->[3]; + my $owner = $args->[3]; my $device = $args->[4]; - my $size = $args->[5]; + my $size = $args->[5]; my $argsSize = @{$args}; if ($argsSize != 6 && $argsSize != 8) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + # status can be used or reserved but not free + if ($status =~ m/^free$/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Status can be used or reserved but not free." ); return; } # Obtain the FCP device, WWPN, and LUN (if any) my $wwpn = ""; - my $lun = ""; + my $lun = ""; if ($argsSize == 8) { $wwpn = lc($args->[6]); - $lun = lc($args->[7]); + $lun = lc($args->[7]); + + # WWPN and LUN must both be specified or both not + if ($wwpn xor $lun) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) WWPN and LUN must both be specified or both not."); + return; + } # Ignore the size if the WWPN and LUN are given $size = ""; @@ -7520,22 +9291,29 @@ sub changeHypervisor { my %criteria; my $resultsRef; if ($wwpn && $lun) { + # Check the status of the FCP device + my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun); + my %zFCP = %$deviceRef; + if ($zFCP{'status'} eq 'used') { + xCAT::zvmUtils->printLn($callback, "$node: (Error) FCP device 0x$wwpn/0x$lun is in use."); + return; + } + %criteria = ( - 'status' => $status, - 'fcp' => $device, - 'wwpn' => $wwpn, - 'lun' => $lun, - 'owner' => $owner + 'status' => $status, + 'fcp' => $device, + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $owner ); $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); } else { - # Do not know the WWPN or LUN in this case %criteria = ( - 'status' => $status, - 'fcp' => $device, - 'size' => $size, - 'owner' => $owner + 'status' => $status, + 'fcp' => $device, + 'size' => $size, + 'owner' => $owner ); $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); } @@ -7544,20 +9322,20 @@ sub changeHypervisor { # Obtain the device assigned by xCAT $device = $results{'fcp'}; - $wwpn = $results{'wwpn'}; - $lun = $results{'lun'}; + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; if ($results{'rc'} == 0) { xCAT::zvmUtils->printLn($callback, "$node: Reserving FCP device... Done"); - xCAT::zvmUtils->printLn($callback, "$node: FCP device $device/0x$wwpn/0x$lun was reserved"); + my $fcpDevice = $device ? "$device/0x$wwpn/0x$lun" : "0x$wwpn/0x$lun"; + xCAT::zvmUtils->printLn($callback, "$node: FCP device $fcpDevice was reserved... Done"); } else { - xCAT::zvmUtils->printLn($callback, "$node: Reserving FCP device... Failed"); + xCAT::zvmUtils->printLn($callback, "$node: (Error) Reserving FCP device... Failed"); } } # resetsmapi - elsif ($args->[0] eq "--resetsmapi") { - + elsif ( $args->[0] eq "--resetsmapi" ) { # IMPORTANT: # This option is only supported for class A privilege! # We cannot change it to use SMAPI only because SMAPI cannot be used to restart itself. @@ -7565,16 +9343,14 @@ sub changeHypervisor { # Check for VSMGUARD in z/VM 6.2 or newer $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q users VSMGUARD"`; if (!($out =~ m/HCPCQU045E/i)) { - # Force VSMGUARD and log it back on using XAUTOLOG $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp force VSMGUARD logoff immediate"`; $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp xautolog VSMGUARD"`; } else { - # Assuming zVM 6.1 or older # Force each worker machine off my @workers = ('VSMWORK1', 'VSMWORK2', 'VSMWORK3', 'VSMREQIN', 'VSMREQIU'); - foreach (@workers) { + foreach ( @workers ) { $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp force $_ logoff immediate"`; } @@ -7586,8 +9362,7 @@ sub changeHypervisor { } # smcli [api] [args] - elsif ($args->[0] eq "--smcli") { - + elsif ( $args->[0] eq "--smcli" ) { # Invoke SMAPI API directly through zHCP smcli my $str = "@{$args}"; $str =~ s/$args->[0]//g; @@ -7599,14 +9374,14 @@ sub changeHypervisor { # Otherwise, print out error else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Option not supported"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); } # Only print if there is content if ($out) { - $out = xCAT::zvmUtils->appendHostname($node, $out); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); chomp($out); - xCAT::zvmUtils->printLn($callback, "$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); } return; @@ -7621,14 +9396,14 @@ sub changeHypervisor { Type of inventory (config|all) Returns : Nothing Example : inventoryHypervisor($callback, $node, $args); - + =cut #------------------------------------------------------- sub inventoryHypervisor { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Set cache directory my $cache = '/var/opt/zhcp/cache'; @@ -7636,14 +9411,16 @@ sub inventoryHypervisor { # Output string my $str = ""; + my $rc; + # Get node properties from 'zvm' table - my @propNames = ('hcp'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get zHCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node zHCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node zHCP" ); return; } @@ -7656,7 +9433,9 @@ sub inventoryHypervisor { my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/modprobe vmcp"`; # Get configuration - if ($args->[0] eq 'config') { + if ( $args->[0] eq 'config' ) { + # Get z/VM host for zhcp + my $hypname = xCAT::zvmCPUtils->getHost($::SUDOER, $hcp); # Get total physical CPU in this LPAR my $lparCpuTotal = xCAT::zvmUtils->getLparCpuTotal($::SUDOER, $hcp); @@ -7673,15 +9452,18 @@ sub inventoryHypervisor { # Get LPAR memory Used my $lparMemUsed = xCAT::zvmUtils->getLparMemoryUsed($::SUDOER, $hcp); - $str .= "z/VM Host: " . uc($node) . "\n"; + $str .= "z/VM Host: $hypname\n"; $str .= "zHCP: $hcp\n"; $str .= "LPAR CPU Total: $lparCpuTotal\n"; $str .= "LPAR CPU Used: $lparCpuUsed\n"; $str .= "LPAR Memory Total: $lparMemTotal\n"; $str .= "LPAR Memory Used: $lparMemUsed\n"; $str .= "LPAR Memory Offline: $lparMemOffline\n"; - } elsif ($args->[0] eq 'all') { + $str .= "xCAT Hypervisor Node: $node\n"; # need node name from table unmodified + } elsif ( $args->[0] eq 'all' ) { + # Get z/VM system name for zhcp + my $hypname = xCAT::zvmCPUtils->getHost($::SUDOER, $hcp); # Get total physical CPU in this LPAR my $lparCpuTotal = xCAT::zvmUtils->getLparCpuTotal($::SUDOER, $hcp); @@ -7712,8 +9494,12 @@ sub inventoryHypervisor { # Get LPAR memory Used my $lparMemUsed = xCAT::zvmUtils->getLparMemoryUsed($::SUDOER, $hcp); + # Get IPL Time + my $ipl = xCAT::zvmCPUtils->getIplTime($::SUDOER, $hcp); + # Create output string - $str .= "z/VM Host: " . uc($node) . "\n"; + + $str .= "z/VM Host: $hypname\n"; $str .= "zHCP: $hcp\n"; $str .= "Architecture: $arch\n"; $str .= "CEC Vendor: $cecVendor\n"; @@ -7725,17 +9511,17 @@ sub inventoryHypervisor { $str .= "LPAR Memory Total: $lparMemTotal\n"; $str .= "LPAR Memory Used: $lparMemUsed\n"; $str .= "LPAR Memory Offline: $lparMemOffline\n"; + $str .= "IPL Time: $ipl\n"; + $str .= "xCAT Hypervisor Node: $node\n"; # need node name from table unmodified } # diskpoolspace - elsif ($args->[0] eq '--diskpoolspace') { - + elsif ( $args->[0] eq '--diskpoolspace' ) { # Check whether disk pool was given my @pools; if (!$args->[1]) { - # Get all known disk pool names - $out = `rinv $node --diskpoolnames`; + $out = `/opt/xcat/bin/rinv $node "--diskpoolnames"`; $out =~ s/$node: //g; $out = xCAT::zvmUtils->trimStr($out); @pools = split('\n', $out); @@ -7744,17 +9530,19 @@ sub inventoryHypervisor { push(@pools, $pool); # Check whether disk pool is a valid pool - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Query_DM -q 1 -e 3 -n $pool -T $hcpUserId" | grep "Failed"`; xCAT::zvmUtils->printSyslog("smcli Image_Volume_Space_Query_DM -q 1 -e 3 -n $pool -T $hcpUserId"); - if ($out) { - xCAT::zvmUtils->printLn($callback, "$node: Disk pool $pool does not exist"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Volume_Space_Query_DM -q 1 -e 3 -n $pool -T $hcpUserId"`; + if ( $out eq '') { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to communicate with zHCP agent" ); + return; + } elsif ($out =~ m/Failed/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to obtain disk pool information for $pool, additional information: $out" ); return; } } # Go through each pool and find it's space - foreach (@pools) { - + foreach(@pools) { # Skip empty pool if (!$_) { next; @@ -7766,8 +9554,8 @@ sub inventoryHypervisor { # Change the output format from cylinders to 'G' or 'M' $total = xCAT::zvmUtils->getSizeFromCyl($total); - $used = xCAT::zvmUtils->getSizeFromCyl($used); - $free = xCAT::zvmUtils->getSizeFromCyl($free); + $used = xCAT::zvmUtils->getSizeFromCyl($used); + $free = xCAT::zvmUtils->getSizeFromCyl($free); $str .= "$_ Total: $total\n"; $str .= "$_ Used: $used\n"; @@ -7776,8 +9564,7 @@ sub inventoryHypervisor { } # diskpool [pool] [all|free|used] - elsif ($args->[0] eq "--diskpool") { - + elsif ( $args->[0] eq "--diskpool" ) { # Get disk pool configuration my $pool = $args->[1]; my $space = $args->[2]; @@ -7793,36 +9580,36 @@ sub inventoryHypervisor { } # diskpoolnames - elsif ($args->[0] eq "--diskpoolnames") { - + elsif ( $args->[0] eq "--diskpoolnames" ) { # Get disk pool names # If the cache directory does not exist if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -d $cache && echo Exists"`)) { - # Create cache directory $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $cache"`; } + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) unable to communicate with the zhcp system: $hcp" ); + return; + } my $file = "$cache/diskpoolnames"; # If a cache for disk pool names exists if (`ssh $::SUDOER\@$hcp "$::SUDO ls $file"`) { - # Get current Epoch my $curTime = time(); - # Get time of last change as seconds since Epoch my $fileTime = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO stat -c %Z $file"`); # If the current time is greater than 5 minutes of the file timestamp - my $interval = 300; # 300 seconds = 5 minutes * 60 seconds/minute + my $interval = 300; # 300 seconds = 5 minutes * 60 seconds/minute if ($curTime > $fileTime + $interval) { - # Get disk pool names and save it in a file $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpoolnames $hcpUserId > $file"`; } } else { - # Get disk pool names and save it in a file $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/getdiskpoolnames $hcpUserId > $file"`; } @@ -7832,10 +9619,10 @@ sub inventoryHypervisor { } # fcpdevices [active|free|offline] [details (optional)] - elsif ($args->[0] eq "--fcpdevices") { + elsif ( $args->[0] eq "--fcpdevices" ) { my $argsSize = @{$args}; - my $space = $args->[1]; - my $details = 0; + my $space = $args->[1]; + my $details = 0; if ($argsSize == 3 && $args->[2] eq "details") { $details = 1; } @@ -7850,16 +9637,15 @@ sub inventoryHypervisor { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId"`; xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId"); - @devices = split("\n", $out); - for ($i = 0 ; $i < @devices ; $i++) { - + @devices = split( "\n", $out ); + for ($i = 0; $i < @devices; $i++) { # Extract the device number and status $devNo = $devices[$i]; $devNo =~ s/^FCP device number:(.*)/$1/; $devNo =~ s/^\s+//; $devNo =~ s/\s+$//; - $status = $devices[ $i + 1 ]; + $status = $devices[$i + 1]; $status =~ s/^Status:(.*)/$1/; $status =~ s/^\s+//; $status =~ s/\s+$//; @@ -7875,12 +9661,11 @@ sub inventoryHypervisor { } } } else { - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; - xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId | egrep -i FCP device number|Status"); - - @devices = split("\n", $out); - for ($i = 0 ; $i < @devices ; $i++) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId" | egrep -a -i "FCP device number|Status"`; + xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId | egrep -a -i FCP device number|Status"); + @devices = split( "\n", $out ); + for ($i = 0; $i < @devices; $i++) { # Extract the device number and status $devNo = $devices[$i]; $devNo =~ s/^FCP device number:(.*)/$1/; @@ -7900,32 +9685,31 @@ sub inventoryHypervisor { } } } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Query supported on active, free, or offline devices"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Query supported on active, free, or offline devices" ); } } # luns [fcp_device] (supported only on z/VM 6.2) - elsif ($args->[0] eq "--luns") { - + elsif ( $args->[0] eq "--luns" ) { # Find the LUNs accessible thru given zFCP device - my $fcp = lc($args->[1]); + my $fcp = lc($args->[1]); my $argsSize = @{$args}; if ($argsSize < 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp" | egrep -i "FCP device number:|World wide port number:|Logical unit number:|Number of bytes residing on the logical unit:"`; - xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp | egrep -i FCP device number:|World wide port number:|Logical unit number:|Number of bytes residing on the logical unit:"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp" | egrep -a -i "FCP device number:|World wide port number:|Logical unit number:|Number of bytes residing on the logical unit:"`; + xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp | egrep -a -i FCP device number:|World wide port number:|Logical unit number:|Number of bytes residing on the logical unit:"); - my @wwpns = split("\n", $out); + my @wwpns = split( "\n", $out ); my %map; my $wwpn = ""; - my $lun = ""; + my $lun = ""; my $size = ""; foreach (@wwpns) { - + if (!(length $_)) {next;} # Extract the device number if ($_ =~ "World wide port number:") { $_ =~ s/^\s+World wide port number:(.*)/$1/; @@ -7955,14 +9739,13 @@ sub inventoryHypervisor { xCAT::zvmUtils->printLn($callback, "#status,wwpn,lun,size,range,owner,channel,tag"); foreach $wwpn (sort keys %map) { - foreach $lun (sort keys %{ $map{$wwpn} }) { - + foreach $lun (sort keys %{$map{$wwpn}}) { # status, wwpn, lun, size, range, owner, channel, tag - $size = sprintf("%.1f", $map{$wwpn}{$lun} / 1073741824); # Convert size to GB + $size = sprintf("%.1f", $map{$wwpn}{$lun}/1073741824); # Convert size to GB if ($size > 0) { $size .= "G"; - xCAT::zvmUtils->printLn($callback, "unknown,$wwpn,$lun,$size,,,,"); + xCAT::zvmUtils->printLn($callback, "unknown,$wwpn,$lun,$size,,,,"); } } } @@ -7971,19 +9754,19 @@ sub inventoryHypervisor { } # networknames - elsif ($args->[0] eq "--networknames" || $args->[0] eq "--getnetworknames") { + elsif ( $args->[0] eq "--networknames" || $args->[0] eq "--getnetworknames" ) { $str = xCAT::zvmCPUtils->getNetworkNames($::SUDOER, $hcp); } # network [name] - elsif ($args->[0] eq "--network" || $args->[0] eq "--getnetwork") { + elsif ( $args->[0] eq "--network" || $args->[0] eq "--getnetwork" ) { my $netName = $args->[1]; - $str = xCAT::zvmCPUtils->getNetwork($::SUDOER, $hcp, $netName); + my $netType = $args->[2]; + $str = xCAT::zvmCPUtils->getNetwork( $::SUDOER, $hcp, $netName, $netType ); } # responsedata [failed Id] - elsif ($args->[0] eq "--responsedata") { - + elsif ( $args->[0] eq "--responsedata" ) { # This has not be completed! my $failedId = $args->[1]; $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Response_Recovery -T $hcpUserId -k $failedId"`; @@ -7991,10 +9774,10 @@ sub inventoryHypervisor { } # freefcp [fcp_dev] - elsif ($args->[0] eq "--freefcp") { + elsif ( $args->[0] eq "--freefcp" ) { my $argsSize = @{$args}; if ($argsSize != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -8005,10 +9788,10 @@ sub inventoryHypervisor { } # scsidisk [dev_no] - elsif ($args->[0] eq "--scsidisk") { + elsif ( $args->[0] eq "--scsidisk" ) { my $argsSize = @{$args}; if ($argsSize != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -8019,22 +9802,22 @@ sub inventoryHypervisor { } # ssi - elsif ($args->[0] eq "--ssi") { + elsif ( $args->[0] eq "--ssi" ) { $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli SSI_Query"`; xCAT::zvmUtils->printSyslog("smcli SSI_Query"); } # smapilevel - elsif ($args->[0] eq "--smapilevel") { + elsif ( $args->[0] eq "--smapilevel" ) { $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Query_API_Functional_Level -T $hcpUserId"`; xCAT::zvmUtils->printSyslog("smcli Query_API_Functional_Level -T $hcpUserId"); } # systemdisk [dev_no] - elsif ($args->[0] eq "--systemdisk") { + elsif ( $args->[0] eq "--systemdisk" ) { my $argsSize = @{$args}; if ($argsSize != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -8045,10 +9828,10 @@ sub inventoryHypervisor { } # systemdiskaccessibility [dev_no] - elsif ($args->[0] eq "--systemdiskaccessibility") { + elsif ( $args->[0] eq "--systemdiskaccessibility" ) { my $argsSize = @{$args}; if ($argsSize != 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -8059,10 +9842,10 @@ sub inventoryHypervisor { } # userprofilenames - elsif ($args->[0] eq "--userprofilenames") { + elsif ( $args->[0] eq "--userprofilenames" ) { my $argsSize = @{$args}; if ($argsSize != 1) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } @@ -8070,7 +9853,7 @@ sub inventoryHypervisor { my $tmp = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Directory_Manager_Search_DM -T $hcpUserId -s PROFILE"`; my @profiles = split('\n', $tmp); foreach (@profiles) { - + if (!(length $_)) {next;} # Extract user profile if ($_) { $_ =~ /([a-zA-Z]*):*/; @@ -8082,19 +9865,18 @@ sub inventoryHypervisor { } # vlanstats [vlan_id] [user_id] [device] [version] - elsif ($args->[0] eq "--vlanstats") { - + elsif ( $args->[0] eq "--vlanstats" ) { # This is not completed! my $argsSize = @{$args}; if ($argsSize < 4 && $argsSize > 5) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - my $vlanId = "VLAN_id=" . $args->[1]; - my $tgtUserId = "userid=" . $args->[2]; - my $device = "device=" . $args->[3]; - my $fmtVersion = "fmt_version=" . $args->[4]; # Optional + my $vlanId = "VLAN_id=" . $args->[1]; + my $tgtUserId = "userid=" . $args->[2]; + my $device = "device=" . $args->[3]; + my $fmtVersion = "fmt_version=" . $args->[4]; # Optional my $argStr = "-k $vlanId -k $tgtUserId -k $device"; if ($argsSize == 5) { @@ -8106,16 +9888,16 @@ sub inventoryHypervisor { } # vswitchstats [name] [version] - elsif ($args->[0] eq "--vswitchstats") { + elsif ( $args->[0] eq "--vswitchstats" ) { my $argsSize = @{$args}; if ($argsSize < 2 && $argsSize > 3) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } my $switchName = "switch_name=" . $args->[1]; - my $fmtVersion = "fmt_version=" . $args->[2]; # Optional - my $argStr = "-k $switchName"; + my $fmtVersion = "fmt_version=" . $args->[2]; # Optional + my $argStr = "-k $switchName"; if ($argsSize == 3) { $argStr .= " -k $fmtVersion" @@ -8126,21 +9908,21 @@ sub inventoryHypervisor { } # wwpn [fcp_device] (supported only on z/VM 6.2) - elsif ($args->[0] eq "--wwpns") { - my $fcp = lc($args->[1]); + elsif ( $args->[0] eq "--wwpns" ) { + my $fcp = lc($args->[1]); my $argsSize = @{$args}; if ($argsSize < 2) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp" | egrep -i "World wide port number:"`; - xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp | egrep -i World wide port number:"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp" | egrep -a -i "World wide port number:"`; + xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp | egrep -a -i World wide port number:"); - my @wwpns = split("\n", $out); + my @wwpns = split( "\n", $out ); my %uniqueWwpns; foreach (@wwpns) { - + if (!(length $_)) {next;} # Extract the device number if ($_ =~ "World wide port number:") { $_ =~ s/^\s+World wide port number:(.*)/$1/; @@ -8153,14 +9935,13 @@ sub inventoryHypervisor { } my $wwpn; - for $wwpn (keys %uniqueWwpns) { + for $wwpn ( keys %uniqueWwpns ) { $str .= "$wwpn\n"; } } # zfcppool [pool] [space] - elsif ($args->[0] eq "--zfcppool") { - + elsif ( $args->[0] eq "--zfcppool" ) { # Get zFCP disk pool configuration my $pool = lc($args->[1]); my $space = $args->[2]; @@ -8169,32 +9950,48 @@ sub inventoryHypervisor { $str = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf"`; } else { $str = "#status,wwpn,lun,size,range,owner,channel,tag\n"; - $str .= `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -i $space`; + $str .= `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -a -i $space`; } } # zfcppoolnames - elsif ($args->[0] eq "--zfcppoolnames") { - + elsif ( $args->[0] eq "--zfcppoolnames") { # Get zFCP disk pool names # Go through each zFCP pool + # Note: the code makes an assumption that the only files in this directory are + # zfcp configuration files. (*.conf) my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`); foreach (@pools) { - $_ = xCAT::zvmUtils->replaceStr($_, ".conf", ""); - $str .= "$_\n"; + if (!(length $_)) {next;} + my $pool = $_; + # Check if pool is empty (just one line), if so delete + my $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "cat $::ZFCPPOOL/$pool"); + my @linesLeft = split("\n",$out); + my $lineCount = scalar(@linesLeft); + my $emptyLines = `grep -cavP '\\S' $::ZFCPPOOL/$pool`; # Count "empty" lines + if ($lineCount <= 1 || $lineCount == $emptyLines) { + # Delete the empty pool, write log entry + xCAT::zvmUtils->printSyslog("Deleting empty zfcp pool: $pool"); + $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "rm -f $::ZFCPPOOL/$pool"); + next; + } + + # return just the pool name without the ".conf" + $pool = xCAT::zvmUtils->replaceStr( $pool, ".conf", "" ); + $str .= "$pool\n"; } } else { $str = "$node: (Error) Option not supported"; - xCAT::zvmUtils->printLn($callback, "$str"); + xCAT::zvmUtils->printLn( $callback, "$str" ); return; } # Append hostname (e.g. pokdev61) in front - $str = xCAT::zvmUtils->appendHostname($node, $str); + $str = xCAT::zvmUtils->appendHostname( $node, $str ); - xCAT::zvmUtils->printLn($callback, "$str"); + xCAT::zvmUtils->printLn( $callback, "$str" ); return; } @@ -8212,132 +10009,126 @@ sub inventoryHypervisor { Force (optional) Returns : Nothing Example : migrateVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub migrateVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # 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, "$node: (Error) Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing user ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } - # Capitalize user ID $userId =~ tr/a-z/A-Z/; - xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); + xCAT::zvmUtils->printSyslog("migrateVM() node:$node userid:$userId zHCP:$hcp sudoer:$::SUDOER sudo:$::SUDO"); # Get zHCP user ID my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); $hcpUserId =~ tr/a-z/A-Z/; - # Check required keys: target_identifier, destination, action, immediate, and max_total - # Optional keys: max_quiesce, and force - if (!$args || @{$args} < 4) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); - return; - } - # Output string my $out; my $migrateCmd = "VMRELOCATE -T $userId"; - my $i; my $destination; my $action; my $value; - foreach $i (0 .. 5) { - if ($args->[$i]) { - + foreach my $operand ( @$args ) { + if ( $operand ) { # Find destination key - if ($args->[$i] =~ m/destination=/i) { - $destination = $args->[$i]; + if ( $operand =~ m/destination=/i ) { + $destination = $operand; $destination =~ s/destination=//g; $destination =~ s/"//g; $destination =~ s/'//g; - } elsif ($args->[$i] =~ m/action=/i) { - $action = $args->[$i]; + } elsif ( $operand =~ m/action=/i ) { + $action = $operand; $action =~ s/action=//g; - } elsif ($args->[$i] =~ m/max_total=/i) { - $value = $args->[$i]; + } elsif ( $operand =~ m/max_total=/i ) { + $value = $operand; $value =~ s/max_total=//g; # Strip leading zeros - if (!($value =~ m/[^0-9.]/)) { + if (!($value =~ m/[^0-9.]/ )) { $value =~ s/^0+//; - $args->[$i] = "max_total=$value"; + $operand = "max_total=$value"; } - } elsif ($args->[$i] =~ m/max_quiesce=/i) { - $value = $args->[$i]; + } elsif ( $operand =~ m/max_quiesce=/i ) { + $value = $operand; $value =~ s/max_quiesce=//g; # Strip leading zeros - if (!($value =~ m/[^0-9.]/)) { + if (!($value =~ m/[^0-9.]/ )) { $value =~ s/^0+//; - $args->[$i] = "max_quiesce=$value"; + $operand = "max_quiesce=$value"; } } # Keys passed directly to smcli - $migrateCmd .= " -k $args->[$i]"; + $migrateCmd .= " -k $operand"; } } + if (!$action || !$destination) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) One or more required operands was not specified: 'action' or 'destination'." ); + return; + } + my $destHcp; if ($action =~ m/MOVE/i) { - # Find the zHCP for the destination host and set the node zHCP as it # Otherwise, it is up to the user to manually change the zHCP - @propNames = ('hcp'); - $propVals = xCAT::zvmUtils->getNodeProps('zvm', lc($destination), @propNames); + @propNames = ( 'hcp' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', lc($destination), @propNames ); $destHcp = $propVals->{'hcp'}; - if (!$destHcp) { + if ( !$destHcp ) { # Try upper-case - $propVals = xCAT::zvmUtils->getNodeProps('zvm', uc($destination), @propNames); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', uc($destination), @propNames ); $destHcp = $propVals->{'hcp'}; } if (!$destHcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find zHCP of $destination"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Set the hcp appropriately in the zvm table"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find zHCP of $destination" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Set the hcp appropriately in the zvm table" ); return; } } # Begin migration $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli $migrateCmd"`; - xCAT::zvmUtils->printSyslog("smcli $migrateCmd"); - xCAT::zvmUtils->printLn($callback, "$node: $out"); + xCAT::zvmUtils->printSyslog("On $hcp, smcli $migrateCmd"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Check for errors on migration only - my $rc = xCAT::zvmUtils->checkOutput($callback, $out); - if ($rc != -1 && $action =~ m/MOVE/i) { + my $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc != -1 && $action =~ m/MOVE/i) { # Check the migration status - my $check = 4; + my $check = 4; my $isMigrated = 0; while ($check > 0) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli VMRELOCATE_Status -T $hcpUserId" -k status_target=$userId`; xCAT::zvmUtils->printSyslog("smcli VMRELOCATE_Status -T $hcpUserId -k status_target=$userId"); - if ($out =~ m/No active relocations found/i) { + if ( $out =~ m/No active relocations found/i ) { $isMigrated = 1; last; } @@ -8348,9 +10139,9 @@ sub migrateVM { # Change the zHCP if migration successful if ($isMigrated) { -`/opt/xcat/bin/nodech $node zvm.hcp=$destHcp zvm.parent=$destination`; + `/opt/xcat/bin/nodech $node zvm.hcp=$destHcp zvm.parent=$destination`; } else { - xCAT::zvmUtils->printLn($callback, "$node: Could not determine progress of relocation"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not determine progress of relocation" ); } } @@ -8365,39 +10156,39 @@ sub migrateVM { Arguments : Node (hypervisor) Returns : Nothing Example : evacuate($callback, $node, $args); - + =cut #------------------------------------------------------- sub evacuate { # Get inputs, e.g. revacuate pokdev62 poktst62 - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # In order for this command to work, issue under /opt/xcat/bin: # ln -s /opt/xcat/bin/xcatclient revacuate my $destination = $args->[0]; if (!$destination) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing z/VM SSI cluster name of the destination system"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing z/VM SSI cluster name of the destination system" ); return; } # Get node properties from 'zvm' table - my @propNames = ('hcp', 'nodetype'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'nodetype' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get zHCP of hypervisor my $srcHcp = $propVals->{'hcp'}; - if (!$srcHcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + if ( !$srcHcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } my $type = $propVals->{'nodetype'}; if ($type ne 'zvm') { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Invalid nodetype"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Set the nodetype appropriately in the zvm table"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid nodetype" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Set the nodetype appropriately in the zvm table" ); return; } @@ -8405,26 +10196,25 @@ sub evacuate { # Find the zHCP for the destination host and set the node zHCP as it # Otherwise, it is up to the user to manually change the zHCP - @propNames = ('hcp'); - $propVals = xCAT::zvmUtils->getNodeProps('zvm', lc($destination), @propNames); + @propNames = ( 'hcp' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', lc($destination), @propNames ); $destHcp = $propVals->{'hcp'}; - if (!$destHcp) { - + if ( !$destHcp ) { # Try upper-case - $propVals = xCAT::zvmUtils->getNodeProps('zvm', uc($destination), @propNames); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', uc($destination), @propNames ); $destHcp = $propVals->{'hcp'}; } if (!$destHcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find zHCP of $destination"); - xCAT::zvmUtils->printLn($callback, "$node: (Solution) Set the hcp appropriately in the zvm table"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find zHCP of $destination" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Set the hcp appropriately in the zvm table" ); return; } # Get nodes managed by this zHCP # Look in 'zvm' table - my $tab = xCAT::Table->new('zvm', -create => 1, -autocommit => 0); - my @entries = $tab->getAllAttribsWhere("hcp like '%" . $srcHcp . "%' and nodetype=='vm'", 'node', 'userid'); + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); + my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $srcHcp . "%' and nodetype=='vm'", 'node', 'userid' ); my $out; my $iNode; @@ -8432,7 +10222,7 @@ sub evacuate { my $smcliArgs; my $nodes = ""; foreach (@entries) { - $iNode = $_->{'node'}; + $iNode = $_->{'node'}; $iUserId = $_->{'userid'}; # Skip zHCP entry @@ -8440,7 +10230,7 @@ sub evacuate { next; } - $nodes .= $iNode . ","; + $nodes .= $iNode . ","; } # Strip last comma @@ -8448,14 +10238,14 @@ sub evacuate { # Do not continue if no nodes to migrate if (!$nodes) { - xCAT::zvmUtils->printLn($callback, "$node: No nodes to evacuate"); + xCAT::zvmUtils->printLn( $callback, "$node: No nodes to evacuate" ); return; } # Begin migration # Required keys: target_identifier, destination, action, immediate, and max_total $out = `/opt/xcat/bin/rmigrate $nodes action=MOVE destination=$destination immediate=NO max_total=NOLIMIT`; - xCAT::zvmUtils->printLn($callback, "$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); return; } @@ -8470,18 +10260,18 @@ sub evacuate { Location to place log Returns : Nothing Example : eventLog($callback, $node, $args); - + =cut #------------------------------------------------------- sub eventLog { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; - my $srcLog = ''; - my $tgtLog = ''; - my $clear = 0; + my $srcLog = ''; + my $tgtLog = ''; + my $clear = 0; my $options = ''; if ($args) { @ARGV = @$args; @@ -8489,34 +10279,33 @@ sub eventLog { # Parse options GetOptions( 's=s' => \$srcLog, - 't=s' => \$tgtLog, # Optional - 'c' => \$clear, - 'o=s' => \$options); # Set logging options + 't=s' => \$tgtLog, # Optional + 'c' => \$clear, + 'o=s' => \$options); # Set logging options } # Event log required if (!$srcLog) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing event log"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing event log" ); return; } # Limit to logs in /var/log/* and configurations in /var/opt/* my $tmp = substr($srcLog, 0, 9); if ($tmp ne "/var/opt/" && $tmp ne "/var/log/") { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Files are restricted to those in /var/log and /var/opt"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Files are restricted to those in /var/log and /var/opt" ); return; } # 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 - xCAT::zvmUtils->printLn($callback, "$node: This is the management node"); + xCAT::zvmUtils->printLn( $callback, "$node: This is the management node" ); $mn = 1; } @@ -8526,10 +10315,16 @@ sub eventLog { if ($mn) { $out = `cat /dev/null > $srcLog`; } else { - $out = `ssh $::SUDOER\@$node "cat /dev/null > $srcLog"`; + #$out = `ssh $::SUDOER\@$node "cat /dev/null > $srcLog"`; + my $cmd = "$::SUDO cat /dev/null > $srcLog"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + } - xCAT::zvmUtils->printLn($callback, "$node: Clearing event log ($srcLog)... Done"); + xCAT::zvmUtils->printLn( $callback, "$node: Clearing event log ($srcLog)... Done" ); return; } @@ -8539,48 +10334,86 @@ sub eventLog { $out = `echo -e \"$options\" > $srcLog`; } else { $out = `echo -e \"$options\" > /tmp/$node.tracing`; - $out = `ssh $::SUDOER\@$node "rm -rf $srcLog"`; - $out = `cat /tmp/$node.tracing | ssh $::SUDOER\@$node "cat > /tmp/$node.tracing"`; - $out = `ssh $::SUDOER\@$node "mv /tmp/$node.tracing $srcLog"`; + #$out = `ssh $::SUDOER\@$node "rm -rf $srcLog"`; + my $cmd = "$::SUDO rm -rf $srcLog"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + # Get node communicate type. + my @propNames = ( 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + if ($propVals->{'status'} =~ /SSH=1/){ + $out = `cat /tmp/$node.tracing | ssh $::SUDOER\@$node "cat > /tmp/$node.tracing"`; + } + elsif ($propVals->{'status'} =~ /IUCV=1/){ + #$cmd = "$::SUDO cat /tmp/$node.tracing | ssh $::SUDOER\@$node cat > /tmp/$node.tracing"; + $cmd = "file_transport /tmp/$node.tracing /tmp/$node.tracing"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + }else{ + xCAT::zvmUtils->printLn( $callback, "$node: Not set communicate type."); + return; + } + + + #$out = `ssh $::SUDOER\@$node "mv /tmp/$node.tracing $srcLog"`; + $cmd = "$::SUDO mv /tmp/$node.tracing $srcLog"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + $out = `rm -rf /tmp/$node.tracing`; } - xCAT::zvmUtils->printLn($callback, "$node: Setting event logging options... Done"); + xCAT::zvmUtils->printLn( $callback, "$node: Setting event logging options... Done" ); return; } # Default log location is /install/logs if (!$tgtLog) { - my @entries = xCAT::TableUtils->get_site_attribute("installdir"); + my @entries = xCAT::TableUtils->get_site_attribute("installdir"); my $install = $entries[0]; $tgtLog = "$install/logs/"; - $out = `mkdir -p $tgtLog`; + $out = `mkdir -p $tgtLog`; } # Copy over event log onto xCAT - xCAT::zvmUtils->printLn($callback, "$node: Retrieving event log ($srcLog)"); + xCAT::zvmUtils->printLn( $callback, "$node: Retrieving event log ($srcLog)" ); if ($mn) { if (!(`test -e $srcLog && echo Exists`)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Specified log does not exist"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Specified log does not exist" ); return; } $out = `cp $srcLog $tgtLog`; } else { - if (!(`ssh $::SUDOER\@$node "test -e $srcLog && echo Exists"`)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Specified log does not exist"); + #if (!(`ssh $::SUDOER\@$node "test -e $srcLog && echo Exists"`)) { + my $cmd = "$::SUDO test -e $srcLog && echo Exists"; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return; } + if (!($out)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Specified log does not exist" ); + return; + } + + # ???? Must have SSH key to do the copy $out = `scp $::SUDOER\@$node:$srcLog $tgtLog`; } - if (-e $tgtLog) { - xCAT::zvmUtils->printLn($callback, "$node: Log copied to $tgtLog"); + if ( -e $tgtLog ) { + xCAT::zvmUtils->printLn( $callback, "$node: Log copied to $tgtLog" ); $out = `chmod -R 644 $tgtLog/*`; } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to copy log"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy log" ); } } @@ -8592,31 +10425,55 @@ sub eventLog { Arguments : Node OS Archictecture + Specified provisioning method type Profile Device information + Compression level: 0 - none, 1 thru 9 - gzip compression level Returns : Nothing - Example : imageCapture( $callback, $node, $os, $arch, $profile, $osimg, $device ); - + Example : imageCapture( $callback, $node, $os, $arch, $type, $profile, $osimg, $device, $comp ); + =cut #------------------------------------------------------- sub imageCapture { - my ($class, $callback, $node, $os, $arch, $profile, $osimg, $device) = @_; + my ($class, $callback, $node, $os, $arch, $type, $profile, $osimg, $device, $comp) = @_; my $rc; - my $out = ''; + my $out = ''; my $reason = ""; + my $provMethod; + my $compParm = ""; + my $cmd = ''; - xCAT::zvmUtils->printSyslog("imageCapture() $node:$node os:$os arch:$arch profile:$profile osimg:$osimg device:$device"); + xCAT::zvmUtils->printSyslog( "imageCapture() node:$node os:$os arch:$arch type:$type profile:$profile osimg:$osimg device:$device comp:$comp" ); # Verify required properties are defined if (!defined($os) || !defined($arch) || !defined($profile)) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) One or more of the required properties is not specified: os version, architecture or profile"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) One or more of the required properties is not specified: os version, architecture or profile" ); + return; + } + + if (defined($type)) { + $provMethod = $type; + } else { + # Type operand was not specified on command, therefore need to get provmethod from the nodetype table. + my $nodetypetab = xCAT::Table->new("nodetype"); + my $ref_nodetype = $nodetypetab->getNodeAttribs($node, ['provmethod']); + $provMethod = $ref_nodetype->{provmethod}; + if ( !defined($provMethod) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) provmethod property is not specified in the nodetype table" ); + return; + } + } + + # Ensure provmethod is one of the supported methods + if (( $provMethod ne 'sysclone') && ( $provMethod ne 'netboot' )) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) provmethod property is not 'netboot' or 'sysclone'" ); return; } # Ensure the architecture property is 's390x' if ($arch ne 's390x') { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Architecture was $arch instead of 's390x'. 's390x' will be used instead of the specified value."); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Architecture was $arch instead of 's390x'. 's390x' will be used instead of the specified value." ); $arch = 's390x'; } @@ -8631,145 +10488,391 @@ sub imageCapture { # This looks in the passwd table for a key = sudoer my ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); + # Process ID for xfork() + my $pid; + + # Child process IDs + my @children; + # 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 zHCP my $hcp = $propVals->{'hcp'}; if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing node HCP"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get zHCP user ID - my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + my $hcpUserId = xCAT::zvmCPUtils->getUserId($sudoer, $hcp); $hcpUserId =~ tr/a-z/A-Z/; # Get capture target's user ID my $targetUserId = $propVals->{'userid'}; $targetUserId =~ tr/a-z/A-Z/; - # Get node properties from 'zvm' table - @propNames = ('ip', 'hostnames'); + # Get node properties from 'hosts' table + @propNames = ( 'ip', 'hostnames' ); $propVals = xCAT::zvmUtils->getNodeProps('hosts', $node, @propNames); - # Check if node is pingable - if (`/opt/xcat/bin/pping $node | egrep -i "noping"`) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Host is unreachable"); - return; - } - + # Determine the disks to be captured. my $vaddr; - my $devName; + my @vaddrList; - # Set the default is device option was specified without any parameters. - if (!$device) { - $devName = "/dev/root"; - } + if ($provMethod eq 'netboot') { - # Obtain the device number from the target system. - if ($devName eq '/dev/root') { + # Check if node is pingable + my $ping = xCAT::zvmUtils->pingNode($node); + if ($ping eq "noping") { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Host is unreachable" ); + return; + } - # Determine which Linux device is associated with the root directory - $out = `ssh $sudoer\@$node $sudo cat /proc/cmdline | tr " " "\\n" | grep "^root=" | cut -c6-`; - if ($out) { - $out = `ssh $sudoer\@$node $sudo "/usr/bin/readlink -f $out"`; + my $devName; + # Set the default is device option was specified without any parameters. + if (!$device) { + $devName = "/dev/root"; + } + + # Obtain the device number from the target system. + if ($devName eq '/dev/root') { + # Determine which Linux device is associated with the root directory + #$out = `ssh $sudoer\@$node $sudo 'cat /proc/cmdline | tr " " "\\n" | grep -a "^root=" | cut -c6-'`; + $cmd = "$::SUDO" . ' cat /proc/cmdline | tr " " "\\n" | grep -a "^root=" | cut -c6-'; + $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + + my $rootDev = ''; if ($out) { - $devName = substr($out, 5); - $devName =~ s/\s+$//; - $devName =~ s/\d+$//; + if ( $out =~ m/^UUID=/i ) { + $rootDev = "/dev/disk/by-uuid/".substr($out, 5); + } + elsif ( $out =~ m/^LABEL=/i ) { + $rootDev = "/dev/disk/by-label/".substr($out, 6); + } + elsif ( $out =~ /mapper/ ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Capturing a disk with root filesystem on logical volume is not supported" ); + return; + } else { + $rootDev = $out; + } + #$out = `ssh $sudoer\@$node $sudo "readlink -f $rootDev 2>&1"`; + $cmd = "$::SUDO readlink -f $rootDev 2>&1"; + $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + + + if ( $rc != 0 ) { + xCAT::zvmUtils->printSyslog( "imageCapture() failed to execute readlink -f $rootDev on capture source vm rc: $rc, out: $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: imageCapture() failed to execute readlink to locate the root device rc: $rc, out: $out" ); + return; + } + + if ($out) { + $devName = substr($out, 5); + $devName =~ s/\s+$//; + $devName =~ s/\d+$//; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to locate the root device from $out" ); + return; + } } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable locate the device associated with the root directory"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to get useful info from /proc/cmdline to locate the device associated with the root directory on capture source vm" ); return; } } else { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable locate the device associated with the root directory"); - return; + $devName = substr $devName, 5; } + + $vaddr = xCAT::zvmUtils->getDeviceNodeAddr( $sudoer, $node, $devName ); + if ($vaddr) { + push( @vaddrList, $vaddr ); + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable determine the device being captured" ); + return 0; + } + } else { - $devName = substr $devName, 5; + # provmethod is 'sysclone' + + # Get the list of mdisks + my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sudoer, $node ); + foreach (@srcDisks) { + # Get disk address + my @words = split( ' ', $_ ); + $vaddr = $words[1]; + my $diskType = $words[2]; + + if ( $diskType eq 'FB-512' ) { + # We do not capture vdisks but we will capture minidisks, dedicated disks and tdisks. + next; + } + + # Add 0 in front if address length is less than 4 + while (length($vaddr) < 4) { + $vaddr = '0' . $vaddr; + } + push( @vaddrList, $vaddr ); + } } - $vaddr = xCAT::zvmUtils->getDeviceNodeAddr($sudoer, $node, $devName); - if (!$vaddr) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable determine the device being captured"); - return 0; + # Set the compression invocation parameter if compression was specified. + # Note: Some older zHCP do not support compression specification. + if ( defined($comp) ) { + # Determine if zHCP supports the compression property. + $out = `ssh -o ConnectTimeout=30 $sudoer\@$hcp "$sudo $dir/creatediskimage -V"`; + $rc = $?; + + if ( $rc == 65280 ) { + xCAT::zvmUtils->printSyslog( "imageCapture() Unable to communicate with zHCP agent" ); + xCAT::zvmUtils->printLn( $callback, "$node: imageCapture() is unable to communicate with zHCP agent: $hcp" ); + return; + } + + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc != -1 ) { + # No error. It is probably that the zHCP supports compression. + # We will check the version to see if it is high enough. Any error + # or too low of a version means that we should ignore the compression + # operand in the future creatediskimage call. + # Process the version output. + my @outLn = split("\n", $out); + if ( $#outLn == 0 ) { + # Only a single line of output should come back from a compatable zHCP. + my @versionInfo = split( '\.', $out ); + if ( $versionInfo[0] >= 2 ) { + # zHCP supports compression specification. + if (( $comp =~ /[\d]/ ) and ( length($comp) == 1 )) { + $compParm = "--compression $comp"; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) compression property is not a single digit from 0 to 9" ); + return; + } + } + } + } } # Shutdown and logoff the virtual machine so that its disks are stable for the capture step. - xCAT::zvmUtils->printSyslog("imageCapture() Shutting down $node prior to disk capture"); - $out = `ssh -o ConnectTimeout=10 $node "shutdown -h now"`; - sleep(15); # Wait 15 seconds to let shutdown start before logging user off + xCAT::zvmUtils->printSyslog( "imageCapture() Shutting down $node prior to disk capture" ); + #$out = `ssh -o ConnectTimeout=10 $node "shutdown -h now"`; + $cmd = "$::SUDO shutdown -h now"; + $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd); + + sleep(15); # Wait 15 seconds to let shutdown start before logging user off # If the OS is not shutdown and the machine is enabled for shutdown signals # then deactivate will cause CP to send the shutdown signal and # wait an additional (z/VM installation configurable) time before forcing # the virtual machine off the z/VM system. - xCAT::zvmUtils->printSyslog("$sudo $dir/smcli Image_Deactivate -T $targetUserId"); + xCAT::zvmUtils->printSyslog( "$sudo $dir/smcli Image_Deactivate -T $targetUserId" ); $out = `ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Deactivate -T $targetUserId"`; - xCAT::zvmUtils->printSyslog("imageCapture() smcli response: $out"); + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + $out = "$targetUserId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + $out = "$targetUserId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $targetUserId output: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + return; + } + xCAT::zvmUtils->printSyslog( "imageCapture() smcli response: $out" ); - xCAT::zvmUtils->printSyslog("imageCapture() Preparing the staging directory"); + # Wait (checking every 15 seconds) until user is finally logged off or maximum wait time has elapsed + my $max = 0; + $out=`ssh $sudoer\@$hcp "$sudo /sbin/vmcp q user $targetUserId 2>/dev/null | grep -a HCPCQU045E"`; + while ( !$out && $max < 60 ) { + sleep(15); # Wait 15 seconds + $max++; + $out=`ssh $sudoer\@$hcp "$sudo /sbin/vmcp q user $targetUserId 2>/dev/null | grep -a HCPCQU045E"`; + } + my $totalMinutes = $max * 15 / 60; + if ( $out ) { + # Target system was successfully logged off + xCAT::zvmUtils->printSyslog( "imageCapture() Target system was logged off after $totalMinutes minutes" ); + } else { + # Target system was not logged off + xCAT::zvmUtils->printSyslog( "imageCapture() Target system was not logged off after $totalMinutes minutes" ); + xCAT::zvmUtils->printSyslog( "$sudo $dir/smcli Image_Deactivate -T $targetUserId -f IMMED" ); + $out = `ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Deactivate -T $targetUserId -f IMMED"`; + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $hcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $hcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + $out = "$targetUserId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + $out = "$targetUserId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $targetUserId output: $out"); + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + return; + } + xCAT::zvmUtils->printSyslog( "imageCapture() smcli response: $out" ); + sleep(15); # Wait 15 seconds + } + + xCAT::zvmUtils->printSyslog( "imageCapture() Preparing the staging directory" ); # Create the staging area location for the image my $stagingImgDir = "$installRoot/staging/$os/$arch/$profile"; - if (-d $stagingImgDir) { - unlink $stagingImgDir; + if(-d $stagingImgDir) { + rmtree $stagingImgDir; } mkpath($stagingImgDir); # Prepare the staging mount point on zHCP, if they need to be established. my $remoteStagingDir; - $rc = xCAT::zvmUtils->establishMount($callback, $sudoer, $sudo, $hcp, "$installRoot/staging", "rw", \$remoteStagingDir); + $rc = xCAT::zvmUtils->establishMount( $callback, $sudoer, $sudo, $hcp, $installRoot, "staging", "rw", \$remoteStagingDir ); if ($rc) { - # Mount failed rmtree "$stagingImgDir"; return; } - xCAT::zvmUtils->printLn($callback, "$node: Capturing the image using zHCP node"); + xCAT::zvmUtils->printLn( $callback, "$node: Capturing the image using zHCP node" ); - # Drive the capture on the zHCP node - xCAT::zvmUtils->printSyslog("imageCapture() creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img"); - $out = `ssh $sudoer\@$hcp "$sudo $dir/creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img"`; - $rc = $?; + # Drive each device capture separately. Up to 10 at a time. + # Each capture request to zHCP is driven from a child process. + foreach my $vaddr ( @vaddrList ) { + $pid = xCAT::Utils->xfork(); - # If the capture failed then clean up and return - my $reasonString = ""; - $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); - if ($rc != 0) { - $reason = "Reason: $reasonString"; - xCAT::zvmUtils->printSyslog("imageCapture() creatediskimage of $targetUserId $vaddr failed. $reason"); - xCAT::zvmUtils->printLn($callback, "$node: (Error) Image capture of $targetUserId $vaddr failed on the zHCP node. $reason"); + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process. + elsif ( $pid == 0 ) { + # Drive the capture on the zHCP node + xCAT::zvmUtils->printSyslog( "imageCapture() creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img $compParm" ); + $out = `ssh $sudoer\@$hcp "$sudo $dir/creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img $compParm"`; + $rc=$?; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Check for script errors + my $reasonString = ""; + $rc = xCAT::zvmUtils->checkOutputExtractReason( $out, \$reasonString ); + if ($rc != 0) { + $reason = "Reason: $reasonString"; + xCAT::zvmUtils->printSyslog( "imageCapture() creatediskimage of $targetUserId $vaddr failed. $reason" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Image capture of $targetUserId $vaddr failed on the zHCP node. $reason" ); + # Create a "FAILED" file to indicate the failure. + if ( ! open FILE, '>'."$stagingImgDir/FAILED" ) { + # if we can't open it then we log the problem. + xCAT::zvmUtils->printSyslog( "imageCapture() unable to create a 'FAILED' file." ); + } + } + + # Exit the child process + exit(0); + } + + else { + # Ran out of resources + # Create a "FAILED" file to indicate the failure. + if ( ! open FILE, '>'."$stagingImgDir/FAILED" ) { + # if we can't open it then we log the problem. + xCAT::zvmUtils->printSyslog( "imageCapture() unable to create a 'FAILED' file." ); + } + + $reason = ". Reason: Could not fork\n"; + last; + } + + # Handle 10 nodes at a time, else you will get errors + if ( !( @children % 10 ) ) { + + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + + # Clear children + @children = (); + } + } # End of foreach + + # If any children remain, then wait for them to complete. + foreach $pid ( @children ) { + xCAT::zvmUtils->printSyslog( "imageCapture() Waiting for child process $pid to complete" ); + waitpid( $pid, 0 ); + } + + # if the capture failed then clean up and return + if ( -e "$stagingImgDir/FAILED" ) { + xCAT::zvmUtils->printSyslog( "imageCapture() 'FAILED' file found. Removing staging directory." ); rmtree "$stagingImgDir"; return; } # Now that all image files have been successfully created, move them to the deployable directory. - my $imageName = "$os-$arch-netboot-$profile"; - my $deployImgDir = "$installRoot/netboot/$os/$arch/$profile"; + my $imageName = "$os-$arch-$provMethod-$profile"; + my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile"; - xCAT::zvmUtils->printLn($callback, "$node: Moving the image files to the deployable directory: $deployImgDir"); + xCAT::zvmUtils->printLn( $callback, "$node: Moving the image files to the deployable directory: $deployImgDir" ); my @stagedFiles = glob "$stagingImgDir/*.img"; if (!@stagedFiles) { rmtree "$stagingImgDir"; - xCAT::zvmUtils->printLn($callback, "$node: (Error) No image files were created"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) No image files were created" ); return 0; } - mkpath($deployImgDir); + if ( -e "$deployImgDir" ) { + $out=`/bin/rm -f $deployImgDir/*.img`; + } else { + mkpath($deployImgDir); + } foreach my $oldFile (@stagedFiles) { $rc = move($oldFile, $deployImgDir); $reason = $!; if ($rc == 0) { - # Move failed rmtree "$stagingImgDir"; - xCAT::zvmUtils->printLn($callback, "$node: (Error) Could not move $oldFile to $deployImgDir. $reason"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not move $oldFile to $deployImgDir. $reason" ); + return; + } + } + + # For sysclone, obtain the userid/identity entry for the user and move it to the deploy image directory. + if ($provMethod eq 'sysclone') { + $out=`ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Query_DM -T $targetUserId | sed '\$d' > $remoteStagingDir/$os/$arch/$profile/$targetUserId.direct"`; + # Move the direct file to the deploy image directory + $rc = move("$stagingImgDir/$targetUserId.direct", $deployImgDir); + $reason = $!; + if ($rc == 0) { + # Move failed + rmtree "$stagingImgDir"; + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not move $stagingImgDir/$targetUserId.direct to $deployImgDir. $reason" ); return; } } @@ -8777,41 +10880,927 @@ sub imageCapture { # Remove the staging directory and files rmtree "$stagingImgDir"; - xCAT::zvmUtils->printSyslog("imageCapture() Updating the osimage table"); + xCAT::zvmUtils->printSyslog( "imageCapture() Updating the osimage table" ); # Update osimage table - my $osTab = xCAT::Table->new('osimage', -create => 1, -autocommit => 0); + my $osTab = xCAT::Table->new('osimage',-create => 1,-autocommit => 0); my %keyHash; unless ($osTab) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to open table 'osimage'"); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to open table 'osimage'" ); return; } - $keyHash{provmethod} = 'netboot'; - $keyHash{profile} = $profile; - $keyHash{osvers} = $os; - $keyHash{osarch} = $arch; - $keyHash{imagetype} = 'linux'; - $keyHash{osname} = 'Linux'; - $keyHash{imagename} = $imageName; + $keyHash{provmethod} = $provMethod; + $keyHash{profile} = $profile; + $keyHash{osvers} = $os; + $keyHash{osarch} = $arch; + $keyHash{imagetype} = 'linux'; + $keyHash{osname} = 'Linux'; + $keyHash{imagename} = $imageName; - $osTab->setAttribs({ imagename => $imageName }, \%keyHash); + $osTab->setAttribs({imagename => $imageName}, \%keyHash); $osTab->commit; - xCAT::zvmUtils->printSyslog("imageCapture() Updating the linuximage table"); + xCAT::zvmUtils->printSyslog( "imageCapture() Updating the linuximage table" ); # Update linuximage table - my $linuxTab = xCAT::Table->new('linuximage', -create => 1, -autocommit => 0); + my $linuxTab = xCAT::Table->new('linuximage',-create => 1,-autocommit => 0); - %keyHash = (); - $keyHash{imagename} = $imageName; + %keyHash = (); + $keyHash{imagename} = $imageName; $keyHash{rootimgdir} = $deployImgDir; - $linuxTab->setAttribs({ imagename => $imageName }, \%keyHash); + $linuxTab->setAttribs({imagename => $imageName}, \%keyHash); $linuxTab->commit; - xCAT::zvmUtils->printLn($callback, "$node: Completed capturing the image($imageName) and stored at $deployImgDir"); + xCAT::zvmUtils->printLn( $callback, "$node: Completed capturing the image($imageName) and stored at $deployImgDir" ); return; } + +#------------------------------------------------------- + +=head3 specialcloneVM + + Description : Do a special clone of virtual server + Arguments : callback + Node(s) array + args with: maybe disk password, imagename + clone info hash, can be empty + Returns : Nothing + Example : cloneVM($callback, \@targetNodes, $args, \%cloneInfoHash); + +=cut + +#------------------------------------------------------- +sub specialcloneVM { + + # Get inputs + my ( $callback, $nodes, $args, $cloneInfoHash ) = @_; + + # Get nodes + my @nodes = @$nodes; + my $nodeCount = @nodes; + + my %cloneInfo = %$cloneInfoHash; + + my $sourceId; + my $srcOS = "unknown"; + + my $sudo = "sudo"; + my $user = $::SUDOER; + + # Return code for each command + my $rc; + my $out; + my $i; + + # Child process IDs + my @children; + + # Process ID for xfork() + my $pid; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + if ($user eq "root") { + $sudo = ""; + } + + # Do some parameter checking + if ( defined $cloneInfo{'CLONE_FROM'} ) { + $sourceId = $cloneInfo{'CLONE_FROM'}; + xCAT::zvmUtils->printSyslog("Clone of <@nodes> count:$nodeCount to be done from: $sourceId\n"); + } else { + xCAT::zvmUtils->printLn( $callback, "(Error) CLONE_FROM value is missing from DOCLONE COPY on 193." ); + return; + } + + + if ($nodeCount < 1) { + xCAT::zvmUtils->printLn( $callback, "(Error) Missing target nodes to clone." ); + return; + } + + # Verify that all the target nodes use the same zhcp + my $tgtFirstNode = $nodes[0]; + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtFirstNode, @propNames ); + + # Get target zhcp, all nodes must use the same one, and the source userid must be + # also on that zhcp + my $tgtZhcp = $propVals->{'hcp'}; + my $tgtFirstUserid = $propVals->{'userid'}; + my $founderror = 0; + + foreach (@nodes) { + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames ); + my $temp = $propVals->{'hcp'}; + my $tempUserid = $propVals->{'userid'}; + if ($temp ne $tgtZhcp) { + $founderror = 1; + xCAT::zvmUtils->printLn( $callback, "(Error) node $_ does not match zhcp $tgtZhcp." ); + } + if ( length($tempUserid) < 1) { + $founderror = 1; + xCAT::zvmUtils->printLn( $callback, "(Error) node $_ does not have a zVM userid." ); + } + } + if ($founderror == 1) { return; } + + # Get the source userid directory using original call. + xCAT::zvmUtils->printSyslog("Executing: ssh $::SUDOER\@$tgtZhcp $::SUDO $dir/smcli Image_Query_DM -T $sourceId | sed '\$d'\n"); + $out = `ssh $::SUDOER\@$tgtZhcp "$::SUDO $dir/smcli Image_Query_DM -T $sourceId" | sed '\$d'`; + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + xCAT::zvmUtils->printLn( $callback, "(Error) Did not get the $sourceId directory. Return output was: $out." ); + return; + } + + my @sourceDirectory = split('\n', $out); + my $sourceMdisks = `echo "$out" | grep -a -E -i "MDISK"`; # maybe not use this? + $sourceMdisks = xCAT::zvmUtils->trimStr($sourceMdisks); + + my $sourceLinks = `echo "$out" | grep -a -E -i "LINK"`; + $sourceLinks = xCAT::zvmUtils->trimStr($sourceLinks); + + my $sourceWithoutMdisks = `echo "$out" | grep -a -E -i -v "MDISK"`; + $sourceWithoutMdisks = xCAT::zvmUtils->trimStr($sourceWithoutMdisks); + + # Get the available mini disks using Image Definition in case this is SSI + # Output is similar to: + # MDISK=VDEV=0100 DEVTYPE=3390 START=0001 COUNT=10016 VOLID=EMC2C4 MODE=MR + $out = `ssh $::SUDOER\@$tgtZhcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $sourceId -k MDISK"`; + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + xCAT::zvmUtils->printLn( $callback, "(Error) Did not get the $sourceId mini disks. Return output was: $out." ); + return; + } + + my @lines = split( '\n', $out ); + my @srcMdisks = (); + my $foundECKD = 0; + my $foundFBA = 0; + + # Loop through each mini disk line, make a hash table and add it to the disk array + for ( $i = 0 ; $i < @lines ; $i++ ) { + # remove the MDISK= from the line + $lines[$i] =~ s/MDISK=//g; + + my %hash = ($lines[$i] =~ m/(\w+)\s*=\s*(\w+)/g); + #foreach (keys%hash) { + # xCAT::zvmUtils->printSyslog("Mdisk key: $_ value: $hash{$_}\n"); + #} + if ($hash{'DEVTYPE'} eq '3390') { + $foundECKD = 1; + } else { + $foundFBA = 1; + } + push @srcMdisks, \%hash; + } + + # Check for missing disk pool + if ($foundFBA && !(defined $cloneInfo{'FBA_POOL'})) { + xCAT::zvmUtils->printLn( $callback, "(Error) FBA disk was found but no FBA_POOL was defined in DOCLONE COPY." ); + xCAT::zvmUtils->printSyslog("(Error) FBA disk was found but no FBA_POOL was defined in DOCLONE COPY.\n" ); + return; + } + # Check for missing disk pool + if ($foundECKD && !(defined $cloneInfo{'ECKD_POOL'})) { + xCAT::zvmUtils->printLn( $callback, "(Error) ECKD disk was found but no ECKD_POOL was defined in DOCLONE COPY." ); + xCAT::zvmUtils->printSyslog("(Error) ECKD disk was found but no ECKD_POOL was defined in DOCLONE COPY.\n" ); + return; + } + + # Update the zvm table status column to indicate the special processing. + foreach (@nodes) { + my %propHash = (); + %propHash = ( 'status' => 'CLONE_ONLY=1;CLONING=1;'); + xCAT::zvmUtils->setNodeProps( 'zvm', $_, \%propHash ); + %propHash = (); + %propHash = ( 'disable' => '1'); + xCAT::zvmUtils->setNodeProps( 'hosts', $_, \%propHash ); + xCAT::zvmUtils->setNodeProps( 'mac', $_, \%propHash ); + + } + + #*** Link source disks + # Hash table of source disk addresses to linked address + my %srcLinkAddr; + my $addr; + my $linkAddr; + + for my $rowdisk ( @srcMdisks ) { + my %rowhash = %$rowdisk; + # Get disk address from the array entry hash + $addr = $rowhash{'VDEV'}; + + # Add 0 in front if address length is less than 4 + while (length($addr) < 4) { + $addr = '0' . $addr; + } + # Save any updates to length of addr + $rowhash{'VDEV'} = $addr; + + # If source disk is not linked + my $try = 5; + while ( $try > 0 ) { + + # New disk address + $linkAddr = $addr + 1000; + + # Check if new disk address is used (source) + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $tgtZhcp, $linkAddr ); + + # If disk address is used (source) + while ( $rc == 0 ) { + + # Generate a new disk address + # Sleep 5 seconds to let existing disk appear + sleep(5); + $linkAddr = $linkAddr + 1; + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $tgtZhcp, $linkAddr ); + } + + $srcLinkAddr{$addr} = $linkAddr; + + # Link source disk to HCP + xCAT::zvmUtils->printLn( $callback, "Linking source disk ($addr) as ($linkAddr)" ); + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$tgtZhcp "$::SUDO /sbin/vmcp link $sourceId $addr $linkAddr RR"`; + + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printSyslog("Retry linking source disk ($addr) as ($linkAddr)\n"); + xCAT::zvmUtils->printSyslog($callback, "Retry linking source disk ($addr) as ($linkAddr)"); + # Do nothing + } else { + last; + } + + $try = $try - 1; + + # Wait before next try + sleep(5); + } # End of while ( $try > 0 ) + + # If source disk is not linked + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printSyslog("Failed to link source disk $addr from userid $sourceId.\n" ); + xCAT::zvmUtils->printLn( $callback, "Failed to link source disk $addr from userid $sourceId." ); + # Exit + return; + } + + # Enable source disk + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $tgtZhcp, "-e", $linkAddr ); + } # End of foreach (@srcMdisks) + + + # Save the source directory without any mdisk statements, use temp file name + my $srcUserEntry = `/bin/mktemp /tmp/$sourceId.txtXXXXXXXX`; + if ( $? ) { + xCAT::zvmUtils->printLn( $callback, "Failed to create temp file using pattern /tmp/$sourceId.txtXXXXXXXX" ); + xCAT::zvmUtils->printSyslog( "Failed to create temp file using pattern /tmp/$sourceId.txtXXXXXXXX" ); + return; + } + chomp ($srcUserEntry); # need to remove line ends + #xCAT::zvmUtils->printSyslog("Created temp file called ($srcUserEntry)"); + + # Create a file to save output + open( DIRENTRY, ">$srcUserEntry" ); + @lines = split( '\n', $sourceWithoutMdisks ); + foreach (@lines) { + + # Trim line + $_ = xCAT::zvmUtils->trimStr($_); + + # Write directory entry into file + print DIRENTRY "$_\n"; + } + close(DIRENTRY); + + # Turn off source node + xCAT::zvmUtils->printSyslog("Calling smcli Image_Deactivate -T $sourceId (On clone for @nodes)"); + $out = `ssh $::SUDOER\@$tgtZhcp "$::SUDO $::DIR/smcli Image_Deactivate -T $sourceId"`; + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "(Error) Failed to communicate with the zhcp system: $tgtZhcp" ); + xCAT::zvmUtils->printLn( $callback, "(Error) Failed to communicate with the zhcp system: $tgtZhcp" ); + return; + } + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ($out =~ m/Return Code: 200/i){ + if ($out =~ m/Reason Code: 12/i) { + $out = "$sourceId already logged off."; + $rc = 0; + } elsif ($out =~ m/Reason Code: 16/i) { + $out = "$sourceId in process of logging off."; + $rc = 0; + } + } + if ( $rc == -1 ) { + xCAT::zvmUtils->printSyslog("smcli Image_Deactivate $sourceId output: $out"); + xCAT::zvmUtils->printLn( $callback, "$out"); + return; + } + xCAT::zvmUtils->printSyslog("$out"); + + #*** Clone source node *** + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + specialClone( $callback, $_, $args, \@srcMdisks, \%srcLinkAddr, \%cloneInfo, $sourceId, $srcUserEntry); + # Exit process + exit(0); + } + + # End of elsif + else { + # Ran out of resources + die "Error: Could not fork\n"; + } + + # Clone 4 nodes at a time + # If you handle more than this, some nodes will not be cloned + # You will get errors because SMAPI cannot handle many nodes + if ( !( @children % 4 ) ) { + + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + + # Clear children + @children = (); + } + } # End of foreach + + # Handle the remaining nodes + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + + # Remove source user entry + $out = `rm $srcUserEntry`; + + + #*** Detatch source disks *** + + for $addr ( keys %srcLinkAddr ) { + + $linkAddr = $srcLinkAddr{$addr}; + + + # Disable and detatch source disk + + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $tgtZhcp, "-d", $linkAddr ); + + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$tgtZhcp "$::SUDO /sbin/vmcp det $linkAddr"`; + + + xCAT::zvmUtils->printLn( $callback, "Detatching source disk ($addr) at ($linkAddr)" ); + + } + + + #*** Done *** + + foreach (@nodes) { + + xCAT::zvmUtils->printLn( $callback, "$_: Done" ); + + } + + return; +} + + +#------------------------------------------------------- + +=head3 specialClone + + Description : Clone a virtual server from a zvm userid + Arguments : Target node + args (Disk pool, Disk password (optional)) + Source disks array of hash + Source disk link addresses + clone info hash from doclone.txt + Source zvm userid + Source userid directory file name + Returns : Nothing + Example : specialClone($callback, $_, $args, \@srcMdisks, \%srcLinkAddr, \%cloneInfoHash, $sourceId, $srcUserEntry); + +=cut + +#------------------------------------------------------- +sub specialClone { + + # Get inputs + my ( + $callback, $tgtNode, $args, $srcMdisksRef, $srcLinkAddrRef, $cloneInfoHash, $sourceId, $srcUserEntry)= @_; + + # Get source disks + my @srcMdisks = @$srcMdisksRef; + my %srcLinkAddr = %$srcLinkAddrRef; + my %cloneInfo = %$cloneInfoHash; + + # Return code for each command + my $rc; + + # Disk pools + my $ECKD_Pool = ''; + my $FBA_Pool = ''; + + my $cmsVDEVs = ''; + if ( defined $cloneInfo{'CMS_VDEVS'} ) { + $cmsVDEVs = $cloneInfo{'CMS_VDEVS'}; + } + + # Get the Dirmaint pool information + if (defined $cloneInfo{'ECKD_POOL'}) { + $ECKD_Pool = $cloneInfo{'ECKD_POOL'}; + } + if (defined $cloneInfo{'FBA_POOL'}) { + $FBA_Pool = $cloneInfo{'FBA_POOL'}; + } + + # Get target node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames ); + + # Get node user ID + my $tgtUserId = $propVals->{'userid'}; + if ( !$tgtUserId ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target user ID" ); + return; + } + # Capitalize user ID + $tgtUserId =~ tr/a-z/A-Z/; + + # Get zHCP + my $hcp = $propVals->{'hcp'}; + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target node HCP" ); + return; + } + + # Get zHCP user ID + my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + + my $out; + my @lines; + my @words; + + # Get disk pool and multi password + # parameters are in "--key value" + my $i; + my %inputs; + my $argsSize = @{$args}; + for ( my $i = 0 ; $i < $argsSize ; $i++ ) { + if ( ($i+1) < $argsSize) { + # add to hash array + $inputs{ $args->[$i] } = $args->[$i+1]; + } + } + + # Get multi password + # It is Ok not have a password + my $tgtPw = "''"; + if ($inputs{"--password"}) { + $tgtPw = $inputs{"--password"}; + } + + # Save user directory entry as /tmp/hostname.txt, e.g. /tmp/gpok3.txt + # The source user entry is retrieved and passed as parameter in specialCloneVM() + my $userEntry = "/tmp/$tgtNode.txt"; + + # Remove existing user entry if any + $out = `rm $userEntry`; + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $userEntry"`; + + # Copy user entry of source node + $out = `cp $srcUserEntry $userEntry`; + if ( $? ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to copy $srcUserEntry to $userEntry" ); + xCAT::zvmUtils->printSyslog( "$tgtNode: (Error) Failed to copy $srcUserEntry to $userEntry" ); + return; + } + xCAT::zvmUtils->printSyslog("Copied temp named source <$srcUserEntry> to <$userEntry>"); + + # Replace source userID with target userID + $out = `sed -i -e "s,$sourceId,$tgtUserId,i" $userEntry`; + + # SCP user entry file over to HCP + my $reasonString = ''; + my $remoteUserEntry = ''; + $rc = xCAT::zvmUtils->sendFile( $::SUDOER, $hcp, $userEntry, $userEntry ); + if ( $rc == 0 ) { + $remoteUserEntry = $userEntry; + } else { + $reasonString = "Unable to send $userEntry to $hcp, SCP rc: $rc"; + } + + #*** Create new virtual server *** + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" ); + if ( $remoteUserEntry ne '' ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Create_DM -T $tgtUserId -f $remoteUserEntry"`; + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog( "$tgtNode: Result from smcli Image_Create_DM -T $tgtUserId, rc: $rc, out: $out" ); + + if ( $rc == 0 ) { + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc != 0 ) { + $rc = -1; + $reasonString = "Image_Create_DM returned $out"; + } + } elsif ( $rc == 255 ) { + $reasonString = "Unable to communicate with $hcp"; + } else { + $reasonString = "Image_Create_DM returned rc: $rc, out: $out"; + } + } + + # Remove user entry + $out = `rm $userEntry`; + if ( $remoteUserEntry ne '' ) { + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO rm $remoteUserEntry"`; + } + + # Exit on bad output + if ( $rc != 0 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry. $reasonString" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify that the node's zHCP and its zVM's SMAPI are both online" ); + return; + } + + # Load VMCP module on HCP and source node + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; + + #*** Add MDisk to target user entry *** + my $addr; + my @tgtDisks; + my $type; + my $mode; + my $disksize; + my $try; + for my $rowdisk ( @srcMdisks ) { + my %rowhash = %$rowdisk; + # Get disk address from the array entry hash + $addr = $rowhash{'VDEV'}; + + push( @tgtDisks, $addr ); + + $type = $rowhash{'DEVTYPE'}; + $disksize = $rowhash{'COUNT'}; + + if ( defined $rowhash{'MODE'} ) { + $mode = $rowhash{'MODE'}; + } else { + $mode = "MR"; + } + + # Add ECKD disk + if ( $type eq '3390' ) { + + $try = 5; + while ( $try > 0 ) { + + # Add ECKD disk + if ( $try > 4 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" ); + } else { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" ); + } + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $ECKD_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`; + xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 3390 -a AUTOG -r $ECKD_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"); + xCAT::zvmUtils->printSyslog("$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); + + # Check output + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + + # Wait before trying again + sleep(5); + + # One less try + $try = $try - 1; + } else { + + # If output is good, exit loop + last; + } + } # End of while ( $try > 0 ) + + # Exit on bad output + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" ); + return; + } + } # End of if ( $type eq '3390' ) + + # Add FBA disk + elsif ( $type eq '9336' ) { + + # Get disk size (blocks) + my $blkSize = '512'; + + $try = 10; + while ( $try > 0 ) { + + # Add FBA disk + if ( $try > 9 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" ); + } else { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" ); + } + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $FBA_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"`; + xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $tgtUserId -v $addr -t 9336 -a AUTOG -r $FBA_Pool -u 1 -z $disksize -m $mode -f 1 -R $tgtPw -W $tgtPw -M $tgtPw"); + xCAT::zvmUtils->printSyslog("$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); + + # Check output + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + + # Wait before trying again + sleep(5); + + # One less try + $try = $try - 1; + } else { + + # If output is good, exit loop + last; + } + } # End of while ( $try > 0 ) + + # Exit on bad output + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not add minidisk ($addr) $out" ); + return; + } + } # End of elsif ( $type eq '9336' ) + } + + # Check if the number of disks in target user entry + # is equal to the number of disks added + my @disks; + $try = 10; + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks added (@tgtDisks). Checking directory for those disks..." ); + while ( $try > 0 ) { + + # Get disks within user entry + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId" | sed '\$d' | grep -a "MDISK"`; + xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId | grep -a MDISK"); + xCAT::zvmUtils->printSyslog("$out"); + @disks = split( '\n', $out ); + + if ( @disks != @tgtDisks ) { + $try = $try - 1; + + # Wait before trying again + sleep(5); + } else { + last; + } + } + + # Exit if all disks are not present + if ( @disks != @tgtDisks ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." ); + xCAT::zvmUtils->printSyslog( $callback, "$tgtNode: (Error) After 50 seconds, all disks not present in target directory." ); + + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks found in $sourceId source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" ); + xCAT::zvmUtils->printSyslog( $callback, "$tgtNode: Disks found in $sourceId + source directory (@tgtDisks). Disks found in $tgtUserId target directory (@disks)" ); + + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Solution) Verify disk pool has free disks and that directory updates are working" ); + return; + } + + #** * Link, format, and copy source disks *** + my $srcAddr; + my $srcHcpLinkAddr; + my $mdiskAddr; + my $tgtAddr; + my $srcDevNode; + my $tgtDevNode; + my $tgtDiskType; + + # source and target disks will be same type and size + for my $rowdisk ( @srcMdisks ) { + my %rowhash = %$rowdisk; + # Get disk address from the array entry hash + $mdiskAddr = $rowhash{'VDEV'}; + $tgtDiskType = $rowhash{'DEVTYPE'}; + + #*** Try to use SMAPI flashcopy first if ECKD *** + # Otherwise link the target disks and if ECKD, try CP Flashcopy. If + # CP flashcopy does not work or not ECKD; use Linux DD + my $ddCopy = 0; + my $cpFlashcopy = 1; + my $smapiFlashCopyDone = 0; + + if ($tgtDiskType eq '3390') { + # Try SMAPI FLASHCOPY + if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($mdiskAddr) to target disk ($mdiskAddr) using FLASHCOPY" ); + xCAT::zvmUtils->printSyslog( "$tgtNode: Doing SMAPI flashcopy source disk ($sourceId $mdiskAddr) to target disk ($tgtUserId $mdiskAddr) using FLASHCOPY" ); + $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $mdiskAddr, $tgtUserId, $mdiskAddr); + + # Check if flashcopy completed successfully, or it completed and is asynchronous + if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) { + chomp($out); + xCAT::zvmUtils->printSyslog( "$tgtNode: SMAPI flashcopy done. output($out)"); + $cpFlashcopy = 0; + $smapiFlashCopyDone = 1; + } else { + # Continue to try a Linux format and DD. Put out information message to log and back to caller + chomp($out); + my $outlen = length($out); + xCAT::zvmUtils->printSyslog("$tgtNode: SMAPI Flashcopy did not work, continuing with Linux DD. SMAPI output($outlen bytes):" ); + xCAT::zvmUtils->printSyslog("$tgtNode: $out" ); + + # Change any (error) or "failed" to info so that OpenStack does not reject this clone + $out =~ s/\(error\)/\(info\)/gi; + $out =~ s/failed/info/gi; + xCAT::zvmUtils->printLn( $callback, "$out" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: SMAPI Flashcopy did not work, continuing with Linux format and DD." ); + } + } + } + + # If SMAPI flashcopy did not work or this is not an ECKD, then link the target disks in write mode + if ( !$smapiFlashCopyDone ) { + $ddCopy = 1; + + #*** Link target disk *** + $try = 10; + while ( $try > 0 ) { + + # New disk address + $srcAddr = $mdiskAddr; + $srcHcpLinkAddr = $srcLinkAddr{$mdiskAddr}; + $tgtAddr = $mdiskAddr + 2000; + + # Check if new disk address is used (target) + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr ); + + # If disk address is used (target) + while ( $rc == 0 ) { + + # Generate a new disk address + # Sleep 5 seconds to let existing disk appear + sleep(5); + $tgtAddr = $tgtAddr + 1; + $rc = xCAT::zvmUtils->isAddressUsed( $::SUDOER, $hcp, $tgtAddr ); + } + + # Link target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($mdiskAddr) as ($tgtAddr) in write mode" ); + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp link $tgtUserId $mdiskAddr $tgtAddr MR $tgtPw"`; + + # If link fails + if ( $out =~ m/not linked/i || $out =~ m/not write-enabled/i ) { + + # Wait before trying again + sleep(5); + + $try = $try - 1; + } else { + last; + } + } # End of while ( $try > 0 ) + + # If target disk is not linked + if ( $out =~ m/not linked/i ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($mdiskAddr) in write mode" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); + + # Exit + return; + } + # Flashcopy not supported, use Linux dd + if ($ddCopy) { + + #*** Use Linux dd to copy *** + xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not working. Using Linux DD" ); + + # Enable target disk + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr ); + + # Determine source device node + $srcDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $srcHcpLinkAddr); + + # Determine target device node + $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr); + + # Format target disk + # Only ECKD disks need to be formated + if ($tgtDiskType eq '3390') { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; + xCAT::zvmUtils->printSyslog("dasdfmt -b 4096 -y -f /dev/$tgtDevNode"); + + # Check for errors + $rc = xCAT::zvmUtils->checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + + # Detatch disks from HCP + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + + # Copy source disk to target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync && $::SUDO echo $?"`; + $out = xCAT::zvmUtils->trimStr($out); + if (int($out) != 0) { + # If $? is not 0 then there was an error during Linux dd + $out = "(Error) Failed to copy /dev/$srcDevNode"; + } + + xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync"); + xCAT::zvmUtils->printSyslog("$out"); + } else { + # Copy source disk to target disk + # Block size = 512 + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync && $::SUDO echo $?"`; + $out = xCAT::zvmUtils->trimStr($out); + if (int($out) != 0) { + # If $? is not 0 then there was an error during Linux dd + $out = "(Error) Failed to copy /dev/$srcDevNode"; + } + + xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=512 oflag=sync"); + xCAT::zvmUtils->printSyslog("$out"); + + # Force Linux to re-read partition table + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Forcing Linux to re-read partition table" ); + $out = + `ssh $::SUDOER\@$hcp "$::SUDO cat<checkOutput( $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr ); + + # Detatch disks from zHCP + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + } # end if ddcopy + } # end if SMAPI flashcopy did not complete + + + # If not Flashcopy Disable and enable target disk + if ( !$smapiFlashCopyDone ) { + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-e", $tgtAddr ); + + # Determine target device node (it might have changed) + $tgtDevNode = xCAT::zvmUtils->getDeviceNode($::SUDOER, $hcp, $tgtAddr); + } + + # Flush disk + $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/sync"`; + + # If not Flashcopy disable and detach disk from zhcp + if ( !$smapiFlashCopyDone ) { + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $tgtAddr ); + + # Detatch disks from HCP + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtAddr"`; + } + + sleep(5); + } # End of foreach (@srcMdisks) + + # Power on target virtual server will be done in later call +} diff --git a/xCAT-server/lib/xcat/plugins/zvmdiagnostics.pm b/xCAT-server/lib/xcat/plugins/zvmdiagnostics.pm new file mode 100644 index 000000000..c62cee9f4 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/zvmdiagnostics.pm @@ -0,0 +1,306 @@ +# IBM(c) 2016 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + xCAT plugin to support z/VM (s390x) diagnostics command + +=cut + +#------------------------------------------------------- +package xCAT_plugin::zvmdiagnostics; + +#use xCAT::Client; +use xCAT::zvmUtils; + +#use xCAT::zvmCPUtils; +#use xCAT::MsgUtils; +use Sys::Hostname; + +#use xCAT::Table; +#use xCAT::Utils; +#use xCAT::TableUtils; +#use xCAT::ServiceNodeUtils; +#use xCAT::NetworkUtils; +#use XML::Simple; +#use File::Basename; +#use File::Copy; +#use File::Path; +#use File::Temp; +use Time::HiRes; +use POSIX; +use Getopt::Long; +use strict; +use warnings; + +#use Cwd; +# Set $DEBUGGING = 1 to get extra message logging +my $DEBUGGING = 0; + +# Common prefix for log messages +my $ROUTINE = "zvmdiagnostics"; +my $COMMAND = "diagnostics"; + +my $NOTIFY_FILENAME = "/var/lib/sspmod/appliance_system_role"; +my $NOTIFY_KEYWORD = "notify"; +my $NOTIFY_KEYWORD_DELIMITER = "="; + +# If the following line ("1;") is not included, you get: +# /opt/xcat/lib/perl/xCAT_plugin/... did not return a true value +# where ... is the name of this file +1; + +#------------------------------------------------------- + +=head3 handled_commands + + Return list of commands handled by this plugin + +=cut + +#------------------------------------------------------- +sub handled_commands { + return { $COMMAND => $ROUTINE, }; +} + +#------------------------------------------------------- + +=head3 preprocess_request + + Check and setup for hierarchy + +=cut + +#------------------------------------------------------- +sub preprocess_request { + my $req = shift; + my $callback = shift; + my $SUBROUTINE = "preprocess_request"; + + # Hash array + my %sn; + + # Scalar variable + my $sn; + + # Array + my @requests; + + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE entry"); + } + + # If already preprocessed, go straight to request + if ( $req->{_xcatpreprocessed}->[0] == 1 ) { + return [$req]; + } + my $nodes = $req->{node}; + my $service = "xcat"; + + # Find service nodes for requested nodes + # Build an individual request for each service node + if ($nodes) { + $sn = xCAT::ServiceNodeUtils->get_ServiceNode( $nodes, $service, "MN" ); + + # Build each request for each service node + foreach my $snkey ( keys %$sn ) { + my $n = $sn->{$snkey}; + print "snkey=$snkey, nodes=@$n\n"; + my $reqcopy = {%$req}; + $reqcopy->{node} = $sn->{$snkey}; + $reqcopy->{'_xcatdest'} = $snkey; + $reqcopy->{_xcatpreprocessed}->[0] = 1; + push @requests, $reqcopy; + } + + return \@requests; + } + else { + + # Input error + my %rsp; + my $rsp; + $rsp->{data}->[0] = + "Input noderange missing. Usage: $ROUTINE \n"; + xCAT::MsgUtils->message( "I", $rsp, $callback, 0 ); + return 1; + } +} + +#------------------------------------------------------- + +=head3 process_request + + Process the command. This is the main call. + +=cut + +#------------------------------------------------------- +sub process_request { + my $SUBROUTINE = "process_request"; + my $request = shift; + my $callback = shift; + my $nodes = $request->{node}; + my $command = $request->{command}->[0]; + my $args = $request->{arg}; + my $envs = $request->{env}; + $::STDIN = $request->{stdin}->[0]; + my %rsp; + my $rsp; + my @nodes = @$nodes; + my $host = hostname(); + + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE entry"); + } + + # Process ID for xfork() + my $pid; + + # Child process IDs + my @children = (); + + #*** Collect or manage diagnostics*** + if ( $command eq $COMMAND ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + if ( xCAT::zvmUtils->isHypervisor($_) ) { + #TODO should this be handled inside the subroutine, ala rmvm? + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE for hypervisor - semantically coherent?"); + } + } + else { + collectDiags( $callback, $_, $args ); + } + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + + return; +} + +#------------------------------------------------------- + +=head3 collectDiags + + Description : Collect diagnostics + Arguments : Node to collect diagnostics about + Returns : Nothing + Example : collectDiags($callback, $node); + +=cut + +#------------------------------------------------------- +sub collectDiags { + my $SUBROUTINE = "collectDiags"; + + # Get inputs + my ( $callback, $node, $args ) = @_; + my $rc; + + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE entry"); + } + + # Get node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid', 'discovered' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # Get zHCP + my $hcp = $propVals->{'hcp'}; + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); + return; + } + + # Get node user ID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); + return; + } + + # Capitalize user ID + $userId =~ tr/a-z/A-Z/; + + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO userid:$::userId"); + } + my $out; + + my $requestId = "NoUpstreamRequestID"; + my $objectId = "NoUpstreamObjectID"; + my $projectName = "NoUpstreamProjectName"; + my $userUuid = "NoUpstreamUserUuid"; + if ($args) { + @ARGV = @$args; + xCAT::zvmUtils->printSyslog( + "$ROUTINE $SUBROUTINE for node:$node on zhcp:$hcp args @$args"); + + # Parse options + GetOptions( + 'requestid=s' => \$requestId # Optional + , 'objectid=s' => \$objectId # Optional + ); + } + + my $xcatnotify = "OPERATOR"; # Default value + my $xcatnotify_found = 0; # Not found yet + my (@array, $varname); + open( FILE, "<$NOTIFY_FILENAME" ); + #TODO If file not found ("should never happen"), log error but continue + while () { + # Find record in file with NOTIFY=something on it, optionally delimited with whitespace + next unless ( /^[\s]*$NOTIFY_KEYWORD[\s]*$NOTIFY_KEYWORD_DELIMITER[\s]*(\S+)[\s]*$/iaa ); + $xcatnotify_found = 1; + $xcatnotify = $1; # First parenthesized expression in regex above, that is: \S+ + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE xCAT will notify $xcatnotify."); + } + last; # Ignore anything past the first matching record. Absent a bug elsewhere, there is only one value to find. + } + close(FILE); + if (not $xcatnotify_found) { + xCAT::zvmUtils->printSyslog( + "$ROUTINE $SUBROUTINE error: failed to parse $NOTIFY_KEYWORD$NOTIFY_KEYWORD_DELIMITER " . + "from $NOTIFY_FILENAME, defaulting to notify $xcatnotify"); + } + #TODO add COZ... message ID + my $msg = "vmcp MSG $xcatnotify deployment failed: node $node userid $userId on zHCP $hcp"; + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE $msg"); + system($msg); + #TODO check system()'s rc + + #TODO Capture diagnostic files + + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE ... rest of implementation stubbed out "); + + if ( $DEBUGGING == 1 ) { + xCAT::zvmUtils->printSyslog("$ROUTINE $SUBROUTINE exit"); + } + return; +} + diff --git a/xCAT-server/lib/xcat/plugins/zvmdiscovery.pm b/xCAT-server/lib/xcat/plugins/zvmdiscovery.pm new file mode 100644 index 000000000..7f327d3fb --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/zvmdiscovery.pm @@ -0,0 +1,2310 @@ +#!/usr/bin/env perl +## IBM(c) 2015 EPL license http://www.eclipse.org/legal/epl-v10.html +# +# This plugin is used to handle the z/VM discovery. +# z/VM discovery will discover the z/VM virtual machines running +# on a specified z/VM host and define them to xCAT DB. +# In addition, it will optionally define the systems to OpenStack. +# + +package xCAT_plugin::zvmdiscovery; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +use strict; +use Data::Dumper; +use Getopt::Long; +use XML::Simple; +$XML::Simple::PREFERRED_PARSER='XML::Parser'; + +use lib "$::XCATROOT/lib/perl"; +use Time::HiRes qw(gettimeofday sleep); +use xCAT::Table; +use xCAT::MsgUtils; +use xCAT::DiscoveryUtils; +use xCAT::Utils; +use xCAT::zvmCPUtils; +use xCAT::zvmUtils; + +my $request_command; +( $::SUDOER, $::SUDO ) = xCAT::zvmUtils->getSudoer(); +my $ZHCP_BIN = '/opt/zhcp/bin'; + +# Hash of host name resolution commands to be issued in a virtual OS. +my @hostnameCmds = ( 'hostname --fqdn', + 'hostname --long', + 'hostname', + ); + +# Location of the various OpenStack plugins for discovery. +my $locOpenStackDiscovery = '/var/lib/sspmod/discovery.py'; # location of OpenStack discovery +my $locOpenStackNodeNameInfo = '/var/lib/sspmod/nodenameinfo.py'; # location of OpenStack node name info + + +#------------------------------------------------------- + +=head3 addOpenStackSystem + + Description : Add a single system to OpenStack. + Arguments : class + callback + z/VM host node + verbose flag: 1 - verbose output, 0 - non-verbose + UUID generated for this node to be used in the discoverydata table + z/VM userid of the discovered system + Reference to the discoverable hash variable + Returns : Node as provisioned in OpenStack or an empty string if it failed. + Example : my $OSnode = addOpenStackSystem( $callback, + $zvmHost, $hcp, $verbose, $activeSystem, $discoverableRef ); + +=cut + +#------------------------------------------------------- +sub addOpenStackSystem { + my ( $callback, $zvmHost, $hcp, $verbose, $activeSystem, $discoverableRef ) = @_; + my %discoverable = %$discoverableRef; + + my $junk; + my $openstackNodeName = ''; + my $out = ''; + my $rc = 0; + + # Argument mapping between xCAT and the OpenStack python code. + # Argument name is as known to xCAT is the key and the value is + # the argument name as it is known to the OpenStack python code. + my %passingArgs = ( + 'cpuCount' => '--cpucount', + 'hostname' => '--hostname', + 'ipAddr' => '--ipaddr', + 'memory' => '--memory', + 'node' => '--guestname', + 'os' => '--os', + 'openstackoperands' => '', + ); + my $args = ""; + + # Build the argument string for the OpenStack call. + foreach my $key ( keys %passingArgs ) { + if ( defined( $discoverable{$activeSystem}{$key} ) ) { + if ( $key ne '' ) { + # Pass the key and the value. + $args = "$args $passingArgs{$key} $discoverable{$activeSystem}{$key}"; + } else { + # When name of parm to pass is '', we just pass the value of the parm. + # The name would only complicates things for the call because it contains multiple subparms. + # We also remove any surrounding quotes or double quotes. + $args = "$args $discoverable{$activeSystem}{$key}"; + } + } + } + $args = "$args --verbose $verbose --zvmhost $zvmHost --uuid $discoverable{$activeSystem}{'uuid'}"; + + # Call the python discovery command + if ( $verbose == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Passing $discoverable{$activeSystem}{'node'} to OpenStack " . + "for userid $activeSystem on z/VM $zvmHost with arguments: $args"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + xCAT::MsgUtils->message( "S", "Calling $locOpenStackDiscovery for $discoverable{$activeSystem}{'node'} on $zvmHost" ); + $out = `python $locOpenStackDiscovery $args`; + xCAT::MsgUtils->message( "S", "Returned from $locOpenStackDiscovery" ); + + if ( $out ) { + chomp( $out ); + + if ( $verbose == 1 ) { + my $rsp; + push @{$rsp->{data}}, $out; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + + my @lines= split( /\n/, $out ); + my (@createdLine) = grep( /Node\screated:/, @lines ); + if ( @createdLine ) { + # Node created. Do not need to show any output from OpenStack. + ($junk, $openstackNodeName) = split( /:\s/, $createdLine[0], 2 ); + } else { + # Node was not created. + my @failedLine = grep( /Node\screation\sfailed/, @lines ); + if ( @failedLine ) { + my $rsp; + push @{$rsp->{data}}, "Unable to create the node " . + "in OpenStack. xCAT node creation is being undone " . + "for $discoverable{$activeSystem}{'node'}."; + if (( @lines > 1 ) && ( $verbose == 0 )) { + # Had more then the "Node creation failed" line AND we have not + # shown them (vebose == 0) so show all of the lines now. + push @{$rsp->{data}}, "Response from the OpenStack plugin:"; + push @{$rsp->{data}}, @lines; + } + xCAT::MsgUtils->message("E", $rsp, $callback); + } else { + my @alreadyCreatedLine = grep( /already\screated/, @lines ); + if ( @alreadyCreatedLine ) { + # The node is already known to OpenStack as an instance. We will get + # the node name from the response in case OpenStack wants the xCAT node to + # be a different name. + ($openstackNodeName) = $alreadyCreatedLine[0] =~ m/Node (.*) already created/; + } else { + my $rsp; + push @{$rsp->{data}}, "Response from the Openstack plugin " . + "did not contain 'Node created:' or 'Node creation failed' " . + "or 'already created' string. It is assumed to have failed."; + if (( @lines > 1 ) && ( $verbose == 0 )) { + # Had more then the "Node creation failed" line AND we have not + # shown them (vebose == 0) so show all of the lines now. + push @{$rsp->{data}}, "Response from the OpenStack plugin:"; + push @{$rsp->{data}}, @lines; + } + xCAT::MsgUtils->message("E", $rsp, $callback); + } + } + } + } else { + my $rsp; + push @{$rsp->{data}}, "No response was received from the Openstack plugin."; + xCAT::MsgUtils->message("E", $rsp, $callback); + } + + # If the xCAT node was renamed by OpenStack then update xCAT to use the new node name + # so that the node names match. + if (( $openstackNodeName ne '' ) and ( $discoverable{$activeSystem}{'node'} ne $openstackNodeName )) { + if ( $verbose == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Renaming the xCAT node $discoverable{$activeSystem}{'node'} " . + "to $openstackNodeName as requested by the OpenStack plugin."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + my $renameRC = changeNode( $callback, + $discoverable{$activeSystem}{'node'}, + 'r', + $openstackNodeName ); + if ( $renameRC == 0 ) { + $discoverable{$activeSystem}{'node'} = $openstackNodeName; + } else { + $openstackNodeName = ''; # Want to undo the created xCAT node. + my $rsp; + push @{$rsp->{data}}, "Unable to rename the xCAT node $discoverable{$activeSystem}{'node'} " . + "to $openstackNodeName, as requested by the OpenStack plugin, " . + "rc: $renameRC"; + xCAT::MsgUtils->message("E", $rsp, $callback); + } + } + +FINISH_addOpenStackSystem: + return $openstackNodeName; +} + + +#------------------------------------------------------- + +=head3 addPrevDisc + + Description : Add previously discovered xCAT nodes + to OpenStack. (Not done. Waiting for input from Emily) + Arguments : class + callback + z/VM host node + ZHCP node + Time when discovery was started. Used to determine if + a stop was requested and then we were restarted. + nodediscoverstart argument hash + Returns : None. + Example : my $out = addPrevDisc( $callback, $zvmHost, + $hcp, $initStartTime, \%args ); + +=cut + +#------------------------------------------------------- +sub addPrevDisc { + my ( $callback, $zvmHost, $hcp, $initStartTime, $argsRef ) = @_; + + my %discoverable; + my @nodes; + my $rc = 0; + my $verbose = $argsRef->{'verbose'}; + + # Get the list of discovered nodes from the 'zvm' table. + my $zvmTab = xCAT::Table->new("zvm"); + if ( !$zvmTab ) { + my $rsp; + push @{$rsp->{data}}, "Could not open table: zvm."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_addPrevDisc; + } + my @results = $zvmTab->getAttribs( { 'discovered'=>'1', 'hcp'=>$hcp }, ('userid', 'node') ); + foreach my $id ( @results ) { + if ( $id->{'userid'} and $id->{'node'} ) { + $discoverable{$id->{'userid'}}{'node'} = $id->{'node'}; + $discoverable{$id->{'userid'}}{'hcp'} = $hcp; + } else { + if ( $verbose == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Node $id->{'node'} is missing 'userid' or 'node' property in the zvm table."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + } + } + $zvmTab->close; + + if ( $verbose == 1 ) { + my @discoverableKeys = keys %discoverable; + my $discoverableCount = scalar( @discoverableKeys ); + my $rsp; + push @{$rsp->{data}}, "$discoverableCount nodes have been previously discovered for $zvmHost."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + + # Get the ip and hostname for each discovered node from the hosts table. + my $hostsTab = xCAT::Table->new('hosts'); + if ( !$hostsTab ) { + my $rsp; + push @{$rsp->{data}}, "Could not open table: hosts."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_addPrevDisc; + } + my %nodes; + my @attribs = ('node', 'ip', 'hostnames'); + my @hosts = $hostsTab->getAllAttribs( @attribs ); + foreach my $nodeRef ( @hosts ) { + my $node; + if ( $nodeRef->{'node'} ) { + $node = $nodeRef->{'node'}; + } else { + next; + } + if ( $nodeRef->{'ip'} ) { + $nodes{$node}{'ip'} = $nodeRef->{'ip'}; + } else { + next; + } + if ( $nodeRef->{'hostnames'} ) { + $nodes{$node}{'hostnames'} = $nodeRef->{'hostnames'}; + } else { + # Don't have enough info, remove it from the nodes hash. + delete( @nodes{$node} ); + } + } + $hostsTab->close; + + foreach my $activeSystem ( keys %discoverable ) { + my $node = $discoverable{$activeSystem}{'node'}; + if ( $nodes{$node}{'ip'} ) { + $discoverable{$activeSystem}{'ipAddr'} = $nodes{$node}{'ip'}; + $discoverable{$activeSystem}{'hostname'} = $nodes{$node}{'hostnames'}; + } else { + # Don't have enough info, remove it from the discoverable hash. + delete( $discoverable{$activeSystem} ); + } + } + + # Get the OS info, memory, CPU count and UUID. + foreach my $activeSystem ( keys %discoverable ) { + # Verify that the virtual machine is currently running. + my $out = `ssh -q $::SUDOER\@$hcp $::SUDO $ZHCP_BIN/smcli "Image_Status_Query -T '$activeSystem'"`; + $rc = $? >> 8; + if ( $rc == 255 ) { + my $rsp; + push @{$rsp->{data}}, "z/VM discovery is unable to communicate with the zhcp server: $hcp"; + xCAT::MsgUtils->message("E", $rsp, $callback); + delete( $discoverable{$activeSystem} ); + next; + } elsif ( $rc == 1 ) { + if ( $verbose == 1 ) { + my $rsp; + push @{$rsp->{data}}, "ignoring: $activeSystem - virtual machine is not logged on"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + delete( $discoverable{$activeSystem} ); + next; + } elsif ( $rc != 0 ) { + my $rsp; + push @{$rsp->{data}}, "An unexpected return code $rc was received from " . + "the zhcp server $hcp for an smcli Image_Status_Query " . + "request. SMAPI servers may be unavailable. " . + "Received response: $out"; + + xCAT::MsgUtils->message("E", $rsp, $callback); + delete( $discoverable{$activeSystem} ); + next; + } + + # Get the OS version from the OS. + my $os = xCAT::zvmUtils->getOsVersion( $::SUDOER, $discoverable{$activeSystem}{'node'}, $callback ); + if ( $os ) { + $discoverable{$activeSystem}{'os'} = $os; + } else { + if ( $verbose == 1 ) { + my $rsp; + push @{$rsp->{data}}, "ignoring: $activeSystem - Unable to obtain OS version information from node $discoverable{$activeSystem}{'node'}"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + delete( $discoverable{$activeSystem} ); + next; + } + + # Install vmcp in the target system just in case no one has done that yet. + xCAT::zvmCPUtils->loadVmcp( $::SUDOER, $discoverable{$activeSystem}{'node'} ); + + # Get the current memory from CP. + my $memory = xCAT::zvmCPUtils->getMemory( $::SUDOER, $discoverable{$activeSystem}{'node'} ); + if ( $memory ) { + $discoverable{$activeSystem}{'memory'} = $memory; + } else { + my $rsp; + push @{$rsp->{data}}, "Could not obtain the current virtual machine memory size from node: $discoverable{$activeSystem}{'node'}"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + delete( $discoverable{$activeSystem} ); + next; + } + + # Get the current CPU count from CP. + my $cpuString = xCAT::zvmCPUtils->getCpu( $::SUDOER, $discoverable{$activeSystem}{'node'} ); + if ( $cpuString ) { + my @cpuLines = split( /\n/, $cpuString ); + $discoverable{$activeSystem}{'cpuCount'} = scalar( @cpuLines ); + } else { + my $rsp; + push @{$rsp->{data}}, "Could not obtain the current virtual machine CPU count from node: $discoverable{$activeSystem}{'node'}"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + delete( $discoverable{$activeSystem} ); + next; + } + + $discoverable{$activeSystem}{'uuid'} = xCAT::Utils::genUUID(); + } + + my $rsp; + if ( %discoverable ) { + if ( $verbose == 1 ) { + push @{$rsp->{data}}, "The following xCAT nodes are eligible for OpenStack only discovery:"; + } + } else { + push @{$rsp->{data}}, "No xCAT nodes are eligible for OpenStack only discovery."; + } + foreach my $activeSystem ( keys %discoverable ) { + $discoverable{$activeSystem}{'openstackoperands'} = $argsRef->{'openstackoperands'}; + if ( $verbose == 1 ) { + push @{$rsp->{data}}, " $discoverable{$activeSystem}{'node'}"; + } + } + if ( $rsp ) { + xCAT::MsgUtils->message("I", $rsp, $callback); + } + + foreach my $activeSystem ( keys %discoverable ) { + # Exit if we have been asked to stop discovery for this host. + my $startTime = getRunningDiscTimestamp( $callback, $zvmHost ); + if ( $startTime != $initStartTime ) { + # Start time for this run is different from start time in the site table. + # User must have stopped and restarted discovery for this host. + # End now to let other discovery handle the work. + push @{$rsp->{data}}, "Stopping due to a detected stop request."; + xCAT::MsgUtils->message("I", $rsp, $callback); + goto FINISH_addPrevDisc; + } + + # Define the system to OpenStack + my $openstackNodeName = addOpenStackSystem( $callback, $zvmHost, $hcp, $verbose, $activeSystem, \%discoverable ); + if ( $openstackNodeName ) { + updateDiscoverydata( $callback, 'add', $verbose, $zvmHost, $activeSystem, \%discoverable ); + } + } + +FINISH_addPrevDisc: + return; +} + + +#------------------------------------------------------- + +=head3 changeNode + + Description : Change an xCAT node. The change can + be a rename or a deletion of the node from + the xCAT tables. + Arguments : Callback handle + node name + Type of change: + 'r' - rename the node name and redo makehosts + 'd' - delete the node from xCAT tables and /etc/hosts + 'do' - delete the node from xCAT tables only, don't + undo makehosts + Returns : Return code + 0 - It worked + 1 - It failed + Example : rc = changeNode( $callback, $nodeName, 'r', $newName ); + +=cut + +#------------------------------------------------------- +sub changeNode { + my $callback = shift; + my $nodeName = shift; + my $changeType = shift; + my $newNode = shift; + + my $rc = 0; # Assume everything works + my $retStrRef; + + # Remove the node from the /etc/hosts file + if (( $changeType eq 'r' ) or ( $changeType eq 'd' )) { + my $out = `/opt/xcat/sbin/makehosts -d $nodeName 2>&1`; + if ( $out ne '' ) { + my $rsp; + push @{$rsp->{data}}, "'makehosts -d' failed for $nodeName. Node is still defined in xCAT MN's /etc/hosts file. 'makehosts' response: $out"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + $rc = 1; + } + } + + # Remove the node. + if (( $changeType eq 'd' ) or ( $changeType eq 'do' )) { + my $retRef = xCAT::Utils->runxcmd({command => ['rmdef'], stdin=>['NO_NODE_RANGE'], arg => [ "-t", "node", "-o", $nodeName ]}, $request_command, 0, 2); + if ( $::RUNCMD_RC != 0 ) { + my $rsp; + $retStrRef = parse_runxcmd_ret($retRef); + push @{$rsp->{data}}, "Unable to remove node $nodeName from the xCAT tables. rmdef response: $retStrRef->[1]"; + push @{$rsp->{data}}, "-t". "node". "-o". $nodeName; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + $rc = 1; + } + } + + # Rename the node. + if ( $changeType eq 'r' ) { + my $retRef = xCAT::Utils->runxcmd({command=>['chdef'], stdin=>['NO_NODE_RANGE'], arg=>[ '-t', 'node', '-o', $nodeName, '-n', $newNode, '--nocache' ]}, $request_command, 0, 2); + if ( $::RUNCMD_RC != 0 ) { + my $rsp; + $retStrRef = parse_runxcmd_ret($retRef); + push @{$rsp->{data}}, '-t'. 'node'. '-o'. $nodeName. '-n'. $newNode; + push @{$rsp->{data}}, "Unable to rename node $nodeName " . + "to $newNode in the xCAT tables. " . + "The node definition for $nodeName will be removed. " . + "chdef response: $retStrRef->[1]"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + $rc = 1; + changeNode( $callback, $nodeName, 'do' ); + } else { + my $out = `/opt/xcat/sbin/makehosts $newNode 2>&1`; + if ( $out ne '' ) { + my $rsp; + push @{$rsp->{data}}, "'makehosts' failed for $newNode. " . + "The node definition for $newNode will be removed. " . + "'makehosts' response: $out"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + $rc = 1; + changeNode( $callback, $newNode, 'do' ); + } + } + } + return $rc; +} + + +#------------------------------------------------------- + +=head3 createNode + + Description : Find an available xCAT nodename and create a node + for the virtual machine and indicate it is a + discovered node. Also, do a MAKEHOSTS so xCAT MN + can access it by the node name. + Arguments : Callback handle + DNS name associated with the OS in the machine + Desired name format template, if specified + Current numeric adjustment value, used to get + to the next available nodename. + xCAT STANZA information in a string that will + be used to create the node. + Reference for the xCAT NODEs hash so that we + can check for already known node names. + Returns : Node name if created or empty string if an error occurred. + Current numeric value + Example : ( $node, $numeric ) = createNode($callback, $discoverable{$activeSystem}{'hostname'}, + $args{'nodenameformat'}, $numeric, $retstr_gen, \%xcatNodes); + +=cut + +#------------------------------------------------------- +sub createNode { + my $callback = shift; + my $dnsName = shift; + my $nameFormat = shift; + my $numeric = shift; + my $stanzaInfo = shift; + my $xcatNodesRef = shift; + + my $attempts = 0; # Number of attempts to find a usable node name + my $nodeName; # Node name found or being checked + my $prefix = ''; # Node name prefix, used with templates + my $rsp; # Message work variable + my $shortName; # Short form of the DNS name + my $suffix = ''; # Node name suffix, initially none + my $templateType = 0; # Type of template; 0: none, 1: old style xCAT, 2: OpenStack (sprintf) + + # Determine the component parts of the new node name based on whether + # a template is specified. + if ( $nameFormat ) { + if ( $nameFormat =~ /#NNN/ ) { + # Old Style xCAT template e.g. node#NNN -> node001 + $templateType = 1; + + # Deconstruct the name format template into its component parts. + my @fmtParts = split ( '#NNN', $nameFormat ); + $prefix = $fmtParts[0]; + $suffix = $fmtParts[1]; + } elsif ( $nameFormat =~ /%/ ) { + # OpenStack style template, uses sprintf for formatting + $templateType = 2; + } else { + $nodeName = ''; + push @{$rsp->{data}}, "Unrecognized node name template. Nodes will not be discovered."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_createNode; + } + + if ( $numeric eq '' ) { + $numeric = 0; + } + } else { + # Set up to use the DNS short name as the root of the node name. + my @nameParts = split( /\./, $dnsName ); + $shortName = lc( $nameParts[0] ); + $numeric = ""; + } + + # Loop to find an available node name and reserve it by creating the + # node with minimal information. + while ( 1 ) { + # Create the next nodename + if ( $templateType == 1 ) { + $numeric = $numeric + 1; + my $numSize = length($numeric); + if ( $numSize < 3 ) { + $numSize = 3; + } + my $format = "%0".$numSize."d"; + $numeric = sprintf($format, $numeric); + $nodeName = $prefix.$numeric.$suffix; + } elsif ( $templateType == 2 ) { + $numeric = $numeric + 1; + $nodeName = sprintf($nameFormat, $numeric); + } else { + if ( $numeric ne '' ){ + $numeric += 1; + } + $nodeName = $shortName.$numeric; + } + + # Verify that the nodename is available. + if ( !$xcatNodesRef->{$nodeName} ) { + # Found an available node name + # Attempt to create the node with that name. + $attempts += 1; + my $retstr_gen = "$nodeName:\n$stanzaInfo"; + my $retRef = xCAT::Utils->runxcmd({command=>["mkdef"], stdin=>[$retstr_gen], arg=>['-z']}, $request_command, 0, 2); + + if ( $::RUNCMD_RC == 0 ) { + # Node created. All done. + $xcatNodesRef->{$nodeName} = 1; + + # Update the zvm table for the node to indicate that it is a discovered system. + my %zvmProps = ( "discovered" => "1" ); + my $zvmTab = xCAT::Table->new('zvm'); + if ( !$zvmTab ) { + # Node was created but there is not much we can do about it since + # not being able to update the zvm table indicates a severe error. + push @{$rsp->{data}}, "Could not open table: zvm. $nodeName was created but discovered=1 could not be set in the zvm table."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } else { + $zvmTab->setAttribs( {node => $nodeName}, \%zvmProps ); + $zvmTab->commit(); + } + last; + } else { + if ( $attempts > 10 ) { + # Quit trying to create a node after 10 attempts + my $retStrRef = parse_runxcmd_ret($retRef); + my $rsp; + push @{$rsp->{data}}, "Unable to create a node, $nodeName. Last attempt response: $retStrRef->[1]"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + $nodeName = ''; + last; + } else { + # Assume xCAT daemon is unavailable. Give it 15 seconds to come back + # before next attempt. + sleep(15); + } + } + } + + # Did not find an available node name on this pass. + # Wipe out the nodename in case we exit the loop. Also, ensure a numeric + # is used next time around. + $nodeName = ''; + if ( $numeric eq '' ){ + $numeric = 0; + } + } + + if ( $nodeName ne '' ) { + # Issue MAKEHOSTS so that xCAT MN can drive commands to the host using the node name. + # Note: If OpenStack changes the node name then we will have to redo this later. + my $out = `/opt/xcat/sbin/makehosts $nodeName 2>&1`; + if ( $out ne '' ) { + my $rsp; + push @{$rsp->{data}}, "'makehosts' failed for $nodeName. Node creation is being undone. 'makehosts' response: $out"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + changeNode( $callback, $nodeName, 'do' ); + $nodeName = ''; + } + } + +FINISH_createNode: + return ( $nodeName, $numeric ); +} + + + +#------------------------------------------------------- + +=head3 findme + + Description : Handle the request form node to map and + define the request to a node. + Arguments : request handle + callback + sub request + Returns : 0 - No error + non-zero - Error detected. + Example : findme( $request, $callback, $request_command ); + +=cut + +#------------------------------------------------------- +sub findme { + my $request = shift; + my $callback = shift; + my $subreq = shift; + + my $SEQdiscover = getSiteVal("__SEQDiscover"); + my $PCMdiscover = getSiteVal("__PCMDiscover"); + my $ZVMdiscover = getSiteVal("__ZVMDiscover"); + unless ( $ZVMdiscover ) { + if ( $SEQdiscover or $PCMdiscover ) { + # profile or sequential discovery is running, then just return + # to make the other discovery handle it + return; + } + + # update the discoverydata table to have an undefined node + $request->{discoverymethod}->[0] = 'undef'; + xCAT::DiscoveryUtils->update_discovery_data($request); + return; + } +} + + +#------------------------------------------------------- + +=head3 getOpenStackTemplate + + Description : Get the current template used by OpenStack + for this host and the largest numeric + value currently in use. + Arguments : callback + z/VM host node + Returns : Template to be used for node naming + Highest numeric value in use + Example : my $out = getOpenStackTemplate( $callback, $zvmHost ); + +=cut + +#------------------------------------------------------- +sub getOpenStackTemplate { + my ( $callback, $zvmHost ) = @_; + + my $out = ''; + my %response = ( + 'template' => '', + 'number' => '' ); + + xCAT::MsgUtils->message( "S", "Calling $locOpenStackNodeNameInfo for $zvmHost" ); + $out = `python $locOpenStackNodeNameInfo`; + xCAT::MsgUtils->message( "S", "Returned from $locOpenStackNodeNameInfo with $out" ); + + if (( $out ne '' ) && ( $out !~ /^Error detected/ )) { + my @parts = split( /\s/, $out ); + my $key; + foreach my $part ( @parts ) { + if ( $part eq 'Template:' ) { + $key = 'template'; + } elsif ( $part eq 'Number:' ) { + $key = 'number'; + } else { + $part =~ s/^\s+|\s+$//g; # Trim leading & ending blanks + $response{$key} = $part; + } + } + } else { + my $rsp; + my @lines = split( /\n/, $out ); + shift( @lines ); + if ( @lines ) { + push @{$rsp->{data}}, @lines; + } else { + push @{$rsp->{data}}, "An error was detected in the nova instance name template." + } + xCAT::MsgUtils->message( "E", $rsp, $callback ); + $response{'template'} = ''; + } + +FINISH_getOpenStackTemplate: + return ( $response{'template'}, $response{'number'} ); +} + + +#------------------------------------------------------- + +=head3 getRunningDiscTimestamp + + Description : Get the timestamp on a specific running + discovery from the site table variable. + Arguments : Callback handle + z/VM host node + Returns : Timestamp for the run that was specified or + empty string if a run was not found. + Example : $ts = getRunningDiscTimestamp( 'zvm1' ); + +=cut + +#------------------------------------------------------- +sub getRunningDiscTimestamp { + my $callback = shift; + my $zvmHost = shift; + + my $rsp; # Response buffer for output messages + my $ts = ''; # Timestamp value + + my $val = getSiteVal("__ZVMDiscover"); + if ( $val ) { + if ( $val =~ /zvmhost=$zvmHost,/ ) { + my @discoveries = split( /zvmhost=/, $val ); + foreach my $discovery ( @discoveries ) { + if ( $discovery =~ "^$zvmHost" ) { + my @parts = split( ',', $discovery ); + if ( $parts[1] ) { + $ts = $parts[1]; + } + } + } + } + } + + return $ts; +} + + +#------------------------------------------------------- + +=head3 getSiteVal + + Description : Bypasses a problem with get_site_attribute + returning an old cashe value instead of + the current value. + Arguments : Name of attribute + Returns : Value from the site table + Example : ( $val ) = getSiteVal( '__ZVMDiscover' ); + +=cut + +#------------------------------------------------------- +sub getSiteVal { + my $attribute = shift; + + my $response = ''; # Response buffer for return + + my $siteTab = xCAT::Table->new( "site", -autocommit=>1 ); + my @results = $siteTab->getAttribs( { 'key'=>$attribute }, ('value') ); + foreach my $id ( @results ) { + $response .= $id->{'value'}; + } + $siteTab->close; + + return $response; +} + + +#------------------------------------------------------- + +=head3 handled_commands + + Description : Returns the supported commands and their handler. + Arguments : None. + Returns : Command handler hash for this module. + +=cut + +#------------------------------------------------------- +sub handled_commands { + return { + findme => 'zvmdiscovery', + #nodediscoverdef => 'zvmdiscovery', # Handled by sequential discovery + nodediscoverls => 'zvmdiscovery', + nodediscoverstart => 'zvmdiscovery', + nodediscoverstatus => 'zvmdiscovery', + nodediscoverstop => 'zvmdiscovery', + } +} + + +#------------------------------------------------------- + +=head3 nodediscoverls + + Description : List discovered z/VM systems. + Arguments : callback + arguments for nodediscoverls + Returns : None. + Example : nodediscoverls( $callback, $args ); + +=cut + +#------------------------------------------------------- +sub nodediscoverls { + my $callback = shift; + my $args = shift; + + my %origArgs; # Original arguments from the command invocation + my $maxNodeSize = 4; # Maximum size of a nodename in the current list + my $maxHostSize = 6; # Maximum size of a host nodename in the current list + my $maxUseridSize = 6; # Maximum size of a userid in the current list + + # Determine which options were specified and their values. + if ( $args ) { + @ARGV = @$args; + } + + GetOptions( + 't=s' => \$origArgs{'type'}, + 'u=s' => \$origArgs{'uuid'}, + 'l' => \$origArgs{'long'}, + 'h|help' => \$origArgs{'help'}, + 'v|version' => \$origArgs{'ver'}, + 'z|zvmhost=s' => \$origArgs{'zvmHost'} ); + + # If '-u' was specified then let seqdiscovery handle it as common output. + if ( $origArgs{'uuid'} ) { + return; + } + + # If z/VM discovery is running and the type was not already specified then + # we treat this as a z/VM type of listing. + my @ZVMDiscover = xCAT::TableUtils->get_site_attribute( "__ZVMDiscover" ); + if ( $ZVMDiscover[0] ) { + $origArgs{'type'} = 'zvm'; + } + + # Weed out invocations that this routine does not handle but instead + # leaves to sequential discovery to handle. + if ( $origArgs{'help'} || + $origArgs{'ver'} || + ( $origArgs{'type'} && $origArgs{'type'} ne 'zvm' )) + { + # Sequential discovery will have handled these options. + return; + } elsif ( $origArgs{'zvmHost'} || ( $origArgs{'type'} && $origArgs{'type'} eq 'zvm' )) { + # z/VM related operands are handled here. + } else { + # Sequential discovery will have handled other combinations of options. + return; + } + + # If a zvmHost was specified then process it into an array + my %zvmHosts; + my @inputZvmHosts; + if ( $origArgs{'zvmHost'} ) { + if ( index( $origArgs{'zvmHost'}, ',' ) != -1 ) { + # Must have specified multiple host node names + my @hosts = split( /,/, $origArgs{'zvmHost'} ); + foreach my $host ( @hosts ) { + if ( !$host ) { + # Tolerate zvmhost value beginning with a comma. + # It is wrong but not worth an error message. + next; + } + push( @inputZvmHosts, $host ); + } + } else { + push( @inputZvmHosts, $origArgs{'zvmHost'} ); + } + %zvmHosts = map { $_ => 1 } @inputZvmHosts; + } + + # Get the list xCAT nodes and their userids. + my $zvmTab = xCAT::Table->new("zvm"); + if ( !$zvmTab ) { + my $rsp; + push @{$rsp->{data}}, "Could not open table: zvm."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_nodediscoverls; + } + my %xcatNodes; + my @attribs = ('node', 'userid'); + my @nodes = $zvmTab->getAllAttribs( @attribs ); + foreach my $nodeRef ( @nodes ) { + if ( !$nodeRef->{'node'} || !$nodeRef->{'userid'} ) { + next; + } + $xcatNodes{$nodeRef->{'node'}} = $nodeRef->{'userid'}; + } + + # Get the list of discovered systems for the specified z/VMs. + my %discoveredNodes; + my $disTab = xCAT::Table->new('discoverydata'); + if ( !$disTab ) { + my $rsp; + push @{$rsp->{data}}, "Could not open table: discoverydata."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_nodediscoverls; + } + + my @disData = $disTab->getAllAttribsWhere( "method='zvm'", 'node', 'uuid', 'otherdata', + 'method', 'discoverytime', 'arch', 'cpucount', + 'memory'); + foreach my $disRef ( @disData ) { + if ( !$disRef->{'uuid'} || !$disRef->{'node'} || !$disRef->{'otherdata'} ) { + next; + } + + my $host = $disRef->{'otherdata'}; + $host =~ s/^zvmhost.//g; + + if ( !%zvmHosts | $zvmHosts{$host} ) { + my $node = $disRef->{'node'}; + $discoveredNodes{$node}{'uuid'} = $disRef->{'uuid'}; + $discoveredNodes{$node}{'host'} = $host; + if ( $xcatNodes{$node} ) { + $discoveredNodes{$node}{'userid'} = $xcatNodes{$node}; + } + $discoveredNodes{$node}{'method'} = $disRef->{'method'}; + $discoveredNodes{$node}{'discoverytime'} = $disRef->{'discoverytime'}; + $discoveredNodes{$node}{'arch'} = $disRef->{'arch'}; + $discoveredNodes{$node}{'cpucount'} = $disRef->{'cpucount'}; + $discoveredNodes{$node}{'memory'} = $disRef->{'memory'}; + + # Update size of node and host node names if size has increased. + # This is used later when producing the output. + my $length = length( $host ); + if ( $length > $maxHostSize ) { + $maxHostSize = $length; + } + $length = length( $node ); + if ( $length > $maxNodeSize ) { + $maxNodeSize = $length; + } + $length = length( $discoveredNodes{$node}{'userid'} ); + if ( $length > $maxUseridSize ) { + $maxUseridSize = $length; + } + } + } + + # Produce the output + my $rsp; + my $discoverednum = keys %discoveredNodes; + push @{$rsp->{data}}, "Discovered $discoverednum nodes."; + if ( %discoveredNodes ) { + # Create the format string for the column output + if ( $maxHostSize > 20 ) { + $maxHostSize = 20; # Set a maximum, individual lines may throw it off but we need to be reasonable. + } + $maxHostSize += 2; + if ( $maxNodeSize > 20 ) { + $maxNodeSize = 20; # Set a maximum, individual lines may throw it off but we need to be reasonable. + } + $maxNodeSize += 2; + if ( $maxUseridSize > 20 ) { + $maxUseridSize = 20; # Set a maximum, individual lines may throw it off but we need to be reasonable. + } + $maxUseridSize += 2; + + my $fmtString; + if ( !$origArgs{'long'} ) { + $fmtString = ' %-' . $maxNodeSize . 's%-' . $maxUseridSize . 's%-' . $maxHostSize . 's'; + push @{$rsp->{data}}, sprintf( $fmtString, 'NODE', 'USERID', 'ZVM HOST' ); + } + + # Create the output + foreach my $node (keys %discoveredNodes) { + if ( $origArgs{'long'} ) { + push @{$rsp->{data}}, "Object uuid: $discoveredNodes{$node}{'uuid'}"; + push @{$rsp->{data}}, " node=$node"; + push @{$rsp->{data}}, " userid=$discoveredNodes{$node}{'userid'}"; + push @{$rsp->{data}}, " host=$discoveredNodes{$node}{'host'}"; + push @{$rsp->{data}}, " method=$discoveredNodes{$node}{'method'}"; + push @{$rsp->{data}}, " discoverytime=$discoveredNodes{$node}{'discoverytime'}"; + push @{$rsp->{data}}, " arch=$discoveredNodes{$node}{'arch'}"; + push @{$rsp->{data}}, " cpucount=$discoveredNodes{$node}{'cpucount'}"; + push @{$rsp->{data}}, " memory=$discoveredNodes{$node}{'memory'}"; + } else { + push @{$rsp->{data}}, sprintf( $fmtString, + $node, + $discoveredNodes{$node}{'userid'}, + $discoveredNodes{$node}{'host'} ); + } + } + } + + xCAT::MsgUtils->message("I", $rsp, $callback); + +FINISH_nodediscoverls: + return; +} + + +#------------------------------------------------------- + +=head3 nodediscoverstart + + Description : Initiate the z/VM discovery process. + Arguments : callback + arguments for nodediscoverstart + Returns : None. + Example : nodediscoverstart( $callback, $args ); + +=cut + +#------------------------------------------------------- +sub nodediscoverstart { + my $callback = shift; + my $args = shift; + + my $lock = 0; # Lock word, 0: not obtained, 1: lock failed, other: lock handle + my @newZvmHosts; # Array of z/VM host nodes on this command invocation + my %origArgs; # Original arguments from the command invocation + my %parms; # Parameters to pass along to start routine + my $rsp; # Response buffer for output messages + my %runningZvmHosts; # List of z/VM host nodes from the __ZVMDiscover property in the site table + my $zvmHost; # Short scope work parameter used to contain a z/VM host node name + + # Valid attributes for nodediscoverstart + my %validArgs = ( + 'defineto' => 1, + 'groups' => 1, + 'ipfilter' => 1, + 'nodenameformat' => 1, + 'useridfilter' => 1, + 'zvmhost' => 1, + 'openstackoperands' => 1, + ); + + if ( $args ) { + @ARGV = @$args; + } + + $origArgs{'verbose'} = 0; # Assume we are not doing verbose + my ($help, $ver); + if (!GetOptions( + 'h|help' => \$help, + 'V|verbose' => \$origArgs{'verbose'}, + 'v|version' => \$ver)) { + # Sequential discovery will have produced an error message. + # We don't need another + return; + } + + if ( $help | $ver ) { + # Sequential discovery will have handled these options. + return; + } + + foreach ( @ARGV ) { + my ($name, $value) = split ('=', $_); + $origArgs{$name} = $value; + } + + if ( !defined( $origArgs{'zvmhost'} ) ) { + # If zvmhost parm is not present then this is not a z/VM discovery. + goto FINISH_NODEDISCOVERSTART; + } + + push @{$rsp->{data}}, "Processing: nodediscoverstart @$args"; + xCAT::MsgUtils->message("I", $rsp, $callback, 1); + + # Check the running of sequential or profile-based discovery + my $SEQdiscover = getSiteVal("__SEQDiscover"); + my $PCMdiscover = getSiteVal("__PCMDiscover"); + if ( $PCMdiscover or $SEQdiscover ) { + push @{$rsp->{data}}, "z/VM Discovery cannot be run together with Sequential or Profile-based discovery"; + xCAT::MsgUtils->message("E", $rsp, $callback, 1); + goto FINISH_NODEDISCOVERSTART; + } + + # Verify that specified filters are valid. + if ( defined( $origArgs{'ipfilter'} )) { + eval {''=~/$origArgs{'ipfilter'}/}; + if ( $@ ) { + push @{$rsp->{data}}, "The ipfilter is not a valid regular expression: $@"; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + } + if ( defined( $origArgs{'useridfilter'} )) { + eval {''=~/$origArgs{'useridfilter'}/}; + if ( $@ ) { + push @{$rsp->{data}}, "The useridfilter is not a valid regular expression: $@"; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + } + + # Set the default defineto option if none was specified and verify the value. + if ( ! defined( $origArgs{'defineto'} ) ) { + $origArgs{'defineto'} = 'both'; + } else { + if (( $origArgs{'defineto'} ne 'both' ) and ( $origArgs{'defineto'} ne 'xcatonly' ) and + ( $origArgs{'defineto'} ne 'openstackonly' )) { + push @{$rsp->{data}}, "Specified 'defineto' value is not 'both', 'xcatonly' or " . + "'openstackonly': $origArgs{'defineto'}"; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + } + + # Verify that the OpenStack plugin is available. + if ( $origArgs{'defineto'} ne 'xcatonly' ) { + if ( !-e $locOpenStackDiscovery ) { + my $rsp; + push @{$rsp->{data}}, "$locOpenStackDiscovery does not exist. " . + "Discovery cannot occur."; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_NODEDISCOVERSTART; + } + if ( $origArgs{'defineto'} ne 'both' ) { + if ( !-e $locOpenStackNodeNameInfo ) { + my $rsp; + push @{$rsp->{data}}, "$locOpenStackNodeNameInfo does not exist. " . + "Discovery cannot occur."; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_NODEDISCOVERSTART; + } + } + } + + # Use sudo or not + # This looks in the passwd table for a key = sudoer + ($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer(); + + # Obtain any ongoing z/VM discovery parms and get the list of z/VM hosts. + $lock = xCAT::Utils->acquire_lock( "nodemgmt", 0 ); + if ( $lock == 1 ) { + push @{$rsp->{data}}, "Unable to acquire the 'nodemgmt' lock to protect __ZVMDiscover property in the xCAT site table from changes."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + + my $ZVMdiscover = getSiteVal( "__ZVMDiscover" ); + if ( $ZVMdiscover ) { + if ( $ZVMdiscover =~ '^zvmhost=' ) { + my @discoveries = split(/zvmhost=/, $ZVMdiscover); + foreach my $activeParms ( @discoveries ) { + if ( !$activeParms ) { + next; + } + if ( index( $activeParms, ',' ) != -1 ) { + $zvmHost = substr( $activeParms, 0, index( $activeParms, ',' )); + } else { + $zvmHost = $activeParms; + } + $runningZvmHosts{$zvmHost} = 1; + } + } else { + # Not an expected format, Drop it when we push out the new saved parameters. + push @{$rsp->{data}}, "Wrong format"; + xCAT::MsgUtils->message("E", $rsp, $callback, 1); + $ZVMdiscover = ''; + } + } + + my %param; # The valid parameters in a hash + my $textParam; # The valid parameters in 'name=value,name=value...' format + my @newZvmhosts; # Array of z/VM hosts to be added + my %zhcpServers; # Hash of ZHCP servers for each new host to be discovered. + + # Validate the parameters + foreach my $name ( keys %origArgs ) { + if ( $name eq 'verbose' ) { + # Verbose is a hyphenated option and is not listed in the + # validArgs hash. So we won't do a validation check on it. + } elsif ( !defined( $validArgs{$name} ) ) { + push @{$rsp->{data}}, "Argument \"$name\" is not valid."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + if ( !defined( $origArgs{$name} ) ) { + push @{$rsp->{data}}, "The parameter \"$name\" needs a value."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + + if (( $name eq 'nodenameformat' ) and ( index( $origArgs{$name}, '#NNN' ) == -1)) { + push @{$rsp->{data}}, "The parameter \"$name\" is missing the '#NNN' string."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + + # Invoke the OpenStack plugin to validate OpenStack related variables. + if (( $name eq 'openstackoperands' ) and + (( $origArgs{'defineto'} eq 'both' ) || ( $origArgs{'defineto'} eq 'openstackonly' )) && + defined( $origArgs{$name} )) { + $origArgs{$name} =~ s/^\'+|\'+$//g; + $origArgs{$name} =~ s/^\"+|\"+$//g; + xCAT::MsgUtils->message( "S", "Calling $locOpenStackDiscovery to validate parms: $origArgs{$name}" ); + my $out = `python $locOpenStackDiscovery --validate $origArgs{$name}`; + chomp( $out ); + xCAT::MsgUtils->message( "S", "Returned from $locOpenStackDiscovery with $out" ); + if ( $out ne '0' ) { + if ( $out eq '' ) { + $out = "No response was received from $locOpenStackDiscovery for OpenStack operand validation. z/VM discovery will not be started."; + } + push @{$rsp->{data}}, "$out"; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + } + + # Keep the valid parameters + if ( $name eq 'zvmhost' ) { + if ( index( $origArgs{$name}, ',' ) != -1 ) { + # Must have specified multiple host node names + my @hosts = split( /,/, $origArgs{$name} ); + foreach $zvmHost ( @hosts ) { + if ( !$zvmHost ) { + # Tolerate zvmhost value beginning with a comma. + # It is wrong but not worth an error message. + next; + } + push( @newZvmhosts, $zvmHost ); + } + } else { + push( @newZvmhosts, $origArgs{$name} ); + } + foreach $zvmHost ( @newZvmhosts ) { + if ( exists( $runningZvmHosts{$zvmHost} )) { + push @{$rsp->{data}}, "The node \"$zvmHost\" specified with the zvmhost parameter is already running z/VM discovery."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_NODEDISCOVERSTART; + } + } + } else { + # Non-zvmhost parms get added to textParam string + $param{$name} = $origArgs{$name}; + $param{$name} =~ s/^\s+|\s+$//g; + $textParam .= $name . '=' . $param{$name} . ' '; + } + } + + $textParam =~ s/,\z//; + if ( $textParam ) { + $textParam = $textParam; + } + + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); + my $currTime = sprintf("%02d-%02d-%04d %02d:%02d:%02d", + $mon + 1, $mday, $year + 1900, $hour, $min, $sec); + + # Save the discovery parameters to the site. __ZVMDiscover which will be used by nodediscoverls/status/stop and findme. + foreach $zvmHost ( @newZvmhosts ) { + # Verify that the zvmHost node exists + my @reqProps = ( 'node' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'nodetype', $zvmHost, @reqProps ); + if ( !$propVals->{'node'} ) { + push @{$rsp->{data}}, "The z/VM host node is not a defined node. " . + "The node $zvmHost is missing from the nodetype table."; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + # Verify that the node is a z/VM host and locate the ZHCP server for this host. + my @propNames = ( 'hcp', 'nodetype' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $zvmHost, @propNames ); + + if ( $propVals->{'nodetype'} ne 'zvm') { + push @{$rsp->{data}}, "The specified z/VM host $zvmHost does not appear to be a z/VM host. " . + "The 'nodetype' property in the zvm table should be set to 'zvm'."; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + my $hcp = $propVals->{'hcp'}; + if ( $hcp ) { + # Remember the ZHCP info so we can pass it along + $zhcpServers{$zvmHost} = $hcp; + } else { + push @{$rsp->{data}}, "The 'hcp' property is not defined in the zvm table for $zvmHost node."; + xCAT::MsgUtils->message("E", $rsp, $callback); + return; + } + + # Add the parms for the new z/VM host to the list of running servers and their parms + if ( $ZVMdiscover eq '' ) { + $ZVMdiscover = "zvmhost=$zvmHost,$currTime,$textParam"; + } else { + $ZVMdiscover .= ",zvmhost=$zvmHost,$currTime,$textParam"; + } + } + + my $siteTab = xCAT::Table->new( "site", -autocommit=>1 ); + $siteTab->setAttribs( {"key" => "__ZVMDiscover"}, {"value" => "$ZVMdiscover"} ); + $siteTab->commit(); + $siteTab->close(); + + xCAT::Utils->release_lock( $lock, 1 ); + $lock = 0; + + # Start each new discovery. + foreach $zvmHost ( @newZvmhosts ) { + startDiscovery( $callback, $zvmHost, $zhcpServers{$zvmHost}, $currTime, \%param ); + } + + # Common exit point to ensure that any lock has been freed. +FINISH_NODEDISCOVERSTART: + # Release the lock if we obtained it. + if (( $lock != 1 ) and ( $lock != 0 )) { + xCAT::Utils->release_lock( $lock, 1 ); + } +} + + +#------------------------------------------------------- + +=head3 nodediscoverstatus + + Description : Display the z/VM discovery status. + Arguments : callback + arguments for nodediscoverstatus + Returns : None. + Example : nodediscoverstatus( $callback, $args ); + +=cut + +#------------------------------------------------------- +sub nodediscoverstatus { + my $callback = shift; + my $args = shift; + + my @inputZvmHosts; # Input list of z/VM host nodes + my $rsp; # Response buffer for output messages + my @runningZvmHosts; # z/VM host nodes being queried + my $zvmHost; # Small scope variable to temporarily hold a host node value + + # Valid attributes for z/VM discovery + my ( $help, $ver ); + if ( !GetOptions( + 'h|help' => \$help, + 'v|version' => \$ver, + 'z|zvmhost=s' => \$zvmHost )) { + # Return if unrecognized parms found so other discoveries can respond. + goto FINISH_NODEDISCOVERSTATUS; + } + + # Return if the user asked for help or version because sequential discovery will handle that. + if ( $help or $ver ) { + return; + } + + # Return if sequential or profile discovery is running or all are stopped. + # Sequential discovery will handle the response in that case. + my $SEQdiscover = getSiteVal("__SEQDiscover"); + my $PCMdiscover = getSiteVal("__PCMDiscover"); + my $ZVMdiscover = getSiteVal("__ZVMDiscover"); + if (( $PCMdiscover or $SEQdiscover ) or ( !$SEQdiscover and !$PCMdiscover and !$ZVMdiscover )) { + return; + } + + # Put any specified zvmhosts into a hash that we can query. + if ( $zvmHost ) { + if ( index( $zvmHost, ',' ) != -1 ) { + # Must have specified multiple host node names + my @hosts = split( /,/, $zvmHost ); + foreach $zvmHost ( @hosts ) { + if ( !$zvmHost ) { + # Tolerate zvmhost value beginning with a comma. + # It is wrong but not worth an error message. + next; + } + push( @inputZvmHosts, $zvmHost ); + } + } else { + push( @inputZvmHosts, $zvmHost ); + } + } + my %inputZvmHostsHash = map { $_ => 1 } @inputZvmHosts; + + # Get the list of z/VM hosts. + my $newZVMdiscover; + if ( $ZVMdiscover ) { + if ( $ZVMdiscover =~ '^zvmhost=' ) { + my @discoveries = split( /zvmhost=/, $ZVMdiscover ); + foreach my $activeParms ( @discoveries ) { + if ( !$activeParms ) { + next; + } + if ( index( $activeParms, ',' ) != -1 ) { + $zvmHost = substr( $activeParms, 0, index( $activeParms, ',' )); + } else { + $zvmHost = $activeParms; + } + push( @runningZvmHosts, $zvmHost ); + if ( exists( $inputZvmHostsHash{$zvmHost} )) { + $inputZvmHostsHash{$zvmHost} = 2; + } + } + } else { + # Not an expected format, Drop it when we push out the new saved parameters. + push @{$rsp->{data}}, "__ZVMDiscover property in the xCAT site table is corrupted. It has been cleared so that all z/VM discovery stops. You may restart z/VM discovery."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + + # Remove the site.__ZVMDiscover property + # We don't need a lock because we are whipping out the value and not trying to keep it around. + my $siteTab = xCAT::Table->new( "site", -autocommit=>1 ); + $siteTab->delEntries({key => '__ZVMDiscover'}); + $siteTab->commit(); + $siteTab->close(); + undef $siteTab; + goto FINISH_NODEDISCOVERSTATUS; + } + } + + if ( !@inputZvmHosts ) { + # Not a specific status request so let's remind them that sequential + # and Profile discovery are stopped. + push @{$rsp->{data}}, "Sequential discovery is stopped."; + push @{$rsp->{data}}, "Profile discovery is stopped."; + xCAT::MsgUtils->message( "I", $rsp, $callback, 1 ); + } + + # Inform the user about any node that is specified as input but is not running discovery. + if ( %inputZvmHostsHash ) { + # --zvmhost was specified so indicate a response for each host. + foreach $zvmHost ( keys %inputZvmHostsHash ) { + if ( $inputZvmHostsHash{$zvmHost} == 1 ) { + push @{$rsp->{data}}, "z/VM Discovery is stopped for: $zvmHost."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } else { + push @{$rsp->{data}}, "z/VM Discovery is started for: $zvmHost."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } + } else { + # --zvmhost was not specified so give a single line response. + if ( @runningZvmHosts ) { + my $runningList; + foreach $zvmHost ( @runningZvmHosts ) { + if ( $runningList ) { + $runningList .= ', ' . $zvmHost; + } else { + $runningList = $zvmHost; + } + } + push @{$rsp->{data}}, "z/VM Discovery is started for: $runningList"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } else { + # No on-going z/VM discovery. + push @{$rsp->{data}}, "z/VM Discovery is stopped."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } + + # Common exit point to ensure that any lock has been freed. + # Currently, we do not use locks in this routine. +FINISH_NODEDISCOVERSTATUS: + return; +} + + +#------------------------------------------------------- + +=head3 nodediscoverstop + + Description : Stop the z/VM discovery process. + Arguments : callback + arguments for nodediscoverstop + $auto option (not used by z/VM) + Returns : None. + Example : nodediscoverstop( $callback, $args, $auto ); + +=cut + +#------------------------------------------------------- +sub nodediscoverstop { + my $callback = shift; + my $args = shift; + my $auto = shift; + + my @inputZvmHosts; # Input list of z/VM host nodes to stop + my $rsp; # Response buffer for output messages + my @stoppingZvmHosts; # z/VM host nodes that can be stopped because they are running + my $zvmHost; # Small scope variable to temporarily hold a host node value + + # Check for a running of z/VM discovery + my $ZVMdiscover = getSiteVal("__ZVMDiscover"); + if ( !$ZVMdiscover ) { + # Return so one of the other discoveries can handle the response. + goto FINISH_NODEDISCOVERSTOP; + } + + # Handle parameters + if ( $args ) { + @ARGV = @$args; + } + + my ( $help, $ver ); + if ( !GetOptions( + 'h|help' => \$help, + 'v|version' => \$ver, + 'z|zvmhost=s' => \$zvmHost )) {} + + # Return if the user asked for help or version because sequential will handle that. + if ( $help or $ver ) { + goto FINISH_NODEDISCOVERSTOP; + } + + if ( $zvmHost ) { + if ( index( $zvmHost, ',' ) != -1 ) { + # Must have specified multiple host node names + my @hosts = split( /,/, $zvmHost ); + foreach $zvmHost ( @hosts ) { + if ( !$zvmHost ) { + # Tolerate zvmhost value beginning with a comma. + # It is wrong but not worth an error message. + next; + } + push( @inputZvmHosts, $zvmHost ); + } + } else { + push( @inputZvmHosts, $zvmHost ); + } + } else { + # If zvmhost parm is not present then this is not a z/VM discovery. + push @{$rsp->{data}}, "nodediscoverstop did not specify a --zvmhost property."; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_NODEDISCOVERSTOP; + } + my %inputZvmHostsHash = map { $_ => 1 } @inputZvmHosts; + + # Obtain any on-going z/VM discovery parms and get the list of z/VM hosts. + my $ZVMdiscover = getSiteVal("__ZVMDiscover"); + my $newZVMdiscover; + if ( $ZVMdiscover ) { + if ( $ZVMdiscover =~ '^zvmhost=' ) { + my @discoveries = split( /zvmhost=/, $ZVMdiscover ); + foreach my $activeParms ( @discoveries ) { + if ( !$activeParms ) { + next; + } + if ( index( $activeParms, ',' ) != -1 ) { + $zvmHost = substr( $activeParms, 0, index( $activeParms, ',' )); + } else { + $zvmHost = $activeParms; + } + + if ( exists( $inputZvmHostsHash{$zvmHost} )) { + $inputZvmHostsHash{$zvmHost} = 2; + push( @stoppingZvmHosts, $zvmHost ); + } + } + } else { + # Not an expected format, Drop it when we push out the new saved parameters. + push @{$rsp->{data}}, "__ZVMDiscover property in the xCAT site table is corrupted. It has been cleared so that all z/VM discovery stops. You may restart z/VM discovery."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + + # Remove the site.__ZVMDiscover property + my $siteTab = xCAT::Table->new( "site", -autocommit=>1 ); + $siteTab->delEntries({key => '__ZVMDiscover'}); + $siteTab->commit(); + $siteTab->close(); + undef $siteTab; + goto FINISH_NODEDISCOVERSTOP; + } + } + + # Inform the user about any node that is specified as input but is not running discovery. + foreach $zvmHost ( keys %inputZvmHostsHash ) { + if ( $inputZvmHostsHash{$zvmHost} == 1 ) { + push @{$rsp->{data}}, "z/VM discovery is not running for node: $zvmHost."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + + # Stop the host discovery immediately, if possible. + foreach $zvmHost ( @stoppingZvmHosts ) { + stopDiscovery( $callback, $zvmHost, \@ARGV ); + } + + # Common exit point. +FINISH_NODEDISCOVERSTOP: + return; +} + + +#----------------------------------------------------- +=head3 parse_runxcmd_ret + + Description : Get return of runxcmd and convert it into strings. + Arguments : The return reference of runxcmd + Returns : [$outstr, $errstr], A reference of list, placing + standard output and standard error message. + Example : my $retStrRef = parse_runxcmd_ret($retRef); + +=cut + +#----------------------------------------------------- +sub parse_runxcmd_ret { + my $retRef = shift; + + my $msglistref; + my $outstr = ""; + my $errstr = ""; + if ($retRef){ + if($retRef->{data}){ + $msglistref = $retRef->{data}; + $outstr = Dumper(@$msglistref); + xCAT::MsgUtils->message( 'S', "Command standard output: $outstr" ); + } + if($retRef->{error}){ + $msglistref = $retRef->{error}; + $errstr = Dumper(@$msglistref); + xCAT::MsgUtils->message( 'S', "Command error output: $errstr" ); + } + } + return [$outstr, $errstr]; +} + + +#------------------------------------------------------- + +=head3 process_request + + Description : Process a request and drive the function handler. + Arguments : Request handle + Callback handle + Command that is requested + Returns : None + Example : process_request( $request, $callback, $request_command ); + +=cut + +#------------------------------------------------------- +sub process_request { + my $request = shift; + my $callback = shift; + $request_command = shift; + + my $command = $request->{command}->[0]; + my $args = $request->{arg}; + + if ($command eq "findme"){ + findme( $request, $callback, $request_command ); + } elsif ($command eq "nodediscoverls") { + nodediscoverls( $callback, $args ); + } elsif ($command eq "nodediscoverstart") { + nodediscoverstart( $callback, $args ); + } elsif ($command eq "nodediscoverstop") { + nodediscoverstop( $callback, $args ); + } elsif ($command eq "nodediscoverstatus") { + nodediscoverstatus( $callback, $args ); + } +} + + +#------------------------------------------------------- + +=head3 removeHostInfo + + Description : Remove z/VM host info from the __ZVMDiscover + property in the site table. + Arguments : Callback handle + z/VM host node name + Returns : None. + Example : $rc = removeHostInfo( $callback, $zvmHost ); + +=cut + +#------------------------------------------------------- +sub removeHostInfo { + my $callback = shift; + my $zvmHost = shift; + + my $lock = 0; # Lock word, 0: not obtained, 1: lock failed, other: lock handle + my $rsp; # Response buffer for output messages + + # Obtain any on-going z/VM discovery parms and get the list of z/VM hosts. + $lock = xCAT::Utils->acquire_lock( "nodemgmt", 0 ); + if ( $lock == 1 ) { + push @{$rsp->{data}}, "Unable to acquire the 'nodemgmt' lock to protect __ZVMDiscover property in the xCAT site table."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + goto FINISH_removeHostInfo; + } + + my $origZVMdiscover = getSiteVal("__ZVMDiscover"); + my $newZVMdiscover; + if ( $origZVMdiscover ) { + if ( $origZVMdiscover =~ '^zvmhost=' ) { + my $currHost; + my @discoveries = split( /zvmhost=/, $origZVMdiscover ); + foreach my $activeParms ( @discoveries ) { + if ( !$activeParms ) { + next; + } + if ( index( $activeParms, ',' ) != -1 ) { + $currHost = substr( $activeParms, 0, index( $activeParms, ',' )); + } else { + $currHost = $activeParms; + } + + if ( $zvmHost ne $currHost ) { + # Not stopping this host so keep it in the new __ZVMdiscover property. + $activeParms =~ s/\,+$//; + my $hostDiscParms = "zvmhost=$activeParms"; + if ( $newZVMdiscover ) { + $newZVMdiscover = "$newZVMdiscover,$hostDiscParms"; + } else { + $newZVMdiscover = $hostDiscParms; + } + } + } + } else { + # Not an expected format, Drop it when we push out the new saved parameters. + push @{$rsp->{data}}, "__ZVMDiscover property in the xCAT site table is corrupted. It has been cleared so that all z/VM discovery stops. You may restart z/VM discovery."; + xCAT::MsgUtils->message( "E", $rsp, $callback, 1 ); + + # Remove the site table's '__ZVMDiscover property. + my $siteTab = xCAT::Table->new( "site", -autocommit=>1 ); + $siteTab->delEntries({key => '__ZVMDiscover'}); + $siteTab->commit(); + $siteTab->close(); + undef $siteTab; + goto FINISH_removeHostInfo; + } + } + + # Update the site table to have the remaining discovery host information. + my $siteTab = xCAT::Table->new( "site", -autocommit=>1 ); + if ( !$newZVMdiscover ) { + $siteTab->delEntries({key => '__ZVMDiscover'}); + } else { + $siteTab->setAttribs({"key" => "__ZVMDiscover"}, {"value" => "$newZVMdiscover"}); + } + $siteTab->commit(); + $siteTab->close(); + undef $siteTab; + + # Common exit point so we can make certain to release any lock that is held. +FINISH_removeHostInfo: + # Release the lock if we obtained it. + if (( $lock != 1 ) and ( $lock != 0 )) { + xCAT::Utils->release_lock( $lock, 1 ); + } +} + + +#------------------------------------------------------- + +=head3 startDiscovery + + Description : Start z/VM discovery for a particular z/VM host. + Arguments : Callback handle + z/VM host node name + ZHCP + Start time of the discovery + Hash of arguments specified on the nodediscoverstart + command and their values + Returns : None + Example : startDiscovery( $callback, $zvmHost, $hcp, $startTime, \%args ); + +=cut + +#------------------------------------------------------- +sub startDiscovery{ + my $callback = shift; + my $zvmHost = shift; + my $hcp = shift; + my $initStartTime = shift; + my $argsRef = shift; + my %args = %$argsRef; + + my $numeric = ""; # Numeric portion of last generated node name + my $out; # Output work buffer + my $rc; # Return code + my $rsp; # Response buffer for output messages + my $startOpenStack = 0; # Tell OpenStack provisioner to begin, 0: no, 1: yes + + push @{$rsp->{data}}, "z/VM discovery started for $zvmHost"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + + # Clean the entries in the discoverydata table for discovery method 'zvm' + # and the specific zvmHost that we are going to begin to discover. + my $disTab = xCAT::Table->new("discoverydata"); + if ( !$disTab ) { + my $rsp; + push @{$rsp->{data}}, "Could not open table: discoverydata."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_startDiscovery; + } + + my %keyhash; + $keyhash{'method'} = 'zvm'; + $keyhash{'otherdata'} = "zvmhost." . $zvmHost; + $disTab->delEntries( \%keyhash ); + $disTab->commit(); + + # Handle 'openstackonly' discovery or get the template for 'both' discovery. + if ( $args{'defineto'} eq 'openstackonly' ) { + # Verify that OpenStack has a valid template to use when it is called + # to handle the node. + my ( $osTemplate, $osNumeric ) = getOpenStackTemplate( $callback, $zvmHost ); + if ( $osTemplate eq '' ) { + # An error was detected in the template and message produced leave now. + goto FINISH_startDiscovery; + } + # Discover the xCAT nodes available to OpenStack + $out = addPrevDisc( $callback, $zvmHost, $hcp, $initStartTime, \%args ); + goto FINISH_startDiscovery; + } elsif ( $args{'defineto'} eq 'both') { + # Obtain the template and highest numeric value from OpenStack + my ( $osTemplate, $osNumeric ) = getOpenStackTemplate( $callback, $zvmHost ); + if ( $osTemplate ne '' ) { + $args{'nodenameformat'} = $osTemplate; + $numeric = $osNumeric; + } else { + # An error was detected in the template and message produced leave now. + goto FINISH_startDiscovery; + } + + $startOpenStack = 1; + } + + # Get the current list of node names. + my @nodeNames; + my $nodelistTab = xCAT::Table->new('nodelist'); + if ( !$nodelistTab ) { + push @{$rsp->{data}}, "Could not open table: nodelist."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_startDiscovery; + } + + my @attribs = ('node'); + my @nodes = $nodelistTab->getAllAttribs( @attribs ); + foreach my $node ( @nodes ) { + push @nodeNames,$node->{'node'}; + } + @nodes = sort @nodeNames; + my %xcatNodes = map { $_ => 1 } @nodes; + + # Obtain the list of logged on users. + $out = `ssh -q $::SUDOER\@$hcp $::SUDO $ZHCP_BIN/smcli "Image_Status_Query '-T *'"`; + $rc = $? >> 8; + if ( $rc == 255 ) { + push @{$rsp->{data}}, "z/VM discovery is unable to communicate with the zhcp system: $hcp"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_startDiscovery; + } elsif ( $rc != 0 ) { + my $rsp; + push @{$rsp->{data}}, "An unexpected return code $rc was received from " . + "the zhcp server $hcp for an smcli Image_Status_Query " . + "request. SMAPI servers may be unavailable. " . + "Received response: $out"; + + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_startDiscovery; + } + + # Build the hash of running systems. + my @runningSystems = split( "\n", lc( $out ) ); + + # Create a hash of discoverable systems by starting with the + # list of running systems and removing any systems in the + # list of non-discoverable (known to be z/VM servers or + # non-Linux systems). + my @nonDiscoverable = ( + 'auditor', 'autolog1', 'autolog2', 'avsvm', + 'bldcms', 'bldnuc', 'bldracf', 'bldseg', + 'cbdiodsp', 'cmsbatch', + 'datamove', 'datamov2', 'datamov3', 'datamov4', 'diskacnt', + 'dirmaint', 'dirmsat', 'dirmsat2', 'dirmsat3', 'dirmsat4', + 'dtcens1', 'dtcens2', 'dtcsmapi', 'dtcvsw1', 'dtcvsw2', + 'erep', + 'ftpserve', 'gcs', 'gskadmin', + 'ibmuser', 'imap', 'imapauth', + 'ldapsrv', 'lohcost', + 'maint', 'maint630', 'migmaint', 'monwrite', 'mproute', + 'operator', 'operatns', 'opersymp', 'osadmin1', 'osadmin2', + 'osadmin3', 'osamaint', 'osasf', 'ovfdev62', + 'perfsvm', 'persmapi', 'pmaint', 'portmap', + 'racfsmf', 'racfvm', 'racmaint', 'rexecd', + 'rscs', 'rscsauth', 'rscsdns', 'rxagent1', + 'smtp', 'snmpd', 'snmpsuba', 'ssl', 'ssldcssm', + 'sysadmin', 'sysmon', + 'tcpip', 'tcpmaint', 'tsafvm', + 'uftd', + 'vmnfs', 'vmrmadmn', 'vmrmsvm', + 'vmservp', 'vmservr', 'vmservu', 'vmservs', 'vsmevsrv', + 'vsmguard', 'vsmproxy', 'vsmreqim', 'vsmreqin', 'vsmreqiu', + 'vsmreqi6', 'vsmwork1', 'vsmwork2', 'vsmwork3', + 'xcat', 'xcatserv', 'xchange', + 'zhcp', 'zvmlxapp', 'zvmmaplx', + '4osasf40', '5684042j', '6vmdir30', '6vmhcd20', '6vmlen20', + '6vmptk30', '6vmrac30', '6vmrsc30', '6vmtcp30', + ); + my %discoverable; + @discoverable {@runningSystems} = ( ); + delete @discoverable{@nonDiscoverable}; + + # Apply any user specified userid filter to the list to weed it further. + if ( $args{'useridfilter'} ) { + if ( $args{'verbose'} ) { + push @{$rsp->{data}}, "Applying useridfilter: '" . $args{'useridfilter'} . "'"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + foreach my $activeSystem ( keys %discoverable ) { + if ( $activeSystem !~ m/$args{'useridfilter'}/i ) { + delete( $discoverable{$activeSystem} ); + if ( $args{'verbose'} ) { + push @{$rsp->{data}}, "ignoring: $activeSystem - filtered by user"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } else { + if ( $args{'verbose'} ) { + push @{$rsp->{data}}, "keeping: $activeSystem"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } + } + } + + # Determine the long and short zhcp DNS name. + my ( $longName, $shortName ); + if ( $hcp =~ /\./ ) { + $longName = lc( $hcp ); + my @parts = split( /\./, $longName ); + if ( $parts[0] ne '' ) { + $shortName = $parts[0]; + } + } else { + $shortName = lc( $hcp ); + } + if (( !defined $longName ) && ( -e '/etc/hosts' )) { + # Search /etc/hosts for the short name in a non-commented out portion of the lines and + # look for the long name (contains periods). The short and long form can be in any order + # after the IP address. + $out = `cat /etc/hosts | sed 's/#\.*\$//g' | sed 's/\$/ /g' | grep -i " $shortName "`; + my @lines = split( /\n/, $out ); + my @parts = split( / /, $lines[0] ); + my $numParts = @parts; + for( my $i = 1; $i < $numParts; $i++ ) { + if ( $parts[$i] =~ /\./ ) { + $longName = lc( $parts[$i] ); + last; + } + } + } + + # Get the list of systems that are known to xCAT already for this host. + my %knownToXCAT; + my @knownUserids; + my $zvmTab = xCAT::Table->new("zvm"); + my @attribs = ('hcp', 'userid'); + @nodes = $zvmTab->getAllAttribs( @attribs ); + foreach my $nodeRef ( @nodes ) { + my $nodeHCP; + if ( $nodeRef->{'hcp'} && $nodeRef->{'userid'} ) { + $nodeHCP = lc( $nodeRef->{'hcp'} ); + if ((( defined $longName) && ( $longName eq $nodeHCP )) || + (( defined $shortName) && ( $shortName eq $nodeHCP ))) { + push @knownUserids, lc( $nodeRef->{'userid'} ); + } + } + } + my %knownToXCAT = map { $_ => 1 } @knownUserids; + + # Weed out any systems that are already defined as xCAT nodes. + foreach my $activeSystem ( keys %discoverable ) { + if ( $knownToXCAT{$activeSystem} ) { + delete( $discoverable{$activeSystem} ); + if ( $args{'verbose'} ) { + push @{$rsp->{data}}, "ignoring: $activeSystem - already defined to xCAT"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } + } + + my $numSystems = length( %discoverable ); + xCAT::MsgUtils->message( "S", "Discovery for $zvmHost found $numSystems virtual machines." ); + + # Perform a set of potentially long running functions. We do this one + # server at a time so that we can stop if we are told to do so. + # Loop through the list performing the following: + # - See if discovery has been stopped early. + # - Attempt to access the system to identify which server can be discovered. + # - Contact the system to obtain system information. + # - Create a xCAT node and update xCAT tables. + # - Drive the OpenStack definition of the node, if requested. + my ($ipAddr,$ipVersion, $hostname); + foreach my $activeSystem ( keys %discoverable ) { + + # Exit if we have been asked to stop discovery for this host. + my $startTime = getRunningDiscTimestamp( $callback, $zvmHost ); + if ( $startTime != $initStartTime ) { + # Start time for this run is different from start time in the site table. + # User must have stopped and restarted discovery for this host. + # End now to let other discovery handle the work. + push @{$rsp->{data}}, "Stopping due to a detected stop request."; + xCAT::MsgUtils->message("I", $rsp, $callback); + goto FINISH_startDiscovery; + } + + # Further refine the list by finding only systems which have a NICs with known IP addresses + # that will allow us to SSH into them. + $rc = xCAT::zvmUtils->findAccessIP( $callback, $activeSystem, $hcp, \%discoverable, \%args, $::SUDOER ); + if ( $rc != 0 ) { + delete( $discoverable{$activeSystem} ); + if ( $args{'verbose'} ) { + push @{$rsp->{data}}, "ignoring: $activeSystem - could not access the virtual server."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + next; + } + + # Obtain the memory and CPU count from the active system information. + $out = `ssh -q $::SUDOER\@$hcp $::SUDO $ZHCP_BIN/smcli "Image_Active_Configuration_Query -T '$activeSystem'"`; + $rc = $? >> 8; + if ($rc == 255) { + delete( $discoverable{$activeSystem} ); + push @{$rsp->{data}}, "z/VM discovery is unable to communicate with the zhcp system: $hcp"; + xCAT::MsgUtils->message("E", $rsp, $callback); + next; + } elsif ( $rc != 0 ) { + my $rsp; + push @{$rsp->{data}}, "An unexpected return code $rc was received from " . + "the zhcp server $hcp for an smcli Image_Active_Configuration_Query " . + "request. SMAPI servers may be unavailable. " . + "Received response: $out"; + xCAT::MsgUtils->message("E", $rsp, $callback); + next; + } + + my $memOut = `echo "$out" | egrep -i 'Memory:'`; + chomp $memOut; + my @parts = split( /Memory: /, $memOut ); + @parts = split( / /, $parts[1] ); + $discoverable{$activeSystem}{'memory'} = $parts[0].$parts[1]; + my $cpuOut = `echo "$out" | egrep -i 'CPU count:'`; + chomp $cpuOut; + @parts = split( 'CPU count: ', $cpuOut ); + $discoverable{$activeSystem}{'cpuCount'} = $parts[1]; + + my $os = xCAT::zvmUtils->getOSFromIP( $callback, $activeSystem, $discoverable{$activeSystem}{'ipAddr'}, $discoverable{$activeSystem}{'ipVersion'} ); + if ( $os ne '' ) { + $discoverable{$activeSystem}{'os'} = $os; + } else { + if ( $args{'verbose'} ) { + push @{$rsp->{data}}, "ignoring: $activeSystem - unable to obtain OS version information from the operating system"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + delete( $discoverable{$activeSystem} ); + next; + } + + xCAT::MsgUtils->message( "S", "Discovery for $zvmHost is preparing to create a node for $activeSystem." ); + + # Set up to define the node. + $discoverable{$activeSystem}{'uuid'} = xCAT::Utils::genUUID(); + $discoverable{$activeSystem}{'openstackoperands'} = $args{'openstackoperands'}; + + # Generate an xCAT node name for the newly discovered system. + my $node; + + # Create an xCAT node. + my $retstr_gen = ''; + + $retstr_gen .= " arch=s390x\n"; + if ( $args{'groups'} ) { + $retstr_gen .= " groups=$args{'groups'}\n"; + } else { + $retstr_gen .= " groups=all\n"; + } + $retstr_gen .= " hcp=$hcp\n"; + if ( $discoverable{$activeSystem}{'hostname'} ) { + $retstr_gen .= " hostnames=$discoverable{$activeSystem}{'hostname'}\n"; + } + $retstr_gen .= " ip=$discoverable{$activeSystem}{'ipAddr'}\n"; + $retstr_gen .= " mgt=zvm\n"; + $retstr_gen .= " objtype=node\n"; + $retstr_gen .= " os=$discoverable{$activeSystem}{'os'}\n"; + $retstr_gen .= " userid=$activeSystem\n"; + + ( $node, $numeric ) = createNode( $callback, $discoverable{$activeSystem}{'hostname'}, $args{'nodenameformat'}, $numeric, $retstr_gen, \%xcatNodes ); + if ( $node eq '' ) { + # If we cannot create a node then skip this one and go on to the next. + next; + } + $discoverable{$activeSystem}{'node'} = $node; + + # Start OpenStack Provisioning for this node, if desired. + if ( $startOpenStack ) { + my $openstackNodeName = addOpenStackSystem( $callback, $zvmHost, $hcp, $args{'verbose'}, $activeSystem, \%discoverable ); + + if ( !$openstackNodeName ) { + # Node was not created in OpenStack. Remove it from xCAT. + changeNode( $callback, $node, 'd' ); + next; + } + } + + updateDiscoverydata( $callback, 'add', $args{'verbose'}, $zvmHost, $activeSystem, \%discoverable ); + } + + # Common exit point. +FINISH_startDiscovery: + my $startTime = getRunningDiscTimestamp( $callback, $zvmHost ); + if ( $startTime == $initStartTime ) { + my @stopArgs = (); + stopDiscovery( $callback, $zvmHost, \@stopArgs ); + } + return; +} + + +#------------------------------------------------------- + +=head3 stopDiscovery + + Description : Stop z/VM discovery for a particular z/VM host. + Arguments : Callback handle + z/VM host node name + Array of arguments specified on nodediscoverstop or + an empty array if this is an internal call. + Returns : None. + Example : stopDiscovery( $callback, $zvmHost, \@args ); + +=cut + +#------------------------------------------------------- +sub stopDiscovery{ + my $callback = shift; + my $zvmHost = shift; + my $argsRef = shift; + my @args = @$argsRef; + + my $rsp; + push @{$rsp->{data}}, "z/VM discovery is being stopped for $zvmHost."; + xCAT::MsgUtils->message( "I", $rsp, $callback, 1 ); + + # Get the hcp from the zvm table. + my @propNames = ( 'hcp', 'nodetype' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $zvmHost, @propNames ); + my $hcp = $propVals->{'hcp'}; + + # Get the list of discovered systems from the zvm table. + my $zvmTab = xCAT::Table->new('zvm'); + if ( !$zvmTab ) { + push @{$rsp->{data}}, "Could not open table: zvm."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + return; + } + + my %discoveredNodes; + my @zvmData = $zvmTab->getAllAttribsWhere( "hcp='$hcp'", 'node', 'userid' ); + foreach ( @zvmData ) { + $discoveredNodes{$_->{'node'}} = $_->{'userid'}; + } + + # Go though the discoverydata table and display the z/VM discovery entries + my $disTab = xCAT::Table->new('discoverydata'); + if ( !$disTab ) { + push @{$rsp->{data}}, "Could not open table: discoverydata."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + return; + } + + my @disData = $disTab->getAllAttribsWhere( "method='zvm' and otherdata='zvmhost.$zvmHost'", 'node' ); + push @{$rsp->{data}}, "Discovered ".($#disData+1)." nodes running on $zvmHost."; + + if ( @disData ) { + push @{$rsp->{data}}, sprintf(" %-20s%-8s", 'NODE', 'z/VM USERID'); + foreach ( @disData ) { + push @{$rsp->{data}}, sprintf(" %-20s%-8s", $_->{'node'}, $discoveredNodes{$_->{'node'}} ); + } + } + + removeHostInfo( $callback, $zvmHost ); + + xCAT::MsgUtils->message( "I", $rsp, $callback ); + xCAT::MsgUtils->message( "I", "z/VM discovery stopped for z/VM host: $zvmHost" ); + + return; +} + + +#------------------------------------------------------- + +=head3 updateDiscoverydata + + Description : Update the discoverydata table. + Arguments : Callback handle + function: 'add' is the only function + currently supported. + verbose flag + z/VM host node name + Virtual machine userid + discoverable hash which contains lots of properties + Returns : None. + Example : updateDiscoverydata( $callback, 'add', $verbose, $zvmHost, + $activeSystem, \%discoverable ): + +=cut + +#------------------------------------------------------- +sub updateDiscoverydata{ + my ( $callback, $function, $verbose, $zvmHost, $activeSystem, $discoverableRef ) = @_; + my %discoverable = %$discoverableRef; + + my %discoverInfo; + my $disTab = xCAT::Table->new("discoverydata"); + if ( !$disTab ) { + my $rsp; + push @{$rsp->{data}}, "Could not open table: discoverydata."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + goto FINISH_updateDiscoverydata; + } + + if ( $function = 'add' ) { + # Create a row in the discoverydata table to represent this discovered system. + $discoverInfo{'arch'} = "s390x"; + $discoverInfo{'cpucount'} = $discoverable{$activeSystem}{'cpuCount'}; + $discoverInfo{'memory'} = $discoverable{$activeSystem}{'memory'}; + $discoverInfo{'method'} = "zvm"; + $discoverInfo{'node'} = $discoverable{$activeSystem}{'node'}; + $discoverInfo{'otherdata'} = 'zvmhost.' . $zvmHost; + + # Set the discovery time. + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); + my $currtime = sprintf("%02d-%02d-%04d %02d:%02d:%02d", + $mon + 1, $mday, $year + 1900, $hour, $min, $sec); + $discoverInfo{'discoverytime'} = $currtime; + + # Update the discoverydata table. + $disTab->setAttribs({uuid => $discoverable{$activeSystem}{'uuid'}}, \%discoverInfo); + $disTab->commit(); + } + +FINISH_updateDiscoverydata: + return; +} +1; diff --git a/xCAT-server/share/xcat/install/rh/compute.rhel5.s390x.tmpl b/xCAT-server/share/xcat/install/rh/compute.rhel5.s390x.tmpl index a6a1de738..11db26e25 100644 --- a/xCAT-server/share/xcat/install/rh/compute.rhel5.s390x.tmpl +++ b/xCAT-server/share/xcat/install/rh/compute.rhel5.s390x.tmpl @@ -4,7 +4,7 @@ install url --url replace_url key --skip lang en_US.UTF-8 -network --bootproto dhcp +network --bootproto=static --ip=replace_ip --netmask=replace_netmask --gateway=replace_gateway --nameserver=replace_nameserver --hostname=replace_hostname rootpw replace_rootpw firewall --disabled authconfig --enableshadow --enablemd5 @@ -18,7 +18,7 @@ key --skip # here so unless you clear all partitions first, this is # not guaranteed to work zerombr yes -clearpart --initlabel --drives=dasda +clearpart --all --initlabel --drives=dasda part / --fstype ext3 --size=100 --grow --ondisk=dasda part swap --size=512 --ondisk=dasda diff --git a/xCAT-server/share/xcat/install/rh/compute.rhels6.s390x.tmpl b/xCAT-server/share/xcat/install/rh/compute.rhels6.s390x.tmpl index cf137e9ce..6b84a5701 100644 --- a/xCAT-server/share/xcat/install/rh/compute.rhels6.s390x.tmpl +++ b/xCAT-server/share/xcat/install/rh/compute.rhels6.s390x.tmpl @@ -4,7 +4,7 @@ install url --url replace_url key --skip lang en_US.UTF-8 -network --bootproto dhcp +network --bootproto=static --ip=replace_ip --netmask=replace_netmask --gateway=replace_gateway --nameserver=replace_nameserver --hostname=replace_hostname rootpw replace_rootpw firewall --disabled authconfig --enableshadow --enablemd5 @@ -18,7 +18,7 @@ key --skip # here so unless you clear all partitions first, this is # not guaranteed to work zerombr -clearpart --initlabel --drives=dasda +clearpart --all --initlabel --drives=dasda part / --fstype ext3 --size=100 --grow --ondisk=dasda part swap --size=512 --ondisk=dasda diff --git a/xCAT-server/share/xcat/install/rh/zfcp.rhel5.s390x.tmpl b/xCAT-server/share/xcat/install/rh/zfcp.rhel5.s390x.tmpl index 1b7c0b0a8..8d1e29749 100644 --- a/xCAT-server/share/xcat/install/rh/zfcp.rhel5.s390x.tmpl +++ b/xCAT-server/share/xcat/install/rh/zfcp.rhel5.s390x.tmpl @@ -4,7 +4,7 @@ install url --url replace_url key --skip lang en_US.UTF-8 -network --bootproto dhcp +network --bootproto=static --ip=replace_ip --netmask=replace_netmask --gateway=replace_gateway --nameserver=replace_nameserver --hostname=replace_hostname rootpw replace_rootpw firewall --disabled authconfig --enableshadow --enablemd5 diff --git a/xCAT-server/share/xcat/install/rh/zfcp.rhels6.s390x.tmpl b/xCAT-server/share/xcat/install/rh/zfcp.rhels6.s390x.tmpl index 1b7c0b0a8..8d1e29749 100644 --- a/xCAT-server/share/xcat/install/rh/zfcp.rhels6.s390x.tmpl +++ b/xCAT-server/share/xcat/install/rh/zfcp.rhels6.s390x.tmpl @@ -4,7 +4,7 @@ install url --url replace_url key --skip lang en_US.UTF-8 -network --bootproto dhcp +network --bootproto=static --ip=replace_ip --netmask=replace_netmask --gateway=replace_gateway --nameserver=replace_nameserver --hostname=replace_hostname rootpw replace_rootpw firewall --disabled authconfig --enableshadow --enablemd5 diff --git a/xCAT-server/share/xcat/install/scripts/post.rhel5.s390x b/xCAT-server/share/xcat/install/scripts/post.rhel5.s390x index 63de0a3a2..3e997728b 100644 --- a/xCAT-server/share/xcat/install/scripts/post.rhel5.s390x +++ b/xCAT-server/share/xcat/install/scripts/post.rhel5.s390x @@ -1,4 +1,3 @@ -echo ARP=no >> /etc/sysconfig/network-scripts/ifcfg-eth0 # Get hostname export HOSTNAME=$(host $IP 2>/dev/null | awk '{print $5}' | awk -F. '{print $1}') @@ -93,34 +92,26 @@ cd /xcatpost export PATH=$PATH:/xcatpost # Use the run_ps subroutine to run the postscripts -TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps postscript \1/;s/run_ps postscript\s*#/#/;s/run_ps postscript\s*$//" /xcatpost/mypostscript` +TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /xcatpost/mypostscript` echo " # Subroutine used to run postscripts -# \$1 argument is the script type -# rest argument is the script name and arguments run_ps () { logdir=\"/var/log/xcat\" mkdir -p \$logdir logfile=\"/var/log/xcat/xcat.log\" - scriptype=\$1 - shift; - - if [ -z \"\$scriptype\" ]; then - scriptype=\"postscript\" - fi if [ -f \$1 ]; then - echo \"Running \$scriptype: \$1\" | tee -a \$logfile - ./\$@ 2>&1 | tee -a \$logfile + echo \"Running postscript: \$@\" | tee -a \$logfile + ./\$@ 2>&1 | tee -a $logfile else - echo \"\$scriptype \$1 does NOT exist.\" | tee -a \$logfile + echo \"Postscript \$1 does NOT exist.\" | tee -a \$logfile fi } # Subroutine end " > /xcatpost/mypostscript echo "$TMP" >> /xcatpost/mypostscript -TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps postbootscript \1/;s/run_ps postbootscript\s*#/#/;s/run_ps postbootscript\s*$//" /xcatpost/mypostscript` +TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /xcatpost/mypostscript` echo "$TMP" > /xcatpost/mypostscript diff --git a/xCAT-server/share/xcat/install/scripts/post.rhel6.s390x b/xCAT-server/share/xcat/install/scripts/post.rhel6.s390x index 5bfd78b99..53d10e7f2 100644 --- a/xCAT-server/share/xcat/install/scripts/post.rhel6.s390x +++ b/xCAT-server/share/xcat/install/scripts/post.rhel6.s390x @@ -1,4 +1,3 @@ -echo ARP=no >> /etc/sysconfig/network-scripts/ifcfg-eth0 # Get hostname export HOSTNAME=$(host $IP 2>/dev/null | awk '{print $5}' | awk -F. '{print $1}') @@ -92,34 +91,26 @@ cd /xcatpost export PATH=$PATH:/xcatpost # Use the run_ps subroutine to run the postscripts -TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps postscript \1/;s/run_ps postscript\s*#/#/;s/run_ps postscript\s*$//" /xcatpost/mypostscript` +TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /xcatpost/mypostscript` echo " # Subroutine used to run postscripts -# \$1 argument is the script type -# rest argument is the script name and arguments run_ps () { logdir=\"/var/log/xcat\" mkdir -p \$logdir logfile=\"/var/log/xcat/xcat.log\" - scriptype=\$1 - shift; - - if [ -z \"\$scriptype\" ]; then - scriptype=\"postscript\" - fi if [ -f \$1 ]; then - echo \"Running \$scriptype: \$1\" | tee -a \$logfile - ./\$@ 2>&1 | tee -a \$logfile + echo \"Running postscript: \$@\" | tee -a \$logfile + ./\$@ 2>&1 | tee -a $logfile else - echo \"\$scriptype \$1 does NOT exist.\" | tee -a \$logfile + echo \"Postscript \$1 does NOT exist.\" | tee -a \$logfile fi } # Subroutine end " > /xcatpost/mypostscript echo "$TMP" >> /xcatpost/mypostscript -TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps postbootscript \1/;s/run_ps postbootscript\s*#/#/;s/run_ps postbootscript\s*$//" /xcatpost/mypostscript` +TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /xcatpost/mypostscript` echo "$TMP" > /xcatpost/mypostscript diff --git a/xCAT-server/share/xcat/install/scripts/post.sles10.s390x b/xCAT-server/share/xcat/install/scripts/post.sles10.s390x index 9770af8b8..3902c8917 100644 --- a/xCAT-server/share/xcat/install/scripts/post.sles10.s390x +++ b/xCAT-server/share/xcat/install/scripts/post.sles10.s390x @@ -100,28 +100,20 @@ PATH=$PATH:/xcatpost export PATH # use the run_ps subroutine to run the postscripts -TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps postscript \1/;s/run_ps postscript\s*#/#/;s/run_ps postscript\s*$//" /tmp/mypostscript` +TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /tmp/mypostscript` echo " # subroutine used to run postscripts -# \$1 argument is the script type -# rest argument is the script name and arguments run_ps () { logdir=\"/var/log/xcat\" mkdir -p \$logdir logfile=\"/var/log/xcat/xcat.log\" - scriptype=\$1 - shift; - - if [ -z \"\$scriptype\" ]; then - scriptype=\"postscript\" - fi if [ -f \$1 ]; then - echo \"Running \$scriptype: \$1\" | tee -a \$logfile + echo \"Running postscript: \$@\" | tee -a \$logfile ./\$@ 2>&1 1> /tmp/tmp4xcatlog cat /tmp/tmp4xcatlog | tee -a \$logfile else - echo \"\$scriptype \$1 does NOT exist.\" | tee -a \$logfile + echo \"Postscript \$1 does NOT exist.\" | tee -a \$logfile fi } # subroutine end @@ -129,7 +121,7 @@ run_ps () { " > /tmp/mypostscript echo "cd /xcatpost" >> /tmp/mypostscript echo "$TMP" >> /tmp/mypostscript -TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps postbootscript \1/;s/run_ps postbootscript\s*#/#/;s/run_ps postbootscript\s*$//" /tmp/mypostscript` +TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /tmp/mypostscript` echo "$TMP" > /tmp/mypostscript diff --git a/xCAT-server/share/xcat/install/scripts/post.sles11.s390x b/xCAT-server/share/xcat/install/scripts/post.sles11.s390x index d3f244404..3e84ad99b 100644 --- a/xCAT-server/share/xcat/install/scripts/post.sles11.s390x +++ b/xCAT-server/share/xcat/install/scripts/post.sles11.s390x @@ -101,28 +101,20 @@ PATH=$PATH:/xcatpost export PATH # use the run_ps subroutine to run the postscripts -TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps postscript \1/;s/run_ps postscript\s*#/#/;s/run_ps postscript\s*$//" /tmp/mypostscript` +TMP=`sed "/postscripts-start-here/,/postscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /tmp/mypostscript` echo " # subroutine used to run postscripts -# \$1 argument is the script type -# rest argument is the script name and arguments run_ps () { logdir=\"/var/log/xcat\" mkdir -p \$logdir logfile=\"/var/log/xcat/xcat.log\" - scriptype=\$1 - shift; - - if [ -z \"\$scriptype\" ]; then - scriptype=\"postscript\" - fi if [ -f \$1 ]; then - echo \"Running \$scriptype: \$1\" | tee -a \$logfile + echo \"Running postscript: \$@\" | tee -a \$logfile ./\$@ 2>&1 1> /tmp/tmp4xcatlog cat /tmp/tmp4xcatlog | tee -a \$logfile else - echo \"\$scriptype \$1 does NOT exist.\" | tee -a \$logfile + echo \"Postscript \$1 does NOT exist.\" | tee -a \$logfile fi } # subroutine end @@ -130,7 +122,7 @@ run_ps () { " > /tmp/mypostscript echo "cd /xcatpost" >> /tmp/mypostscript echo "$TMP" >> /tmp/mypostscript -TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps postbootscript \1/;s/run_ps postbootscript\s*#/#/;s/run_ps postbootscript\s*$//" /tmp/mypostscript` +TMP=`sed "/postbootscripts-start-here/,/postbootscripts-end-here/ s/\(.*\)/run_ps \1/;s/run_ps\s*#/#/;s/run_ps\s*$//" /tmp/mypostscript` echo "$TMP" > /tmp/mypostscript diff --git a/xCAT-server/share/xcat/install/sles/compute.sles10.s390x.tmpl b/xCAT-server/share/xcat/install/sles/compute.sles10.s390x.tmpl index 69465f184..d6ce4225d 100644 --- a/xCAT-server/share/xcat/install/sles/compute.sles10.s390x.tmpl +++ b/xCAT-server/share/xcat/install/sles/compute.sles10.s390x.tmpl @@ -284,8 +284,7 @@ AUTO - true - true + false replace_domain replace_hostname @@ -294,11 +293,14 @@ - dhcp + static + replace_broadcast replace_device + replace_ipaddr replace_lladdr + replace_netmask + replace_network auto - no false diff --git a/xCAT-server/share/xcat/install/sles/compute.sles11.s390x.tmpl b/xCAT-server/share/xcat/install/sles/compute.sles11.s390x.tmpl index 69465f184..d6ce4225d 100644 --- a/xCAT-server/share/xcat/install/sles/compute.sles11.s390x.tmpl +++ b/xCAT-server/share/xcat/install/sles/compute.sles11.s390x.tmpl @@ -284,8 +284,7 @@ AUTO - true - true + false replace_domain replace_hostname @@ -294,11 +293,14 @@ - dhcp + static + replace_broadcast replace_device + replace_ipaddr replace_lladdr + replace_netmask + replace_network auto - no false diff --git a/xCAT-server/share/xcat/install/sles/zfcp.sles10.s390x.tmpl b/xCAT-server/share/xcat/install/sles/zfcp.sles10.s390x.tmpl index 071980b7a..293b11610 100644 --- a/xCAT-server/share/xcat/install/sles/zfcp.sles10.s390x.tmpl +++ b/xCAT-server/share/xcat/install/sles/zfcp.sles10.s390x.tmpl @@ -238,7 +238,7 @@ AUTO - true + false true replace_domain replace_hostname @@ -248,11 +248,14 @@ - dhcp + static + replace_broadcast replace_device + replace_ipaddr replace_lladdr + replace_netmask + replace_network auto - no false diff --git a/xCAT-server/share/xcat/install/sles/zfcp.sles11.s390x.tmpl b/xCAT-server/share/xcat/install/sles/zfcp.sles11.s390x.tmpl index 071980b7a..293b11610 100644 --- a/xCAT-server/share/xcat/install/sles/zfcp.sles11.s390x.tmpl +++ b/xCAT-server/share/xcat/install/sles/zfcp.sles11.s390x.tmpl @@ -238,7 +238,7 @@ AUTO - true + false true replace_domain replace_hostname @@ -248,11 +248,14 @@ - dhcp + static + replace_broadcast replace_device + replace_ipaddr replace_lladdr + replace_netmask + replace_network auto - no false diff --git a/xCAT-server/share/xcat/netboot/rh/compute.rhel5.s390x.pkglist b/xCAT-server/share/xcat/netboot/rh/compute.rhel5.s390x.pkglist index 4083ebcd8..b448841a0 100644 --- a/xCAT-server/share/xcat/netboot/rh/compute.rhel5.s390x.pkglist +++ b/xCAT-server/share/xcat/netboot/rh/compute.rhel5.s390x.pkglist @@ -15,6 +15,3 @@ rpm rsync udev s390utils -tar -gzip -xz diff --git a/xCAT-server/share/xcat/netboot/rh/compute.rhel6.s390x.pkglist b/xCAT-server/share/xcat/netboot/rh/compute.rhel6.s390x.pkglist index bcbff4391..bfa4a7fc0 100644 --- a/xCAT-server/share/xcat/netboot/rh/compute.rhel6.s390x.pkglist +++ b/xCAT-server/share/xcat/netboot/rh/compute.rhel6.s390x.pkglist @@ -14,6 +14,3 @@ rsync rsyslog udev s390utils -tar -gzip -xz diff --git a/xCAT-server/share/xcat/scripts/setupDisk b/xCAT-server/share/xcat/scripts/setupDisk new file mode 100644 index 000000000..c9a93ec78 --- /dev/null +++ b/xCAT-server/share/xcat/scripts/setupDisk @@ -0,0 +1,579 @@ +#!/bin/bash +# IBM(c) 2013, 2015 EPL license http://www.eclipse.org/legal/epl-v10.html + +############################################################################### +# This script is used to handle xCAT disk initialization and configuration(eg. +# attach/detach a SCSI volume, add an additional ephemeral disk when vm is in +# inactive status). It will be invoked and executed when vm start up. +############################################################################### +version=3.0 + +function getOsVersion { + # @Description: + # Returns the Linux distro version in an easy to parse format. + # @Input: + # None + # @Output: + # os - Variable set with OS and version information. For example: + # "rhel62" or "sles11sp2" + # @Code: + if [[ -e "/etc/os-release" ]]; then + os=`cat /etc/os-release | grep "^ID=" | sed \ + -e 's/ID=//' \ + -e 's/"//g'` + version=`cat /etc/os-release | grep "^VERSION_ID=" | sed \ + -e 's/VERSION_ID=//' \ + -e 's/"//g' \ + -e 's/\.//'` + os=$os$version + + #The /etc/SuSE-release file will be deprecated in sles11.4 and later release + elif [[ -e "/etc/SuSE-release" ]]; then + os='sles' + version=`cat /etc/SuSE-release | grep "VERSION =" | sed \ + -e 's/^.*VERSION =//' \ + -e 's/\s*$//' \ + -e 's/.//' \ + -e 's/[^0-9]*([0-9]+).*/$1/'` + os=$os$version + + # Append service level + level=`echo "/etc/SuSE-release" | grep "LEVEL =" | sed \ + -e 's/^.*LEVEL =//' \ + -e 's/\s*$//' \ + -e 's/.//' \ + -e 's/[^0-9]*([0-9]+).*/$1/'` + os=$os'sp'$level + + #The /etc/redhat-release file will be deprecated in rhel7 and later release + elif [[ -e "/etc/redhat-release" ]]; then + os='rhel' + version=`cat /etc/redhat-release | grep -i "Red Hat Enterprise Linux Server" | sed \ + -e 's/[A-Za-z\/\.\(\)]//g' \ + -e 's/^ *//g' \ + -e 's/ *$//g' \ + -e 's/\s.*$//'` + os=$os$version + fi + return +} + +function onlineDevice { + # @Description: + # Brings a Linux device online. + # @Input: + # Device number, e.g. "0.0.000c" + # @Output: + # Return code indicates success or failure + # @Code: + device=$1 + local funcName="onlineDevice" + rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) + if (( rc != 0 )); then + if [[ -e /sbin/cio_ignore ]]; then + rc=$(/sbin/cio_ignore -r 0.0.$device > /dev/null; echo $?) + which udevadm &> /dev/null && udevadm settle || udevsettle + fi + rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) + if (( rc != 0 )); then + echo "$funcName (Error) Could not activate the virtual device $device" + return 1 + fi + fi + which udevadm &> /dev/null && udevadm settle || udevsettle + return 0 +} + +function setupDisk { + # @Description: + # Processes a disk file for the following functions: + # Create a file system node + # Remove a file system node + # Setup a SCSI volume + # Removes a SCSI volume + # Add a mdisk based ephemeral disk + # @Input: + # Disk handling parameters + # @Output: + # None + # @Code: + local funcName="setupDisk" + + # Parse the parameter from parameter list + for parameter in $@; do + keyName=${parameter%\=*} + value=${parameter#*\=} + value=$(echo ${value} | sed -e 's/^ *//g') + newKey='xcat_'$keyName + eval $newKey=$value + done + + # Remove the invokeScript.sh file after we have read it + rm -f invokeScript.sh + + ########################################################################## + # Handle creating a file system node + # Disk handler input parameters: + # action - "createfilesysnode" + # srcFile - location/name of the source file for the mknod command + # xcat_tgtFile - location/name of the target file for the mknod command + ########################################################################## + if [[ $xcat_action == "createfilesysnode" ]]; then + echo "Creating a file system node, source: $xcat_srcFile, target: $xcat_tgtFile" + + if [[ ! -n $xcat_srcFile ]]; then + echo "$funcName (Error) Source file for creating a file system node was not specified" + return + fi + + if [[ ! -n $xcat_tgtFile ]]; then + echo "$funcName (Error) Target file for creating a file system node was not specified" + return + fi + if [[ -e $xcat_tgtFile ]]; then + echo "$funcName (Error) Target file for creating a file system node already exists" + return + fi + + configFile='/etc/udev/rules.d/56-zfcp.rules' + # Create udev config file if not exist + if [[ ! -e $configFile ]]; then + touch $configFile + if [[ $os == rhel* ]]; then + echo "KERNEL==\"zfcp\", RUN+=\"/sbin/zfcpconf.sh\"" >> ${configFile} + echo "KERNEL==\"zfcp\", RUN+=\"/sbin/multipath -r\"" >> ${configFile} + fi + fi + + tgtNode=$(echo ${xcat_tgtFile} | sed -e 's/^\/dev\///') + if [[ $os == sles* || $os == rhel* ]]; then + wwpn_lun=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*-zfcp-//') + wwpn=$(echo ${wwpn_lun} | sed -e 's/:0x.*//') + lun=$(echo ${wwpn_lun} | sed -e 's/^0x.*://') + else + wwpn_lun=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*-fc-//') + wwpn=$(echo ${wwpn_lun} | sed -e 's/-lun-.*//') + lun=$(echo ${wwpn_lun} | sed -e 's/^0x.*-lun-//') + fi + + + multipath=0 + out=`echo $wwpn | grep ","` + if [[ -n "$out" ]]; then + multipath=1 + fi + + if [[ $os == sles* || $os == rhel* ]]; then + fcp=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*ccw-0.0.//' | sed -e 's/-zfcp-.*$//') + else + fcp=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*ccw-0.0.//' | sed -e 's/-fc-.*$//') + fi + oldIFS=$IFS + IFS="," + fcpList=($fcp) + for fcp in ${fcpList[@]} + do + if [[ $multipath == 1 ]]; then + # Find the name of the multipath device by arbitrary one path in the set + wwpnList=($wwpn) + for wwpn in ${wwpnList[@]} + do + if [[ ${wwpn:0:2} -ne "0x" ]]; then + wwpn="0x$wwpn" + fi + if [[ $os == sles* || $os == rhel* ]]; then + cur_wwpn_lun=${wwpn}:${lun} + srcFile=$(echo ${xcat_srcFile} | sed -e 's/-zfcp-.*//')"-zfcp-"$cur_wwpn_lun + srcFile=$(echo ${srcFile} | sed -e 's/ccw-.*-zfcp/ccw-0.0.'$fcp'-zfcp/') + else + cur_wwpn_lun=${wwpn}-lun-${lun} + srcFile=$(echo ${xcat_srcFile} | sed -e 's/-fc-.*//')"-fc-"$cur_wwpn_lun + srcFile=$(echo ${srcFile} | sed -e 's/ccw-.*-fc/ccw-0.0.'$fcp'-fc/') + fi + + out=`/usr/bin/stat --printf=%n ${srcFile}` + if (( $? != 0 )); then + echo "$funcName (Error) Unable to stat the source file: $srcFile" + continue + fi + + out=`/sbin/udevadm info --query=all --name=$srcFile | grep ID_SERIAL=` + devName=$(echo ${out} | sed -e 's/^E:\s//') + multipathUuid=$(echo $devName | sed -e 's/ID_SERIAL=//') + if [[ -n "$multipathUuid" ]]; then + break + fi + done + + if [[ -z "$multipathUuid" ]]; then + echo "$funcName (Error) Building up multipath failed!" + return + fi + else + if [[ $os == sles* || $os == rhel* ]]; then + srcFile=$(echo ${xcat_srcFile} | sed -e 's/ccw-.*-zfcp/ccw-0.0.'$fcp'-zfcp/') + else + srcFile=$(echo ${xcat_srcFile} | sed -e 's/ccw-.*-zfcp/ccw-0.0.'$fcp'-fc/') + fi + out=`/usr/bin/stat --printf=%n ${srcFile}` + if (( $? != 0 )); then + echo "$funcName (Error) Unable to stat the source file: $xcat_srcFile" + return + fi + fi + done + IFS=$oldIFS + + # Add the entry into udev config file + if [[ $multipath == 1 ]]; then + echo "KERNEL==\"dm*\", ENV{DM_UUID}==\"mpath-${multipathUuid}\", SYMLINK+=\"${tgtNode}\"" >> ${configFile} + udevadm control --reload + udevadm trigger --sysname-match=dm-* + else + echo "KERNEL==\"sd*\", ATTRS{wwpn}==\"${wwpn}\", ATTRS{fcp_lun}==\"${lun}\", SYMLINK+=\"${tgtNode}%n\"" >> ${configFile} + udevadm control --reload + udevadm trigger --sysname-match=sd* + fi + + echo "$funcName successfully create the file system node ${xcat_tgtFile}" + + ########################################################################## + # Handle removing a file system node + # Disk file input parameters: + # action - "removefilesysnode" + # tgtFile - location/name of the target file for the mknod command + ########################################################################## + elif [[ $xcat_action == "removefilesysnode" ]]; then + echo "Removing a file system node, target: $xcat_tgtFile" + if [[ ! -n $xcat_tgtFile ]]; then + echo "$funcName (Error) Target file for creating a file system node was not specified" + return + fi + + configFile='/etc/udev/rules.d/56-zfcp.rules' + tgtNode=$(echo ${xcat_tgtFile} | sed -e 's/^\/dev\///') + + sed -i -e /SYMLINK+=\"${tgtNode}%n\"/d ${configFile} + sed -i -e /SYMLINK+=\"${tgtNode}\"/d ${configFile} + udevadm control --reload + udevadm trigger --sysname-match=sd* + udevadm trigger --sysname-match=dm-* + echo "$funcName successfully remove the file system node ${xcat_tgtFile}" + + ########################################################################## + # Handle adding a SCSI volume + # Disk file input parameters: + # action - "addScsiVolume" + # fcpAddr - FCP device address + # wwpn - WWPN number + # lun - LUN number + ########################################################################## + elif [[ $xcat_action == "addScsiVolume" ]]; then + echo "Adding a SCSI Volume, FCP addr: $xcat_fcpAddr, WWPN: $xcat_wwpn, LUN: $xcat_lun" + + # Validate the input + if [[ ! -n $xcat_fcpAddr ]]; then + echo "$funcName (Error) FCP address was not specified" + return + fi + xcat_fcpAddr=`echo $xcat_fcpAddr | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_wwpn ]]; then + echo "$funcName (Error) WWPN was not specified" + return + fi + xcat_wwpn=`echo $xcat_wwpn | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_lun ]]; then + echo "$funcName (Error) LUN was not specified" + return + fi + xcat_lun=`echo $xcat_lun | tr '[A-Z]' '[a-z]'` + decimal_lun=$((16#${xcat_lun:0:4})) + + # Online the device + oldIFS=$IFS + IFS="," + fcp_list=($xcat_fcpAddr) + for fcp in ${fcp_list[@]} + do + rc= onlineDevice $fcp + if (( rc != 0 )); then + return + fi + if [[ $os == sles12* ]]; then + out=`cat /boot/zipl/active_devices.txt | grep -i "0.0.$fcp"` + if [[ -z $out ]]; then + /sbin/zfcp_host_configure 0.0.$fcp 1 + fi + elif [[ $os == sles11* ]]; then + /sbin/zfcp_host_configure 0.0.$fcp 1 + elif [[ $os == ubuntu* ]]; then + /sbin/chzdev zfcp-host $fcp -e + fi + done + + multipath=0 + out=`echo $xcat_wwpn | grep ","` + if [[ -n "$out" ]]; then + multipath=1 + fi + + # Start multipathd service + if [[ $multipath == 1 ]]; then + if [[ $os == sles* ]]; then + insserv multipathd + elif [[ $os == rhel6* ]]; then + chkconfig multipathd on + else + systemctl enable multipathd + fi + modprobe dm-multipath + fi + + for fcp in ${fcp_list[@]} + do + wwpn_list=($xcat_wwpn) + for wwpn in ${wwpn_list[@]} + do + # Set WWPN and LUN in sysfs + echo 0x$xcat_lun > /sys/bus/ccw/drivers/zfcp/0.0.$fcp/0x$wwpn/unit_add + + # Set WWPN and LUN in configuration files + if [[ $os == sles* ]]; then + # SLES: /etc/udev/rules.d/51-zfcp* + /sbin/zfcp_disk_configure 0.0.$fcp $wwpn $xcat_lun 1 + + # Configure zFCP device to be persistent + touch /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules + + # Check if the file already contains the zFCP channel + out=`cat "/etc/udev/rules.d/51-zfcp-0.0.$fcp.rules" | egrep -i "ccw/0.0.$fcp]online"` + if [[ ! $out ]]; then + echo "ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.$fcp\", IMPORT{program}=\"collect 0.0.$fcp %k 0.0.$fcp zfcp\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules + echo "ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.$fcp %k 0.0.$fcp zfcp\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules + echo "ACTION==\"add\", ENV{COLLECT_0.0.$fcp}==\"0\", ATTR{[ccw/0.0.$fcp]online}=\"1\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules + fi + + echo "ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x$wwpn\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.$fcp\", ATTR{[ccw/0.0.$fcp]0x$wwpn/unit_add}=\"0x$xcat_lun\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules + elif [[ $os == rhel* ]]; then + # RHEL: /etc/zfcp.conf + echo "0.0.$fcp 0x$wwpn 0x$xcat_lun" >> /etc/zfcp.conf + echo "add" > /sys/bus/ccw/devices/0.0.$fcp/uevent + elif [[ $os == ubuntu* ]]; then + # Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -e + /sbin/chzdev zfcp-lun 0.0.$fcp:0x$wwpn:0x$xcat_lun -e + fi + + # Settle the file system so when we are done the device is fully available + if [[ $(which udevadm 2> /dev/null) != '' ]]; then + udevadm settle + else + udevsettle + fi + if [[ $os == rhel* || $os == sles* ]]; then + if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun}" ]]; then + # Sometimes the file takes longer to appear. We will wait up to 3 minutes. + maxTime=0 + for time in 1 2 2 5 10 10 30 60 60 + do + if [[ -e "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun}" ]]; then + # Leave the loop now that the file exists + break + fi + maxTime=$maxTime+$time + echo "Sleeping for $time seconds to allow /dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun} to be created" + sleep $time + done + fi + if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun}" ]]; then + echo "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun} did not appear in $maxTime seconds, continuing." + fi + elif [[ $os == ubuntu* ]]; then + if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun}" ]]; then + # Sometimes the file takes longer to appear. We will wait up to 3 minutes. + maxTime=0 + for time in 1 2 2 5 10 10 30 60 60 + do + if [[ -e "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun}" ]]; then + # Leave the loop now that the file exists + break + fi + maxTime=$maxTime+$time + echo "Sleeping for $time seconds to allow /dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun} to be created" + sleep $time + done + fi + if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun}" ]]; then + echo "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun} did not appear in $maxTime seconds, continuing." + fi + fi + done + done + IFS=$oldIFS + + /sbin/multipath -r + + echo "$funcName successfully create the SCSI volume" + + + ########################################################################## + # Handle removing a SCSI volume + # Disk file input parameters: + # action - "removeScsiVolume" + # fcpAddr - FCP device address + # wwpn - WWPN number + # lun - LUN number + ########################################################################## + elif [[ $xcat_action == "removeScsiVolume" ]]; then + echo "Removing a SCSI Volume, FCP addr: $xcat_fcpAddr, WWPN: $xcat_wwpn, LUN: $xcat_lun" + + # Validate the input + if [[ ! -n $xcat_fcpAddr ]]; then + echo "$funcName (Error) FCP address was not specified" + return + fi + xcat_fcpAddr=`echo $xcat_fcpAddr | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_wwpn ]]; then + echo "$funcName (Error) WWPN was not specified" + return + fi + xcat_wwpn=`echo $xcat_wwpn | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_lun ]]; then + echo "$funcName (Error) LUN was not specified" + return + fi + xcat_lun=`echo $xcat_lun | tr '[A-Z]' '[a-z]'` + + oldIFS=$IFS + IFS="," + fcp_list=($xcat_fcpAddr) + for fcp in ${fcp_list[@]} + do + wwpn_list=($xcat_wwpn) + for wwpn in ${wwpn_list[@]} + do + # Delete the SCSI device + scsiDevice=`lszfcp -l 0x$xcat_lun | grep 0x$wwpn | cut -d " " -f2` + if [[ -n $scsiDevice ]]; then + echo 1 > "/sys/bus/scsi/devices/$scsiDevice/delete" + fi + + # Delete WWPN and LUN from sysfs + if [[ -e /sys/bus/ccw/drivers/zfcp/0.0.$fcp/0x$wwpn/unit_remove ]]; then + if [[ $(which udevadm 2> /dev/null) != '' ]]; then + udevadm settle + else + udevsettle + fi + echo 0x$xcat_lun > /sys/bus/ccw/drivers/zfcp/0.0.$fcp/0x$wwpn/unit_remove + fi + + # Delete WWPN and LUN from configuration files + if [[ $os == sles11* || $os == sles12* ]]; then + # SLES: /etc/udev/rules.d/51-zfcp* + expression="/$xcat_lun/d" + sed --in-place -e $expression /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules + elif [[ $os == rhel* ]]; then + # RHEL: /etc/zfcp.conf + expression="/$xcat_lun/d" + sed --in-place -e $expression /etc/zfcp.conf + elif [[ $os == ubuntu* ]]; then + # Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -d + /sbin/chzdev zfcp-lun 0.0.$fcp:0x$wwpn:0x$xcat_lun -d + fi + done + done + IFS=$oldIFS + /sbin/multipath -W + /sbin/multipath -r + echo "$funcName successfully remove the SCSI volume" + + ########################################################################### + # Handle adding a mdisk based ephemeral disk. + # Disk file input parameters: + # action - "addMdisk" + # vaddr - virtual address of the minidisk + # filesys - Filesystem type + # mntdir - The directory that mount the mdisk to + ########################################################################## + elif [[ $xcat_action == "addMdisk" ]]; then + echo "Adding a minidisk based ephemeral disk, Vaddr: $xcat_vaddr, Filesystem: $xcat_filesys mountpoint:$xcat_mntdir" + + # Validate the input + if [[ ! -n $xcat_vaddr ]]; then + echo "$funcName (Error) Virtual address was not specified" + return + fi + xcat_vaddr=`echo $xcat_vaddr | tr '[A-Z]' '[a-z]'` + + # Online the device + rc= onlineDevice $xcat_vaddr + if (( rc != 0 )); then + echo "$funcName (Error) fail to online the disk $xcat_vaddr" + return + fi + + # Configure the added dasd to be persistent + echo "Permanently online the ephemeral disk" + if [[ $os == rhel* ]]; then + out=`cat "/etc/dasd.conf" | egrep -i $xcat_vaddr` + if [[ ! $out ]]; then + echo "0.0.$xcat_vaddr" >> /etc/dasd.conf + fi + elif [[ $os == sles* ]]; then + /sbin/dasd_configure 0.0.$xcat_vaddr 1 + elif [[ $os == ubuntu16* ]]; then + touch /etc/sysconfig/hardware/config-ccw-0.0.$xcat_vaddr + else + echo "$funcName (Error) failed to permanently online the disk:$xcat_vaddr on os: $os, please check if $os is in the supported distribution list" + return + fi + + + # Mount the mdisk to the specified mount point + echo "Mounting the ephemeral disk $xcat_vaddr to directory $xcat_mntdir" + if [[ -d $xcat_mntdir ]]; then + rm -rf $xcat_mntdir + fi + mkdir -p $xcat_mntdir + + cp /etc/fstab /etc/fstab.bak + out=`cat "/etc/fstab" | egrep -i "ccw-0.0.$xcat_vaddr"` + if [[ $out ]]; then + sed -i '/ccw-0.0.'"$xcat_vaddr"'/d' /etc/fstab + fi + + if [[ $os == sles12* ]]; then + echo "/dev/disk/by-path/ccw-0.0.${xcat_vaddr}-part1 $xcat_mntdir $xcat_filesys defaults,nofail 0 0" >> /etc/fstab + else + echo "/dev/disk/by-path/ccw-0.0.${xcat_vaddr}-part1 $xcat_mntdir $xcat_filesys defaults 0 0" >> /etc/fstab + fi + + out=`mount -a 2>&1` + if [[ "$out" ]]; then + echo "Fail to mount the disk $xcat_vaddr with reason $out" + mv /etc/fstab.bak /etc/fstab + mount -a + else + echo "The disk $xcat_vaddr has been mounted to $xcat_mntdir in format $xcat_filesys successfully" + fi + + fi + + return +} + +############################################################################ +# Main Code Section +############################################################################ +# Get Linux version +getOsVersion +setupDisk $@ +rm -f setupDisk diff --git a/xCAT-server/share/xcat/scripts/xcatconf4z b/xCAT-server/share/xcat/scripts/xcatconf4z index cc0181705..9340df53d 100644 --- a/xCAT-server/share/xcat/scripts/xcatconf4z +++ b/xCAT-server/share/xcat/scripts/xcatconf4z @@ -1,4 +1,5 @@ -#!/bin/sh +#!/bin/bash +# IBM(c) 2013, 2015 EPL license http://www.eclipse.org/legal/epl-v10.html ### BEGIN INIT INFO # Provides: xcatconf4z @@ -7,10 +8,11 @@ # Required-Start: $syslog # Should-Start: # Required-Stop: -# Short-Description: xCAT disk initialization and configuration +# Short-Description: A basic active engine used to initialize and configure vm. # Description: Reads class x files from the reader and acts based on the type of file. -# Files of filetype "disk" cause SCSI disks be configured. -# Other files are used to generate an ISO9660 disk for transporting xCAT configurations. +# Generate an ISO9660 disk for cloud-init to handle openstack configurations. +# Files of filetype "disk" cause disks be configured. (deprecated) +# Other files are used to configure vm when it start up. ### END INIT INFO ############################################################################### @@ -20,6 +22,7 @@ # If nothing is specified then this function will not process any # configuration files in the reader. ############################################################################### +version=3.0 authorizedSenders='' function getOsVersion { @@ -80,7 +83,7 @@ function onlineDevice { fi rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) if (( rc != 0 )); then - echo "xcatconf4z $funcName (Error) Could not activate the virtual reader" + echo "xcatconf4z $funcName (Error) Could not activate the virtual device $device" return 1 fi fi @@ -94,7 +97,8 @@ function pullReader { # Drives special processing functions for files of a specific type. # Files with a filetype of: # tgz are unpacked into the transport directory - # disk files are read and cause the setupDisk function to be driven + # disk files are read and cause the setupDisk function to be driven (deprecated) + # doscript that contains a invokeScript.sh which will call the other script in it to do the speicial work # all other files are unpacked into the transport directory # @Input: # None @@ -145,14 +149,26 @@ function pullReader { # Receive the spool file echo "Downloading record $spoolid: $file" - if [[ $type == "txt" ]] || [[ $type == "sh" ]]; then + if [[ $type == "txt" ]]; then # Receiving text - rc=$(/usr/sbin/vmur re -t $spoolid $file) + rc=$(/usr/sbin/vmur re -f $spoolid $file) + elif [[ $type == "sh" ]]; then + # Receiving shell + rc=$(/usr/sbin/vmur re -f $spoolid $file) + /bin/bash $file + rm $file elif [[ $type == "tgz" ]]; then rc=$(/usr/sbin/vmur re $spoolid $file) /bin/tar xzf $file -C $transportdir rm $file + injectFiles + elif [[ $type == "doscript" ]]; then + rc=$(/usr/sbin/vmur re $spoolid $file) + /bin/tar xf $file -C $transportdir + rm $file + /bin/bash invokeScript.sh elif [[ $type == "disk" ]]; then + echo 'disk file encountered and will be handled by the deprecated setupDisk function' rc=$(/usr/sbin/vmur re $spoolid $file) if (( rc == 0 )); then setupDisk $transportdir'/'$file @@ -180,11 +196,15 @@ function setupIso { # @Output: # None # @Code: + local funcName="setupIso" iso="/var/opt/xcat/transport.iso" # If there are files in the transport directory then create an ISO system. if [ "$(ls -A .)" ]; then - /usr/bin/mkisofs -l -o $iso $transportdir + /usr/bin/mkisofs -l -V 'config-2' -o $iso $transportdir + if [ -e /tmp/znetconfig.sh ]; then + /bin/bash /tmp/znetconfig.sh + fi fi # If the ISO filesystem exists then create loop back device pointing @@ -350,7 +370,7 @@ function setupDisk { /sbin/zfcp_host_configure 0.0.$xcat_fcpAddr 1 /sbin/zfcp_disk_configure 0.0.$xcat_fcpAddr $xcat_wwpn $xcat_lun 1 echo "0x$xcat_wwpn:0x$xcat_lun" >> /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$xcat_fcpAddr - elif [[ $os == sles* ]]; then + elif [[ $os == sles11* ]]; then /sbin/zfcp_host_configure 0.0.$xcat_fcpAddr 1 /sbin/zfcp_disk_configure 0.0.$xcat_fcpAddr $xcat_wwpn $xcat_lun 1 @@ -455,18 +475,115 @@ function setupDisk { if [[ $os == sles10* ]]; then expression="/$xcat_lun/d" sed --in-place -e $expression /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$xcat_fcpAddr - elif [[ $os == sles* ]]; then + elif [[ $os == sles11* ]]; then expression="/$xcat_lun/d" sed --in-place -e $expression /etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules elif [[ $os == rhel* ]]; then expression="/$xcat_lun/d" sed --in-place -e $expression /etc/zfcp.conf fi - fi + + ########################################################################### + # Handle adding a mdisk based ephemeral disk. + # Disk file input parameters: + # action - "addMdisk" + # vaddr - virtual address of the minidisk + # filesys - Filesystem type + # mntdir - The directory that mount the mdisk to + ########################################################################## + elif [[ $xcat_action == "addMdisk" ]]; then + echo "Adding a minidisk based ephemeral disk, Vaddr: $xcat_vaddr, Filesystem: $xcat_filesys mountpoint:$xcat_mntdir" + + # Validate the input + if [[ ! -n $xcat_vaddr ]]; then + echo "xcatconf4z $funcName (Error) Virtual address was not specified" + return + fi + xcat_vaddr=`echo $xcat_vaddr | tr '[A-Z]' '[a-z]'` + + # Online the device + rc= onlineDevice $xcat_vaddr + if (( rc != 0 )); then + echo "xcatconf4z $funcName (Error) fail to online the disk $xcat_vaddr" + return + fi + + # Configure the added dasd to be persistent + echo "Permenently online the ephemeral disk" + if [[ $os == rhel* ]]; then + out=`cat "/etc/dasd.conf" | egrep -i $xcat_vaddr` + if [[ ! $out ]]; then + echo "0.0.$xcat_vaddr" >> /etc/dasd.conf + fi + else + /sbin/dasd_configure 0.0.$xcat_vaddr 1 + fi + + # Mount the mdisk to the specified mount point + echo "Mounting the ephemeral disk $xcat_vaddr to directory $xcat_mntdir" + if [[ -d $xcat_mntdir ]]; then + rm -rf $xcat_mntdir + fi + mkdir -p $xcat_mntdir + + cp /etc/fstab /etc/fstab.bak + out=`cat "/etc/fstab" | egrep -i "ccw-0.0.$xcat_vaddr"` + if [[ $out ]]; then + sed -i '/ccw-0.0.'"$xcat_vaddr"'/d' /etc/fstab + fi + echo "/dev/disk/by-path/ccw-0.0.${xcat_vaddr}-part1 $xcat_mntdir $xcat_filesys defaults 0 0" >> /etc/fstab + + out=`mount -a 2>&1` + if [[ "$out" ]]; then + echo "Fail to mount the disk $xcat_vaddr with reason $out" + mv /etc/fstab.bak /etc/fstab + mount -a + else + echo "The disk $xcat_vaddr has been mounted to $xcat_mntdir in format $xcat_filesys successfully" + fi + + fi return } +function injectFiles { + # @Description: + # Inject network files and scripts + # @Input: + # None + # @Output: + # None + # @Code: + local funcName="injectFiles" + + if [[ ! -e $transportdir/openstack/latest/meta_data.json ]]; then + echo "Can not inject files, because no meta_data.json" + return + fi + + echo "File injecting ...." + awk '{ + #get inject files info + split($0 ,res1,/"files": \[/) + split(res1[2], res2, /\]/) + n=split(res2[1], res, /}, /) + + for(m=1;m<=n;m++) + { + split(res[m], temp1, /{"path": "/) + k=split(temp1[2], temp2, /", "content_path": "/) + sub(/"}*$/, "", temp2[2]) + #print temp2[1] " : " temp2[2] + des = dir "/openstack" temp2[2] + cmd = "cp " des " " temp2[1] + #print cmd + system(cmd) + } + }' dir=$transportdir <$transportdir/openstack/latest/meta_data.json + return +} + ############################################################################ # Main Code Section ############################################################################ @@ -483,13 +600,27 @@ case "$1" in if [[ -n "$authorizedSenders" ]]; then pullReader + echo "xcatconf4z has successfully processed the reader files." else echo "xcatconf4z is disabled from accepting configuration reader files." fi setupIso - ;; - stop|status|restart|reload|force-reload) + ;; + + status) + if [[ -n "$authorizedSenders" ]]; then + echo "xcatconf4z is enabled to accept configuration reader files from: $authorizedSenders" + else + echo "xcatconf4z is disabled from accepting configuration reader files." + fi + ;; + + version) + echo "xcatconf4z version:" $version + ;; + + stop|restart|reload|force-reload) # Do nothing ;; esac diff --git a/xCAT-server/share/xcat/scripts/xcatconf4z.service b/xCAT-server/share/xcat/scripts/xcatconf4z.service new file mode 100644 index 000000000..11c68f35d --- /dev/null +++ b/xCAT-server/share/xcat/scripts/xcatconf4z.service @@ -0,0 +1,14 @@ +[Unit] +Description=Activation engine for configuring vm when it start up +Wants=local-fs.target NetworkManager.service +After=local-fs.target +Before=NetworkManager.service cloud-init-local.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/xcatconf4z start + +StandardOutput=journal+console + +[Install] +WantedBy=multi-user.target diff --git a/xCAT-server/share/xcat/tools/zvm/README b/xCAT-server/share/xcat/tools/zvm/README new file mode 100644 index 000000000..4254fff77 --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/README @@ -0,0 +1,13 @@ +z/VM Toolkit + +The list of resources related: +Files: + prep_zxcatIVP_*.pl + + +The prep_zxcatIVP_*.pl files are provided for each supported OpenStack release +that interacts with xCAT. These scripts end contain the name of the OpenStack +release that they support. The scripts scan the OpenStack configuration files +and produce a driver script which is used to validate the installation of the +OpenStack code with xCAT. For more information on the scripts, invoke the +help function for the individual script (operand --help). \ No newline at end of file diff --git a/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_HAVANA.pl b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_HAVANA.pl new file mode 100644 index 000000000..3f0bfc1bc --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_HAVANA.pl @@ -0,0 +1,938 @@ +#!/usr/bin/perl +############################################################################### +# (c) Copyright International Business Machines Corporation 2014. +# All Rights Reserved. +############################################################################### +# COMPONENT: prep_zxcatIVP_HAVANA.pl +# +# This is a preparation script for Installation Verification Program for xCAT +# on z/VM. It prepares the driver script by gathering information from +# OpenStack configuration files on the compute node. +############################################################################### + +use strict; +#use warnings; + +use Getopt::Long; +use Sys::Hostname; +use Socket; + +my %cinderConf; +my %novaConf; +my %neutronConf; +my %ovsNeutronPluginIni; +my %neutronZvmPluginIni; + +my $version = "1.1"; +my $supportString = "Supports code based on the OpenStack Havana release."; + +my $driver; # Name of driver file to be created less the ".pl" +my $driverLocation = "/opt/xcat/bin/"; # Location of the IVP program in xCAT MN. +my $driverPrefix = "zxcatIVPDriver_"; # Prefix used in naming the driver program. +my $displayHelp = 0; # Display help information. +my $ivp = "zxcatIVP.pl"; # z/VM xCAT IVP script name +my $obfuscatePw; # Obfuscate the PW in the driver file that is built +my $scan; # Type of scan to be performed +my $verbose; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Shov version information. + +my $localIpAddress; # Local IP address of system where we are prepping + +# set the usage message +my $usage_string = "Usage:\n + $0\n + or\n + $0 -s serviceToScan -d driverProgramName\n + or\n + $0 --scan serviceToScan -driver driverProgramName\n + -s | --scan Services to scan ('all', 'nova' or 'neutron').\n + -d | --driver Name of driver program to construct.\n + --help Display help information.\n + -v Display script version information.\n + -V Display the verbose message\n"; + +#------------------------------------------------------- + +=head3 buildDriverProgram + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = buildDriverProgram(); + +=cut + +#------------------------------------------------------- +sub buildDriverProgram{ + my $rc; + my @driverText; + + if ( $verbose ) { + print "Building the IVP driver program.\n"; + } + + # Erase any existing driver program. + if ( -e $driver and ! -z $driver ) { + # Make certain the file is one of our driver files. + my $found = `grep 'Function: z/VM xCAT IVP driver program' $driver`; + if ( ! $found ) { + print "$driver is not a z/VM xCAT IVP driver program\n"; + print "File will not be changed.\n"; + return 251; + } else { + # Rename the existing driver file. + print "Existing driver file is being saved as $driver.old\n"; + rename $driver,"$driver.old"; + } + } + + # Open the driver program for output. + $rc = open( my $fileHandle, '>', $driver ) or die; + if ( $rc != 1 ) { + print "Unable to open $driver for output: $!\n"; + return ( 200 + $rc ); + } + + # Construct the file in an array. + push( @driverText, "#!/bin/bash" ); + push( @driverText, "# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html" ); + push( @driverText, "" ); + push( @driverText, "# Function: z/VM xCAT IVP driver program" ); + push( @driverText, "# Built by $0 version $version." ); + push( @driverText, "# $supportString" ); + push( @driverText, "" ); + push( @driverText, "############## Start of Nova Config Properties" ); + if ( exists $novaConf{'DEFAULT'}{'my_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From \'my_ip\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$novaConf{'DEFAULT'}{'my_ip'}\"" ); + } else { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From the local IP address of this system." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$localIpAddress\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_user_profile'} ) { + push( @driverText, "" ); + push( @driverText, "# Default profile used in creation of server instances." ); + push( @driverText, "# From \'zvm_user_profile\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_defaultUserProfile=\"$novaConf{'DEFAULT'}{'zvm_user_profile'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_diskpool'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of disk pools that are expected to exist." ); + push( @driverText, "# From \'zvm_diskpool\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_diskpools=\"$novaConf{'DEFAULT'}{'zvm_diskpool'}\"" ); + } + + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by instances." ); + push( @driverText, "# From \'zvm_fcp_list\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_instFCPList=\"$novaConf{'DEFAULT'}{'zvm_fcp_list'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_host'} ) { + push( @driverText, "" ); + push( @driverText, "# Node of host being managed. If blank, IVP will search for the host node." ); + push( @driverText, "# From \'zvm_host\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_hostNode=\"$novaConf{'DEFAULT'}{'zvm_host'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_master'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT MN (optional)." ); + push( @driverText, "# From \'zvm_xcat_master\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_mnNode=\"$novaConf{'DEFAULT'}{'zvm_xcat_master'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + push( @driverText, "" ); + if ( $obfuscatePw ) { + # Obfuscate the password so that it is not easily read. + # Currently not used due to GUI restrictions that modify the obfuscated password. + push( @driverText, "# User password defined to communicate with xCAT MN. Note: Password is hidden." ); + push( @driverText, "export zxcatIVP_pw_obfuscated=1" ); + push( @driverText, "# From \'zvm_xcat_password\' in /etc/nova/nova.conf." ); + my @chars = split( //, $novaConf{'DEFAULT'}{'zvm_xcat_password'} ); + my @newChars; + foreach my $char ( @chars ) { + $char = ~$char; + push( @newChars, $char ); + } + my $hiddenPw = join( "", @newChars ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$hiddenPw\"" ); + } else { + push( @driverText, "# User password defined to communicate with xCAT MN." ); + push( @driverText, "# From \'zvm_xcat_password\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$novaConf{'DEFAULT'}{'zvm_xcat_password'}\"" ); + } + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected IP address of the xcat MN" ); + push( @driverText, "# From \'zvm_xcat_server\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_xcatMNIp=\"$novaConf{'DEFAULT'}{'zvm_xcat_server'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + push( @driverText, "" ); + push( @driverText, "# User defined to communicate with xCAT MN" ); + push( @driverText, "# From \'zvm_xcat_username\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_xcatUser=\"$novaConf{'DEFAULT'}{'zvm_xcat_username'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by zHCP." ); + push( @driverText, "# From \'zvm_zhcp_fcp_list\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_zhcpFCPList=\"$novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'}\"" ); + } + + push( @driverText, "" ); + push( @driverText, "# Expected space available in the xCAT MN image repository" ); + push( @driverText, "# From \'xcat_free_space_threshold\' in /etc/nova/nova.conf." ); + push( @driverText, "export zxcatIVP_expectedReposSpace=\"$novaConf{'DEFAULT'}{'xcat_free_space_threshold'}G\"" ); + + push( @driverText, "" ); + push( @driverText, "############## End of Nova Config Properties" ); + push( @driverText, "" ); + push( @driverText, "############## Start of Neutron Config Properties" ); + if ( exists $neutronConf{'DEFAULT'}{'base_mac'} ) { + my $prefix = $neutronConf{'DEFAULT'}{'base_mac'}; + $prefix =~ tr/://d; + $prefix = substr( $prefix, 0, 6 ); + push( @driverText, "" ); + push( @driverText, "# User prefix for MAC Addresses of Linux level 2 interfaces" ); + push( @driverText, "# From \'base_mac\' in /etc/neutron/neutron.conf." ); + push( @driverText, "export zxcatIVP_macPrefix=\"$prefix\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat MN's address on the xCAT management network" ); + push( @driverText, "# From \'xcat_mgt_ip\' in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini." ); + push( @driverText, "export zxcatIVP_xcatMgtIp=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat management interface netmask" ); + push( @driverText, "# From \'xcat_mgt_mask\' in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini." ); + push( @driverText, "export zxcatIVP_mgtNetmask=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'}\"" ); + } + if ( exists $ovsNeutronPluginIni{'ovs'}{'network_vlan_ranges'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of networks and possible VLAN ranges" ); + push( @driverText, "# From \'network_vlan_ranges\' in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini." ); + push( @driverText, "export zxcatIVP_networks=\"$ovsNeutronPluginIni{'ovs'}{'network_vlan_ranges'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT zHCP server" ); + push( @driverText, "# From \'xcat_zhcp_nodename\' in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini." ); + push( @driverText, "export zxcatIVP_zhcpNode=\"$neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'}\"" ); + } + # Create the zxcatIVP_vswitchOSAs variable for any networks specified with an rdev list. + my $vswitchOSAs = ''; + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + next if ( $list eq '' ); + $list =~ s/\s+/\,/g; # insert comma between words + $vswitchOSAs = "$section $list $vswitchOSAs"; + } + } + if ( defined $vswitchOSAs ) { + push( @driverText, "" ); + push( @driverText, "# Vswitches and their related OSAs" ); + push( @driverText, "# From \'rdev_list\' in vswitch sections of /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini." ); + push( @driverText, "export zxcatIVP_vswitchOSAs=\"$vswitchOSAs\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Neutron Config Properties" ); + + push( @driverText, "" ); + push( @driverText, "# Name of user under which nova runs, default is nova." ); + push( @driverText, "# If you system is different then change this property." ); + push( @driverText, "export zxcatIVP_expUser=\"nova\"" ); + push( @driverText, "" ); + push( @driverText, "# Controls whether Warnings/Errors detected by the IVP are" ); + push( @driverText, "# logged in the xCAT MN syslog, 0: do not log, 1: log to syslog." ); + push( @driverText, "export zxcatIVP_syslogErrors=1" ); + push( @driverText, "" ); + push( @driverText, "perl $driverLocation$ivp" ); + + # Write the array to the driver file. + foreach (@driverText) { + #print "$_\n"; + print $fileHandle "$_\n"; # Print each entry in our array to the file + } + + close $fileHandle; + + print "$driver was built.\n"; +} + +#------------------------------------------------------- + +=head3 hashFile + + Description : Read a file with equal signs designating + key value pairs and create a hash from it. + Arguments : File to read + Reference to hash that should be constructed. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = hashFile( $file, \%novaConf, 1 ); + +=cut + +#------------------------------------------------------- +sub hashFile{ + my ( $file, $hash, $required ) = @_; + my $section = "null"; + my $caseInsensitive = 1; # assume section/properties are case insensitive + my @parts; + + if ( !-e $file ) { + if ( $required ) { + print "Warning: $file does not exist.\n"; + } else { + print "Info: $file does not exist.\n"; + } + return 601; + } + + if ( $file =~ /.conf$/ ) { + # File is case sensitive, translate sections and property names to uppercase. + $caseInsensitive = 0; + } + + # Read the configuration file and construct the hash of values. + my $out = `egrep -v '(^#|^\\s*\\t*#)' $file`; + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^\[/ ) { + # Section header line + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the line + $line =~ s/^\[+|\]+$//g; # trim [] from ends of the line + if ( $caseInsensitive ) { + $section = lc( $line ); + } else { + $section = $line; + } + } else { + # Property line + @parts = split( "=", $line ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( $caseInsensitive ) { + $parts[0] = lc( $parts[0] ); + } + + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$section}{$parts[0]} = $parts[1]; + #print "$section $parts[0]" . ": " . $parts[1]. "\n"; + #print $parts[0] . ": " . $$hash{$section}{$parts[0]}. "\n"; + } + + } + + return 0; +} + +#------------------------------------------------------- + +=head3 scanCinder + + Description : Scan the cinder configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCinder(); + +=cut + +#------------------------------------------------------- +sub scanCinder{ + my $rc; + + if ( $verbose ) { + print "Scanning the Cinder configuration files.\n"; + } + + # Read the configuration file and construct the hash of values. + my $file = '/etc/cinder/cinder.conf'; + $rc = hashFile( $file, \%cinderConf, 0 ); + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNeutron + + Description : Scan the neutron configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNeutron(); + +=cut + +#------------------------------------------------------- +sub scanNeutron{ + my $rc; + + if ( $verbose ) { + print "Scanning the Neutron configuration files.\n"; + } + + # Read the configuration file and construct the hash of values. + my $file = '/etc/neutron/neutron.conf'; + $rc = hashFile( $file, \%neutronConf, 1 ); + + # Read the configuration file and construct the hash of values. + $file = '/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini'; + $rc = hashFile( $file, \%ovsNeutronPluginIni, 1 ); + + # Read the configuration file and construct the hash of values. + $file = '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini'; + $rc = hashFile( $file, \%neutronZvmPluginIni, 1 ); + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNova + + Description : Scan the Nova configuration files. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNova(); + +=cut + +#------------------------------------------------------- +sub scanNova{ + my $rc; + + if ( $verbose ) { + print "Scanning the Nova configuration files.\n"; + } + # Verify the /etc/nova/nova.conf exists. + my $file = '/etc/nova/nova.conf'; + + $rc = hashFile( $file, \%novaConf, 1 ); + + return $rc; +} + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares and builds a z/VM xCAT IVP driver program + using the information from the configuration files in the + compute node. The default name of the driver program is + '$driverPrefix' following by the IP address of the + system where the driver is being prepared and ending with + '.sh'. + + $supportString + + The following files are scanned for input: + /etc/cinder/cinder.conf + /etc/nova/nova.conf + /etc/neutron/neutron.conf + /etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini + /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini + The constructed driver program can then be uploaded to + the xCAT MN and used to validate the configuration between + the compute node and xCAT.\n\n"; + print $usage_string; + return; +} + +#------------------------------------------------------- + +=head3 validateConfigs + + Description : Compare and validate the configuration + values obtained by the scans. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = validateConfigs(); + +=cut + +#------------------------------------------------------- +sub validateConfigs{ + my $rc = 0; + my $option; + if ( $verbose ) { + print "Performing a local validation of the configuration files.\n"; + } + + #******************************************************* + # Verify required configuration options were specified. + #******************************************************* + if ( keys %cinderConf ) { + my @requiredCinderOpts = (); + foreach $option ( @requiredCinderOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$option} ) { + #print "option:$option.\nvalue:$cinderConf{$option}\n"; + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in /etc/cinder/cinder.conf.\n"; + } + } + } + + my @requiredNovaOpts = ( + "compute_driver", + "config_drive_format", + "force_config_drive", + "host", + 'instance_name_template', + 'zvm_diskpool', + 'zvm_host', + 'zvm_user_profile', + 'zvm_xcat_master', + 'zvm_xcat_server', + 'zvm_xcat_username', + 'zvm_xcat_password', + ); + foreach $option ( @requiredNovaOpts ) { + if ( !exists $novaConf{'DEFAULT'}{$option} ) { + #print "option:$option.\nvalue:$novaConf{$option}\n"; + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in /etc/nova/nova.conf.\n"; + } + } + + my @requiredNeutronConfOpts = ( + 'base_mac', + 'core_plugin', + ); + foreach $option ( @requiredNeutronConfOpts ) { + if ( !exists $neutronConf{'DEFAULT'}{$option} ) { + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in /etc/neutron/neutron.conf.\n"; + } + } + + my @requiredOvsNeutronPluginIniOpts = ( + 'network_vlan_ranges', + 'tenant_network_type', + ); + foreach $option ( @requiredOvsNeutronPluginIniOpts ) { + if ( !exists $ovsNeutronPluginIni{'ovs'}{$option} ) { + print "Warning: \'$option\' is missing from section \'ovs\'\n" . + " in /etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini.\n"; + } + } + + my @requiredNeutronZvmPluginIniOpts = ( + "zvm_xcat_server", + ); + foreach $option ( @requiredNeutronZvmPluginIniOpts ) { + if ( !exists $neutronZvmPluginIni{'agent'}{$option} ) { + print "Warning: \'$option\' is missing from section \'agent\'\n" . + " in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } + } + + #****************************************** + # Verify optional operands were specified. + #****************************************** + if ( keys %cinderConf ) { + if ( !exists $cinderConf{'DEFAULT'}{'san_ip'} and + !exists $cinderConf{'DEFAULT'}{'san_private_key'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_volpool_name'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_vol_iogrp'} and + !exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + print "Info: z/VM specific Cinder keys are not defined in section \'DEFAULT\'\n" . + " in /etc/cinder/cinder.conf. Cinder support for creation of persistent\n" . + " disks for z/VM is not enabled. Further testing of these options will\n" . + " not occur in this script.\n" + } else { + my %optionalCinderConfOpts = ( + "san_ip" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + "san_private_key" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + "storwize_svc_connection_protocol" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + "storwize_svc_volpool_name" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + 'storwize_svc_vol_iogrp' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + 'volume_driver' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + ); + my %defaultCinderConfOpts = (); + foreach my $key ( keys %optionalCinderConfOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$key} ) { + print "Info: \'$key\' is missing from section \'DEFAULT\'\n" . + " in /etc/cinder/cinder.conf.\n"; + if ( $optionalCinderConfOpts{$key} ne '' ) { + print " " . $optionalCinderConfOpts{$key} . "\n"; + } + if ( exists $defaultCinderConfOpts{$key} ) { + $cinderConf{'DEFAULT'}{$key} = $defaultCinderConfOpts{$key}; + } + } + } + } + } + + my %optionalNovaConfOpts = ( + "image_cache_manager_interval" => "Default of 2400 (seconds) will be used.", + "ram_allocation_ratio" => "", + "rpc_response_timeout" => "zVM Live migration may timeout with the default " . + "value (60 seconds).\n The recommended value for z/VM is 180 to allow " . + "zVM live migration\n to succeed.", + "xcat_free_space_threshold" => "Default of 50 (G) will be used.", + "xcat_image_clean_period" => "Default of 30 (days) will be used.", + "zvm_config_drive_inject_password" => "This value will default to 'FALSE'.", + 'zvm_diskpool_type' => "This value will default to \'ECKD\'.", + "zvm_fcp_list" => "Persistent disks cannot be attached to server instances.", + "zvm_zhcp_fcp_list" => "", + "zvm_image_tmp_path" => "Defaults to '/var/lib/nova/images'.", + "zvm_reachable_timeout" => "Default of 300 (seconds) will be used.", + "zvm_scsi_pool" => "Default of \'xcatzfcp\' will be used.", + "zvm_vmrelocate_force" => "", + "zvm_xcat_connection_timeout" => "Default of 3600 seconds will be used.", + ); + my %defaultNovaConfOpts = ( + "xcat_free_space_threshold" => 50, + "xcat_image_clean_period" => 30, + 'zvm_diskpool_type' => 'ECKD', + "zvm_scsi_pool" => "xcatzfcp", + ); + foreach my $key ( keys %optionalNovaConfOpts ) { + if ( !exists $novaConf{'DEFAULT'}{$key} ) { + print "Info: \'$key\' is missing from section \'DEFAULT\'\n" . + " in /etc/nova/nova.conf.\n"; + if ( $optionalNovaConfOpts{$key} ne '' ) { + print " " . $optionalNovaConfOpts{$key} . "\n"; + } + if ( exists $defaultNovaConfOpts{$key} ) { + $novaConf{'DEFAULT'}{$key} = $defaultNovaConfOpts{$key}; + } + } + } + + my %optionalNeutronConfOpts = (); + my %defaultNeutronConfOpts = (); + foreach my $key ( keys %optionalNeutronConfOpts ) { + if ( !exists $neutronConf{'DEFAULT'}{$key} ) { + print "Info: \'$key\' is missing from section \'DEFAULT\'\n" . + " in /etc/neutron/neutron.conf.\n"; + if ( $optionalNeutronConfOpts{$key} ne '' ) { + print " " . $optionalNeutronConfOpts{$key} . "\n"; + } + if ( exists $defaultNeutronConfOpts{$key} ) { + $neutronConf{'DEFAULT'}{$key} = $defaultNeutronConfOpts{$key}; + } + } + } + + my %optionalOvsNeutronPluginIniOpts = (); + my %defaultOvsNeutronPluginIniOpts = (); + foreach my $key ( keys %optionalOvsNeutronPluginIniOpts ) { + if ( !exists $ovsNeutronPluginIni{'agent'}{$key} ) { + print "Info: \'$key\' is missing from section \'agent\'\n" . + " in /etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini.\n"; + if ( $optionalOvsNeutronPluginIniOpts{$key} ne '' ) { + print " " . $optionalOvsNeutronPluginIniOpts{$key} . "\n"; + } + if ( exists $defaultOvsNeutronPluginIniOpts{'agent'}{$key} ) { + $ovsNeutronPluginIni{'agent'}{$key} = $defaultOvsNeutronPluginIniOpts{$key}; + } + } + } + + my %optionalNeutronZvmPluginIniOpts = ( + "xcat_mgt_ip" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "xcat_mgt_mask" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "polling_interval" => "A default value of \'2\' will be used.", + "xcat_zhcp_nodename" => "A default value of \'zhcp\' will be used.", + "zvm_xcat_password" => "A default value of \'admin\' is used.", + "zvm_xcat_timeout" => "A default value of 300 seconds is used.", + "zvm_xcat_username" => "A default value of \'admin\' is used.", + ); + my %defaultNeutronZvmPluginIniOpts = ( + "polling_interval" => 2, + "xcat_zhcp_nodename" => "zhcp", + "zvm_xcat_password" => "admin", + "zvm_xcat_timeout" => 300, + "zvm_xcat_username" => "admin", + ); + foreach my $key ( keys %optionalNeutronZvmPluginIniOpts ) { + if ( !exists $neutronZvmPluginIni{'agent'}{$key} ) { + print "Info: \'$key\' is missing from section \'agent\'\n" . + " in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + if ( $optionalNeutronZvmPluginIniOpts{$key} ne '' ) { + print " " . $optionalNeutronZvmPluginIniOpts{$key} . "\n"; + } + if ( exists $defaultNeutronZvmPluginIniOpts{'agent'}{$key} ) { + $neutronZvmPluginIni{'agent'}{$key} = $defaultNeutronZvmPluginIniOpts{$key}; + } + } + } + + # Verify xCAT users are the same. + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in /etc/nova/nova.conf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_username'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Warning: xCAT user names mismatch; review 'zvm_xcat_username':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_username'}\' in /etc/nova/nova.conf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_username'}\' in\n" . + " /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } + } + + # Verify xCAT user passwords are the same. + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'.\n" . + " It is not specified in /etc/nova/nova.conf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'. It is not specified\n" . + " in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_password'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Warning: xCAT user passwords are not the same:\n" . + " Please review 'zvm_xcat_password' in /etc/nova/nova.conf and\n" . + " /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } + } + + # Verify the xcat server IP addresses are the same. + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in /etc/nova/nova.conf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_server'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Warning: xCAT server addresses mismatch; review 'zvm_xcat_server':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_server'}\' in /etc/nova/nova.conf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_server'}\' in /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.\n"; + } + } + + # Verify the instance name template is valid + if ( exists $novaConf{'DEFAULT'}{'instance_name_template'} ) { + # Use sprintf which is close enough to the python % support for formatting to construct a sample. + my $base_name = sprintf( $novaConf{'DEFAULT'}{'instance_name_template'}, 1 ); + if ( length( $base_name ) > 8 ) { + print "Warning: In /etc/nova/nova.conf, section \`DEFAULT\`, instance_name_template would\n" . + " construct a value greater than 8 in length: \'$novaConf{'DEFAULT'}{'instance_name_template'}\'.\n"; + } + if ( $novaConf{'DEFAULT'}{'instance_name_template'} =~ /(^RSZ)/ or $novaConf{'DEFAULT'}{'instance_name_template'} =~ /(^rsz)/ ) { + print "Warning: In /etc/nova/nova.conf, instance_name_template begins\n" . + " with 'RSZ' or 'rsz': \'$novaConf{'DEFAULT'}{'instance_name_template'}\'\n"; + } + } + + # Verify the compute_driver is for z/VM + if ( exists $novaConf{'DEFAULT'}{'compute_driver'} ) { + if ( $novaConf{'DEFAULT'}{'compute_driver'} ne "nova.virt.zvm.ZVMDriver" and + $novaConf{'DEFAULT'}{'compute_driver'} ne "zvm.ZVMDriver") { + print "Warning: In /etc/nova/nova.conf, compute_driver does not contain the\n" . + " expected value of \'zvm.ZVMDriver\' and instead contains:\n" . + " \'$novaConf{'DEFAULT'}{'compute_driver'}\'\n"; + } + } + + # Check whether the rpc timeout is too small for z/VM + if ( exists $novaConf{'DEFAULT'}{'rpc_response_timeout'} ) { + if ( $novaConf{'DEFAULT'}{'rpc_response_timeout'} < 180 ) { + print "Warning: In /etc/nova/nova.conf, section \'DEFAULT\', rpc_response_timeout\n" . + " specifies a value, \'$novaConf{'DEFAULT'}{'rpc_response_timeout'}\', which is " . + "less than the recommended value\n of \'180\'.\n"; + } + } + + # Verify all SCSI disk operands are specified, if one exists. + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} or exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + print "Warning: In /etc/nova/nova.conf, \'zvm_fcp_list\' does not exist but\n" . + " but other SCSI disk related operands exist. Both should be \'\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + if ( !exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + print "Warning: In /etc/nova/nova.conf, \'zvm_zhcp_fcp_list\' does not exist but\n" . + " but other SCSI disk related operands exist. Both should be \'\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + } + + # Verify any rdev_list in Neutron z/VM Plugin ini file contains a single value and/or not a comma + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list eq '' ) { + print "Warning: In /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini, section \'$section\',\n" . + " \'rdev_list\' is specified but has no value.\n"; + } else { + my @vals = split ( /\s/, $list ); + if ( $#vals > 0 ) { + # $#vals is array size - 1. + print "Warning: In /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini, section \'$section\',\n" . + " \'rdev_list\' contains too many values.\n"; + } + foreach my $op ( @vals ) { + if ( $op =~ m/[^0-9a-fA-F]+/ ) { + print "Warning: In /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini, section \'$section\',\n" . + " \'rdev_list\' contains non-hexadecimal characters: \'$op\'.\n"; + } elsif ( length($op) > 4 ) { + print "Warning: In /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini, section \'$section\',\n" . + " \'rdev_list\' contains a value that is not 1-4 characters in\n" . + " length: \'$op\'.\n"; + } + } + } + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ) { + if ( $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ne 'FC' ) { + print "Warning: In /etc/cinder/cinder.conf, section \'DEFAULT\',\n" . + " storwize_svc_connection_protocol specifies a value, " . + "\'$cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'}\',\n" . + " which is not the required value of \'FC\'.\n"; + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + if ( $cinderConf{'DEFAULT'}{'volume_driver'} ne 'cinder.volume.drivers.zvm.storwize_svc.StorwizeSVCZVMDriver' ) { + print "Warning: In /etc/cinder/cinder.conf, section \'DEFAULT\', volume_driver specifies\n" . + " a value, \'$cinderConf{'DEFAULT'}{'volume_driver'}\',\n which is " . + "not the required value of\n \'cinder.volume.drivers.zvm.storwize_svc.StorwizeSVCZVMDriver\'.\n"; + } + } + + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $rc = 0; +my $clearPwOpt; +my $obfuscateOpt; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( 's|scan=s' => \$scan, + 'd|driver=s' => \$driver, + 'h|help' => \$displayHelp, + #'c' => \$clearPwOpt, + #'o' => \$obfuscateOpt, + 'v' => \$versionOpt, + 'V' => \$verbose )) { + print $usage_string; + goto FINISH; +} + +if ( $versionOpt ) { + print "Version: $version\n"; + print "$supportString\n"; +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH; +} + +$localIpAddress = inet_ntoa((gethostbyname(hostname))[4]); + +if ( defined( $scan ) ) { + if ( $verbose ) { + print "Operand --scan: $scan\n"; + } + if ( 'all nova neutron' !~ $scan ) { + print "--scan operand($scan) is not all, nova or neutron\n"; + $rc = 400; + goto FINISH; + } +} else { + $scan = 'all'; +} + +if ( defined( $driver ) ) { + if ( $verbose ) { + print "Operand --driver: $driver\n"; + } + $driver = "$driver.sh"; +} else { + $driver = "$driverPrefix$localIpAddress.sh"; +} + +if ( $scan =~ 'all' or $scan =~ 'nova' ) { + !( $rc = scanNova() ) or goto FINISH; +} + +if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + scanCinder(); +} + +if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + !( $rc = scanNeutron() ) or goto FINISH; +} + +#if ( $obfuscateOpt ) { +# print "obfuscateOpt: $obfuscateOpt\n"; +# $obfuscatePw = 1; +#} elsif ( $clearPwOpt ) { +# $obfuscatePw = 0; +#} + +!( $rc = validateConfigs() ) or goto FINISH; + +!( $rc = buildDriverProgram() ) or goto FINISH; + +FINISH: +exit $rc; + diff --git a/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_ICEHOUSE.pl b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_ICEHOUSE.pl new file mode 100644 index 000000000..db8f9d5f5 --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_ICEHOUSE.pl @@ -0,0 +1,1096 @@ +#!/usr/bin/perl +############################################################################### +# (c) Copyright International Business Machines Corporation 2014. +# All Rights Reserved. +############################################################################### +# COMPONENT: prep_zxcatIVP_ICEHOUSE.pl +# +# This is a preparation script for Installation Verification Program for xCAT +# on z/VM. It prepares the driver script by gathering information from +# OpenStack configuration files on the compute node. +############################################################################### + +use strict; +use warnings; + +use File::Basename; +use Getopt::Long; +use Sys::Hostname; +use Socket; + +my %cinderConf; +my %novaConf; +my %neutronConf; +my %ml2ConfIni; +my %neutronZvmPluginIni; +my %dmssicmoCopy; + +my $version = "2.2"; +my $supportString = "Supports code based on the OpenStack Icehouse release."; + +my $cmoAppliance = 0; # Assumed to not be run in a CMO appliance +my $cmoVersionString = ''; # CMO version string for a CMO appliance +my $driver; # Name of driver file to be created less the ".pl" +my $driverLocation = "/opt/xcat/bin/"; # Location of the IVP program in xCAT MN. +my $driverPrefix = "zxcatIVPDriver_"; # Prefix used in naming the driver program. +my $displayHelp = 0; # Display help information. +my $ivp = "zxcatIVP.pl"; # z/VM xCAT IVP script name +my $obfuscatePw; # Obfuscate the PW in the driver file that is built +my $scan; # Type of scan to be performed +my $verbose; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Shov version information. + +my $localIpAddress; # Local IP address of system where we are prepping + +# Locations of configuration files +my $locCinderConf = '/etc/cinder/cinder.conf'; +my $locMl2ConfIni = '/etc/neutron/plugins/ml2/ml2_conf.ini'; +my $locNeutronConf = '/etc/neutron/neutron.conf'; +my $locNeutronZvmPluginIni = '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini'; +my $locNovaConf = '/etc/nova/nova.conf'; +my $locVersionFileCMO = '/opt/ibm/cmo/version'; +my $locDMSSICMOCopy = '/var/lib/sspmod/DMSSICMO.COPY'; + +# set the usage message +my $usage_string = "Usage:\n + $0\n + or\n + $0 -s serviceToScan -d driverProgramName\n + or\n + $0 --scan serviceToScan -driver driverProgramName\n + -s | --scan Services to scan ('all', 'nova' or 'neutron').\n + -d | --driver File specification of driver program to construct, or\n + name of directory to contain the driver program.\n + --help Display help information.\n + -v Display script version information.\n + -V Display the verbose message\n"; + +#------------------------------------------------------- + +=head3 buildDriverProgram + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = buildDriverProgram(); + +=cut + +#------------------------------------------------------- +sub buildDriverProgram{ + my $rc; + my @driverText; + + if ( $verbose ) { + print "Building the IVP driver program.\n"; + } + + # Erase any existing driver program. + if ( -e $driver and ! -z $driver ) { + # Make certain the file is one of our driver files. + my $found = `grep 'Function: z/VM xCAT IVP driver program' $driver`; + if ( ! $found ) { + print "$driver is not a z/VM xCAT IVP driver program\n"; + print "File will not be changed.\n"; + return 251; + } else { + # Rename the existing driver file. + print "Existing driver file is being saved as $driver.old\n"; + rename $driver,"$driver.old"; + } + } + + # Open the driver program for output. + $rc = open( my $fileHandle, '>', $driver ) or die; + if ( $rc != 1 ) { + print "Unable to open $driver for output: $!\n"; + return ( 200 + $rc ); + } + + # Construct the file in an array. + push( @driverText, "#!/bin/bash" ); + push( @driverText, "# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html" ); + push( @driverText, "#" ); + push( @driverText, "# Function: z/VM xCAT IVP driver program" ); + push( @driverText, "# Built by $0 version $version." ); + push( @driverText, "# $supportString" ); + push( @driverText, "" ); + if ( $cmoAppliance == 1 ) { + push( @driverText, "# System is a CMO Appliance" ); + if ( exists $dmssicmoCopy{'openstack_system_role'} ) { + push( @driverText, "# CMO system role: $dmssicmoCopy{'openstack_system_role'}" ); + } else { + push( @driverText, "# CMO system role: unknown" ); + } + push( @driverText, "# $cmoVersionString" ); + push( @driverText, "" ); + } + push( @driverText, "############## Start of Nova Config Properties" ); + if ( exists $novaConf{'DEFAULT'}{'my_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From \'my_ip\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$novaConf{'DEFAULT'}{'my_ip'}\"" ); + } else { + print "Info: 'my_ip' property is missing from section 'DEFAULT'\n" . + " in $locNovaConf. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From the local IP address of this system." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$localIpAddress\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_user_profile'} ) { + push( @driverText, "" ); + push( @driverText, "# Default profile used in creation of server instances." ); + push( @driverText, "# From \'zvm_user_profile\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_defaultUserProfile=\"$novaConf{'DEFAULT'}{'zvm_user_profile'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_diskpool'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of disk pools that are expected to exist." ); + push( @driverText, "# From \'zvm_diskpool\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_diskpools=\"$novaConf{'DEFAULT'}{'zvm_diskpool'}\"" ); + } + + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by instances." ); + push( @driverText, "# From \'zvm_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_instFCPList=\"$novaConf{'DEFAULT'}{'zvm_fcp_list'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_host'} ) { + push( @driverText, "" ); + push( @driverText, "# Node of host being managed. If blank, IVP will search for the host node." ); + push( @driverText, "# From \'zvm_host\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_hostNode=\"$novaConf{'DEFAULT'}{'zvm_host'}\"" ); + + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_master'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT MN (optional)." ); + push( @driverText, "# From \'zvm_xcat_master\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_mnNode=\"$novaConf{'DEFAULT'}{'zvm_xcat_master'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + push( @driverText, "" ); + if ( $obfuscatePw ) { + # Obfuscate the password so that it is not easily read. + # Currently not used due to GUI restrictions that modify the obfuscated password. + push( @driverText, "# User password defined to communicate with xCAT MN. Note: Password is hidden." ); + push( @driverText, "export zxcatIVP_pw_obfuscated=1" ); + push( @driverText, "# From \'zvm_xcat_password\' in $locNovaConf." ); + my @chars = split( //, $novaConf{'DEFAULT'}{'zvm_xcat_password'} ); + my @newChars; + foreach my $char ( @chars ) { + $char = ~$char; + push( @newChars, $char ); + } + my $hiddenPw = join( "", @newChars ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$hiddenPw\"" ); + } else { + push( @driverText, "# User password defined to communicate with xCAT MN." ); + push( @driverText, "# From \'zvm_xcat_password\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$novaConf{'DEFAULT'}{'zvm_xcat_password'}\"" ); + } + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected IP address of the xcat MN" ); + push( @driverText, "# From \'zvm_xcat_server\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatMNIp=\"$novaConf{'DEFAULT'}{'zvm_xcat_server'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + push( @driverText, "" ); + push( @driverText, "# User defined to communicate with xCAT MN" ); + push( @driverText, "# From \'zvm_xcat_username\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatUser=\"$novaConf{'DEFAULT'}{'zvm_xcat_username'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by zHCP." ); + push( @driverText, "# From \'zvm_zhcp_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_zhcpFCPList=\"$novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'}\"" ); + } + + push( @driverText, "" ); + push( @driverText, "# Expected space available in the xCAT MN image repository" ); + push( @driverText, "# From \'xcat_free_space_threshold\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_expectedReposSpace=\"$novaConf{'DEFAULT'}{'xcat_free_space_threshold'}G\"" ); + + push( @driverText, "" ); + push( @driverText, "############## End of Nova Config Properties" ); + push( @driverText, "" ); + push( @driverText, "############## Start of Neutron Config Properties" ); + if ( exists $neutronConf{'DEFAULT'}{'base_mac'} ) { + my $prefix = $neutronConf{'DEFAULT'}{'base_mac'}; + $prefix =~ tr/://d; + $prefix = substr( $prefix, 0, 6 ); + push( @driverText, "" ); + push( @driverText, "# User prefix for MAC Addresses of Linux level 2 interfaces" ); + push( @driverText, "# From \'base_mac\' in $locNeutronConf." ); + push( @driverText, "export zxcatIVP_macPrefix=\"$prefix\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat MN's address on the xCAT management network" ); + push( @driverText, "# From \'xcat_mgt_ip\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_xcatMgtIp=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat management interface netmask" ); + push( @driverText, "# From \'xcat_mgt_mask\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_mgtNetmask=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'}\"" ); + } + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} or + exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + my $list = ''; + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} and + $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} ne '*' ) { + $list = $ml2ConfIni{'ml2_type_flat'}{'flat_networks'}; + } + if ( exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + if ( $list ne '' ) { + $list = "$list,"; + } + $list = "$list$ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'}"; + } + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Array of networks and possible VLAN ranges" ); + push( @driverText, "# From $locMl2ConfIni,"); + push( @driverText, "# \'flat_networks\' in section \'ml2_type_flat\' and "); + push( @driverText, "# \'network_vlan_ranges\' in section \'ml2_type_vlan\'."); + push( @driverText, "export zxcatIVP_networks=\"$list\"" ); + } + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT zHCP server" ); + push( @driverText, "# From \'xcat_zhcp_nodename\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_zhcpNode=\"$neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'}\"" ); + } + # Create the zxcatIVP_vswitchOSAs variable for any networks specified with an rdev list. + my $vswitchOSAs = ''; + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + next if ( $list eq '' ); + $list =~ s/\s+/\,/g; # insert comma between words + $vswitchOSAs = "$section $list $vswitchOSAs"; + } + } + if ( defined $vswitchOSAs ) { + push( @driverText, "" ); + push( @driverText, "# Vswitches and their related OSAs" ); + push( @driverText, "# From \'rdev_list\' in vswitch sections of $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_vswitchOSAs=\"$vswitchOSAs\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Neutron Config Properties" ); + + push( @driverText, "" ); + push( @driverText, "# Name of user under which nova runs, default is nova." ); + push( @driverText, "# If you system is different then change this property." ); + push( @driverText, "export zxcatIVP_expUser=\"nova\"" ); + push( @driverText, "" ); + push( @driverText, "# Controls whether Warnings/Errors detected by the IVP are" ); + push( @driverText, "# logged in the xCAT MN syslog, 0: do not log, 1: log to syslog." ); + push( @driverText, "export zxcatIVP_syslogErrors=1" ); + push( @driverText, "" ); + push( @driverText, "perl $driverLocation$ivp" ); + + # Write the array to the driver file. + foreach (@driverText) { + #print "$_\n"; + print $fileHandle "$_\n"; # Print each entry in our array to the file + } + + close $fileHandle; + + print "$driver was built.\n"; +} + +#------------------------------------------------------- + +=head3 hashFile + + Description : Read a file with equal signs designating + key value pairs and create a hash from it. + Arguments : File to read + Reference to hash that should be constructed. + File is required to exist or there is a serious error. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = hashFile( $file, \%novaConf, 1 ); + +=cut + +#------------------------------------------------------- +sub hashFile{ + my ( $file, $hash, $required ) = @_; + my $section = "null"; + my $caseSensitive = 0; # assume section/properties are case insensitive + my @parts; + my $out; + #print "File: $file\n"; + + if ( !-e $file ) { + if ( $required ) { + print "Warning: $file does not exist.\n"; + } else { + print "Info: $file does not exist.\n"; + } + return 601; + } + + if (( $file =~ /.conf$/ ) or ( $file =~ /.COPY$/ )) { + # File is case sensitive, translate sections and property names to uppercase. + $caseSensitive = 1; + } + + # Read the configuration file and construct the hash of values. + if (( $file =~ /.conf$/ ) or ( $file =~ /.ini$/ )) { + $out = `egrep -v '(^#|^\\s*\\t*#)' $file`; + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^\[/ ) { + # Section header line + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the line + $line =~ s/^\[+|\]+$//g; # trim [] from ends of the line + if ( !$caseSensitive ) { + $section = lc( $line ); + } else { + $section = $line; + } + } else { + # Property line + @parts = split( "=", $line ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$section}{$parts[0]} = $parts[1]; + #print "$section $parts[0]" . ": " . $parts[1]. "\n"; + #print $parts[0] . ": " . $$hash{$section}{$parts[0]}. "\n"; + } + } + } else { + # Hash .COPY files + # Read the file and remove comment lines and sequence columns (72-80) + $out = `grep -v ^\$ $file| cut -c1-71`; + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Weed out blank lines + $line =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $line ) == 0 ); + + # Parse the line + @parts = split( "=", $line ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + # Add the property to the hash if it has data. + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + $parts[1] =~ s/^"+|"+$//g; # trim double quotes from both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$parts[0]} = $parts[1]; + #print $parts[0] . ": " . $$hash{$parts[0]}. "\n"; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 scanCinder + + Description : Scan the cinder configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCinder(); + +=cut + +#------------------------------------------------------- +sub scanCinder{ + my $rc; + + if ( $verbose ) { + print "Scanning the Cinder configuration files.\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCinderConf, \%cinderConf, 0 ); + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNeutron + + Description : Scan the neutron configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNeutron(); + +=cut + +#------------------------------------------------------- +sub scanNeutron{ + my $rc; + + if ( $verbose ) { + print "Scanning the Neutron configuration files.\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronConf, \%neutronConf, 1 ); + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locMl2ConfIni, \%ml2ConfIni, 1 ); + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronZvmPluginIni, \%neutronZvmPluginIni, 1 ); + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNova + + Description : Scan the Nova configuration files. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNova(); + +=cut + +#------------------------------------------------------- +sub scanNova{ + my $rc; + + if ( $verbose ) { + print "Scanning the Nova configuration files.\n"; + } + # Verify the /etc/nova/nova.conf exists. + $rc = hashFile( $locNovaConf, \%novaConf, 1 ); + + return $rc; +} + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares and builds a z/VM xCAT IVP driver program + using the information from the configuration files in the + compute node. The default name of the driver program is + '$driverPrefix' following by the IP address of the + system where the driver is being prepared and ending with '.sh'. + + $supportString + + The following files are scanned for input: + $locCinderConf + $locNovaConf + $locNeutronConf + $locMl2ConfIni + $locNeutronZvmPluginIni + The constructed driver program can then be uploaded to + the xCAT MN and used to validate the configuration between + the compute node and xCAT.\n\n"; + print $usage_string; + return; +} + +#------------------------------------------------------- + +=head3 validateConfigs + + Description : Compare and validate the configuration + values obtained by the scans. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = validateConfigs(); + +=cut + +#------------------------------------------------------- +sub validateConfigs{ + my $rc = 0; + my $option; + if ( $verbose ) { + print "Performing a local validation of the configuration files.\n"; + } + + #******************************************************* + # Verify required configuration options were specified. + #******************************************************* + if ( keys %cinderConf ) { + my @requiredCinderOpts = (); + foreach $option ( @requiredCinderOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$option} ) { + #print "option:$option.\nvalue:$cinderConf{$option}\n"; + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in $locCinderConf.\n"; + } + } + } + + my @requiredNovaOpts = ( + 'DEFAULT','compute_driver', + 'DEFAULT','config_drive_format', + 'DEFAULT','force_config_drive', + 'DEFAULT','host', + 'DEFAULT','instance_name_template', + 'DEFAULT','zvm_diskpool', + 'DEFAULT','zvm_host', + 'DEFAULT','zvm_user_profile', + 'DEFAULT','zvm_xcat_master', + 'DEFAULT','zvm_xcat_server', + 'DEFAULT','zvm_xcat_username', + 'DEFAULT','zvm_xcat_password', + ); + for ( my $i = 0; $i < $#requiredNovaOpts; $i = $i + 2 ) { + my $section = $requiredNovaOpts[$i]; + my $option = $requiredNovaOpts[$i+1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + } + } + + my @requiredNeutronConfOpts = ( + 'DEFAULT','base_mac', + 'DEFAULT','core_plugin', + ); + for ( my $i = 0; $i < $#requiredNeutronConfOpts; $i = $i + 2 ) { + my $section = $requiredNeutronConfOpts[$i]; + my $option = $requiredNeutronConfOpts[$i+1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf/neutron/neutron.conf.\n"; + } + } + + if ( keys %ml2ConfIni > 0 ) { + my @requiredMl2ConfIniOpts = ( + 'ml2','mechanism_drivers', + 'ml2','tenant_network_types', + 'ml2','type_drivers', + ); + for ( my $i = 0; $i < $#requiredMl2ConfIniOpts; $i = $i + 2 ) { + my $section = $requiredMl2ConfIniOpts[$i]; + my $option = $requiredMl2ConfIniOpts[$i+1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni/neutron/plugins/ml2/ml2_conf.ini.\n"; + } + } + } + + my @requiredNeutronZvmPluginIniOpts = ( + 'agent', 'zvm_host', + 'agent','zvm_xcat_server', + + ); + for ( my $i = 0; $i < $#requiredNeutronZvmPluginIniOpts; $i = $i + 2 ) { + my $section = $requiredNeutronZvmPluginIniOpts[$i]; + my $option = $requiredNeutronZvmPluginIniOpts[$i+1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + } + } + + #****************************************** + # Verify optional operands were specified. + #****************************************** + if ( keys %cinderConf ) { + if ( exists $dmssicmoCopy{'openstack_system_role'} and uc( $dmssicmoCopy{'openstack_system_role'} ) eq "COMPUTE" ) { + print "Info: CMO appliance system role is a compute server.\n Cinder will not be validated.\n"; + } elsif ( !exists $cinderConf{'DEFAULT'}{'san_ip'} and + !exists $cinderConf{'DEFAULT'}{'san_private_key'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_volpool_name'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_vol_iogrp'} and + !exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + print "Info: z/VM specific Cinder keys are not defined in $locCinderConf.\n" . + " Cinder support for creation of persistent disks for z/VM\n" . + " is not enabled. Further testing of these options will\n" . + " not occur in this script.\n" + } else { + my %optionalCinderConfOpts = ( + "DEFAULT san_ip" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + "DEFAULT san_private_key" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_connection_protocol" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_volpool_name" => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + 'DEFAULT storwize_svc_vol_iogrp' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + 'DEFAULT volume_driver' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + ); + my %defaultCinderConfOpts = (); + foreach my $key ( keys %optionalCinderConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $cinderConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locCinderConf.\n"; + if ( $optionalCinderConfOpts{$key} ne '' ) { + print " " . $optionalCinderConfOpts{$key} . "\n"; + } + if ( exists $defaultCinderConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCinderConfOpts{$key}; + } + } + } + } + } + + my %optionalNovaConfOpts = ( + "DEFAULT image_cache_manager_interval" => "Default of 2400 (seconds) will be used.", + "DEFAULT ram_allocation_ratio" => "", + "DEFAULT rpc_response_timeout" => "zVM Live migration may timeout with the default " . + "value (60 seconds).\n The recommended value for z/VM is 180 to allow " . + "zVM live migration\n to succeed.", + "DEFAULT xcat_free_space_threshold" => "Default of 50 (G) will be used.", + "DEFAULT xcat_image_clean_period" => "Default of 30 (days) will be used.", + "DEFAULT zvm_config_drive_inject_password" => "This value will default to 'FALSE'.", + 'DEFAULT zvm_diskpool_type' => "This value will default to \'ECKD\'.", + "DEFAULT zvm_fcp_list" => "Persistent disks cannot be attached to server instances.", + "DEFAULT zvm_zhcp_fcp_list" => "", + "DEFAULT zvm_image_tmp_path" => "Defaults to '/var/lib/nova/images'.", + "DEFAULT zvm_reachable_timeout" => "Default of 300 (seconds) will be used.", + "DEFAULT zvm_scsi_pool" => "Default of \'xcatzfcp\' will be used.", + "DEFAULT zvm_vmrelocate_force" => "", + "DEFAULT zvm_xcat_connection_timeout" => "Default of 3600 seconds will be used.", + "DEFAULT zvm_image_compression_level" => "Image compression is controlled by the xCAT ZHCP settings.conf file." + ); + my %defaultNovaConfOpts = ( + "DEFAULT xcat_free_space_threshold" => 50, + "DEFAULT xcat_image_clean_period" => 30, + 'DEFAULT zvm_diskpool_type' => 'ECKD', + "DEFAULT zvm_scsi_pool" => "xcatzfcp", + ); + foreach my $key ( keys %optionalNovaConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + if ( $optionalNovaConfOpts{$key} ne '' ) { + print " " . $optionalNovaConfOpts{$key} . "\n"; + } + if ( exists $defaultNovaConfOpts{$key} ) { + $novaConf{$section}{$option} = $defaultNovaConfOpts{$key}; + } + } + } + + my %optionalNeutronConfOpts = (); + my %defaultNeutronConfOpts = (); + foreach my $key ( keys %optionalNeutronConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf.\n"; + if ( $optionalNeutronConfOpts{$key} ne '' ) { + print " " . $optionalNeutronConfOpts{$key} . "\n"; + } + if ( exists $defaultNeutronConfOpts{$key} ) { + $neutronConf{$section}{$option} = $defaultNeutronConfOpts{$key}; + } + } + } + + my %optionalMl2ConfIniOpts = ( + 'ml2_type_vlan network_vlan_ranges' => "", + 'ml2_type_flat flat_networks' => "", + ); + my %defaultMl2ConfIniOpts = (); + foreach my $key ( keys %optionalMl2ConfIniOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni.\n"; + if ( $optionalMl2ConfIniOpts{$key} ne '' ) { + print " " . $optionalMl2ConfIniOpts{$key} . "\n"; + } + if ( exists $defaultMl2ConfIniOpts{$key} ) { + $ml2ConfIni{$section}{$option} = $defaultMl2ConfIniOpts{$key}; + } + } + } + + my %optionalNeutronZvmPluginIniOpts = ( + "agent xcat_mgt_ip" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent xcat_mgt_mask" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent polling_interval" => "A default value of \'2\' will be used.", + "agent xcat_zhcp_nodename" => "A default value of \'zhcp\' will be used.", + "agent zvm_xcat_password" => "A default value of \'admin\' is used.", + "agent zvm_xcat_timeout" => "A default value of 300 seconds is used.", + "agent zvm_xcat_username" => "A default value of \'admin\' is used.", + ); + my %defaultNeutronZvmPluginIniOpts = ( + "agent polling_interval" => 2, + "agent xcat_zhcp_nodename" => "zhcp", + "agent zvm_xcat_password" => "admin", + "agent zvm_xcat_timeout" => 300, + "agent zvm_xcat_username" => "admin", + ); + foreach my $key ( keys %optionalNeutronZvmPluginIniOpts ) { + my @opts = split( '\s', $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + if ( $optionalNeutronZvmPluginIniOpts{$key} ne '' ) { + print " " . $optionalNeutronZvmPluginIniOpts{$key} . "\n"; + } + if ( exists $defaultNeutronZvmPluginIniOpts{$key} ) { + $neutronZvmPluginIni{$section}{$option} = $defaultNeutronZvmPluginIniOpts{$key}; + } + } + } + + # Verify xCAT users are the same. + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_username'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Warning: xCAT user names mismatch; review 'zvm_xcat_username':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_username'}\' in $locNovaConf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_username'}\' in\n" . + " $locNeutronZvmPluginIni.\n"; + } + } + + # Verify xCAT user passwords are the same. + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'. It is not specified\n" . + " in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_password'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Warning: xCAT user passwords are not the same:\n" . + " Please review 'zvm_xcat_password' in $locNovaConf and\n" . + " $locNeutronZvmPluginIni.\n"; + } + } + + # Verify the xcat server IP addresses are the same. + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_server'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Warning: xCAT server addresses mismatch; review 'zvm_xcat_server':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_server'}\' in $locNovaConf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_server'}\' in $locNeutronZvmPluginIni.\n"; + } + } + + # Verify host and zvm_host properties are the same. + if ( exists $novaConf{'DEFAULT'}{'host'} and + exists $neutronZvmPluginIni{'agent'}{'zvm_host'} ) { + if ( $novaConf{'DEFAULT'}{'host'} ne $neutronZvmPluginIni{'agent'}{'zvm_host'} ) { + print "Warning: 'host' property in section 'DEFAULT' in $locNovaConf\n" . + " does not specify the same value as 'zvm_host' property in section\n" . + " 'agent' in $locNeutronZvmPluginIni.\n"; + } + } + + # Verify the instance name template is valid + if ( exists $novaConf{'DEFAULT'}{'instance_name_template'} ) { + # Use sprintf which is close enough to the python % support for formatting to construct a sample. + my $base_name = sprintf( $novaConf{'DEFAULT'}{'instance_name_template'}, 1 ); + if ( length( $base_name ) > 8 ) { + print "Warning: In $locNovaConf, section \`DEFAULT\`, instance_name_template would\n" . + " construct a value greater than 8 in length: \'$novaConf{'DEFAULT'}{'instance_name_template'}\'.\n"; + } + if ( $novaConf{'DEFAULT'}{'instance_name_template'} =~ /(^RSZ)/ or $novaConf{'DEFAULT'}{'instance_name_template'} =~ /(^rsz)/ ) { + print "Warning: In $locNovaConf, instance_name_template begins\n" . + " with 'RSZ' or 'rsz': \'$novaConf{'DEFAULT'}{'instance_name_template'}\'\n"; + } + } + + # Verify the compute_driver is for z/VM + if ( exists $novaConf{'DEFAULT'}{'compute_driver'} ) { + if ( $novaConf{'DEFAULT'}{'compute_driver'} ne "nova.virt.zvm.ZVMDriver" and + $novaConf{'DEFAULT'}{'compute_driver'} ne "zvm.ZVMDriver") { + print "Warning: In $locNovaConf, compute_driver does not contain the\n" . + " expected value of \'zvm.ZVMDriver\' and instead contains:\n" . + " \'$novaConf{'DEFAULT'}{'compute_driver'}\'\n"; + } + } + + # Check whether the rpc timeout is too small for z/VM + if ( exists $novaConf{'DEFAULT'}{'rpc_response_timeout'} ) { + if ( $novaConf{'DEFAULT'}{'rpc_response_timeout'} < 180 ) { + print "Warning: In $locNovaConf, section \'DEFAULT\', rpc_response_timeout\n" . + " specifies a value, \'$novaConf{'DEFAULT'}{'rpc_response_timeout'}\', which is " . + "less than the recommended value\n of \'180\'.\n"; + } + } + + # Verify all SCSI disk operands are specified, if one exists. + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} or exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + if ( !exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_zhcp_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + } + + # Verify neutron.conf operands with a fixed set of possible values + if ( exists $neutronConf{'DEFAULT'}{'core_plugin'} ) { + if ( $neutronConf{'DEFAULT'}{'core_plugin'} ne 'neutron.plugins.ml2.plugin.Ml2Plugin' ) { + print "Warning: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value,\n \'$neutronConf{'DEFAULT'}{'core_plugin'}\',\n" . + " which is not \'neutron.plugins.ml2.plugin.Ml2Plugin\'.\n"; + } + } + + # Verify any rdev_list in Neutron z/VM Plugin ini file contains a single value and/or not a comma + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list eq '' ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' is specified but has no value.\n"; + } else { + my @vals = split ( /\s/, $list ); + if ( $#vals > 0 ) { + # $#vals is array size - 1. + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains too many values.\n"; + } + foreach my $op ( @vals ) { + if ( $op =~ m/[^0-9a-fA-F]+/ ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains non-hexadecimal characters: \'$op\'.\n"; + } elsif ( length($op) > 4 ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains a value that is not 1-4 characters in\n" . + " length: \'$op\'.\n"; + } + } + } + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ) { + if ( $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ne 'FC' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\',\n" . + " storwize_svc_connection_protocol specifies a value, " . + "\'$cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'}\',\n" . + " which is not the required value of \'FC\'.\n"; + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + if ( $cinderConf{'DEFAULT'}{'volume_driver'} ne 'cinder.volume.drivers.zvm.storwize_svc.StorwizeSVCZVMDriver' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\', volume_driver specifies\n" . + " a value, \'$cinderConf{'DEFAULT'}{'volume_driver'}\',\n which is " . + "not the required value of\n \'cinder.volume.drivers.zvm.storwize_svc.StorwizeSVCZVMDriver\'.\n"; + } + } + + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $rc = 0; +my $clearPwOpt; +my $obfuscateOpt; +my $out; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( 's|scan=s' => \$scan, + 'd|driver=s' => \$driver, + 'h|help' => \$displayHelp, + #'c' => \$clearPwOpt, + #'o' => \$obfuscateOpt, + 'v' => \$versionOpt, + 'V' => \$verbose )) { + print $usage_string; + goto FINISH; +} + +if ( $versionOpt ) { + print "Version: $version\n"; + print "$supportString\n"; +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH; +} + +$localIpAddress = inet_ntoa((gethostbyname(hostname))[4]); + +if ( defined( $scan ) ) { + if ( $verbose ) { + print "Operand --scan: $scan\n"; + } + if ( 'all cinder neutron nova' !~ $scan ) { + print "--scan operand($scan) is not all, cinder, neutron or nova\n"; + $rc = 400; + goto FINISH; + } +} else { + $scan = 'all'; +} + +# Detect CMO +if ( -e $locVersionFileCMO ) { + # CMO version file exists, treat this as a CMO node + $cmoAppliance = 1; + $cmoVersionString = `cat $locVersionFileCMO`; + chomp( $cmoVersionString ); + if ( $cmoVersionString eq '' ) { + $cmoVersionString = "version unknown"; + } + + $rc = hashFile( $locDMSSICMOCopy, \%dmssicmoCopy, 0 ); +} + +if ( defined( $driver ) ) { + if ( $verbose ) { + print "Operand --driver: $driver\n"; + } + + if ( -d $driver ) { + # Driver operand is a directory name only + $driver =~ s/\/+$//g; + $driver = "$driver/$driverPrefix$localIpAddress.sh"; + } else { + # Driver operand is a bad directory or a file in a directory + my ( $driverFile, $driverDir, $driverSuffix ) = fileparse( $driver ); + if ( -d $driverDir ) { + $driver = "$driverDir$driverFile.sh"; + } else { + print "Driver property does not specify a valid directory: $driverDir\n"; + $rc = 500; + goto FINISH; + } + } +} else { + $driver = "$driverPrefix$localIpAddress.sh"; +} + +if ( $scan =~ 'all' or $scan =~ 'nova' ) { + !( $rc = scanNova() ) or goto FINISH; +} + +if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + scanCinder(); +} + +if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + !( $rc = scanNeutron() ) or goto FINISH; +} + +#if ( $obfuscateOpt ) { +# print "obfuscateOpt: $obfuscateOpt\n"; +# $obfuscatePw = 1; +#} elsif ( $clearPwOpt ) { +# $obfuscatePw = 0; +#} + +!( $rc = validateConfigs() ) or goto FINISH; + +!( $rc = buildDriverProgram() ) or goto FINISH; + +FINISH: +exit $rc; + diff --git a/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_JUNO.pl b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_JUNO.pl new file mode 100644 index 000000000..8bd735be0 --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_JUNO.pl @@ -0,0 +1,1531 @@ +#!/usr/bin/perl +############################################################################### +# (c) Copyright International Business Machines Corporation 2014, 2015. +# All Rights Reserved. +############################################################################### +# COMPONENT: prep_zxcatIVP_JUNO.pl +# +# This is a preparation script for Installation Verification Program for xCAT +# on z/VM. It prepares the driver script by gathering information from +# OpenStack configuration files on the compute node. +############################################################################### + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use Getopt::Long; +use MIME::Base64; +use Sys::Hostname; +use Socket; + +my %cinderConf; +my %novaConf; +my %neutronConf; +my %ml2ConfIni; +my %neutronZvmPluginIni; + +my $version = "3.2"; +my $supportString = "This script supports code based on the OpenStack Juno release."; + +my $cmaAppliance = 0; # Assumed to not be run in a CMA system +my $cmaVersionString = ''; # CMA version string for a CMA +my $cmaSystemRole = ''; # CMA system role string +my $configFound = 0; # At least 1 config file was found. Defaults to false. +my $driver; # Name of driver file to be created less the ".pl" +my $driverLocation = "/opt/xcat/bin/"; # Location of the IVP program in xCAT MN. +my $driverPrefix = "zxcatIVPDriver_"; # Prefix used in naming the driver program. +my $driverSuffix; # Suffix used in naming the driver program. +my $displayHelp = 0; # Display help information. +my $host; # Host command option value +my $initSuffix; # Suffix used in naming multihomed init scripts +my $ivp = "zxcatIVP.pl"; # z/VM xCAT IVP script name +my $obfuscateProg = '/usr/bin/openstack-obfuscate'; # PW obfuscation program +my $pwVisible = 0; # PW is visible in the driver script (1 = yes, 0 = no) +my $scan; # Type of scan to be performed +my $verbose; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Shov version information. + +my $localIpAddress = ''; # Local IP address of system where we are prepping + +# Locations of configuration files +my $locCinderConf = '/etc/cinder/cinder.conf'; +my $locMl2ConfIni = '/etc/neutron/plugin.ini'; +my $locNeutronConf = '/etc/neutron/neutron.conf'; +my $locNeutronZvmPluginIni = '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini'; +my $locNovaConf = '/etc/nova/nova.conf'; + +# Version related files +my $locVersionFileCMO = '/opt/ibm/cmo/version'; +my $locDMSSICMOCopy = '/var/lib/sspmod/DMSSICMO.COPY'; +my $locApplSystemRole = '/var/lib/sspmod/appliance_system_role'; +my $locLicFileICM = '/opt/ibm/cmwo/license'; + +# Stems of startup scripts in /etc/init.d that can be scanned for +# configuration files names and locations. +my $neutronZvmAgentStem = 'neutron-zvm-agent'; +my $novaComputeStem = 'openstack-nova-compute'; + +# Eyecatchers used with --config operand +my $EyeCinderConf = 'cinder_conf'; +my $EyeMl2ConfIni = 'm12_conf'; +my $EyeNeutronConf = 'neutron_conf'; +my $EyeNeutronZvmPluginIni = 'neutron_zvm_plugin_conf'; +my $EyeNovaConf = 'nova_conf'; + +# set the usage message +my $usage_string = "Usage:\n + $0\n + or\n + $0 -s serviceToScan -d driverProgramName\n + or\n + $0 --scan serviceToScan -driver driverProgramName\n + The following options are supported: + + -c | --config + List of configuration files to be processed. This list overrides + the default configuration file locations or the ones determined by + the --init-files operand. Each configuration file is identified + by an eyecatcher indicating which configuration file is being + overriden followed by a colon and the fully qualified file + specification. Multiple configuration files may be specified by + separating them with a comma (one file per eyecatcher). + The following are recognized eyecathers and the files that they + override: + $EyeCinderConf - $locCinderConf + $EyeMl2ConfIni - $locMl2ConfIni + $EyeNeutronConf - $locNeutronConf + $EyeNeutronZvmPluginIni - + $locNeutronZvmPluginIni + $EyeNovaConf - $locNovaConf + -d | --driver File specification of driver program to construct, + or name of directory to contain the driver program. + -h | --help Display help information. + -H | --host Name of z/VM host to process. Startup scripts end + with this suffix for the specified z/VM system. When this option is + used with the -i option, it indicates which startup scripts should + be scanned. + The driver script created will contain the host value as part of + its name. + -i | --init-files Scan OpenStack startup scripts in /etc/init.d + for config file names that this script is interested in processing. + --help Display help information. + -p | --password-visible + If specified, password values in the constructed + driver script program will be visible. Otherwise, + password values are hidden. This option is used + when building a driver script to run against an + older xCAT MN. + -s | --scan Services to scan ('all', 'nova' or 'neutron'). + -v Display script version information. + -V | --verbose Display verbose processing messages.\n"; + +#------------------------------------------------------- + +=head3 buildDriverProgram + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = buildDriverProgram(); + +=cut + +#------------------------------------------------------- +sub buildDriverProgram{ + my $rc; + my @driverText; + + if ( $verbose ) { + print "Building the IVP driver program.\n"; + } + + # Erase any existing driver program. + if ( -e $driver and ! -z $driver ) { + # Make certain the file is one of our driver files. + my $found = `grep 'Function: z/VM xCAT IVP driver program' $driver`; + if ( ! $found ) { + print "$driver is not a z/VM xCAT IVP driver program\n"; + print "File will not be changed.\n"; + return 251; + } else { + # Rename the existing driver file. + print "Existing driver file is being saved as $driver.old\n"; + rename $driver,"$driver.old"; + } + } + + # Open the driver program for output. + $rc = open( my $fileHandle, '>', $driver ) or die; + if ( $rc != 1 ) { + print "Unable to open $driver for output: $!\n"; + return ( 200 + $rc ); + } + + # Construct the file in an array. + push( @driverText, "#!/bin/bash" ); + push( @driverText, "# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html" ); + push( @driverText, "#" ); + push( @driverText, "# Function: z/VM xCAT IVP driver program" ); + push( @driverText, "# Built by $0 version $version." ); + push( @driverText, "# $supportString" ); + push( @driverText, "" ); + if ( $cmaAppliance == 1 ) { + push( @driverText, "# System is a CMA" ); + push( @driverText, "# CMA system role: $cmaSystemRole" ); + push( @driverText, "# $cmaVersionString" ); + push( @driverText, "" ); + } + push( @driverText, "############## Start of Nova Config Properties" ); + if ( exists $novaConf{'DEFAULT'}{'my_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From \'my_ip\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$novaConf{'DEFAULT'}{'my_ip'}\"" ); + } else { + if ( keys %novaConf ) { + print "Info: 'my_ip' property is missing from section 'DEFAULT'\n" . + " in $locNovaConf. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + } else { + print "Info: The Nova configuration file was not found and thus this script is unable\n" . + " to use the 'my_ip' property. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + } + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From the local IP address of this system." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$localIpAddress\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_user_profile'} ) { + push( @driverText, "" ); + push( @driverText, "# Default profile used in creation of server instances." ); + push( @driverText, "# From \'zvm_user_profile\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_defaultUserProfile=\"$novaConf{'DEFAULT'}{'zvm_user_profile'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_diskpool'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of disk pools that are expected to exist." ); + push( @driverText, "# From \'zvm_diskpool\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_diskpools=\"$novaConf{'DEFAULT'}{'zvm_diskpool'}\"" ); + } + + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by instances." ); + push( @driverText, "# From \'zvm_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_instFCPList=\"$novaConf{'DEFAULT'}{'zvm_fcp_list'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_host'} ) { + push( @driverText, "" ); + push( @driverText, "# Node of host being managed. If blank, IVP will search for the host node." ); + push( @driverText, "# From \'zvm_host\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_hostNode=\"$novaConf{'DEFAULT'}{'zvm_host'}\"" ); + + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_master'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT MN (optional)." ); + push( @driverText, "# From \'zvm_xcat_master\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_mnNode=\"$novaConf{'DEFAULT'}{'zvm_xcat_master'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + my $clearPW = ''; + push( @driverText, "" ); + push( @driverText, "# User password defined to communicate with xCAT MN." ); + push( @driverText, "# From \'zvm_xcat_password\' in $locNovaConf." ); + + if ( -e $obfuscateProg ) { + # assume password is obfuscated already and get it in the clear. + $clearPW = `$obfuscateProg -u $novaConf{'DEFAULT'}{'zvm_xcat_password'}`; + $clearPW =~ s/\n+$//g; # trim ending new line + } else { + # Assume password is in the clear because the obfuscation program is missing. + $clearPW = $novaConf{'DEFAULT'}{'zvm_xcat_password'}; + } + + if ( $pwVisible ) { + push( @driverText, "export zxcatIVP_xcatUserPw=\"$clearPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=0" ); + } else { + my $hiddenPW = obfuscate( $clearPW, 1 ); + push( @driverText, "# Note: Password is hidden." ); + push( @driverText, "# To override the support and pass the password in the" ); + push( @driverText, "# clear, either:" ); + push( @driverText, "# - specify the -p or --password-visible operand when" ); + push( @driverText, "# invoking prep_zxcatIVP.pl script, or" ); + push( @driverText, "# - change zxcatIVP_pw_obfuscated variable to 0 and" ); + push( @driverText, "# specify the password in the clear on the" ); + push( @driverText, "# zxcatIVP_xcatUserPw variable in the constructed" ); + push( @driverText, "# driver script." ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$hiddenPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=1" ); + } + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected IP address of the xcat MN" ); + push( @driverText, "# From \'zvm_xcat_server\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatMNIp=\"$novaConf{'DEFAULT'}{'zvm_xcat_server'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + push( @driverText, "" ); + push( @driverText, "# User defined to communicate with xCAT MN" ); + push( @driverText, "# From \'zvm_xcat_username\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatUser=\"$novaConf{'DEFAULT'}{'zvm_xcat_username'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by zHCP." ); + push( @driverText, "# From \'zvm_zhcp_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_zhcpFCPList=\"$novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'}\"" ); + } + + push( @driverText, "" ); + push( @driverText, "# Expected space available in the xCAT MN image repository" ); + push( @driverText, "# From \'xcat_free_space_threshold\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_expectedReposSpace=\"$novaConf{'DEFAULT'}{'xcat_free_space_threshold'}G\"" ); + + push( @driverText, "" ); + push( @driverText, "############## End of Nova Config Properties" ); + + if (( keys %neutronConf ) or ( keys %neutronZvmPluginIni ) or ( keys %ml2ConfIni )) { + push( @driverText, "" ); + push( @driverText, "############## Start of Neutron Config Properties" ); + if ( exists $neutronConf{'DEFAULT'}{'base_mac'} ) { + my $prefix = $neutronConf{'DEFAULT'}{'base_mac'}; + $prefix =~ tr/://d; + $prefix = substr( $prefix, 0, 6 ); + push( @driverText, "" ); + push( @driverText, "# User prefix for MAC Addresses of Linux level 2 interfaces" ); + push( @driverText, "# From \'base_mac\' in $locNeutronConf." ); + push( @driverText, "export zxcatIVP_macPrefix=\"$prefix\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat MN's address on the xCAT management network" ); + push( @driverText, "# From \'xcat_mgt_ip\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_xcatMgtIp=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat management interface netmask" ); + push( @driverText, "# From \'xcat_mgt_mask\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_mgtNetmask=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'}\"" ); + } + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} or + exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + my $list = ''; + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} and + $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} ne '*' ) { + $list = $ml2ConfIni{'ml2_type_flat'}{'flat_networks'}; + } + if ( exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + if ( $list ne '' ) { + $list = "$list,"; + } + $list = "$list$ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'}"; + } + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Array of networks and possible VLAN ranges" ); + push( @driverText, "# From $locMl2ConfIni,"); + push( @driverText, "# \'flat_networks\' in section \'ml2_type_flat\' and "); + push( @driverText, "# \'network_vlan_ranges\' in section \'ml2_type_vlan\'."); + push( @driverText, "export zxcatIVP_networks=\"$list\"" ); + } + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT zHCP server" ); + push( @driverText, "# From \'xcat_zhcp_nodename\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_zhcpNode=\"$neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'}\"" ); + } + + # Create the zxcatIVP_vswitchOSAs variable for any networks specified with an rdev list. + my $vswitchOSAs = ''; + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + next if ( $list eq '' ); + $list =~ s/\s+/\,/g; # insert comma between words + $vswitchOSAs = "$section $list $vswitchOSAs"; + } + } + if ( $vswitchOSAs ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Vswitches and their related OSAs" ); + push( @driverText, "# From \'rdev_list\' in vswitch sections of $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_vswitchOSAs=\"$vswitchOSAs\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Neutron Config Properties" ); + } + + push( @driverText, "" ); + push( @driverText, "# Name of user under which nova runs, default is nova." ); + push( @driverText, "# If you system is different then change this property." ); + push( @driverText, "export zxcatIVP_expUser=\"nova\"" ); + push( @driverText, "" ); + push( @driverText, "# Controls whether Warnings/Errors detected by the IVP are" ); + push( @driverText, "# logged in the xCAT MN syslog, 0: do not log, 1: log to syslog." ); + push( @driverText, "export zxcatIVP_syslogErrors=1" ); + push( @driverText, "" ); + push( @driverText, "perl $driverLocation$ivp" ); + + # Write the array to the driver file. + foreach (@driverText) { + #print "$_\n"; + print $fileHandle "$_\n"; # Print each entry in our array to the file + } + + close $fileHandle; + + print "$driver was built.\n"; + return 0; +} + +#------------------------------------------------------- + +=head3 hashFile + + Description : Read a file with equal signs designating + key value pairs and create a hash from it. + Arguments : File to read + Reference to hash that should be constructed. + File is required to exist or there is a serious error. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = hashFile( $file, \%novaConf, 1 ); + +=cut + +#------------------------------------------------------- +sub hashFile{ + my ( $file, $hash, $required ) = @_; + my $section = "null"; + my $caseSensitive = 0; # assume section/properties are case insensitive + my @parts; + my $out; + + if ( $file eq '' ) { + return 602; + } + + if ( ! -e $file ) { + if ( $required ) { + print "Warning: $file does not exist.\n"; + } else { + print "Info: $file does not exist.\n"; + } + return 601; + } + + if (( $file =~ /.conf$/ ) or ( $file =~ /.COPY$/ )) { + # File is case sensitive, translate sections and property names to uppercase. + $caseSensitive = 1; + } + + # Read the configuration file and construct the hash of values. + if (( $file =~ /.conf$/ ) or ( $file =~ /.ini$/ )) { + $out = `egrep -v '(^#|^\\s*\\t*#)' $file`; + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^\[/ ) { + # Section header line + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the line + $line =~ s/^\[+|\]+$//g; # trim [] from ends of the line + if ( !$caseSensitive ) { + $section = lc( $line ); + } else { + $section = $line; + } + } else { + # Property line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$section}{$parts[0]} = $parts[1]; + #print "$section $parts[0]" . ": " . $parts[1]. "\n"; + #print $parts[0] . ": " . $$hash{$section}{$parts[0]}. "\n"; + } + } + } else { + # Hash .COPY files + # Read the file and remove comment lines and sequence columns (72-80) + $out = `grep -v ^\$ $file| cut -c1-71`; + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Weed out blank lines + $line =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $line ) == 0 ); + + # Parse the line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + # Add the property to the hash if it has data. + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + $parts[1] =~ s/^"+|"+$//g; # trim double quotes from both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$parts[0]} = $parts[1]; + #print $parts[0] . ": " . $$hash{$parts[0]}. "\n"; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 obfuscate + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : string to be processed + direction: 1 - obfuscate (hide it!) + 0 - unobfuscate (unhide it!) + Returns : processed password (either hiden or unhidden)) + Example : $rc = obfuscate( $pw, 1 ); + +=cut + +#------------------------------------------------------- +sub obfuscate{ + my ( $pw, $hide ) = @_; + + if ( $hide == 1 ) { + $pw = encode_base64( $pw, '' ); + } else { + $pw = decode_base64( $pw ); + } + + return $pw; +} + +#------------------------------------------------------- + +=head3 scanCinder + + Description : Scan the cinder configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCinder(); + +=cut + +#------------------------------------------------------- +sub scanCinder{ + my $rc; + + if ( $locCinderConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Cinder configuration files:\n"; + print " $locCinderConf\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCinderConf, \%cinderConf, 0 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanInitScript + + Description : Scan the init script for the + specified configuration file property. + Arguments : Configuration file name + Config property containing the value + Returns : Null - error locating the config file. + Non-null - filespecification of the + configuration file. + Example : $rc = scanInitScript('openstack-cinder-volume-$host', + 'config'); + +=cut + +#------------------------------------------------------- +sub scanInitScript{ + my ( $filename, $property ) = @_; + my $configFile; + my $out; + + # verify the file exists + if ( !-e "/etc/init.d/$filename" ) { + print "Info: /etc/init.d/$filename does not exist.\n"; + return $configFile; + } + + if ( $verbose ) { + print "Scanning $filename for '$property' variable.\n"; + } + + # Strip out the lines after the desired config property is set and + # remove comment lines then echo the property value. + $out = `awk '{print} /$property=/ {exit}' /etc/init.d/$filename | grep -v ^\# | grep .`; + $out = $out . "echo -n \$$property"; + $configFile = `$out`; + + return $configFile; +} + +#------------------------------------------------------- + +=head3 scanNeutron + + Description : Scan the neutron configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNeutron(); + +=cut + +#------------------------------------------------------- +sub scanNeutron{ + my $rc; + + if ( $locNeutronConf eq '' and $locMl2ConfIni eq '' + and $locNeutronZvmPluginIni eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Neutron configuration files:\n"; + print " $locNeutronConf\n"; + print " $locMl2ConfIni\n"; + print " $locNeutronZvmPluginIni\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronConf, \%neutronConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locMl2ConfIni, \%ml2ConfIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronZvmPluginIni, \%neutronZvmPluginIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNova + + Description : Scan the Nova configuration files. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNova(); + +=cut + +#------------------------------------------------------- +sub scanNova{ + my $rc; + + if ( $locNovaConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Nova configuration file:\n"; + print " $locNovaConf\n"; + } + + # Verify the /etc/nova/nova.conf exists. + $rc = hashFile( $locNovaConf, \%novaConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares and builds a z/VM xCAT IVP driver program + using the information from the configuration files in the + compute node. + + The default name of the driver program is composed of the following: + '$driverPrefix', and + IP address of the system where driver was prepared, and + (optionally) a hypen and the value specified on --Host operand, and + '.sh'. + For example: + $driverPrefix"."9.123.345.91.sh + $driverPrefix"."9.123.345.91-hostzvm.sh + + $supportString + + The following configuration files are scanned for input: + $locCinderConf + $locNovaConf + $locNeutronConf + $locMl2ConfIni + $locNeutronZvmPluginIni + + When the --init-files operand is specified, OpenStack startup + scripts are scanned in /etc/init.d. The --host operand should + be specified to indicate the suffix to use for scripts that + are unique to a specific z/VM host. The following startup + scripts are scanned: + neutron-server + $neutronZvmAgentStem- + $novaComputeStem- + openstack-cinder-volume + + When --init-files is specified without the --host operand, + the following scripts are scanned: + neutron-server + $neutronZvmAgentStem + $novaComputeStem + openstack-cinder-volume + + The constructed driver program can then be uploaded to + the xCAT MN and used to validate the configuration between + the compute node and xCAT.\n\n"; + print $usage_string; + return; +} + +#------------------------------------------------------- + +=head3 validateConfigs + + Description : Compare and validate the configuration + values obtained by the scans. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = validateConfigs(); + +=cut + +#------------------------------------------------------- +sub validateConfigs{ + my $bypassCinder = 0; + my $rc = 0; + my $option; + if ( $verbose ) { + print "Performing a local validation of the configuration files.\n"; + } + + #******************************************************* + # Verify required configuration options were specified. + #******************************************************* + if ( keys %cinderConf ) { + my @requiredCinderOpts = (); + foreach $option ( @requiredCinderOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$option} ) { + #print "option:$option.\nvalue:$cinderConf{$option}\n"; + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in $locCinderConf.\n"; + } + } + } + + if ( keys %novaConf ) { + my @requiredNovaOpts = ( + 'DEFAULT','compute_driver', + 'DEFAULT','config_drive_format', + 'DEFAULT','force_config_drive', + 'DEFAULT','host', + 'DEFAULT','instance_name_template', + 'DEFAULT','zvm_diskpool', + 'DEFAULT','zvm_host', + 'DEFAULT','zvm_user_profile', + 'DEFAULT','zvm_xcat_master', + 'DEFAULT','zvm_xcat_server', + 'DEFAULT','zvm_xcat_username', + 'DEFAULT','zvm_xcat_password', + ); + for ( my $i = 0; $i < $#requiredNovaOpts; $i = $i + 2 ) { + my $section = $requiredNovaOpts[$i]; + my $option = $requiredNovaOpts[$i+1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + } + } + } + + if ( keys %neutronConf ) { + my @requiredNeutronConfOpts = ( + 'DEFAULT','core_plugin', + ); + for ( my $i = 0; $i < $#requiredNeutronConfOpts; $i = $i + 2 ) { + my $section = $requiredNeutronConfOpts[$i]; + my $option = $requiredNeutronConfOpts[$i+1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf/neutron/neutron.conf.\n"; + } + } + } + + if ( keys %ml2ConfIni ) { + my @requiredMl2ConfIniOpts = ( + 'ml2','mechanism_drivers', + 'ml2','tenant_network_types', + 'ml2','type_drivers', + ); + for ( my $i = 0; $i < $#requiredMl2ConfIniOpts; $i = $i + 2 ) { + my $section = $requiredMl2ConfIniOpts[$i]; + my $option = $requiredMl2ConfIniOpts[$i+1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni.\n"; + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my @requiredNeutronZvmPluginIniOpts = ( + 'agent', 'zvm_host', + 'agent','zvm_xcat_server', + ); + for ( my $i = 0; $i < $#requiredNeutronZvmPluginIniOpts; $i = $i + 2 ) { + my $section = $requiredNeutronZvmPluginIniOpts[$i]; + my $option = $requiredNeutronZvmPluginIniOpts[$i+1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + } + } + } + + #****************************************** + # Verify optional operands were specified. + #****************************************** + if ( keys %cinderConf ) { + if ( $cmaSystemRole eq "COMPUTE" ) { + print "Info: CMA system role is a compute server.\n Cinder will not be validated.\n"; + $bypassCinder = 1; + } elsif ( !exists $cinderConf{'DEFAULT'}{'san_ip'} and + !exists $cinderConf{'DEFAULT'}{'san_private_key'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_volpool_name'} + ) { + print "Info: Most z/VM specific Cinder keys are not defined in $locCinderConf.\n" . + " Cinder support for creation of persistent disks for z/VM\n" . + " is not enabled. Further testing of these options will\n" . + " not occur in this script.\n"; + $bypassCinder = 1; + } else { + my %optionalCinderConfOpts = ( + "DEFAULT san_ip" => "This property is necessary when using persistent SAN disks obtained\n" . + " from the Cinder service.", + "DEFAULT san_private_key" => "This property is necessary when using persistent SAN disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_connection_protocol" => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_multipath_enabled" => "This property is optional and used when StorWize persistent disks are\n" . + " obtained from the Cinder service. The default is 'false'.", + "DEFAULT storwize_svc_volpool_name" => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + 'DEFAULT storwize_svc_vol_iogrp' => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + 'DEFAULT volume_driver' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + ); + my %defaultCinderConfOpts = (); + foreach my $key ( keys %optionalCinderConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $cinderConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locCinderConf.\n"; + if ( $optionalCinderConfOpts{$key} ne '' ) { + print " " . $optionalCinderConfOpts{$key} . "\n"; + } + if ( exists $defaultCinderConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCinderConfOpts{$key}; + } + } + } + } + } + + if ( keys %novaConf ) { + my %optionalNovaConfOpts = ( + "DEFAULT image_cache_manager_interval" => "Default of 2400 (seconds) will be used.", + "DEFAULT ram_allocation_ratio" => "", + "DEFAULT rpc_response_timeout" => "zVM Live migration may timeout with the default " . + "value (60 seconds).\n The recommended value for z/VM is 180 to allow " . + "zVM live migration\n to succeed.", + "DEFAULT xcat_free_space_threshold" => "Default of 50 (G) will be used.", + "DEFAULT xcat_image_clean_period" => "Default of 30 (days) will be used.", + "DEFAULT zvm_config_drive_inject_password" => "This value will default to 'FALSE'.", + "DEFAULT zvm_diskpool_type" => "Default of \'ECKD\' will be used.", + "DEFAULT zvm_fcp_list" => "As a result, Cinder volumes cannot be attached to server instances.", + "DEFAULT zvm_zhcp_fcp_list" => "", + "DEFAULT zvm_image_tmp_path" => "Default of '/var/lib/nova/images' will be used.", + "DEFAULT zvm_reachable_timeout" => "Default of 300 (seconds) will be used.", + "DEFAULT zvm_scsi_pool" => "Default of \'xcatzfcp\' will be used.", + "DEFAULT zvm_vmrelocate_force" => "", + "DEFAULT zvm_xcat_connection_timeout" => "Default of 3600 seconds will be used.", + "DEFAULT zvm_image_compression_level" => "Image compression is controlled by the xCAT ZHCP " . + "server in\n the /var/opt/zhcp/settings.conf file. Compressing images during image\n" . + " capture is the default." + ); + my %defaultNovaConfOpts = ( + "DEFAULT xcat_free_space_threshold" => 50, + "DEFAULT xcat_image_clean_period" => 30, + "DEFAULT zvm_diskpool_type" => 'ECKD', + "DEFAULT zvm_scsi_pool" => "xcatzfcp", + ); + foreach my $key ( keys %optionalNovaConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + if ( $optionalNovaConfOpts{$key} ne '' ) { + print " " . $optionalNovaConfOpts{$key} . "\n"; + } + if ( exists $defaultNovaConfOpts{$key} ) { + $novaConf{$section}{$option} = $defaultNovaConfOpts{$key}; + } + } + } + } + + if ( keys %neutronConf) { + my %optionalNeutronConfOpts = (); + my %defaultNeutronConfOpts = (); + foreach my $key ( keys %optionalNeutronConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf.\n"; + if ( $optionalNeutronConfOpts{$key} ne '' ) { + print " " . $optionalNeutronConfOpts{$key} . "\n"; + } + if ( exists $defaultNeutronConfOpts{$key} ) { + $neutronConf{$section}{$option} = $defaultNeutronConfOpts{$key}; + } + } + } + } + + if ( keys %ml2ConfIni ) { + my %optionalMl2ConfIniOpts = ( + 'ml2_type_vlan network_vlan_ranges' => "", + 'ml2_type_flat flat_networks' => "", + ); + my %defaultMl2ConfIniOpts = (); + foreach my $key ( keys %optionalMl2ConfIniOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni.\n"; + if ( $optionalMl2ConfIniOpts{$key} ne '' ) { + print " " . $optionalMl2ConfIniOpts{$key} . "\n"; + } + if ( exists $defaultMl2ConfIniOpts{$key} ) { + $ml2ConfIni{$section}{$option} = $defaultMl2ConfIniOpts{$key}; + } + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my %optionalNeutronZvmPluginIniOpts = ( + "agent xcat_mgt_ip" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent xcat_mgt_mask" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent polling_interval" => "A default value of '5' will be used.", + "agent xcat_zhcp_nodename" => "A default value of \'zhcp\' will be used.", + "agent zvm_xcat_password" => "A default value of \'admin\' is used.", + "agent zvm_xcat_timeout" => "A default value of 300 seconds is used.", + "agent zvm_xcat_username" => "A default value of \'admin\' is used.", + ); + my %defaultNeutronZvmPluginIniOpts = ( + "agent polling_interval" => 2, + "agent xcat_zhcp_nodename" => "zhcp", + "agent zvm_xcat_password" => "admin", + "agent zvm_xcat_timeout" => 300, + "agent zvm_xcat_username" => "admin", + ); + foreach my $key ( keys %optionalNeutronZvmPluginIniOpts ) { + my @opts = split( '\s', $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + if ( $optionalNeutronZvmPluginIniOpts{$key} ne '' ) { + print " " . $optionalNeutronZvmPluginIniOpts{$key} . "\n"; + } + if ( exists $defaultNeutronZvmPluginIniOpts{$key} ) { + $neutronZvmPluginIni{$section}{$option} = $defaultNeutronZvmPluginIniOpts{$key}; + } + } + } + } + + # Verify xCAT users are the same. + if (( keys %novaConf ) and ( keys %neutronZvmPluginIni )) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_username'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Warning: xCAT user names mismatch; review 'zvm_xcat_username':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_username'}\' in $locNovaConf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_username'}\' in\n" . + " $locNeutronZvmPluginIni.\n"; + } + } + } + + # Verify xCAT user passwords are the same. + if (( keys %novaConf ) and ( keys %neutronZvmPluginIni )) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'. It is not specified\n" . + " in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_password'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Warning: xCAT user passwords are not the same:\n" . + " Please review 'zvm_xcat_password' in $locNovaConf and\n" . + " $locNeutronZvmPluginIni.\n"; + } + } + } + + # Verify the xcat server IP addresses are the same. + if (( keys %novaConf ) and ( keys %neutronZvmPluginIni )) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_server'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Warning: xCAT server addresses mismatch; review 'zvm_xcat_server':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_server'}\' in $locNovaConf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_server'}\' in $locNeutronZvmPluginIni.\n"; + } + } + } + + # Verify host and zvm_host properties are the same. + if ( exists $novaConf{'DEFAULT'}{'host'} and + exists $neutronZvmPluginIni{'agent'}{'zvm_host'} ) { + if ( $novaConf{'DEFAULT'}{'host'} ne $neutronZvmPluginIni{'agent'}{'zvm_host'} ) { + print "Warning: 'host' property in section 'DEFAULT' in $locNovaConf\n" . + " does not specify the same value as 'zvm_host' property in section\n" . + " 'agent' in $locNeutronZvmPluginIni.\n"; + } + } + + # Verify the instance name template is valid + if ( exists $novaConf{'DEFAULT'}{'instance_name_template'} ) { + # Use sprintf which is close enough to the python % support for formatting to construct a sample. + my $base_name = sprintf( $novaConf{'DEFAULT'}{'instance_name_template'}, 1 ); + if ( length( $base_name ) > 8 ) { + print "Warning: In $locNovaConf, section \`DEFAULT\`, instance_name_template would\n" . + " construct a value greater than 8 in length: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + if (( $base_name eq $novaConf{'DEFAULT'}{'instance_name_template'} ) || + ( $novaConf{'DEFAULT'}{'instance_name_template'} !~ /%/ )) { + print "Warning: In $locNovaConf, instance_name_template will not\n" . + " create a usable name with the value: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + my $words; + $words++ while $base_name =~ /\S+/g; + if ( $words != 1 ) { + print "Warning: In $locNovaConf, instance_name_template will not create\n" . + " an instance name that is a single blank delimited word using\n" . + " the value: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + if ( $novaConf{'DEFAULT'}{'instance_name_template'} =~ m/(^RSZ)/i ) { + print "Warning: In $locNovaConf, instance_name_template should not begin\n" . + " with 'rsz': $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + } + + # Verify the compute_driver is for z/VM + if ( exists $novaConf{'DEFAULT'}{'compute_driver'} ) { + if ( $novaConf{'DEFAULT'}{'compute_driver'} ne "nova.virt.zvm.ZVMDriver" and + $novaConf{'DEFAULT'}{'compute_driver'} ne "zvm.ZVMDriver") { + print "Warning: In $locNovaConf, compute_driver does not contain the\n" . + " expected value of \'zvm.ZVMDriver\' and instead contains:\n" . + " \'$novaConf{'DEFAULT'}{'compute_driver'}\'\n"; + } + } + + # Check whether the rpc timeout is too small for z/VM + if ( exists $novaConf{'DEFAULT'}{'rpc_response_timeout'} ) { + if ( $novaConf{'DEFAULT'}{'rpc_response_timeout'} < 180 ) { + print "Warning: In $locNovaConf, section \'DEFAULT\', rpc_response_timeout\n" . + " specifies a value, \'$novaConf{'DEFAULT'}{'rpc_response_timeout'}\', which is " . + "less than the recommended value\n of \'180\'.\n"; + } + } + + # Verify all SCSI disk operands are specified, if one exists. + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} or exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + if ( !exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_zhcp_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + } + + # Verify neutron.conf operands with a fixed set of possible values + if ( exists $neutronConf{'DEFAULT'}{'core_plugin'} ) { + if ( $neutronConf{'DEFAULT'}{'core_plugin'} eq 'neutron.plugins.ml2.plugin.Ml2Plugin' ) { + print "Info: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value, '$neutronConf{'DEFAULT'}{'core_plugin'}',\n" . + " which is deprecated. The recommended value is 'ml2'.\n"; + } + elsif ( $neutronConf{'DEFAULT'}{'core_plugin'} ne 'ml2' ) { + print "Warning: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value, '$neutronConf{'DEFAULT'}{'core_plugin'}',\n" . + " which is not 'ml2'.\n"; + } + } + + # Verify base_mac value is specified for non-CMA. + if (( keys %neutronConf ) and ( !exists $neutronConf{'DEFAULT'}{'base_mac'} ) and ( !$cmaAppliance )) { + print "Warning: 'base_mac' is missing from section 'DEFAULT'\n" . + " in $locNeutronConf.\n"; + } + + # Verify any rdev_list in Neutron z/VM Plugin ini file contains a single value and/or not a comma + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list eq '' ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' is specified but has no value.\n"; + } else { + my @vals = split ( /\s/, $list ); + if ( $#vals > 0 ) { + # $#vals is array size - 1. + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains too many values.\n"; + } + foreach my $op ( @vals ) { + if ( $op =~ m/[^0-9a-fA-F]+/ ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains non-hexadecimal characters: \'$op\'.\n"; + } elsif ( length($op) > 4 ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains a value that is not 1-4 characters in\n" . + " length: \'$op\'.\n"; + } + } + } + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ) { + if ( $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ne 'FC' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\',\n" . + " storwize_svc_connection_protocol specifies a value, " . + "\'$cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'}\',\n" . + " which is not the required value of \'FC\'.\n"; + } + } + + # Check whether the volume_driver is correct for z/VM + if ( ! $bypassCinder and exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + if ( $cinderConf{'DEFAULT'}{'volume_driver'} ne 'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\', volume_driver specifies\n" . + " a value, \'$cinderConf{'DEFAULT'}{'volume_driver'}\',\n which is " . + "not the required value of\n \'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver\'.\n"; + } + } + + # Check whether the storwize_svc_multipath_enabled is not 'true' or 'false' for z/VM + if ( ! $bypassCinder and exists $cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'} ) { + my $ucValue = uc( $cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'} ); + if (( $ucValue ne 'TRUE' ) and ( $ucValue ne 'FALSE' )) { + print "Warning: In $locCinderConf, section \'DEFAULT\',\n" . + " storwize_svc_multipath_enabled specifies a value, " . + "'$cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'}',\n" . + " which is not the required value of 'true' or 'false'.\n"; + } + } + + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $configOpt; +my $rc = 0; +my $obfuscateOpt; +my $out; +my $scanInitOpt; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( + 'c|config=s' => \$configOpt, + 'd|driver=s' => \$driver, + 'h|help' => \$displayHelp, + 'H|host=s' => \$host, + 'i|init-files' => \$scanInitOpt, + 'p|password-visible' => \$pwVisible, + 's|scan=s' => \$scan, + 'v' => \$versionOpt, + 'V|verbose' => \$verbose )) { + print $usage_string; + goto FINISH; +} + +if ( $versionOpt ) { + print "Version: $version\n"; + print "$supportString\n"; +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH; +} + +# Determine the local IP address for this system. +my $errorLines = ''; +my $hostname = hostname; +if ( $hostname ne '' ) { + $localIpAddress = gethostbyname( $hostname ); + if ( defined $localIpAddress ) { + my $len = length( $localIpAddress ); + if ( $len == 4 ) { + $localIpAddress = inet_ntoa( $localIpAddress ); + } else { + $localIpAddress = ''; + $errorLines = $errorLines . " The IP address obtained from perl gethostbyname function does not\n" . + " appear to be an IPv4 address. An IPv4 address should be used.\n"; + } + } else { + $errorLines = $errorLines . " The IP address related to the following host name was not found:\n" . + " $hostname\n"; + if ( defined $? ) { + $errorLines = $errorLines . " The perl gethostbyname function failed with errno: $?\n"; + } + $localIpAddress = ''; + } +} else { + $errorLines = $errorLines . " The host name was not found.\n"; +} + +if ( $localIpAddress eq '' ) { + print "Warning: Unable to obtain the host name and/or IP address for this system.\n"; + print " The local IP address is used in the name of the driver script.\n" . + " Some variables in the driver script such as zxcatIVP_cNAddress\n" . + " may be set to an empty string value. You may wish to correct\n" . + " the driver script and your system configuration.\n"; + if ( $errorLines ne '' ) { + print " Additional Information:\n$errorLines"; + } +} + +# Detect CMA +if ( -e $locVersionFileCMO ) { + # CMA version file exists, treat this as a CMA node + $cmaAppliance = 1; + $cmaVersionString = `cat $locVersionFileCMO`; + chomp( $cmaVersionString ); + if ( $cmaVersionString eq '' ) { + $cmaVersionString = "version unknown"; + } + + # Determine the role of this CMA system + my %settings; + if ( -e $locApplSystemRole ) { + $rc = hashFile( $locApplSystemRole, \%settings, 0 ); + $cmaSystemRole = uc( $settings{'role'} ); + } + if ( $cmaSystemRole eq '' and -e $locDMSSICMOCopy ) { + $rc = hashFile( $locDMSSICMOCopy, \%settings, 0 ); + if ( exists $settings{'openstack_system_role'} ) { + $cmaSystemRole = uc( $settings{'openstack_system_role'} ); + } + } + if ( $cmaSystemRole eq '' ) { + $cmaSystemRole = 'unknown'; + } +} + +if ( defined( $scan ) ) { + if ( $verbose ) { + print "Operand --scan: $scan\n"; + } + if ( 'all cinder neutron nova' !~ $scan ) { + print "--scan operand ($scan) is not all, cinder, neutron or nova\n"; + $rc = 400; + goto FINISH; + } +} else { + $scan = 'all'; +} + +if ( defined( $host ) ) { + if ( $verbose ) { + print "Operand --Host: $host\n"; + } + $driverSuffix = "_$host"; + $initSuffix = "-$host"; +} else { + $host = ''; + $driverSuffix = ""; + $initSuffix = ""; +} + +if ( defined( $scanInitOpt ) ) { + if ( $verbose ) { + print "Operand --initFiles\n"; + } + + my $configFile; + if ( $scan =~ 'all' or $scan =~ 'nova' ) { + $configFile = scanInitScript( "$novaComputeStem$initSuffix", 'config' ); + if ( defined( $configFile ) ) { + $locNovaConf = $configFile; + } else { + print "Info: Unable to determine the Nova configuration file.\n"; + $locNovaConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + $configFile = scanInitScript( "openstack-cinder-volume", 'config' ); + if ( defined( $configFile ) ) { + $locCinderConf = $configFile; + } else { + print "Info: Unable to determine the Cinder configuration file.\n"; + $locCinderConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + $configFile = scanInitScript( "neutron-server", 'config' ); + if ( defined( $configFile ) ) { + $locNeutronConf = $configFile; + } else { + print "Info: Unable to determine the Neutron configuration file.\n"; + $locNeutronConf = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'config' ); + if ( defined( $configFile ) ) { + $locNeutronZvmPluginIni = $configFile; + } else { + print "Info: Unable to determine the Neutron z/VM plugin configuration file.\n"; + $locNeutronZvmPluginIni = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'plugin_config' ); + if ( defined( $configFile ) ) { + $locMl2ConfIni = $configFile; + } else { + print "Info: Unable to determine the Neutron ml2 configuration file.\n"; + $locMl2ConfIni = ''; + } + } +} + +if ( defined( $configOpt ) ) { + if ( $verbose ) { + print "Operand --config $configOpt\n"; + } + + my @items = split( ',', $configOpt ); + foreach my $item ( @items ) { + my @parts = split( ':', $item ); + if ( defined $parts[1] ) { + $parts[0] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + if ( defined $parts[1] ) { + $parts[1] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + + if ( !defined $parts[1] or $parts[1] eq '' ) { + print( "Warning: --config option: $parts[0] did not specify a file. " . + "Default file specification will be used.\n" ); + next; + } + + if ( $parts[0] eq $EyeCinderConf ) { + $locCinderConf = $parts[1]; + } elsif ( $parts[0] eq $EyeMl2ConfIni ) { + $locMl2ConfIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronConf ) { + $locNeutronConf = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronZvmPluginIni ) { + $locNeutronZvmPluginIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNovaConf ) { + $locNovaConf = $parts[1]; + } + } +} + +my ( $volume, $directory, $file ) = ''; +if ( defined( $driver ) ) { + if ( $verbose ) { + print "Operand --driver: $driver\n"; + } + + my @dirs; + if ( -d $driver ) { + # Driver operand is a directory name only + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver, 1 ); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); + } else { + # Driver operand is a bad directory or a file in a directory + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver ); + if ( -d $directory ) { + $driver = File::Spec->catpath( $volume, $directory, $file ); + } else { + print "Driver property does not specify a valid directory: $directory\n"; + $rc = 500; + goto FINISH; + } + } +} else { + $directory = File::Spec->curdir(); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); +} + +# Scan the configuration files. +if ( $scan =~ 'all' or $scan =~ 'nova' ) { + scanNova(); +} + +if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + scanCinder(); +} + +if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + scanNeutron(); +} + +# Validate the settings and produce the driver script. +if ( ! $configFound ) { + print "Warning: No configuration files were found. " . + "No further processing will be performed.\n"; + $rc = 501; + # Allow other processing to continue +} + +!( $rc = validateConfigs() ) or goto FINISH; + +# Validate CMA related information +if ( $cmaSystemRole ne '' ) { + # Validate Controller related information + if ( $cmaSystemRole eq 'CONTROLLER' ) { + if ( !( -e $locLicFileICM ) ) { + print "Warning: $locLicFileICM was not found.\n" . + " ICM deployer does not appear to be installed.\n"; + $rc = 502; + # Allow other processing to continue + } + } +} + +!( $rc = buildDriverProgram() ) or goto FINISH; + +FINISH: +exit $rc; + diff --git a/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_KILO.pl b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_KILO.pl new file mode 100644 index 000000000..217c504ba --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_KILO.pl @@ -0,0 +1,1532 @@ +#!/usr/bin/perl +############################################################################### +# (c) Copyright International Business Machines Corporation 2014, 2015. +# All Rights Reserved. +############################################################################### +# COMPONENT: prep_zxcatIVP_KILO.pl +# +# This is a preparation script for Installation Verification Program for xCAT +# on z/VM. It prepares the driver script by gathering information from +# OpenStack configuration files on the compute node. +############################################################################### + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use Getopt::Long; +use MIME::Base64; +use Sys::Hostname; +use Socket; + +my %cinderConf; +my %novaConf; +my %neutronConf; +my %ml2ConfIni; +my %neutronZvmPluginIni; + +my $version = "4.2"; +my $supportString = "This script supports code based on the OpenStack Kilo release."; + +my $cmaAppliance = 0; # Assumed to not be run in a CMA system +my $cmaVersionString = ''; # CMA version string for a CMA +my $cmaSystemRole = ''; # CMA system role string +my $configFound = 0; # At least 1 config file was found. Defaults to false. +my $driver; # Name of driver file to be created less the ".pl" +my $driverLocation = "/opt/xcat/bin/"; # Location of the IVP program in xCAT MN. +my $driverPrefix = "zxcatIVPDriver_"; # Prefix used in naming the driver program. +my $driverSuffix; # Suffix used in naming the driver program. +my $displayHelp = 0; # Display help information. +my $host; # Host command option value +my $initSuffix; # Suffix used in naming multihomed init scripts +my $ivp = "zxcatIVP.pl"; # z/VM xCAT IVP script name +my $obfuscateProg = '/usr/bin/openstack-obfuscate'; # PW obfuscation program +my $pwVisible = 0; # PW is visible in the driver script (1 = yes, 0 = no) +my $scan; # Type of scan to be performed +my $verbose; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Shov version information. + +my $localIpAddress = ''; # Local IP address of system where we are prepping + +# Locations of configuration files +my $locCinderConf = '/etc/cinder/cinder.conf'; +my $locMl2ConfIni = '/etc/neutron/plugins/ml2/ml2_conf.ini'; +my $locNeutronConf = '/etc/neutron/neutron.conf'; +my $locNeutronZvmPluginIni = '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini'; +my $locNovaConf = '/etc/nova/nova.conf'; + +# Version related files +my $locVersionFileCMO = '/opt/ibm/cmo/version'; +my $locDMSSICMOCopy = '/var/lib/sspmod/DMSSICMO.COPY'; +my $locApplSystemRole = '/var/lib/sspmod/appliance_system_role'; +my $locLicFileICM = '/opt/ibm/cmwo/license'; + +# Stems of startup scripts in /etc/init.d that can be scanned for +# configuration files names and locations. +my $neutronZvmAgentStem = 'neutron-zvm-agent'; +my $novaComputeStem = 'openstack-nova-compute'; + +# Eyecatchers used with --config operand +my $EyeCinderConf = 'cinder_conf'; +my $EyeMl2ConfIni = 'm12_conf'; +my $EyeNeutronConf = 'neutron_conf'; +my $EyeNeutronZvmPluginIni = 'neutron_zvm_plugin_conf'; +my $EyeNovaConf = 'nova_conf'; + +# set the usage message +my $usage_string = "Usage:\n + $0\n + or\n + $0 -s serviceToScan -d driverProgramName\n + or\n + $0 --scan serviceToScan -driver driverProgramName\n + The following options are supported: + + -c | --config + List of configuration files to be processed. This list overrides + the default configuration file locations or the ones determined by + the --init-files operand. Each configuration file is identified + by an eyecatcher indicating which configuration file is being + overriden followed by a colon and the fully qualified file + specification. Multiple configuration files may be specified by + separating them with a comma (one file per eyecatcher). + The following are recognized eyecathers and the files that they + override: + $EyeCinderConf - $locCinderConf + $EyeMl2ConfIni - $locMl2ConfIni + $EyeNeutronConf - $locNeutronConf + $EyeNeutronZvmPluginIni - + $locNeutronZvmPluginIni + $EyeNovaConf - $locNovaConf + -d | --driver File specification of driver program to construct, + or name of directory to contain the driver program. + -h | --help Display help information. + -H | --host Name of z/VM host to process. Startup scripts end + with this suffix for the specified z/VM system. When this option is + used with the -i option, it indicates which startup scripts should + be scanned. + The driver script created will contain the host value as part of + its name. + -i | --init-files Scan OpenStack startup scripts in /etc/init.d + for config file names that this script is interested in processing. + --help Display help information. + -p | --password-visible + If specified, password values in the constructed + driver script program will be visible. Otherwise, + password values are hidden. This option is used + when building a driver script to run against an + older xCAT MN. + -s | --scan Services to scan ('all', 'nova' or 'neutron'). + -v Display script version information. + -V | --verbose Display verbose processing messages.\n"; + +#------------------------------------------------------- + +=head3 buildDriverProgram + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = buildDriverProgram(); + +=cut + +#------------------------------------------------------- +sub buildDriverProgram{ + my $rc; + my @driverText; + + if ( $verbose ) { + print "Building the IVP driver program.\n"; + } + + # Erase any existing driver program. + if ( -e $driver and ! -z $driver ) { + # Make certain the file is one of our driver files. + my $found = `grep 'Function: z/VM xCAT IVP driver program' $driver`; + if ( ! $found ) { + print "$driver is not a z/VM xCAT IVP driver program\n"; + print "File will not be changed.\n"; + return 251; + } else { + # Rename the existing driver file. + print "Existing driver file is being saved as $driver.old\n"; + rename $driver,"$driver.old"; + } + } + + # Open the driver program for output. + $rc = open( my $fileHandle, '>', $driver ) or die; + if ( $rc != 1 ) { + print "Unable to open $driver for output: $!\n"; + return ( 200 + $rc ); + } + + # Construct the file in an array. + push( @driverText, "#!/bin/bash" ); + push( @driverText, "# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html" ); + push( @driverText, "#" ); + push( @driverText, "# Function: z/VM xCAT IVP driver program" ); + push( @driverText, "# Built by $0 version $version." ); + push( @driverText, "# $supportString" ); + push( @driverText, "" ); + if ( $cmaAppliance == 1 ) { + push( @driverText, "# System is a CMA" ); + push( @driverText, "# CMA system role: $cmaSystemRole" ); + push( @driverText, "# $cmaVersionString" ); + push( @driverText, "" ); + } + push( @driverText, "############## Start of Nova Config Properties" ); + if ( exists $novaConf{'DEFAULT'}{'my_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From \'my_ip\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$novaConf{'DEFAULT'}{'my_ip'}\"" ); + } else { + if ( keys %novaConf ) { + print "Info: 'my_ip' property is missing from section 'DEFAULT'\n" . + " in $locNovaConf. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + } else { + print "Info: The Nova configuration file was not found and thus this script is unable\n" . + " to use the 'my_ip' property. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + } + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From the local IP address of this system." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$localIpAddress\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_user_profile'} ) { + push( @driverText, "" ); + push( @driverText, "# Default profile used in creation of server instances." ); + push( @driverText, "# From \'zvm_user_profile\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_defaultUserProfile=\"$novaConf{'DEFAULT'}{'zvm_user_profile'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_diskpool'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of disk pools that are expected to exist." ); + push( @driverText, "# From \'zvm_diskpool\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_diskpools=\"$novaConf{'DEFAULT'}{'zvm_diskpool'}\"" ); + } + + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by instances." ); + push( @driverText, "# From \'zvm_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_instFCPList=\"$novaConf{'DEFAULT'}{'zvm_fcp_list'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_host'} ) { + push( @driverText, "" ); + push( @driverText, "# Node of host being managed. If blank, IVP will search for the host node." ); + push( @driverText, "# From \'zvm_host\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_hostNode=\"$novaConf{'DEFAULT'}{'zvm_host'}\"" ); + + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_master'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT MN (optional)." ); + push( @driverText, "# From \'zvm_xcat_master\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_mnNode=\"$novaConf{'DEFAULT'}{'zvm_xcat_master'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + my $clearPW = ''; + push( @driverText, "" ); + push( @driverText, "# User password defined to communicate with xCAT MN." ); + push( @driverText, "# From \'zvm_xcat_password\' in $locNovaConf." ); + + if ( -e $obfuscateProg ) { + # assume password is obfuscated already and get it in the clear. + $clearPW = `$obfuscateProg -u $novaConf{'DEFAULT'}{'zvm_xcat_password'}`; + $clearPW =~ s/\n+$//g; # trim ending new line + } else { + # Assume password is in the clear because the obfuscation program is missing. + $clearPW = $novaConf{'DEFAULT'}{'zvm_xcat_password'}; + } + + if ( $pwVisible ) { + push( @driverText, "export zxcatIVP_xcatUserPw=\"$clearPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=0" ); + } else { + my $hiddenPW = obfuscate( $clearPW, 1 ); + push( @driverText, "# Note: Password is hidden." ); + push( @driverText, "# To override the support and pass the password in the" ); + push( @driverText, "# clear, either:" ); + push( @driverText, "# - specify the -p or --password-visible operand when" ); + push( @driverText, "# invoking prep_zxcatIVP.pl script, or" ); + push( @driverText, "# - change zxcatIVP_pw_obfuscated variable to 0 and" ); + push( @driverText, "# specify the password in the clear on the" ); + push( @driverText, "# zxcatIVP_xcatUserPw variable in the constructed" ); + push( @driverText, "# driver script." ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$hiddenPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=1" ); + } + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected IP address of the xcat MN" ); + push( @driverText, "# From \'zvm_xcat_server\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatMNIp=\"$novaConf{'DEFAULT'}{'zvm_xcat_server'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + push( @driverText, "" ); + push( @driverText, "# User defined to communicate with xCAT MN" ); + push( @driverText, "# From \'zvm_xcat_username\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_xcatUser=\"$novaConf{'DEFAULT'}{'zvm_xcat_username'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by zHCP." ); + push( @driverText, "# From \'zvm_zhcp_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_zhcpFCPList=\"$novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'}\"" ); + } + + push( @driverText, "" ); + push( @driverText, "# Expected space available in the xCAT MN image repository" ); + push( @driverText, "# From \'xcat_free_space_threshold\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_expectedReposSpace=\"$novaConf{'DEFAULT'}{'xcat_free_space_threshold'}G\"" ); + + push( @driverText, "" ); + push( @driverText, "############## End of Nova Config Properties" ); + + if (( keys %neutronConf ) or ( keys %neutronZvmPluginIni ) or ( keys %ml2ConfIni )) { + push( @driverText, "" ); + push( @driverText, "############## Start of Neutron Config Properties" ); + if ( exists $neutronConf{'DEFAULT'}{'base_mac'} ) { + my $prefix = $neutronConf{'DEFAULT'}{'base_mac'}; + $prefix =~ tr/://d; + $prefix = substr( $prefix, 0, 6 ); + push( @driverText, "" ); + push( @driverText, "# User prefix for MAC Addresses of Linux level 2 interfaces" ); + push( @driverText, "# From \'base_mac\' in $locNeutronConf." ); + push( @driverText, "export zxcatIVP_macPrefix=\"$prefix\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat MN's address on the xCAT management network" ); + push( @driverText, "# From \'xcat_mgt_ip\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_xcatMgtIp=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat management interface netmask" ); + push( @driverText, "# From \'xcat_mgt_mask\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_mgtNetmask=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'}\"" ); + } + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} or + exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + my $list = ''; + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} and + $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} ne '*' ) { + $list = $ml2ConfIni{'ml2_type_flat'}{'flat_networks'}; + } + if ( exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + if ( $list ne '' ) { + $list = "$list,"; + } + $list = "$list$ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'}"; + } + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Array of networks and possible VLAN ranges" ); + push( @driverText, "# From $locMl2ConfIni,"); + push( @driverText, "# \'flat_networks\' in section \'ml2_type_flat\' and "); + push( @driverText, "# \'network_vlan_ranges\' in section \'ml2_type_vlan\'."); + push( @driverText, "export zxcatIVP_networks=\"$list\"" ); + } + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT zHCP server" ); + push( @driverText, "# From \'xcat_zhcp_nodename\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_zhcpNode=\"$neutronZvmPluginIni{'agent'}{'xcat_zhcp_nodename'}\"" ); + } + + # Create the zxcatIVP_vswitchOSAs variable for any networks specified with an rdev list. + my $vswitchOSAs = ''; + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + next if ( $list eq '' ); + $list =~ s/\s+/\,/g; # insert comma between words + $vswitchOSAs = "$section $list $vswitchOSAs"; + } + } + if ( $vswitchOSAs ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Vswitches and their related OSAs" ); + push( @driverText, "# From \'rdev_list\' in vswitch sections of $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_vswitchOSAs=\"$vswitchOSAs\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Neutron Config Properties" ); + } + + push( @driverText, "" ); + push( @driverText, "# Name of user under which nova runs, default is nova." ); + push( @driverText, "# If you system is different then change this property." ); + push( @driverText, "export zxcatIVP_expUser=\"nova\"" ); + push( @driverText, "" ); + push( @driverText, "# Controls whether Warnings/Errors detected by the IVP are" ); + push( @driverText, "# logged in the xCAT MN syslog, 0: do not log, 1: log to syslog." ); + push( @driverText, "export zxcatIVP_syslogErrors=1" ); + push( @driverText, "" ); + push( @driverText, "perl $driverLocation$ivp" ); + + # Write the array to the driver file. + foreach (@driverText) { + #print "$_\n"; + print $fileHandle "$_\n"; # Print each entry in our array to the file + } + + close $fileHandle; + + print "$driver was built.\n"; + return 0; +} + +#------------------------------------------------------- + +=head3 hashFile + + Description : Read a file with equal signs designating + key value pairs and create a hash from it. + Arguments : File to read + Reference to hash that should be constructed. + File is required to exist or there is a serious error. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = hashFile( $file, \%novaConf, 1 ); + +=cut + +#------------------------------------------------------- +sub hashFile{ + my ( $file, $hash, $required ) = @_; + my $section = "null"; + my $caseSensitive = 0; # assume section/properties are case insensitive + my @parts; + my $out; + + if ( $file eq '' ) { + return 602; + } + + if ( ! -e $file ) { + if ( $required ) { + print "Warning: $file does not exist.\n"; + } else { + print "Info: $file does not exist.\n"; + } + return 601; + } + + if (( $file =~ /.conf$/ ) or ( $file =~ /.COPY$/ )) { + # File is case sensitive, translate sections and property names to uppercase. + $caseSensitive = 1; + } + + # Read the configuration file and construct the hash of values. + if (( $file =~ /.conf$/ ) or ( $file =~ /.ini$/ )) { + $out = `egrep -v '(^#|^\\s*\\t*#)' $file`; + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^\[/ ) { + # Section header line + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the line + $line =~ s/^\[+|\]+$//g; # trim [] from ends of the line + if ( !$caseSensitive ) { + $section = lc( $line ); + } else { + $section = $line; + } + } else { + # Property line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$section}{$parts[0]} = $parts[1]; + #print "$section $parts[0]" . ": " . $parts[1]. "\n"; + #print $parts[0] . ": " . $$hash{$section}{$parts[0]}. "\n"; + } + } + } else { + # Hash .COPY files + # Read the file and remove comment lines and sequence columns (72-80) + $out = `grep -v ^\$ $file| cut -c1-71`; + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Weed out blank lines + $line =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $line ) == 0 ); + + # Parse the line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + # Add the property to the hash if it has data. + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + $parts[1] =~ s/^"+|"+$//g; # trim double quotes from both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$parts[0]} = $parts[1]; + #print $parts[0] . ": " . $$hash{$parts[0]}. "\n"; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 obfuscate + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : string to be processed + direction: 1 - obfuscate (hide it!) + 0 - unobfuscate (unhide it!) + Returns : processed password (either hiden or unhidden)) + Example : $rc = obfuscate( $pw, 1 ); + +=cut + +#------------------------------------------------------- +sub obfuscate{ + my ( $pw, $hide ) = @_; + + if ( $hide == 1 ) { + $pw = encode_base64( $pw, '' ); + } else { + $pw = decode_base64( $pw ); + } + + return $pw; +} + +#------------------------------------------------------- + +=head3 scanCinder + + Description : Scan the cinder configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCinder(); + +=cut + +#------------------------------------------------------- +sub scanCinder{ + my $rc; + + if ( $locCinderConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Cinder configuration files:\n"; + print " $locCinderConf\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCinderConf, \%cinderConf, 0 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanInitScript + + Description : Scan the init script for the + specified configuration file property. + Arguments : Configuration file name + Config property containing the value + Returns : Null - error locating the config file. + Non-null - filespecification of the + configuration file. + Example : $rc = scanInitScript('openstack-cinder-volume-$host', + 'config'); + +=cut + +#------------------------------------------------------- +sub scanInitScript{ + my ( $filename, $property ) = @_; + my $configFile; + my $out; + + if ( $verbose ) { + print "Scanning $filename for '$property' variable.\n"; + } + + # verify the file exists + if ( !-e "/etc/init.d/$filename" ) { + print "Info: /etc/init.d/$filename does not exist.\n"; + return $configFile; + } + + # Strip out the lines after the desired config property is set and + # remove comment lines then echo the property value. + $out = `awk '{print} /$property=/ {exit}' /etc/init.d/$filename | grep -v ^\# | grep .`; + $out = $out . "echo -n \$$property"; + $configFile = `$out`; + + return $configFile; +} + +#------------------------------------------------------- + +=head3 scanNeutron + + Description : Scan the neutron configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNeutron(); + +=cut + +#------------------------------------------------------- +sub scanNeutron{ + my $rc; + + if ( $locNeutronConf eq '' and $locMl2ConfIni eq '' + and $locNeutronZvmPluginIni eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Neutron configuration files:\n"; + print " $locNeutronConf\n"; + print " $locMl2ConfIni\n"; + print " $locNeutronZvmPluginIni\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronConf, \%neutronConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locMl2ConfIni, \%ml2ConfIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronZvmPluginIni, \%neutronZvmPluginIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNova + + Description : Scan the Nova configuration files. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNova(); + +=cut + +#------------------------------------------------------- +sub scanNova{ + my $rc; + + if ( $locNovaConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Nova configuration file:\n"; + print " $locNovaConf\n"; + } + + # Verify the /etc/nova/nova.conf exists. + $rc = hashFile( $locNovaConf, \%novaConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares and builds a z/VM xCAT IVP driver program + using the information from the configuration files in the + compute node. + + The default name of the driver program is composed of the following: + '$driverPrefix', and + IP address of the system where driver was prepared, and + (optionally) a hypen and the value specified on --Host operand, and + '.sh'. + For example: + $driverPrefix"."9.123.345.91.sh + $driverPrefix"."9.123.345.91-hostzvm.sh + + $supportString + + The following configuration files are scanned for input: + $locCinderConf + $locNovaConf + $locNeutronConf + $locMl2ConfIni + $locNeutronZvmPluginIni + + When the --init-files operand is specified, OpenStack startup + scripts are scanned in /etc/init.d. The --host operand should + be specified to indicate the suffix to use for scripts that + are unique to a specific z/VM host. The following startup + scripts are scanned: + neutron-server + $neutronZvmAgentStem- + $novaComputeStem- + openstack-cinder-volume + + When --init-files is specified without the --host operand, + the following scripts are scanned: + neutron-server + $neutronZvmAgentStem + $novaComputeStem + openstack-cinder-volume + + The constructed driver program can then be uploaded to + the xCAT MN and used to validate the configuration between + the compute node and xCAT.\n\n"; + print $usage_string; + return; +} + +#------------------------------------------------------- + +=head3 validateConfigs + + Description : Compare and validate the configuration + values obtained by the scans. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = validateConfigs(); + +=cut + +#------------------------------------------------------- +sub validateConfigs{ + my $bypassCinder = 0; + my $rc = 0; + my $option; + if ( $verbose ) { + print "Performing a local validation of the configuration files.\n"; + } + + #******************************************************* + # Verify required configuration options were specified. + #******************************************************* + if ( keys %cinderConf ) { + my @requiredCinderOpts = (); + foreach $option ( @requiredCinderOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$option} ) { + #print "option:$option.\nvalue:$cinderConf{$option}\n"; + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in $locCinderConf.\n"; + } + } + } + + if ( keys %novaConf ) { + my @requiredNovaOpts = ( + 'DEFAULT','compute_driver', + 'DEFAULT','config_drive_format', + 'DEFAULT','force_config_drive', + 'DEFAULT','host', + 'DEFAULT','instance_name_template', + 'DEFAULT','zvm_diskpool', + 'DEFAULT','zvm_host', + 'DEFAULT','zvm_user_profile', + 'DEFAULT','zvm_xcat_master', + 'DEFAULT','zvm_xcat_server', + 'DEFAULT','zvm_xcat_username', + 'DEFAULT','zvm_xcat_password', + ); + for ( my $i = 0; $i < $#requiredNovaOpts; $i = $i + 2 ) { + my $section = $requiredNovaOpts[$i]; + my $option = $requiredNovaOpts[$i+1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + } + } + } + + if ( keys %neutronConf ) { + my @requiredNeutronConfOpts = ( + 'DEFAULT','core_plugin', + ); + for ( my $i = 0; $i < $#requiredNeutronConfOpts; $i = $i + 2 ) { + my $section = $requiredNeutronConfOpts[$i]; + my $option = $requiredNeutronConfOpts[$i+1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf/neutron/neutron.conf.\n"; + } + } + } + + if ( keys %ml2ConfIni ) { + my @requiredMl2ConfIniOpts = ( + 'ml2','mechanism_drivers', + 'ml2','tenant_network_types', + 'ml2','type_drivers', + ); + for ( my $i = 0; $i < $#requiredMl2ConfIniOpts; $i = $i + 2 ) { + my $section = $requiredMl2ConfIniOpts[$i]; + my $option = $requiredMl2ConfIniOpts[$i+1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni/neutron/plugins/ml2/ml2_conf.ini.\n"; + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my @requiredNeutronZvmPluginIniOpts = ( + 'agent', 'zvm_host', + 'agent','zvm_xcat_server', + ); + for ( my $i = 0; $i < $#requiredNeutronZvmPluginIniOpts; $i = $i + 2 ) { + my $section = $requiredNeutronZvmPluginIniOpts[$i]; + my $option = $requiredNeutronZvmPluginIniOpts[$i+1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + } + } + } + + #****************************************** + # Verify optional operands were specified. + #****************************************** + if ( keys %cinderConf ) { + if ( $cmaSystemRole eq "COMPUTE" ) { + print "Info: CMA system role is a compute server.\n Cinder will not be validated.\n"; + $bypassCinder = 1; + } elsif ( !exists $cinderConf{'DEFAULT'}{'san_ip'} and + !exists $cinderConf{'DEFAULT'}{'san_private_key'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_volpool_name'} + ) { + print "Info: Most z/VM specific Cinder keys are not defined in $locCinderConf.\n" . + " Cinder support for creation of persistent disks for z/VM\n" . + " is not enabled. Further testing of these options will\n" . + " not occur in this script.\n"; + $bypassCinder = 1; + } else { + my %optionalCinderConfOpts = ( + "DEFAULT san_ip" => "This property is necessary when using persistent SAN disks obtained\n" . + " from the Cinder service.", + "DEFAULT san_private_key" => "This property is necessary when using persistent SAN disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_connection_protocol" => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_multipath_enabled" => "This property is optional and used when StorWize persistent disks are\n" . + " obtained from the Cinder service. The default is 'false'.", + "DEFAULT storwize_svc_volpool_name" => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + 'DEFAULT storwize_svc_vol_iogrp' => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + 'DEFAULT volume_driver' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + ); + my %defaultCinderConfOpts = (); + foreach my $key ( keys %optionalCinderConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $cinderConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locCinderConf.\n"; + if ( $optionalCinderConfOpts{$key} ne '' ) { + print " " . $optionalCinderConfOpts{$key} . "\n"; + } + if ( exists $defaultCinderConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCinderConfOpts{$key}; + } + } + } + } + } + + if ( keys %novaConf ) { + my %optionalNovaConfOpts = ( + "DEFAULT image_cache_manager_interval" => "Default of 2400 (seconds) will be used.", + "DEFAULT ram_allocation_ratio" => "", + "DEFAULT rpc_response_timeout" => "zVM Live migration may timeout with the default " . + "value (60 seconds).\n The recommended value for z/VM is 180 to allow " . + "zVM live migration\n to succeed.", + "DEFAULT xcat_free_space_threshold" => "Default of 50 (G) will be used.", + "DEFAULT xcat_image_clean_period" => "Default of 30 (days) will be used.", + "DEFAULT zvm_config_drive_inject_password" => "This value will default to 'FALSE'.", + "DEFAULT zvm_diskpool_type" => "Default of \'ECKD\' will be used.", + "DEFAULT zvm_fcp_list" => "As a result, Cinder volumes cannot be attached to server instances.", + "DEFAULT zvm_zhcp_fcp_list" => "", + "DEFAULT zvm_image_tmp_path" => "Default of '/var/lib/nova/images' will be used.", + "DEFAULT zvm_reachable_timeout" => "Default of 300 (seconds) will be used.", + "DEFAULT zvm_scsi_pool" => "Default of \'xcatzfcp\' will be used.", + "DEFAULT zvm_vmrelocate_force" => "", + "DEFAULT zvm_xcat_connection_timeout" => "Default of 3600 seconds will be used.", + "DEFAULT zvm_image_compression_level" => "Image compression is controlled by the xCAT ZHCP " . + "server in\n the /var/opt/zhcp/settings.conf file. Compressing images during image\n" . + " capture is the default." + ); + my %defaultNovaConfOpts = ( + "DEFAULT xcat_free_space_threshold" => 50, + "DEFAULT xcat_image_clean_period" => 30, + "DEFAULT zvm_diskpool_type" => 'ECKD', + "DEFAULT zvm_scsi_pool" => "xcatzfcp", + ); + foreach my $key ( keys %optionalNovaConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + if ( $optionalNovaConfOpts{$key} ne '' ) { + print " " . $optionalNovaConfOpts{$key} . "\n"; + } + if ( exists $defaultNovaConfOpts{$key} ) { + $novaConf{$section}{$option} = $defaultNovaConfOpts{$key}; + } + } + } + } + + if ( keys %neutronConf) { + my %optionalNeutronConfOpts = (); + my %defaultNeutronConfOpts = (); + foreach my $key ( keys %optionalNeutronConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf.\n"; + if ( $optionalNeutronConfOpts{$key} ne '' ) { + print " " . $optionalNeutronConfOpts{$key} . "\n"; + } + if ( exists $defaultNeutronConfOpts{$key} ) { + $neutronConf{$section}{$option} = $defaultNeutronConfOpts{$key}; + } + } + } + } + + if ( keys %ml2ConfIni ) { + my %optionalMl2ConfIniOpts = ( + 'ml2_type_vlan network_vlan_ranges' => "", + 'ml2_type_flat flat_networks' => "", + ); + my %defaultMl2ConfIniOpts = (); + foreach my $key ( keys %optionalMl2ConfIniOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni.\n"; + if ( $optionalMl2ConfIniOpts{$key} ne '' ) { + print " " . $optionalMl2ConfIniOpts{$key} . "\n"; + } + if ( exists $defaultMl2ConfIniOpts{$key} ) { + $ml2ConfIni{$section}{$option} = $defaultMl2ConfIniOpts{$key}; + } + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my %optionalNeutronZvmPluginIniOpts = ( + "agent xcat_mgt_ip" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent xcat_mgt_mask" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent polling_interval" => "A default value of '5' will be used.", + "agent xcat_zhcp_nodename" => "A default value of \'zhcp\' will be used.", + "agent zvm_xcat_password" => "A default value of \'admin\' is used.", + "agent zvm_xcat_timeout" => "A default value of 300 seconds is used.", + "agent zvm_xcat_username" => "A default value of \'admin\' is used.", + ); + my %defaultNeutronZvmPluginIniOpts = ( + "agent polling_interval" => 2, + "agent xcat_zhcp_nodename" => "zhcp", + "agent zvm_xcat_password" => "admin", + "agent zvm_xcat_timeout" => 300, + "agent zvm_xcat_username" => "admin", + ); + foreach my $key ( keys %optionalNeutronZvmPluginIniOpts ) { + my @opts = split( '\s', $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + if ( $optionalNeutronZvmPluginIniOpts{$key} ne '' ) { + print " " . $optionalNeutronZvmPluginIniOpts{$key} . "\n"; + } + if ( exists $defaultNeutronZvmPluginIniOpts{$key} ) { + $neutronZvmPluginIni{$section}{$option} = $defaultNeutronZvmPluginIniOpts{$key}; + } + } + } + } + + # Verify xCAT users are the same. + if (( keys %novaConf ) and ( keys %neutronZvmPluginIni )) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Info: Bypassing validation of 'zvm_xcat_username'.\n" . + " It is not specified in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_username'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_username'} ) { + print "Warning: xCAT user names mismatch; review 'zvm_xcat_username':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_username'}\' in $locNovaConf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_username'}\' in\n" . + " $locNeutronZvmPluginIni.\n"; + } + } + } + + # Verify xCAT user passwords are the same. + if (( keys %novaConf ) and ( keys %neutronZvmPluginIni )) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Info: Bypassing validation of 'zvm_xcat_password'. It is not specified\n" . + " in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_password'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_password'} ) { + print "Warning: xCAT user passwords are not the same:\n" . + " Please review 'zvm_xcat_password' in $locNovaConf and\n" . + " $locNeutronZvmPluginIni.\n"; + } + } + } + + # Verify the xcat server IP addresses are the same. + if (( keys %novaConf ) and ( keys %neutronZvmPluginIni )) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in $locNovaConf.\n"; + } elsif ( !exists $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Info: Bypassing validation of 'zvm_xcat_server'.\n" . + " It is not specified in $locNeutronZvmPluginIni.\n"; + } else { + if ( $novaConf{'DEFAULT'}{'zvm_xcat_server'} ne $neutronZvmPluginIni{'agent'}{'zvm_xcat_server'} ) { + print "Warning: xCAT server addresses mismatch; review 'zvm_xcat_server':\n" . + " \'$novaConf{'DEFAULT'}{'zvm_xcat_server'}\' in $locNovaConf.\n" . + " \'$neutronZvmPluginIni{'agent'}{'zvm_xcat_server'}\' in $locNeutronZvmPluginIni.\n"; + } + } + } + + # Verify host and zvm_host properties are the same. + + if ( exists $novaConf{'DEFAULT'}{'host'} and + exists $neutronZvmPluginIni{'agent'}{'zvm_host'} ) { + if ( $novaConf{'DEFAULT'}{'host'} ne $neutronZvmPluginIni{'agent'}{'zvm_host'} ) { + print "Warning: 'host' property in section 'DEFAULT' in $locNovaConf\n" . + " does not specify the same value as 'zvm_host' property in section\n" . + " 'agent' in $locNeutronZvmPluginIni.\n"; + } + } + + # Verify the instance name template is valid + if ( exists $novaConf{'DEFAULT'}{'instance_name_template'} ) { + # Use sprintf which is close enough to the python % support for formatting to construct a sample. + my $base_name = sprintf( $novaConf{'DEFAULT'}{'instance_name_template'}, 1 ); + if ( length( $base_name ) > 8 ) { + print "Warning: In $locNovaConf, section \`DEFAULT\`, instance_name_template would\n" . + " construct a value greater than 8 in length: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + if (( $base_name eq $novaConf{'DEFAULT'}{'instance_name_template'} ) || + ( $novaConf{'DEFAULT'}{'instance_name_template'} !~ /%/ )) { + print "Warning: In $locNovaConf, instance_name_template will not\n" . + " create a usable name with the value: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + my $words; + $words++ while $base_name =~ /\S+/g; + if ( $words != 1 ) { + print "Warning: In $locNovaConf, instance_name_template will not create\n" . + " an instance name that is a single blank delimited word using\n" . + " the value: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + if ( $novaConf{'DEFAULT'}{'instance_name_template'} =~ m/(^RSZ)/i ) { + print "Warning: In $locNovaConf, instance_name_template should not begin\n" . + " with 'rsz': $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + } + + # Verify the compute_driver is for z/VM + if ( exists $novaConf{'DEFAULT'}{'compute_driver'} ) { + if ( $novaConf{'DEFAULT'}{'compute_driver'} ne "nova.virt.zvm.ZVMDriver" and + $novaConf{'DEFAULT'}{'compute_driver'} ne "zvm.ZVMDriver") { + print "Warning: In $locNovaConf, compute_driver does not contain the\n" . + " expected value of \'zvm.ZVMDriver\' and instead contains:\n" . + " \'$novaConf{'DEFAULT'}{'compute_driver'}\'\n"; + } + } + + # Check whether the rpc timeout is too small for z/VM + if ( exists $novaConf{'DEFAULT'}{'rpc_response_timeout'} ) { + if ( $novaConf{'DEFAULT'}{'rpc_response_timeout'} < 180 ) { + print "Warning: In $locNovaConf, section \'DEFAULT\', rpc_response_timeout\n" . + " specifies a value, \'$novaConf{'DEFAULT'}{'rpc_response_timeout'}\', which is " . + "less than the recommended value\n of \'180\'.\n"; + } + } + + # Verify all SCSI disk operands are specified, if one exists. + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} or exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + if ( !exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_zhcp_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + } + + # Verify neutron.conf operands with a fixed set of possible values + if ( exists $neutronConf{'DEFAULT'}{'core_plugin'} ) { + if ( $neutronConf{'DEFAULT'}{'core_plugin'} eq 'neutron.plugins.ml2.plugin.Ml2Plugin' ) { + print "Info: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value, '$neutronConf{'DEFAULT'}{'core_plugin'}',\n" . + " which is deprecated. The recommended value is 'ml2'.\n"; + } + elsif ( $neutronConf{'DEFAULT'}{'core_plugin'} ne 'ml2' ) { + print "Warning: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value, '$neutronConf{'DEFAULT'}{'core_plugin'}',\n" . + " which is not 'ml2'.\n"; + } + } + + # Verify base_mac value is specified for non-CMA. + if (( keys %neutronConf ) and ( !exists $neutronConf{'DEFAULT'}{'base_mac'} ) and ( !$cmaAppliance )) { + print "Warning: 'base_mac' is missing from section 'DEFAULT'\n" . + " in $locNeutronConf.\n"; + } + + # Verify any rdev_list in Neutron z/VM Plugin ini file contains a single value and/or not a comma + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list eq '' ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' is specified but has no value.\n"; + } else { + my @vals = split ( /\s/, $list ); + if ( $#vals > 0 ) { + # $#vals is array size - 1. + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains too many values.\n"; + } + foreach my $op ( @vals ) { + if ( $op =~ m/[^0-9a-fA-F]+/ ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains non-hexadecimal characters: \'$op\'.\n"; + } elsif ( length($op) > 4 ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains a value that is not 1-4 characters in\n" . + " length: \'$op\'.\n"; + } + } + } + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ) { + if ( $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ne 'FC' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\',\n" . + " storwize_svc_connection_protocol specifies a value, " . + "\'$cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'}\',\n" . + " which is not the required value of \'FC\'.\n"; + } + } + + # Check whether the volume_driver is correct for z/VM + if ( ! $bypassCinder and exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + if ( $cinderConf{'DEFAULT'}{'volume_driver'} ne 'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\', volume_driver specifies\n" . + " a value, \'$cinderConf{'DEFAULT'}{'volume_driver'}\',\n which is " . + "not the required value of\n \'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver\'.\n"; + } + } + + # Check whether the storwize_svc_multipath_enabled is not 'true' or 'false' for z/VM + if ( ! $bypassCinder and exists $cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'} ) { + my $ucValue = uc( $cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'} ); + if (( $ucValue ne 'TRUE' ) and ( $ucValue ne 'FALSE' )) { + print "Warning: In $locCinderConf, section \'DEFAULT\',\n" . + " storwize_svc_multipath_enabled specifies a value, " . + "'$cinderConf{'DEFAULT'}{'storwize_svc_multipath_enabled'}',\n" . + " which is not the required value of 'true' or 'false'.\n"; + } + } + + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $configOpt; +my $rc = 0; +my $obfuscateOpt; +my $out; +my $scanInitOpt; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( + 'c|config=s' => \$configOpt, + 'd|driver=s' => \$driver, + 'h|help' => \$displayHelp, + 'H|host=s' => \$host, + 'i|init-files' => \$scanInitOpt, + 'p|password-visible' => \$pwVisible, + 's|scan=s' => \$scan, + 'v' => \$versionOpt, + 'V|verbose' => \$verbose )) { + print $usage_string; + goto FINISH; +} + +if ( $versionOpt ) { + print "Version: $version\n"; + print "$supportString\n"; +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH; +} + +# Determine the local IP address for this system. +my $errorLines = ''; +my $hostname = hostname; +if ( $hostname ne '' ) { + $localIpAddress = gethostbyname( $hostname ); + if ( defined $localIpAddress ) { + my $len = length( $localIpAddress ); + if ( $len == 4 ) { + $localIpAddress = inet_ntoa( $localIpAddress ); + } else { + $localIpAddress = ''; + $errorLines = $errorLines . " The IP address obtained from perl gethostbyname function does not\n" . + " appear to be an IPv4 address. An IPv4 address should be used.\n"; + } + } else { + $errorLines = $errorLines . " The IP address related to the following host name was not found:\n" . + " $hostname\n"; + if ( defined $? ) { + $errorLines = $errorLines . " The perl gethostbyname function failed with errno: $?\n"; + } + $localIpAddress = ''; + } +} else { + $errorLines = $errorLines . " The host name was not found.\n"; +} + +if ( $localIpAddress eq '' ) { + print "Warning: Unable to obtain the host name and/or IP address for this system.\n"; + print " The local IP address is used in the name of the driver script.\n" . + " Some variables in the driver script such as zxcatIVP_cNAddress\n" . + " may be set to an empty string value. You may wish to correct\n" . + " the driver script and your system configuration.\n"; + if ( $errorLines ne '' ) { + print " Additional Information:\n$errorLines"; + } +} + +# Detect CMA +if ( -e $locVersionFileCMO ) { + # CMA version file exists, treat this as a CMA node + $cmaAppliance = 1; + $cmaVersionString = `cat $locVersionFileCMO`; + chomp( $cmaVersionString ); + if ( $cmaVersionString eq '' ) { + $cmaVersionString = "version unknown"; + } + + # Determine the role of this CMA system + my %settings; + if ( -e $locApplSystemRole ) { + $rc = hashFile( $locApplSystemRole, \%settings, 0 ); + $cmaSystemRole = uc( $settings{'role'} ); + } + if ( $cmaSystemRole eq '' and -e $locDMSSICMOCopy ) { + $rc = hashFile( $locDMSSICMOCopy, \%settings, 0 ); + if ( exists $settings{'openstack_system_role'} ) { + $cmaSystemRole = uc( $settings{'openstack_system_role'} ); + } + } + if ( $cmaSystemRole eq '' ) { + $cmaSystemRole = 'unknown'; + } +} + +if ( defined( $scan ) ) { + if ( $verbose ) { + print "Operand --scan: $scan\n"; + } + if ( 'all cinder neutron nova' !~ $scan ) { + print "--scan operand ($scan) is not all, cinder, neutron or nova\n"; + $rc = 400; + goto FINISH; + } +} else { + $scan = 'all'; +} + +if ( defined( $host ) ) { + if ( $verbose ) { + print "Operand --Host: $host\n"; + } + $driverSuffix = "_$host"; + $initSuffix = "-$host"; +} else { + $host = ''; + $driverSuffix = ""; + $initSuffix = ""; +} + +if ( defined( $scanInitOpt ) ) { + if ( $verbose ) { + print "Operand --initFiles\n"; + } + + my $configFile; + if ( $scan =~ 'all' or $scan =~ 'nova' ) { + $configFile = scanInitScript( "$novaComputeStem$initSuffix", 'config' ); + if ( defined( $configFile ) ) { + $locNovaConf = $configFile; + } else { + print "Info: Unable to determine the Nova configuration file.\n"; + $locNovaConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + $configFile = scanInitScript( "openstack-cinder-volume", 'config' ); + if ( defined( $configFile ) ) { + $locCinderConf = $configFile; + } else { + print "Info: Unable to determine the Cinder configuration file.\n"; + $locCinderConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + $configFile = scanInitScript( "neutron-server", 'config' ); + if ( defined( $configFile ) ) { + $locNeutronConf = $configFile; + } else { + print "Info: Unable to determine the Neutron configuration file.\n"; + $locNeutronConf = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'config' ); + if ( defined( $configFile ) ) { + $locNeutronZvmPluginIni = $configFile; + } else { + print "Info: Unable to determine the Neutron z/VM plugin configuration file.\n"; + $locNeutronZvmPluginIni = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'plugin_config' ); + if ( defined( $configFile ) ) { + $locMl2ConfIni = $configFile; + } else { + print "Info: Unable to determine the Neutron ml2 configuration file.\n"; + $locMl2ConfIni = ''; + } + } +} + +if ( defined( $configOpt ) ) { + if ( $verbose ) { + print "Operand --config $configOpt\n"; + } + + my @items = split( ',', $configOpt ); + foreach my $item ( @items ) { + my @parts = split( ':', $item ); + if ( defined $parts[1] ) { + $parts[0] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + if ( defined $parts[1] ) { + $parts[1] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + + if ( !defined $parts[1] or $parts[1] eq '' ) { + print( "Warning: --config option: $parts[0] did not specify a file. " . + "Default file specification will be used.\n" ); + next; + } + + if ( $parts[0] eq $EyeCinderConf ) { + $locCinderConf = $parts[1]; + } elsif ( $parts[0] eq $EyeMl2ConfIni ) { + $locMl2ConfIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronConf ) { + $locNeutronConf = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronZvmPluginIni ) { + $locNeutronZvmPluginIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNovaConf ) { + $locNovaConf = $parts[1]; + } + } +} + +my ( $volume, $directory, $file ) = ''; +if ( defined( $driver ) ) { + if ( $verbose ) { + print "Operand --driver: $driver\n"; + } + + my @dirs; + if ( -d $driver ) { + # Driver operand is a directory name only + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver, 1 ); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); + } else { + # Driver operand is a bad directory or a file in a directory + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver ); + if ( -d $directory ) { + $driver = File::Spec->catpath( $volume, $directory, $file ); + } else { + print "Driver property does not specify a valid directory: $directory\n"; + $rc = 500; + goto FINISH; + } + } +} else { + $directory = File::Spec->curdir(); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); +} + +# Scan the configuration files. +if ( $scan =~ 'all' or $scan =~ 'nova' ) { + scanNova(); +} + +if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + scanCinder(); +} + +if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + scanNeutron(); +} + +# Validate the settings and produce the driver script. +if ( ! $configFound ) { + print "Warning: No configuration files were found. " . + "No further processing will be performed.\n"; + $rc = 501; + # Allow other processing to continue +} + +!( $rc = validateConfigs() ) or goto FINISH; + +# Validate CMA related information +if ( $cmaSystemRole ne '' ) { + # Validate Controller related information + if ( $cmaSystemRole eq 'CONTROLLER' ) { + if ( !( -e $locLicFileICM ) ) { + print "Warning: $locLicFileICM was not found.\n" . + " ICM deployer does not appear to be installed.\n"; + $rc = 502; + # Allow other processing to continue + } + } +} + +!( $rc = buildDriverProgram() ) or goto FINISH; + +FINISH: +exit $rc; + diff --git a/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_LIBERTY.pl b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_LIBERTY.pl new file mode 100644 index 000000000..fca98d2da --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_LIBERTY.pl @@ -0,0 +1,1738 @@ +#!/usr/bin/perl +############################################################################### +# (c) Copyright International Business Machines Corporation 2016. +# All Rights Reserved. +############################################################################### +# COMPONENT: prep_zxcatIVP_LIBERTY.pl +# +# This is a preparation script for Installation Verification Program for xCAT +# on z/VM. It prepares the driver script by gathering information from +# OpenStack configuration files on the compute node. +############################################################################### + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use Getopt::Long; +use MIME::Base64; +use Sys::Hostname; +use Socket; + +my %commonConf; # Common configuration options +my %ceilometerConf; # Ceilometer configuration options +my %cinderConf; # Cinder configuration options +my %novaConf; # Nova configuration options +my %neutronConf; # Neutron configuration options +my %ml2ConfIni; # Neutron m12 configuration options +my %neutronZvmPluginIni; # Neutron zvm configuration options + +my $version = "5.0"; +my $supportString = "This script supports code based on the OpenStack Liberty release."; + +my $cmaAppliance = 0; # Assumed to not be run in a CMA system +my $cmaVersionString = ''; # CMA version string for a CMA +my $cmaSystemRole = ''; # CMA system role string +my $commonOpts = ' host xcat_zhcp_nodename zvm_host zvm_xcat_master zvm_xcat_server zvm_xcat_username zvm_xcat_password '; + # Common options that can be specified in more than one configuration file +my $configFound = 0; # At least 1 config file was found. Defaults to false. +my $driver; # Name of driver file to be created less the ".pl" +my $driverLocation = "/opt/xcat/bin/"; # Location of the IVP program in xCAT MN. +my $driverPrefix = "zxcatIVPDriver_"; # Prefix used in naming the driver program. +my $driverSuffix; # Suffix used in naming the driver program. +my $displayHelp = 0; # Display help information. +my $host; # Host command option value +my $initSuffix; # Suffix used in naming multihomed init scripts +my $ivp = "zxcatIVP.pl"; # z/VM xCAT IVP script name +my $obfuscateProg = '/usr/bin/openstack-obfuscate'; # PW obfuscation program +my $pwVisible = 0; # PW is visible in the driver script (1 = yes, 0 = no) +my $scan; # Type of scan to be performed +my $verbose; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Shov version information. + +my $localIpAddress = ''; # Local IP address of system where we are prepping + +# Locations of configuration files +my $locCeilometerConf = '/etc/ceilometer/ceilometer.conf'; +my $locCinderConf = '/etc/cinder/cinder.conf'; +my $locMl2ConfIni = '/etc/neutron/plugins/ml2/ml2_conf.ini'; +my $locNeutronConf = '/etc/neutron/neutron.conf'; +my $locNeutronZvmPluginIni = '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini'; +my $locNovaConf = '/etc/nova/nova.conf'; + +# Version related files +my $locVersionFileCMO = '/opt/ibm/cmo/version'; +my $locDMSSICMOCopy = '/var/lib/sspmod/DMSSICMO.COPY'; +my $locApplSystemRole = '/var/lib/sspmod/appliance_system_role'; + +# Stems of startup scripts in /etc/init.d that can be scanned for +# configuration files names and locations. +my $ceilometerStem = 'openstack-ceilometer-api'; +my $neutronZvmAgentStem = 'neutron-zvm-agent'; +my $novaComputeStem = 'openstack-nova-compute'; + +# Eyecatchers used with --config operand +my $EyeCeilometerConf = 'ceilometer_conf'; +my $EyeCinderConf = 'cinder_conf'; +my $EyeMl2ConfIni = 'm12_conf'; +my $EyeNeutronConf = 'neutron_conf'; +my $EyeNeutronZvmPluginIni = 'neutron_zvm_plugin_conf'; +my $EyeNovaConf = 'nova_conf'; + +# set the usage message +my $usage_string = "Usage:\n + $0\n + or\n + $0 -s serviceToScan -d driverProgramName\n + or\n + $0 --scan serviceToScan -driver driverProgramName\n + The following options are supported: + + -c | --config + List of configuration files to be processed. This list overrides + the default configuration file locations or the ones determined by + the --init-files operand. Each configuration file is identified + by an eyecatcher indicating which configuration file is being + overriden followed by a colon and the fully qualified file + specification. Multiple configuration files may be specified by + separating them with a comma (one file per eyecatcher). + The following are recognized eyecathers and the files that they + override: + $EyeCeilometerConf - $locCeilometerConf + $EyeCinderConf - $locCinderConf + $EyeMl2ConfIni - $locMl2ConfIni + $EyeNeutronConf - $locNeutronConf + $EyeNeutronZvmPluginIni - + $locNeutronZvmPluginIni + $EyeNovaConf - $locNovaConf + -d | --driver File specification of driver program to construct, + or name of directory to contain the driver program. + -h | --help Display help information. + -H | --host Name of z/VM host to process. Startup scripts end + with this suffix for the specified z/VM system. When this option is + used with the -i option, it indicates which startup scripts should + be scanned. + The driver script created will contain the host value as part of + its name. + -i | --init-files Scan the system for either System V style startup + scripts or systemd service files related to OpenStack. The + files are scanned for the name of the related configuration file. + For System V, /etc/init.d directory is scanned. For systemd, it + will scan /usr/lib/systemd/system/, /run/systemd/system/, and + /etc/systemd/system/ service files (.service). + --help Display help information. + -p | --password-visible + If specified, password values in the constructed + driver script program will be visible. Otherwise, + password values are hidden. This option is used + when building a driver script to run against an + older xCAT MN. + -s | --scan Services to scan ('all', 'nova' or 'neutron'). + -v Display script version information. + -V | --verbose Display verbose processing messages.\n"; + +#------------------------------------------------------- + +=head3 buildDriverProgram + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = buildDriverProgram(); + +=cut + +#------------------------------------------------------- +sub buildDriverProgram{ + my $rc; + my @driverText; + + if ( $verbose ) { + print "Building the IVP driver program.\n"; + } + + # Erase any existing driver program. + if ( -e $driver and ! -z $driver ) { + # Make certain the file is one of our driver files. + my $found = `grep 'Function: z/VM xCAT IVP driver program' $driver`; + if ( ! $found ) { + print "$driver is not a z/VM xCAT IVP driver program\n"; + print "File will not be changed.\n"; + return 251; + } else { + # Rename the existing driver file. + print "Existing driver file is being saved as $driver.old\n"; + rename $driver,"$driver.old"; + } + } + + # Open the driver program for output. + $rc = open( my $fileHandle, '>', $driver ) or die; + if ( $rc != 1 ) { + print "Unable to open $driver for output: $!\n"; + return ( 200 + $rc ); + } + + # Construct the file in an array. + push( @driverText, "#!/bin/bash" ); + push( @driverText, "# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html" ); + push( @driverText, "#" ); + push( @driverText, "# Function: z/VM xCAT IVP driver program" ); + push( @driverText, "# Built by $0 version $version." ); + push( @driverText, "# $supportString" ); + push( @driverText, "" ); + if ( $cmaAppliance == 1 ) { + push( @driverText, "# System is a CMA" ); + push( @driverText, "# CMA system role: $cmaSystemRole" ); + push( @driverText, "# $cmaVersionString" ); + push( @driverText, "" ); + } + push( @driverText, "############## Start of Nova Config Properties" ); + if ( exists $novaConf{'DEFAULT'}{'my_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From \'my_ip\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$novaConf{'DEFAULT'}{'my_ip'}\"" ); + } else { + if ( $locNovaConf ne '' ) { + print "Info: 'my_ip' property is missing from section 'DEFAULT'\n" . + " in $locNovaConf. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + } else { + print "Info: The Nova configuration file was not found and thus this script is unable\n" . + " to use the 'my_ip' property. A default value of '$localIpAddress'\n" . + " has been specified in the driver program for zxcatIVP_cNAddress.\n" . + " If the value is not correct then you should update the driver file\n". + " with the desired IP address.\n"; + } + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From the local IP address of this system." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$localIpAddress\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_user_profile'} ) { + push( @driverText, "" ); + push( @driverText, "# Default profile used in creation of server instances." ); + push( @driverText, "# From \'zvm_user_profile\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_defaultUserProfile=\"$novaConf{'DEFAULT'}{'zvm_user_profile'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_diskpool'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of disk pools that are expected to exist." ); + push( @driverText, "# From \'zvm_diskpool\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_diskpools=\"$novaConf{'DEFAULT'}{'zvm_diskpool'}\"" ); + } + + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by instances." ); + push( @driverText, "# From \'zvm_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_instFCPList=\"$novaConf{'DEFAULT'}{'zvm_fcp_list'}\"" ); + } + if ( exists $commonConf{'zvm_host'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Node of host being managed. If blank, IVP will search for the host node." ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_host'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_hostNode=\"$commonConf{'zvm_host'}{'value'}\"" ); + + } + if ( exists $commonConf{'zvm_xcat_master'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT MN (optional)." ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_master'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_mnNode=\"$commonConf{'zvm_xcat_master'}{'value'}\"" ); + } + if ( exists $commonConf{'zvm_xcat_password'}{'value'} ) { + my $clearPW = ''; + push( @driverText, "" ); + push( @driverText, "# User password defined to communicate with xCAT MN." ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_master'}{'fromLines'} ) ); + + if ( -e $obfuscateProg ) { + # assume password is obfuscated already and get it in the clear. + $clearPW = `$obfuscateProg -u $commonConf{'zvm_xcat_password'}{'value'}`; + $clearPW =~ s/\n+$//g; # trim ending new line + } else { + # Assume password is in the clear because the obfuscation program is missing. + $clearPW = $commonConf{'zvm_xcat_password'}{'value'}; + } + + if ( $pwVisible ) { + push( @driverText, "export zxcatIVP_xcatUserPw=\"$clearPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=0" ); + } else { + my $hiddenPW = obfuscate( $clearPW, 1 ); + push( @driverText, "# Note: Password is hidden." ); + push( @driverText, "# To override the support and pass the password in the" ); + push( @driverText, "# clear, either:" ); + push( @driverText, "# - specify the -p or --password-visible operand when" ); + push( @driverText, "# invoking prep_zxcatIVP.pl script, or" ); + push( @driverText, "# - change zxcatIVP_pw_obfuscated variable to 0 and" ); + push( @driverText, "# specify the password in the clear on the" ); + push( @driverText, "# zxcatIVP_xcatUserPw variable in the constructed" ); + push( @driverText, "# driver script." ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$hiddenPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=1" ); + } + } + if ( exists $commonConf{'zvm_xcat_server'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected IP address of the xcat MN" ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_server'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_xcatMNIp=\"$commonConf{'zvm_xcat_server'}{'value'}\"" ); + } + if ( exists $commonConf{'zvm_xcat_username'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# User defined to communicate with xCAT MN" ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_server'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_xcatUser=\"$commonConf{'zvm_xcat_username'}{'value'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by zHCP." ); + push( @driverText, "# From \'zvm_zhcp_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_zhcpFCPList=\"$novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'xcat_free_space_threshold'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected space available in the xCAT MN image repository" ); + push( @driverText, "# From \'xcat_free_space_threshold\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_expectedReposSpace=\"$novaConf{'DEFAULT'}{'xcat_free_space_threshold'}G\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Nova Config Properties" ); + + if (( keys %neutronConf ) or ( keys %neutronZvmPluginIni ) or ( keys %ml2ConfIni )) { + push( @driverText, "" ); + push( @driverText, "############## Start of Neutron Config Properties" ); + if ( exists $neutronConf{'DEFAULT'}{'base_mac'} ) { + my $prefix = $neutronConf{'DEFAULT'}{'base_mac'}; + $prefix =~ tr/://d; + $prefix = substr( $prefix, 0, 6 ); + push( @driverText, "" ); + push( @driverText, "# User prefix for MAC Addresses of Linux level 2 interfaces" ); + push( @driverText, "# From \'base_mac\' in $locNeutronConf." ); + push( @driverText, "export zxcatIVP_macPrefix=\"$prefix\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat MN's address on the xCAT management network" ); + push( @driverText, "# From \'xcat_mgt_ip\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_xcatMgtIp=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat management interface netmask" ); + push( @driverText, "# From \'xcat_mgt_mask\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_mgtNetmask=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'}\"" ); + } + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} or + exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + my $list = ''; + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} and + $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} ne '*' ) { + $list = $ml2ConfIni{'ml2_type_flat'}{'flat_networks'}; + } + if ( exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + if ( $list ne '' ) { + $list = "$list,"; + } + $list = "$list$ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'}"; + } + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Array of networks and possible VLAN ranges" ); + push( @driverText, "# From $locMl2ConfIni,"); + push( @driverText, "# \'flat_networks\' in section \'ml2_type_flat\' and "); + push( @driverText, "# \'network_vlan_ranges\' in section \'ml2_type_vlan\'."); + push( @driverText, "export zxcatIVP_networks=\"$list\"" ); + } + } + if ( exists $commonConf{'xcat_zhcp_nodename'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT zHCP server" ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'xcat_zhcp_nodename'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_zhcpNode=\"$commonConf{'xcat_zhcp_nodename'}{'value'}\"" ); + } + + # Create the zxcatIVP_vswitchOSAs variable for any networks specified with an rdev list. + my $vswitchOSAs = ''; + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + next if ( $list eq '' ); + $list =~ s/\s+/\,/g; # insert comma between words + $vswitchOSAs = "$section $list $vswitchOSAs"; + } + } + if ( $vswitchOSAs ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Vswitches and their related OSAs" ); + push( @driverText, "# From \'rdev_list\' in vswitch sections of $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_vswitchOSAs=\"$vswitchOSAs\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Neutron Config Properties" ); + } + + push( @driverText, "" ); + push( @driverText, "# Name of user under which nova runs, default is nova." ); + push( @driverText, "# If you system is different then change this property." ); + push( @driverText, "export zxcatIVP_expUser=\"nova\"" ); + push( @driverText, "" ); + push( @driverText, "# Controls whether Warnings/Errors detected by the IVP are" ); + push( @driverText, "# logged in the xCAT MN syslog, 0: do not log, 1: log to syslog." ); + push( @driverText, "export zxcatIVP_syslogErrors=1" ); + push( @driverText, "" ); + push( @driverText, "perl $driverLocation$ivp" ); + + # Write the array to the driver file. + foreach (@driverText) { + #print "$_\n"; + print $fileHandle "$_\n"; # Print each entry in our array to the file + } + + close $fileHandle; + + print "$driver was built.\n"; + return 0; +} + +#------------------------------------------------------- + +=head3 hashFile + + Description : Read a file with equal signs designating + key value pairs and create a hash from it. + Arguments : File to read + Reference to hash that should be constructed. + File is required to exist or there is a serious error. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = hashFile( $file, \%novaConf, 1 ); + +=cut + +#------------------------------------------------------- +sub hashFile{ + my ( $file, $hash, $required ) = @_; + my $section = "null"; + my $caseSensitive = 0; # assume section/properties are case insensitive + my @parts; + my $out; + + # Clear the hash + %$hash = (); + + if ( $file eq '' ) { + return 602; + } + + if ( ! -e $file ) { + if ( $required ) { + print "Warning: $file does not exist.\n"; + } else { + print "Info: $file does not exist.\n"; + } + return 601; + } + + if (( $file =~ /.conf$/ ) or ( $file =~ /.service$/ ) or ( $file =~ /.COPY$/ )) { + # File is case sensitive, translate sections and property names to uppercase. + $caseSensitive = 1; + } + + # Read the configuration file and construct the hash of values. + if (( $file =~ /.conf$/ ) or ( $file =~ /.ini$/ ) or ( $file =~ /.service$/ )) { + $out = `egrep -v '(^#|^\\s*\\t*#)' $file`; + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^\[/ ) { + # Section header line + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the line + $line =~ s/^\[+|\]+$//g; # trim [] from ends of the line + if ( !$caseSensitive ) { + $section = lc( $line ); + } else { + $section = $line; + } + } else { + # Property line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$section}{$parts[0]} = $parts[1]; + #print "$section $parts[0]" . ": " . $parts[1]. "\n"; + #print $parts[0] . ": " . $$hash{$section}{$parts[0]}. "\n"; + } + } + } else { + # Hash .COPY files + # Read the file and remove comment lines and sequence columns (72-80) + $out = `grep -v ^\$ $file| cut -c1-71`; + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Weed out blank lines + $line =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $line ) == 0 ); + + # Parse the line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + # Add the property to the hash if it has data. + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + $parts[1] =~ s/^"+|"+$//g; # trim double quotes from both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$parts[0]} = $parts[1]; + #print $parts[0] . ": " . $$hash{$parts[0]}. "\n"; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 obfuscate + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : string to be processed + direction: 1 - obfuscate (hide it!) + 0 - unobfuscate (unhide it!) + Returns : processed password (either hiden or unhidden)) + Example : $rc = obfuscate( $pw, 1 ); + +=cut + +#------------------------------------------------------- +sub obfuscate{ + my ( $pw, $hide ) = @_; + + if ( $hide == 1 ) { + $pw = encode_base64( $pw, '' ); + } else { + $pw = decode_base64( $pw ); + } + + return $pw; +} + +#------------------------------------------------------- + +=head3 saveCommonOpt + + Description : Save a common option for later use and + verify the value is consistent. + Arguments : option + section that contained the option + configuration file name + name of the option in the commonConf hash + value + Returns : 0 - No error + 1 - Already saved with a different value + Example : $rc = saveCommonOpt( $confFile, $opt, $section, $confFile $commonOptName, $value ); + +=cut + +#------------------------------------------------------- +sub saveCommonOpt { + my ( $opt, $section, $confFile, $commonOptName, $value ) = @_; + my $rc = 0; + + if ( !exists $commonConf{$commonOptName} ) { + $commonConf{$commonOptName}{'value'} = $value; + $commonConf{$commonOptName}{'fromLines'} = '# ' . $opt . ' in ' . $confFile; + $commonConf{$commonOptName}{'firstOpt'} = $opt; + $commonConf{$commonOptName}{'firstSection'} = $section; + $commonConf{$commonOptName}{'firstConf'} = $confFile; + } else { + if ( $commonConf{$commonOptName}{'value'} eq $value ) { + $commonConf{$commonOptName}{'fromLines'} = $commonConf{$commonOptName}{'fromLines'} . + "\n# " . $opt . " in " . $confFile; + } else { + print "Warning: \'$opt\' property in section \'$section\'\n" . + " in $confFile\n" . + " has a value that is different from the value of the\n" . + " \'$commonConf{$commonOptName}{'firstOpt'}\' property in section \'$commonConf{$commonOptName}{'firstSection'}\'\n" . + " in $commonConf{$commonOptName}{'firstConf'}.\n" . + " They should be the same. The value\n" . + " in $confFile is:\n" . + " $value\n" . + " and in $commonConf{$commonOptName}{'firstConf'} is:\n" . + " $commonConf{$commonOptName}{'value'}\n"; + $rc = 1; + } + } + return $rc; +} + +#------------------------------------------------------- + +=head3 scanCeilometer + + Description : Scan the ceilometer configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCeilometer(); + +=cut + +#------------------------------------------------------- +sub scanCeilometer{ + my $rc; + + if ( $locCeilometerConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Ceilometer configuration files:\n"; + print " $locCeilometerConf\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCeilometerConf, \%ceilometerConf, 0 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanCinder + + Description : Scan the cinder configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCinder(); + +=cut + +#------------------------------------------------------- +sub scanCinder{ + my $rc; + + if ( $locCinderConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Cinder configuration files:\n"; + print " $locCinderConf\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCinderConf, \%cinderConf, 0 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanInitScript + + Description : Scan the init script for the + specified configuration file property. + Arguments : Configuration file name + Config property containing the value + Returns : Null - error locating the config file. + Non-null - file specification of the + configuration file. + Example : $confFile = scanInitScript('openstack-cinder-volume-$host', + 'config'); + +=cut + +#------------------------------------------------------- +sub scanInitScript{ + my ( $filename, $property ) = @_; + my $configFile = ''; + my $out; + + if ( -e $filename ) { + if ( $verbose ) { + print "Scanning $filename for '$property' variable.\n"; + } + + # Strip out the lines after the desired config property is set and + # remove comment lines then echo the property value. + $out = `awk '{print} /$property=/ {exit}' /etc/init.d/$filename | grep -v ^\# | grep .`; + $out = $out . "echo -n \$$property"; + $configFile = `$out`; + } + + return $configFile; +} + +#------------------------------------------------------- + +=head3 scanNeutron + + Description : Scan the neutron configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNeutron(); + +=cut + +#------------------------------------------------------- +sub scanNeutron{ + my $rc; + + if ( $locNeutronConf eq '' and $locMl2ConfIni eq '' + and $locNeutronZvmPluginIni eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Neutron configuration files:\n"; + print " $locNeutronConf\n"; + print " $locMl2ConfIni\n"; + print " $locNeutronZvmPluginIni\n"; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronConf, \%neutronConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locMl2ConfIni, \%ml2ConfIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronZvmPluginIni, \%neutronZvmPluginIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNova + + Description : Scan the Nova configuration files. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNova(); + +=cut + +#------------------------------------------------------- +sub scanNova{ + my $rc; + + if ( $locNovaConf eq '' ) { + return 602; + } + + if ( $verbose ) { + print "Scanning the Nova configuration file:\n"; + print " $locNovaConf\n"; + } + + # Verify the /etc/nova/nova.conf exists. + $rc = hashFile( $locNovaConf, \%novaConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanServiceUnit + + Description : Scan the service unit for the + specified configuration file property. + Arguments : Service file name + Section containing the desired property + Config property containing the value + Returns : Null - error locating the config file. + Non-null - file specification of the + configuration file. + Example : $confFile = scanServiceUnit( 'openstack-cinder-volume-$host', + 'Service', 'ExecStart' ); + +=cut + +#------------------------------------------------------- +sub scanServiceUnit{ + my ( $filename, $section, $property ) = @_; + my $configFile = ''; + my $out; + my $serviceFile = ''; + my %serviceFileData; + + # verify the file exists + if ( -e "/etc/systemd/system/$filename" ) { + $serviceFile = "/etc/systemd/system/$filename"; + } elsif ( -e "/run/systemd/system/$filename" ) { + $serviceFile = "/run/systemd/system/$filename"; + } elsif ( -e "/usr/lib/systemd/system/$filename" ) { + $serviceFile = "/usr/lib/systemd/system/$filename"; + } else { + return $configFile; # Unit file was not found + } + + if ( $verbose ) { + print "Scanning $filename for '$property' variable in section $section.\n"; + } + + my $rc = hashFile( $serviceFile, \%serviceFileData, 0 ); + if ( $rc != 0 ) { + return $configFile; # Could not build the hash + } + + if ( exists $serviceFileData{$section}{$property} ) { + my @parms = split( ' --config-file ', $serviceFileData{$section}{$property} ); + @parms = split ( ' ', $parms[1] ); + $configFile = $parms[0]; + } + + return $configFile; +} + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares and builds a z/VM xCAT IVP driver program + using the information from the configuration files in the + compute node. + + The default name of the driver program is composed of the following: + '$driverPrefix', and + IP address of the system where driver was prepared, and + (optionally) a hypen and the value specified on --Host operand, and + '.sh'. + For example: + $driverPrefix"."9.123.345.91.sh + $driverPrefix"."9.123.345.91-hostzvm.sh + + $supportString + + The following configuration files are scanned for input: + $locCeilometerConf + $locCinderConf + $locNovaConf + $locNeutronConf + $locMl2ConfIni + $locNeutronZvmPluginIni + + When the --init-files operand is specified, OpenStack startup + scripts are scanned in /etc/init.d. The --host operand should + be specified to indicate the suffix to use for scripts that + are unique to a specific z/VM host. The following startup + scripts are scanned: + $ceilometerStem- + neutron-server + $neutronZvmAgentStem- + $novaComputeStem- + openstack-cinder-volume + + When --init-files is specified without the --host operand, + the following scripts are scanned: + $ceilometerStem + neutron-server + $neutronZvmAgentStem + $novaComputeStem + openstack-cinder-volume + + The constructed driver program can then be uploaded to + the xCAT MN and used to validate the configuration between + the compute node and xCAT.\n\n"; + print $usage_string; + return; +} + +#------------------------------------------------------- + +=head3 validateConfigs + + Description : Compare and validate the configuration + values obtained by the scans. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = validateConfigs(); + +=cut + +#------------------------------------------------------- +sub validateConfigs{ + my $bypassCinder = 0; + my $rc = 0; + my $option; + if ( $verbose ) { + print "Performing a local validation of the configuration files.\n"; + } + + #******************************************************* + # Verify required configuration options were specified. + #******************************************************* + if ( keys %ceilometerConf ) { + my @requiredCeilometerOpts = ( + 'DEFAULT','host', + 'DEFAULT','hypervisor_inspector', + 'zvm','zvm_host', + 'zvm','zvm_xcat_master', + 'zvm','zvm_xcat_password', + 'zvm','zvm_xcat_server', + 'zvm','zvm_xcat_username', + ); + for ( my $i = 0; $i < $#requiredCeilometerOpts; $i = $i + 2 ) { + my $section = $requiredCeilometerOpts[$i]; + my $option = $requiredCeilometerOpts[$i+1]; + if ( !exists $ceilometerConf{$section}{$option} ) { + #print "option:$option.\nvalue:$ceilometerConf{$section}{$option}\n"; + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locCeilometerConf.\n"; + } else { + saveCommonOpt( $option, $section, $locCeilometerConf, $option, $ceilometerConf{$section}{$option} ); + } + } + } + + if ( keys %cinderConf ) { + my @requiredCinderOpts = (); + foreach $option ( @requiredCinderOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$option} ) { + #print "option:$option.\nvalue:$cinderConf{$option}\n"; + print "Warning: \'$option\' is missing from section \'DEFAULT\'\n" . + " in $locCinderConf.\n"; + } + } + } + + if ( keys %novaConf ) { + my @requiredNovaOpts = ( + 'DEFAULT','compute_driver', + 'DEFAULT','force_config_drive', + 'DEFAULT','host', + 'DEFAULT','instance_name_template', + 'DEFAULT','zvm_diskpool', + 'DEFAULT','zvm_host', + 'DEFAULT','zvm_xcat_master', + 'DEFAULT','zvm_xcat_server', + 'DEFAULT','zvm_xcat_username', + 'DEFAULT','zvm_xcat_password', + ); + for ( my $i = 0; $i < $#requiredNovaOpts; $i = $i + 2 ) { + my $section = $requiredNovaOpts[$i]; + my $option = $requiredNovaOpts[$i+1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + } elsif ( $commonOpts =~ m/ $option / ) { + saveCommonOpt( $option, $section, $locNovaConf, $option, $novaConf{'DEFAULT'}{$option} ); + } elsif ( $option eq 'host' ) { + saveCommonOpt( $option, $section, $locNovaConf, 'zvm_host', $novaConf{'DEFAULT'}{$option} ); + } + } + } + + if ( keys %neutronConf ) { + my @requiredNeutronConfOpts = ( + 'DEFAULT','core_plugin', + ); + for ( my $i = 0; $i < $#requiredNeutronConfOpts; $i = $i + 2 ) { + my $section = $requiredNeutronConfOpts[$i]; + my $option = $requiredNeutronConfOpts[$i+1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf/neutron/neutron.conf.\n"; + } + } + } + + if ( keys %ml2ConfIni ) { + my @requiredMl2ConfIniOpts = ( + 'ml2','mechanism_drivers', + 'ml2_type_flat', 'flat_networks', + ); + for ( my $i = 0; $i < $#requiredMl2ConfIniOpts; $i = $i + 2 ) { + my $section = $requiredMl2ConfIniOpts[$i]; + my $option = $requiredMl2ConfIniOpts[$i+1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni.\n"; + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my @requiredNeutronZvmPluginIniOpts = ( + 'agent', 'zvm_host', + 'agent','zvm_xcat_server', + ); + for ( my $i = 0; $i < $#requiredNeutronZvmPluginIniOpts; $i = $i + 2 ) { + my $section = $requiredNeutronZvmPluginIniOpts[$i]; + my $option = $requiredNeutronZvmPluginIniOpts[$i+1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Warning: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + } else { + saveCommonOpt( $option, $section, $locNeutronZvmPluginIni, $option, $neutronZvmPluginIni{$section}{$option} ); + } + } + } + + #****************************************** + # Verify optional operands were specified. + #****************************************** + if ( keys %ceilometerConf ) { + if ( !exists $ceilometerConf{'DEFAULT'}{'host'} and + !exists $ceilometerConf{'zvm'}{'zvm_host'} + ) { + print "Info: Most z/VM specific Ceilometer options are not defined. Ceilometer\n" . + " support is not enabled for z/VM. Further testing of these options will\n" . + " not occur in this script.\n"; + } else { + my %optionalCeilometerConfOpts = ( + 'zvm xcat_zhcp_nodename' => 'A default of \'zhcp\' will be used.', + ); + my %defaultCeilometerConfOpts = ( + "zvm xcat_zhcp_nodename" => 'zhcp', + ); + foreach my $key ( keys %optionalCeilometerConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ceilometerConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locCeilometerConf.\n"; + if ( $optionalCeilometerConfOpts{$key} ne '' ) { + print " " . $optionalCeilometerConfOpts{$key} . "\n"; + } + if ( exists $defaultCeilometerConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCeilometerConfOpts{$key}; + } + } + } + } + } + + if ( keys %cinderConf ) { + if ( $cmaSystemRole ne "CONTROLLER" ) { + print "Info: CMA system role is NOT a controller.\n Cinder will not be validated.\n"; + $bypassCinder = 1; + } elsif ( !exists $cinderConf{'DEFAULT'}{'san_ip'} and + !exists $cinderConf{'DEFAULT'}{'san_private_key'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_volpool_name'} + ) { + print "Info: Most z/VM specific Cinder options are not defined.\n" . + " Cinder support for creation of persistent disks for z/VM\n" . + " is not enabled. Further testing of these options will\n" . + " not occur in this script.\n"; + $bypassCinder = 1; + } else { + my %optionalCinderConfOpts = ( + "DEFAULT san_ip" => "This property is necessary when using persistent SAN disks obtained\n" . + " from the Cinder service.", + "DEFAULT san_private_key" => "This property is necessary when using persistent SAN disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_connection_protocol" => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service.", + "DEFAULT storwize_svc_volpool_name" => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service. The default is 'volpool'.", + 'DEFAULT storwize_svc_vol_iogrp' => "This property is necessary when using StorWize persistent disks obtained\n" . + " from the Cinder service. The default is 0.", + 'DEFAULT volume_driver' => "This property is necessary when using persistent disks obtained\n" . + " from the Cinder service.", + ); + my %defaultCinderConfOpts = ( + 'DEFAULT storwize_svc_volpool_name' => 'volpool', + ); + foreach my $key ( keys %optionalCinderConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $cinderConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locCinderConf.\n"; + if ( $optionalCinderConfOpts{$key} ne '' ) { + print " " . $optionalCinderConfOpts{$key} . "\n"; + } + if ( exists $defaultCinderConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCinderConfOpts{$key}; + } + } + } + } + } + + if ( keys %novaConf ) { + my %optionalNovaConfOpts = ( + 'DEFAULT config_drive_format' => 'Default of \'iso9660\' will be used.', + "DEFAULT image_cache_manager_interval" => "Default of 2400 (seconds) will be used.", + "DEFAULT ram_allocation_ratio" => "", + "DEFAULT rpc_response_timeout" => "zVM Live migration may timeout with the default " . + "value (60 seconds).\n The recommended value for z/VM is 180 to allow " . + "zVM live migration\n to succeed.", + "DEFAULT xcat_free_space_threshold" => "Default of 50 (G) will be used.", + "DEFAULT xcat_image_clean_period" => "Default of 30 (days) will be used.", + "DEFAULT zvm_config_drive_inject_password" => "This value will default to 'FALSE'.", + "DEFAULT zvm_diskpool_type" => "Default of \'ECKD\' will be used.", + "DEFAULT zvm_fcp_list" => "As a result, Cinder volumes cannot be attached to server instances.", + "DEFAULT zvm_zhcp_fcp_list" => "", + "DEFAULT zvm_image_tmp_path" => "Default of '/var/lib/nova/images' will be used.", + "DEFAULT zvm_multiple_fcp" => "Default of 'false' will be used.", + "DEFAULT zvm_reachable_timeout" => "Default of 300 (seconds) will be used.", + "DEFAULT zvm_scsi_pool" => "Default of \'xcatzfcp\' will be used.", + "DEFAULT zvm_user_default_privilege" => "Default of G will be used.", + "DEFAULT zvm_user_profile" => "Default is 'OSDFLT'.", + "DEFAULT zvm_user_root_vdev" => "Default of 100 will be used.", + "DEFAULT zvm_vmrelocate_force" => "", + "DEFAULT zvm_xcat_connection_timeout" => "Default of 3600 seconds will be used.", + "DEFAULT zvm_image_compression_level" => "Image compression is controlled by the xCAT ZHCP " . + "server in\n the /var/opt/zhcp/settings.conf file. Compressing images during image\n" . + " capture is the default.", + ); + my %defaultNovaConfOpts = ( + 'DEFAULT config_drive_format' => 'iso9660', + "DEFAULT xcat_free_space_threshold" => 50, + "DEFAULT xcat_image_clean_period" => 30, + "DEFAULT zvm_user_default_privilege" => "G", + "DEFAULT zvm_user_profile" => 'OSDFLT', + "DEFAULT zvm_user_root_vdev" => 100, + "DEFAULT zvm_diskpool_type" => 'ECKD', + "DEFAULT zvm_scsi_pool" => "xcatzfcp", + ); + foreach my $key ( keys %optionalNovaConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $novaConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNovaConf.\n"; + if ( $optionalNovaConfOpts{$key} ne '' ) { + print " " . $optionalNovaConfOpts{$key} . "\n"; + } + if ( exists $defaultNovaConfOpts{$key} ) { + $novaConf{$section}{$option} = $defaultNovaConfOpts{$key}; + } + } + } + } + + if ( keys %neutronConf) { + my %optionalNeutronConfOpts = ( + 'DEFAULT base_mac' => 'A default value of \'fa:16:3e:00:00:00\' will be used.', + ); + my %defaultNeutronConfOpts = ( + 'DEFAULT base_mac' => 'fa:16:3e:00:00:00', + ); + foreach my $key ( keys %optionalNeutronConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronConf{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronConf.\n"; + if ( $optionalNeutronConfOpts{$key} ne '' ) { + print " " . $optionalNeutronConfOpts{$key} . "\n"; + } + if ( exists $defaultNeutronConfOpts{$key} ) { + $neutronConf{$section}{$option} = $defaultNeutronConfOpts{$key}; + } + } + } + } + + if ( keys %ml2ConfIni ) { + my %optionalMl2ConfIniOpts = ( + 'ml2_type_vlan network_vlan_ranges' => + "This property is optional and is used to specify z/VM vswitches\n" . + " usable for VLAN provider and tenant networks. In addition,\n" . + " it can include a range of VLAN ids to be used with the switch.", + 'ml2 tenant_network_types' => 'This property is an ordered list of '. + 'network types to allocate as tenant (project) networks, separated by commas. '. + 'A default value of \'local\' will be used.', + 'ml2 type_drivers' => 'This property lists the network types to be supported. '. + 'A default value of \'local,flat,vlan\' will be used.', + ); + my %defaultMl2ConfIniOpts = ( + 'ml2 tenant_network_types' => 'local', + 'ml2 type_drivers' => 'local,flat,vlan', + ); + foreach my $key ( keys %optionalMl2ConfIniOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locMl2ConfIni.\n"; + if ( $optionalMl2ConfIniOpts{$key} ne '' ) { + print " " . $optionalMl2ConfIniOpts{$key} . "\n"; + } + if ( exists $defaultMl2ConfIniOpts{$key} ) { + $ml2ConfIni{$section}{$option} = $defaultMl2ConfIniOpts{$key}; + } + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my %optionalNeutronZvmPluginIniOpts = ( + "agent xcat_mgt_ip" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent xcat_mgt_mask" => "This property is necessary when deploying virtual server " . + "instances that\n do NOT have public IP addresses.", + "agent polling_interval" => "A default value of '5' will be used.", + "agent xcat_zhcp_nodename" => "A default value of \'zhcp\' will be used.", + "agent zvm_xcat_password" => "A default value of \'admin\' is used.", + "agent zvm_xcat_timeout" => "A default value of 300 seconds is used.", + "agent zvm_xcat_username" => "A default value of \'admin\' is used.", + ); + my %defaultNeutronZvmPluginIniOpts = ( + "agent polling_interval" => 2, + "agent xcat_zhcp_nodename" => "zhcp", + "agent zvm_xcat_password" => "admin", + "agent zvm_xcat_timeout" => 300, + "agent zvm_xcat_username" => "admin", + ); + foreach my $key ( keys %optionalNeutronZvmPluginIniOpts ) { + my @opts = split( '\s', $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + print "Info: \'$option\' is missing from section \'$section\'\n" . + " in $locNeutronZvmPluginIni.\n"; + if ( $optionalNeutronZvmPluginIniOpts{$key} ne '' ) { + print " " . $optionalNeutronZvmPluginIniOpts{$key} . "\n"; + } + if ( exists $defaultNeutronZvmPluginIniOpts{$key} ) { + $neutronZvmPluginIni{$section}{$option} = $defaultNeutronZvmPluginIniOpts{$key}; + if ( $commonOpts =~ m/ $option / ) { + saveCommonOpt( $option, $section, $locNeutronZvmPluginIni, $option, $neutronZvmPluginIni{$section}{$option} ); + } + } + } + } + } + + # Verify the instance name template is valid + if ( exists $novaConf{'DEFAULT'}{'instance_name_template'} ) { + # Use sprintf which is close enough to the python % support for formatting to construct a sample. + my $base_name = sprintf( $novaConf{'DEFAULT'}{'instance_name_template'}, 1 ); + if ( length( $base_name ) > 8 ) { + print "Warning: In $locNovaConf, section \`DEFAULT\`, instance_name_template would\n" . + " construct a value greater than 8 in length: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + if (( $base_name eq $novaConf{'DEFAULT'}{'instance_name_template'} ) || + ( $novaConf{'DEFAULT'}{'instance_name_template'} !~ /%/ )) { + print "Warning: In $locNovaConf, instance_name_template will not\n" . + " create a usable name with the value: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + my $words; + $words++ while $base_name =~ /\S+/g; + if ( $words != 1 ) { + print "Warning: In $locNovaConf, instance_name_template will not create\n" . + " an instance name that is a single blank delimited word using\n" . + " the value: $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + if ( $novaConf{'DEFAULT'}{'instance_name_template'} =~ m/(^RSZ)/i ) { + print "Warning: In $locNovaConf, instance_name_template should not begin\n" . + " with 'rsz': $novaConf{'DEFAULT'}{'instance_name_template'}\n"; + } + } + + # Verify the compute_driver is for z/VM + if ( exists $novaConf{'DEFAULT'}{'compute_driver'} ) { + if ( $novaConf{'DEFAULT'}{'compute_driver'} ne "nova.virt.zvm.ZVMDriver" and + $novaConf{'DEFAULT'}{'compute_driver'} ne "zvm.ZVMDriver") { + print "Warning: In $locNovaConf, compute_driver does not contain the\n" . + " expected value of \'zvm.ZVMDriver\' and instead contains:\n" . + " \'$novaConf{'DEFAULT'}{'compute_driver'}\'\n"; + } + } + + # Check whether the rpc timeout is too small for z/VM + if ( exists $novaConf{'DEFAULT'}{'rpc_response_timeout'} ) { + if ( $novaConf{'DEFAULT'}{'rpc_response_timeout'} < 180 ) { + print "Warning: In $locNovaConf, section \'DEFAULT\', rpc_response_timeout\n" . + " specifies a value, \'$novaConf{'DEFAULT'}{'rpc_response_timeout'}\', which is " . + "less than the recommended value\n of \'180\'.\n"; + } + } + + # Verify all SCSI disk operands are specified, if one exists. + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} or exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + if ( !exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + print "Warning: In $locNovaConf, \'zvm_zhcp_fcp_list\' does not exist but\n" . + " other SCSI disk related operands exist. Both should be\n" . + " specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'\n"; + } + } + + # Verify neutron.conf operands with a fixed set of possible values + if ( exists $neutronConf{'DEFAULT'}{'core_plugin'} ) { + if ( $neutronConf{'DEFAULT'}{'core_plugin'} eq 'neutron.plugins.ml2.plugin.Ml2Plugin' ) { + print "Info: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value, '$neutronConf{'DEFAULT'}{'core_plugin'}',\n" . + " which is deprecated. The recommended value is 'ml2'.\n"; + } + elsif ( $neutronConf{'DEFAULT'}{'core_plugin'} ne 'ml2' ) { + print "Warning: In $locNeutronConf, \'core_plugin\' in section \'DEFAULT\'\n" . + " specifies a value, '$neutronConf{'DEFAULT'}{'core_plugin'}',\n" . + " which is not 'ml2'.\n"; + } + } + + # Verify any rdev_list in Neutron z/VM Plugin ini file contains a single value and/or not a comma + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list eq '' ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' is specified but has no value.\n"; + } else { + my @vals = split ( /\s/, $list ); + if ( $#vals > 0 ) { + # $#vals is array size - 1. + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains too many values.\n"; + } + foreach my $op ( @vals ) { + if ( $op =~ m/[^0-9a-fA-F]+/ ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains non-hexadecimal characters: \'$op\'.\n"; + } elsif ( length($op) > 4 ) { + print "Warning: In $locNeutronZvmPluginIni, section \'$section\',\n" . + " \'rdev_list\' contains a value that is not 1-4 characters in\n" . + " length: \'$op\'.\n"; + } + } + } + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ) { + if ( $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ne 'FC' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\',\n" . + " storwize_svc_connection_protocol specifies a value, " . + "\'$cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'}\',\n" . + " which is not the required value of \'FC\'.\n"; + } + } + + # Check whether the volume_driver is correct for z/VM + if ( ! $bypassCinder and exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + if ( $cinderConf{'DEFAULT'}{'volume_driver'} ne 'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver' ) { + print "Warning: In $locCinderConf, section \'DEFAULT\', volume_driver specifies\n" . + " a value, \'$cinderConf{'DEFAULT'}{'volume_driver'}\',\n which is " . + "not the required value of\n \'cinder.volume.drivers.ibm.storwize_svc.StorwizeSVCDriver\'.\n"; + } + } + + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $configOpt; +my $rc = 0; +my $obfuscateOpt; +my $out; +my $scanInitOpt; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( + 'c|config=s' => \$configOpt, + 'd|driver=s' => \$driver, + 'h|help' => \$displayHelp, + 'H|host=s' => \$host, + 'i|init-files' => \$scanInitOpt, + 'p|password-visible' => \$pwVisible, + 's|scan=s' => \$scan, + 'v' => \$versionOpt, + 'V|verbose' => \$verbose )) { + print $usage_string; + goto FINISH; +} + +if ( $versionOpt ) { + print "Version: $version\n"; + print "$supportString\n"; +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH; +} + +# Determine the local IP address for this system. +my $errorLines = ''; +my $hostname = hostname; +if ( $hostname ne '' ) { + $localIpAddress = gethostbyname( $hostname ); + if ( defined $localIpAddress ) { + my $len = length( $localIpAddress ); + if ( $len == 4 ) { + $localIpAddress = inet_ntoa( $localIpAddress ); + } else { + $localIpAddress = ''; + $errorLines = $errorLines . " The IP address obtained from perl gethostbyname function does not\n" . + " appear to be an IPv4 address. An IPv4 address should be used.\n"; + } + } else { + $errorLines = $errorLines . " The IP address related to the following host name was not found:\n" . + " $hostname\n"; + if ( defined $? ) { + $errorLines = $errorLines . " The perl gethostbyname function failed with errno: $?\n"; + } + $localIpAddress = ''; + } +} else { + $errorLines = $errorLines . " The host name was not found.\n"; +} + +if ( $localIpAddress eq '' ) { + print "Warning: Unable to obtain the host name and/or IP address for this system.\n"; + print " The local IP address is used in the name of the driver script.\n" . + " Some variables in the driver script such as zxcatIVP_cNAddress\n" . + " may be set to an empty string value. You may wish to correct\n" . + " the driver script and your system configuration.\n"; + if ( $errorLines ne '' ) { + print " Additional Information:\n$errorLines"; + } +} + +# Detect CMA +if ( -e $locVersionFileCMO ) { + # CMA version file exists, treat this as a CMA node + $cmaAppliance = 1; + $cmaVersionString = `cat $locVersionFileCMO`; + chomp( $cmaVersionString ); + if ( $cmaVersionString eq '' ) { + $cmaVersionString = "version unknown"; + } + + # Determine the role of this CMA system + my %settings; + if ( -e $locApplSystemRole ) { + $rc = hashFile( $locApplSystemRole, \%settings, 0 ); + $cmaSystemRole = uc( $settings{'role'} ); + } + if ( $cmaSystemRole eq '' and -e $locDMSSICMOCopy ) { + $rc = hashFile( $locDMSSICMOCopy, \%settings, 0 ); + if ( exists $settings{'openstack_system_role'} ) { + $cmaSystemRole = uc( $settings{'openstack_system_role'} ); + } + } + if ( $cmaSystemRole eq '' ) { + $cmaSystemRole = 'unknown'; + } +} + +if ( defined( $scan ) ) { + if ( $verbose ) { + print "Operand --scan: $scan\n"; + } + if ( 'all cinder neutron nova' !~ $scan ) { + print "--scan operand ($scan) is not all, cinder, neutron or nova\n"; + $rc = 400; + goto FINISH; + } +} else { + $scan = 'all'; +} + +if ( defined( $host ) ) { + if ( $verbose ) { + print "Operand --Host: $host\n"; + } + $driverSuffix = "_$host"; + $initSuffix = "-$host"; +} else { + $host = ''; + $driverSuffix = ""; + $initSuffix = ""; +} + +if ( defined( $scanInitOpt ) ) { + if ( $verbose ) { + print "Operand --initFiles\n"; + } + + my $configFile = ''; + if ( $scan =~ 'all' or $scan =~ 'nova' ) { + # Look for a System V startup script + $configFile = scanInitScript( "$novaComputeStem$initSuffix", 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$novaComputeStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locNovaConf = $configFile; + } else { + print "Info: Unable to determine the Nova configuration file.\n"; + $locNovaConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'ceilometer' ) { + # Look for a System V startup script + $configFile = scanInitScript( "$ceilometerStem$initSuffix", 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$ceilometerStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locCeilometerConf = $configFile; + } else { + print "Info: Unable to determine the Ceilometer configuration file.\n"; + $locCeilometerConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + # Look for a System V startup script + $configFile = scanInitScript( 'openstack-cinder-volume', 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( 'openstack-cinder-volume.service', 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locCinderConf = $configFile; + } else { + print "Info: Unable to determine the Cinder configuration file.\n"; + $locCinderConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + # Look for a System V startup script + $configFile = scanInitScript( 'neutron-server', 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( 'neutron-server.service', 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locNeutronConf = $configFile; + } else { + print "Info: Unable to determine the Neutron configuration file.\n"; + $locNeutronConf = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$neutronZvmAgentStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locNeutronZvmPluginIni = $configFile; + } else { + print "Info: Unable to determine the Neutron z/VM plugin configuration file.\n"; + $locNeutronZvmPluginIni = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'plugin_config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$neutronZvmAgentStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locMl2ConfIni = $configFile; + } else { + print "Info: Unable to determine the Neutron ml2 configuration file.\n"; + $locMl2ConfIni = ''; + } + } +} + +if ( defined( $configOpt ) ) { + if ( $verbose ) { + print "Operand --config $configOpt\n"; + } + + my @items = split( ',', $configOpt ); + foreach my $item ( @items ) { + my @parts = split( ':', $item ); + if ( defined $parts[1] ) { + $parts[0] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + if ( defined $parts[1] ) { + $parts[1] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + + if ( !defined $parts[1] or $parts[1] eq '' ) { + print( "Warning: --config option: $parts[0] did not specify a file. " . + "Default file specification will be used.\n" ); + next; + } + + if ( $parts[0] eq $EyeCeilometerConf ) { + $locCeilometerConf = $parts[1]; + } elsif ( $parts[0] eq $EyeCinderConf ) { + $locCinderConf = $parts[1]; + } elsif ( $parts[0] eq $EyeMl2ConfIni ) { + $locMl2ConfIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronConf ) { + $locNeutronConf = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronZvmPluginIni ) { + $locNeutronZvmPluginIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNovaConf ) { + $locNovaConf = $parts[1]; + } + } +} + +my ( $volume, $directory, $file ) = ''; +if ( defined( $driver ) ) { + if ( $verbose ) { + print "Operand --driver: $driver\n"; + } + + my @dirs; + if ( -d $driver ) { + # Driver operand is a directory name only + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver, 1 ); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); + } else { + # Driver operand is a bad directory or a file in a directory + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver ); + if ( -d $directory ) { + $driver = File::Spec->catpath( $volume, $directory, $file ); + } else { + print "Driver property does not specify a valid directory: $directory\n"; + $rc = 500; + goto FINISH; + } + } +} else { + $directory = File::Spec->curdir(); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); +} + +# Validate CMA related information +if ( $cmaSystemRole ne '' ) { + my %cmaRoles = ( 'CONTROLLER'=>1, 'COMPUTE'=>1, 'COMPUTE_MN'=>1, 'MN'=>1, 'ZHCP'=>1 ); + # Validate Controller related information + if ( ! exists $cmaRoles{$cmaSystemRole} ) { + my @k = keys %cmaRoles; + my $keyNames = join( ', ', @k ); + print "Warning: The value of the openstack_system_role ($cmaSystemRole) is not\n" . + " one of the possible values:\n" . + " $keyNames\n"; + $rc = 503; + # Allow other processing to continue + } +} + +# Scan the configuration files. +if ( $scan =~ 'all' or $scan =~ 'nova' ) { + scanNova(); +} + +if ( $scan =~ 'all' or $scan =~ 'ceilometer' ) { + scanCeilometer(); +} + +if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + scanCinder(); +} + +if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + scanNeutron(); +} + +# Validate the settings and produce the driver script. +if ( ! $configFound ) { + print "Warning: No configuration files were found. " . + "No further processing will be performed.\n"; + $rc = 501; + # Allow other processing to continue +} + +!( $rc = validateConfigs() ) or goto FINISH; + +!( $rc = buildDriverProgram() ) or goto FINISH; + +FINISH: +exit $rc; + diff --git a/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_NEWTON.pl b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_NEWTON.pl new file mode 100644 index 000000000..6cc3b3270 --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_NEWTON.pl @@ -0,0 +1,2383 @@ +#!/usr/bin/perl +############################################################################### +# (c) Copyright International Business Machines Corporation 2016. +# All Rights Reserved. +############################################################################### +# COMPONENT: prep_zxcatIVP_NEWTON.pl +# +# This is a preparation script for Installation Verification Program for xCAT +# on z/VM. It prepares the driver script by gathering information from +# OpenStack configuration files on the compute node. +############################################################################### + +use strict; +use warnings; + +use File::Basename; +use File::Spec; +use Getopt::Long; +use MIME::Base64; +use Sys::Hostname; +use Socket; +use Text::Wrap; + +my %commonConf; # Common configuration options +my %ceilometerConf; # Ceilometer configuration options +my %cinderConf; # Cinder configuration options +my %novaConf; # Nova configuration options +my %neutronConf; # Neutron configuration options +my %ml2ConfIni; # Neutron m12 configuration options +my %neutronZvmPluginIni; # Neutron zvm configuration options + +my $version = "6.0"; +my $supportString = "This script supports code based on the OpenStack Newton release."; + +my $cmaAppliance = 0; # Assumed to not be run in a CMA system +my $cmaVersionString = ''; # CMA version string for a CMA +my $cmaSystemRole = ''; # CMA system role string +my $commonOpts = ' host xcat_zhcp_nodename zvm_host zvm_xcat_master zvm_xcat_server zvm_xcat_username zvm_xcat_password '; + # Common options that can be specified in more than one configuration file +my $configFound = 0; # At least 1 config file was found. Defaults to false. +my $driver; # Name of driver file to be created less the ".pl" +my $driverLocation = "/opt/xcat/bin/"; # Location of the IVP program in xCAT MN. +my $driverPrefix = "zxcatIVPDriver_"; # Prefix used in naming the driver program. +my $driverSuffix; # Suffix used in naming the driver program. +my $displayHelp = 0; # Display help information. +my $host; # Host command option value +my %ignored; # List of ignored messages +my $ignoreCnt = 0; # Number of times we ignored a message +my $infoCnt = 0; # Count of informations messages +my $initSuffix; # Suffix used in naming multihomed init scripts +my $ivp = "zxcatIVP.pl"; # z/VM xCAT IVP script name +my %msgsToIgnore; # Hash of messages to ignore +my $obfuscateProg = '/usr/bin/openstack-obfuscate'; # PW obfuscation program +my $pwVisible = 0; # PW is visible in the driver script (1 = yes, 0 = no) +my $scan; # Type of scan to be performed +my $verbose; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Show version information. +my $warnErrCnt = 0; # Count of warnings and error messages + +my $localIpAddress = ''; # Local IP address of system where we are prepping + +# Locations of configuration files +my $locCeilometerConf = '/etc/ceilometer/ceilometer.conf'; +my $locCinderConf = '/etc/cinder/cinder.conf'; +my $locMl2ConfIni = '/etc/neutron/plugins/ml2/ml2_conf.ini'; +my $locNeutronConf = '/etc/neutron/neutron.conf'; +my $locNeutronZvmPluginIni = '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini'; +my $locNovaConf = '/etc/nova/nova.conf'; + +# Version related files +my $locVersionFileCMO = '/opt/ibm/cmo/version'; +my $locDMSSICMOCopy = '/var/lib/sspmod/DMSSICMO.COPY'; +my $locApplSystemRole = '/var/lib/sspmod/appliance_system_role'; + +# Stems of startup scripts in /etc/init.d that can be scanned for +# configuration files names and locations. +my $ceilometerStem = 'openstack-ceilometer-api'; +my $neutronZvmAgentStem = 'neutron-zvm-agent'; +my $novaComputeStem = 'openstack-nova-compute'; + +# Eyecatchers used with --config operand +my $EyeCeilometerConf = 'ceilometer_conf'; +my $EyeCinderConf = 'cinder_conf'; +my $EyeMl2ConfIni = 'm12_conf'; +my $EyeNeutronConf = 'neutron_conf'; +my $EyeNeutronZvmPluginIni = 'neutron_zvm_plugin_conf'; +my $EyeNovaConf = 'nova_conf'; + +# 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. +# sysAct - System action to be performed. Normally, this is not specified +# because the severity generates a default system action. +# userResp - Suggested user response. +my %respMsgs = ( + 'FATAL_DEFAULTS' => + { + 'sysAct' => 'No further verification will be performed and the driver script '. + 'will not be created.' + }, + 'NONFATAL_DEFAULTS' => + { + 'sysAct' => 'Verification continues.' + }, + 'GENERIC_RESPONSE' => + { + 'severity' => 0, + 'text' => '%s', + }, + 'DRIV01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => '%s is not a z/VM xCAT IVP driver program. The file will not be changed.', + 'explain' => 'The preparation script creates the indicated file. If the file '. + 'already exists, it will rename it so that a new one can be created. '. + 'However, the indicated file does not appear to be a driver script '. + 'created by a previous run of this script. The indicated file will '. + 'not be renamed in order to prevent a possible problem due to the '. + 'unexpected renaming. ', + 'userResp' => 'Remove or rename the indicated file and rerun the script.', + }, + 'DRIV02' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Unable to open %s for output: %s', + 'explain' => 'The preparation script attempted to create the indicated file but '. + 'encountered an error. ', + 'userResp' => 'Determine why the preparation script could not write to the indicated file '. + 'and correct the error.', + }, + 'DRIV03' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'The driver operand does not specify a valid directory: %s', + 'explain' => 'The -d or --driver operand did not specify a valid directory. '. + 'The script does not know where to create the driver script.', + 'userResp' => 'Determine the correct directory and reinvoke this script.', + }, + 'FILE01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => '%s does not exist.', + 'explain' => 'The indicated file is an OpenStack configuration file which was '. + 'attempted to be scanned to obtain configuration properties. Some of the '. + 'properties in the file would be used to further validate the environment. '. + 'This file was expected to exist.', + 'sysAct' => 'Verification continues but without properties from the specified file.', + 'userResp' => 'Determine why the file does not exist and correct the issue. If the file '. + 'exists under a different name then you may need to modify the invocation '. + 'parameters for this script and rerun it.', + }, + 'FILE02' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '%s does not exist.', + 'explain' => 'The indicated file is an OpenStack configuration file which was '. + 'attempted to be scanned to obtain configuration properties. Some of the '. + 'properties in the file would be used to further validate the environment. '. + 'This file is an optional file.', + 'sysAct' => 'Verification continues but without properties from the specified file.', + 'userResp' => 'Determine why the file does not exist. If the file is not needed then you can '. + 'ignore this message. Otherwise, you should correct the issue. If the file '. + 'exists under a different name then you may need to modify the invocation '. + 'parameters for this script and rerun it.', + }, + 'FILE03' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Unable to determine the %s configuration file.', + 'explain' => 'The configuration file for the indicated OpenStack component was '. + 'not found. Configuration properties related to that file will not be '. + 'validated. This will affect the constructed driver script.', + 'sysAct' => 'Verification continues but without properties for the component.', + 'userResp' => 'Determine why the configuration file was not found and correct the issue. If the file '. + 'exists under a different name then you may need to modify the invocation '. + 'parameters for this script and rerun it.', + }, + 'FILE04' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Unable to determine the %s configuration file for the %s.', + 'explain' => 'One of the configuration files for the indicated OpenStack component was '. + 'not found. Configuration properties related to that file will not be '. + 'validated. This will affect the constructed driver script.', + 'sysAct' => 'Verification continues but without properties for the component.', + 'userResp' => 'Determine why the configuration file was not found and correct the issue. If the file '. + 'exists under a different name then you may need to modify the invocation '. + 'parameters for this script and rerun it.', + }, + 'MISS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The required property \'%s\' is missing from section \'%s\' in %s.', + 'explain' => 'A required property is missing from the indicated configuration file. ', + 'userResp' => 'Consult the Enabling z/VM for OpenStack manual for this OpenStack version. Specify the '. + 'missing property as indicated in the book and restart the OpenStack services '. + 'as described in the manual.', + }, + 'MISS02' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'An optional property \'%s\' is missing from section \'%s\' in %s. %s', + 'explain' => 'A optional property is missing from the indicated configuration file. '. + 'This message is intended to let you know that a default will be used '. + 'so that you are aware of the result of not specifying the value.', + 'userResp' => 'If the indicated result is not what you want then please consult the Enabling z/VM for OpenStack '. + 'manual for this OpenStack version to determine what you can specify for the property. '. + 'Specify the missing property as indicated in the manual and restart the OpenStack services '. + 'as described in the manual.', + }, + 'MISS03' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'An optional property \'%s\' is missing from section \'%s\' in %s.', + 'explain' => 'A optional property is missing from the indicated configuration file. ', + 'userResp' => 'If you intended to specify the property then please consult the Enabling z/VM for OpenStack'. + 'manual for this OpenStack version to determine what you can specify for the property. '. + 'Specify the missing property as indicated in the manual and restart the OpenStack services '. + 'as described in the manual.', + }, + 'MISS04' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Most z/VM specific Ceilometer options are not defined. Ceilometer ' . + 'support is not enabled for z/VM.', + 'explain' => 'A number of properties used for Ceilometer are missing from the Ceilometer configuration file. '. + 'Ceilometer will not be activated.', + 'userResp' => 'If you intended to specify the Ceilometer properties then please consult the Enabling z/VM for OpenStack '. + 'manual for this OpenStack version to determine what you can specify. '. + 'Specify the missing properties as indicated in the manual and restart the OpenStack services '. + 'as described in the manual.', + }, + 'MISS05' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Most z/VM specific Cinder options are not defined. ' . + 'Cinder support for creation of persistent disks for z/VM ' . + 'is not enabled.', + 'explain' => 'A number of properties used for Cinder are missing from the Cinder configuration file. '. + 'Cinder will not be used for the z/VM host.', + 'sysAct' => 'Further testing of the Cinder options will not occur in this script.', + 'userResp' => 'If you intended to specify the Cinder properties then please consult the Enabling z/VM for OpenStack '. + 'manual for this OpenStack version to determine what you can specify. '. + 'Specify the missing properties as indicated in the manual and restart the OpenStack services '. + 'as described in the manual.', + }, + 'MISS06' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'No configuration files were found.', + 'explain' => 'The script attempted to read OpenStack configuration files in order to validate the '. + 'properties and create a driver script. The script was unable to determine the files '. + 'or read the configuration files.', + 'sysAct' => 'Validation of OpenStack configuration properties and creation of a driver script will not '. + 'occur.', + 'userResp' => 'Determine the reason that no configuration properties could be found. '. + 'This can be caused by running the script from the wrong user or the configuration '. + 'properties had the wrong permission set. Other error or warning messages from this run may '. + 'indicate the reason for the problem. After you correct the issue, rerun the script.', + }, + 'MISS07' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'The %s configuration file was not found. The %s property is used by the driver script. %s', + 'explain' => 'A optional property is missing from the indicated configuration file. A default will be used.', + 'userResp' => 'If you intended to specify the property then please consult the Enabling z/VM for OpenStack '. + 'manual for this OpenStack version to determine what you can specify for the property. '. + 'Specify the missing property as indicated in the manual and restart the OpenStack services '. + 'as described in the manual.', + }, + 'MISS08' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The following properties are missing from the %s file: %s.', + 'explain' => 'At least one of the listed properties must be specified.', + 'userResp' => 'Correct the configuration file by specifying one of the listed properties. '. + 'Consult the Enabling z/VM for OpenStack manual '. + 'for this OpenStack version to determine what you can specify for the property. '. + 'Specify the missing property as indicated in the manual and restart the OpenStack services '. + 'as described in the manual.', + }, + 'PROP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \`%s\`, %s would construct a value greater than 8 in length: %s', + 'explain' => 'The value for the indicated property is required to be 8 characters or less and is not. '. + 'The value is invalid.', + 'userResp' => 'Correct the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \`%s\`, %s will not create a usable name with the value: %s', + 'explain' => 'The property is used to create an instance name which is also used as the z/VM '. + 'userid. The value of this property will not allow the plugin to generate '. + 'unique instance names in subsequent deploys.', + 'userResp' => 'Correct the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \`%s\`, %s will not create an instance name that is a single blank delimited word using the value: %s', + 'explain' => 'The instance name is not valid.', + 'userResp' => 'Correct the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \`%s\`, %s should not begin with \'rsz\': %s', + 'explain' => 'The instance name is not valid.', + 'userResp' => 'Correct the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \`%s\`, %s does not contain the expected value of \'%s\' and instead contains: \'%s\'', + 'explain' => 'The value of the property does not match the allowed value(s). This will cause an error.', + 'userResp' => 'Correct the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP06' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \'%s\', %s specifies a value, \'%s\', which is less than the recommended value of \'%s\'.', + 'explain' => 'The value of the property is less than the minimun recommended value. This could cause an error.', + 'userResp' => 'Correct the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP07' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \`%s\`, %s does not exist but other SCSI disk related operands exist. Both should be specified: \'zvm_fcp_list\' and \'zvm_zhcp_fcp_list\'', + 'explain' => 'The two indicated properties should be specified in the configuration file. Failure to do so '. + 'may cause errors.', + 'userResp' => 'Correct the configuration file to specify both properties and rerun the script. Please refer to '. + 'the Enabling z/VM for OpenStack manual for information about the properties and changing them.', + }, + 'PROP08' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \'%s\', %s specifies a value, \'%s\', which is not the required value of \'%s\'.', + 'explain' => 'The value of the property does not match the value required for z/VM. This could cause an error.', + 'userResp' => 'Correct the property and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP09' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \'%s\', \'%s\' is specified but has no value.', + 'explain' => 'The property was expected to contain a value but did not. '. + 'This is expected to cause errors.', + 'userResp' => 'Correct the property and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP10' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \'%s\', \'%s\' contains too many values.', + 'explain' => 'The property contains more than the allowed number of values. Some will be ignored or '. + 'errors could occur.', + 'userResp' => 'Correct the property and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP11' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \'%s\', \'%s\' contains non-hexadecimal characters: \'%s\'.', + 'explain' => 'The property is expected to contain hexadecimal characters but some non-hexadecimal '. + 'characters were detected. This is expected to cause processing errors.', + 'userResp' => 'Correct the property and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'PROP12' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, section \'%s\',\n \'%s\' contains a value that is not 1-4 characters in length: \'%s\'.', + 'explain' => 'The property is expected to have a length of 1-4 characters but is not.', + 'userResp' => 'Correct the property and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'OPTS01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => '\'%s\' property in section \'%s\' in %s has a value that is different from the value of the ' . + '\'%s\' property in section \'%s\' in %s. They should be the same. The value ' . + 'in %s is: %s and in %s is: %s\n', + 'explain' => 'The preparation script detected that two configuration files '. + 'contained properties which should have the same value but did not. '. + 'This can cause an error as different OpenStack processes perform '. + 'functions which use the value.', + 'sysAct' => 'Verification continues with the value from the first property.', + 'userResp' => 'Correct the configuration files so that the indicated properties match. '. + 'After correcting the files, please restart the compute node and verify '. + 'that the new settings remain across a restart. Rerun the preparation '. + 'script.', + }, + 'PARM01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '--config operand: %s did not specify a file. Default file specification will be used.', + 'explain' => 'The --config operand allows you to specify the configuration file to be '. + 'processed and overrides the default values. The specified value did not '. + 'indicate a valid file and as a result the default file will be used.', + 'userResp' => 'Determine the correct file for the indicated file and reinvoke the preparation script.', + }, + 'PARM02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => '%s operand (%s) is not %s.', + 'explain' => 'The indicated operand did not specify a known value.', + 'userResp' => 'Determine the correct value for the operand and reinvoke the preparation script.', + }, + 'ROLE01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'CMA system role is NOT %s but instead %s. Cinder will not be validated.', + 'explain' => 'The Cinder component runs in an OpenStack controller. The CMA system ' . + 'is not configured to run in that system role.', + 'sysAct' => 'Further testing of the Cinder options will not occur in this script.', + 'userResp' => 'If you wanted Cinder to run in the CMA system then you should consider '. + 'changing the system role to \'CONTROLLER\'. Please note that this system '. + 'role would then need additional properties defined to allow OpenStack '. + 'controller related components to run. Otherwise, you should consider '. + 'removing the cinder configuration file from the CMA system. Please '. + 'consult the Enabling z/VM for OpenStack manual for more information.', + }, + 'ROLE02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The value of the openstack_system_role (%s) is not one of the possible values: %s', + 'explain' => 'The openstack_system_role property controls the components that are enabled '. + 'within CMA. The indicated property is not recognized and could cause '. + 'errors to occur due.', + 'userResp' => 'Please consult the Enabling z/VM for OpenStack manual for information on '. + 'the proper values for the property, what they enable and how to set them.', + }, + 'SYS01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Unable to obtain the host name and/or IP address for this system. '. + 'The local IP address is used in the name of the driver script.\n%s' , + 'explain' => 'The preparation script attempted to determine the host name and IP Address '. + 'of the system where it is run. It does this using gethostbyname and inet_ntoa '. + 'functions. An error occurred which prevented the determination of the values. '. + 'This could affect properties in the created driver script. These values are '. + 'used when OpenStack properties do not provide the information. '. + 'Some variables in the driver script such as zxcatIVP_cNAddress ' . + 'may be set to an empty string value.', + 'userResp' => 'If you do not encounter any errors related to the IP address when it is run '. + 'then you can ignore this error. Otherwise, you should investigate why the '. + 'IP related information could not be obtained and resolve the issue or change '. + 'the driver script to specify the correct value. Rerun the preparation script '. + 'if you changed the system to resolve the issue.', + }, + 'TRON01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'In %s, \'%s\' in section \'%s\' specifies a value, \'%s\', that is deprecated. The recommended value is \'%s\'.', + 'explain' => 'The property contains a value that is deprecated. The value was used in a previous release. ', + 'userResp' => 'Change the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + 'TRON02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'In %s, \'%s\' in section \'%s\' specifies a value, \'%s\', that is not the required value, \'%s\'.', + 'explain' => 'The property contains a value that is not recognized. This will cause OpenStack errors.', + 'userResp' => 'Change the value and rerun the script. Please refer to the Enabling z/VM for OpenStack '. + 'manual for information about the property and changing it.', + }, + ); + +# set the usage message +my $usage_string = "Usage:\n + $0\n + or\n + $0 -s serviceToScan -d driverProgramName\n + or\n + $0 --scan serviceToScan -driver driverProgramName\n + The following options are supported: + + -c | --config + List of configuration files to be processed. This list overrides + the default configuration file locations or the ones determined by + the --init-files operand. Each configuration file is identified + by an eyecatcher indicating which configuration file is being + overriden followed by a colon and the fully qualified file + specification. Multiple configuration files may be specified by + separating them with a comma (one file per eyecatcher). + The following are recognized eyecathers and the files that they + override: + $EyeCeilometerConf - $locCeilometerConf + $EyeCinderConf - $locCinderConf + $EyeMl2ConfIni - $locMl2ConfIni + $EyeNeutronConf - $locNeutronConf + $EyeNeutronZvmPluginIni - + $locNeutronZvmPluginIni + $EyeNovaConf - $locNovaConf + -d | --driver + File specification of driver program to construct, + or name of directory to contain the driver program. + -h | --help + Display help information. + -H | --host + Name of z/VM host to process. Startup scripts end with this + suffix for the specified z/VM system. When this option is + used with the -i option, it indicates which startup scripts should + be scanned. + The driver script created will contain the host value as part of + its name. + --ignore + Blank or comma separated list of message ids or message severities + to ignore. Ignored messages are not counted as failures and do + not produce messages. Instead the number of ignored messages and + their message numbers are displayed at the end of processing. + Recognized message severities: 'bypass', 'info', 'warning', + 'error'. + The following is an example of a message id: MISS02. + -i | --init-files Scan the system for either System V style startup + scripts or systemd service files related to OpenStack. The + files are scanned for the name of the related configuration file. + For System V, /etc/init.d directory is scanned. For systemd, it + will scan /usr/lib/systemd/system/, /run/systemd/system/, and + /etc/systemd/system/ service files (.service). + --help Display help information. + -p | --password-visible + If specified, password values in the constructed driver script + program will be visible. Otherwise, password values are + hidden. This option is used when building a driver script + to run against an older xCAT Management Node. + -s | --scan + Services to scan ('all', 'nova' or 'neutron'). + -v + Display script version information. + -V | --verbose + Display verbose processing messages.\n"; + +#------------------------------------------------------- + +=head3 buildDriverProgram + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = buildDriverProgram(); + +=cut + +#------------------------------------------------------- +sub buildDriverProgram{ + my $rc; + my @driverText; + + if ( $verbose ) { + logResponse( 'BLANK_LINE' ); + logResponse( 'GENERIC_RESPONSE', 'Building the IVP driver program.' ); + } + + # Erase any existing driver program. + if ( -e $driver and ! -z $driver ) { + # Make certain the file is one of our driver files. + my $found = `grep 'Function: z/VM xCAT IVP driver program' $driver`; + if ( ! $found ) { + logResponse( 'DRIV01', $driver ); + return 251; + } else { + # Rename the existing driver file. + logResponse( 'BLANK_LINE' ); + logResponse( 'GENERIC_RESPONSE', "The existing driver file is being saved as: $driver.old" ); + rename $driver,"$driver.old"; + } + } + + # Open the driver program for output. + $rc = open( my $fileHandle, '>', $driver ) or die; + if ( $rc != 1 ) { + logResponse( 'DRIV02', $driver, $! ); + return ( 200 + $rc ); + } + + # Construct the file in an array. + push( @driverText, "#!/bin/bash" ); + push( @driverText, "# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html" ); + push( @driverText, "#" ); + push( @driverText, "# Function: z/VM xCAT IVP driver program" ); + push( @driverText, "# Built by $0 version $version." ); + push( @driverText, "# $supportString" ); + push( @driverText, "" ); + if ( $cmaAppliance == 1 ) { + push( @driverText, "# System is a CMA" ); + push( @driverText, "# CMA system role: $cmaSystemRole" ); + push( @driverText, "# $cmaVersionString" ); + push( @driverText, "" ); + } + push( @driverText, "############## Start of Nova Config Properties" ); + if ( exists $novaConf{'DEFAULT'}{'my_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From \'my_ip\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$novaConf{'DEFAULT'}{'my_ip'}\"" ); + } else { + if ( $locNovaConf ne '' ) { + logResponse( 'MISS02', 'my_ip', 'DEFAULT', $locNovaConf, + "A default value of \'$localIpAddress\' will be used. ". + "If the value is not correct then you should ". + "update the zxcatIVP_cNAddress property in the driver script with the desired IP address." ); + } else { + logResponse( 'MISS07', 'Nova', 'my_ip', + "A default value of \'$localIpAddress\' has been specified in the driver ". + "program for zxcatIVP_cNAddress property. If the value is not correct then ". + "you should update the zxcatIVP_cNAddress property in the driver script with ". + "the desired IP address." ); + } + push( @driverText, "" ); + push( @driverText, "# IP address or hostname of the compute node that is accessing this xCAT MN." ); + push( @driverText, "# From the local IP address of this system." ); + push( @driverText, "export zxcatIVP_cNAddress=\"$localIpAddress\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_user_profile'} ) { + push( @driverText, "" ); + push( @driverText, "# Default profile used in creation of server instances." ); + push( @driverText, "# From \'zvm_user_profile\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_defaultUserProfile=\"$novaConf{'DEFAULT'}{'zvm_user_profile'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_diskpool'} ) { + push( @driverText, "" ); + push( @driverText, "# Array of disk pools that are expected to exist." ); + push( @driverText, "# From \'zvm_diskpool\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_diskpools=\"$novaConf{'DEFAULT'}{'zvm_diskpool'}\"" ); + } + + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by instances." ); + push( @driverText, "# From \'zvm_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_instFCPList=\"$novaConf{'DEFAULT'}{'zvm_fcp_list'}\"" ); + } + if ( exists $commonConf{'zvm_host'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Node of host being managed. If blank, IVP will search for the host node." ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_host'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_hostNode=\"$commonConf{'zvm_host'}{'value'}\"" ); + + } + if ( exists $commonConf{'zvm_xcat_master'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT MN (optional)." ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_master'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_mnNode=\"$commonConf{'zvm_xcat_master'}{'value'}\"" ); + } + if ( exists $commonConf{'zvm_xcat_password'}{'value'} ) { + my $clearPW = ''; + push( @driverText, "" ); + push( @driverText, "# User password defined to communicate with xCAT MN." ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_master'}{'fromLines'} ) ); + + if ( -e $obfuscateProg ) { + # assume password is obfuscated already and get it in the clear. + $clearPW = `$obfuscateProg -u $commonConf{'zvm_xcat_password'}{'value'}`; + $clearPW =~ s/\n+$//g; # trim ending new line + } else { + # Assume password is in the clear because the obfuscation program is missing. + $clearPW = $commonConf{'zvm_xcat_password'}{'value'}; + } + + if ( $pwVisible ) { + push( @driverText, "export zxcatIVP_xcatUserPw=\"$clearPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=0" ); + } else { + my $hiddenPW = obfuscate( $clearPW, 1 ); + push( @driverText, "# Note: Password is hidden." ); + push( @driverText, "# To override the support and pass the password in the" ); + push( @driverText, "# clear, either:" ); + push( @driverText, "# - specify the -p or --password-visible operand when" ); + push( @driverText, "# invoking prep_zxcatIVP.pl script, or" ); + push( @driverText, "# - change zxcatIVP_pw_obfuscated variable to 0 and" ); + push( @driverText, "# specify the password in the clear on the" ); + push( @driverText, "# zxcatIVP_xcatUserPw variable in the constructed" ); + push( @driverText, "# driver script." ); + push( @driverText, "export zxcatIVP_xcatUserPw=\"$hiddenPW\"" ); + push( @driverText, "export zxcatIVP_pw_obfuscated=1" ); + } + } + if ( exists $commonConf{'zvm_xcat_server'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected IP address of the xcat MN" ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_server'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_xcatMNIp=\"$commonConf{'zvm_xcat_server'}{'value'}\"" ); + } + if ( exists $commonConf{'zvm_xcat_username'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# User defined to communicate with xCAT MN" ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'zvm_xcat_server'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_xcatUser=\"$commonConf{'zvm_xcat_username'}{'value'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + push( @driverText, "" ); + push( @driverText, "# The list of FCPs used by zHCP." ); + push( @driverText, "# From \'zvm_zhcp_fcp_list\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_zhcpFCPList=\"$novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'}\"" ); + } + if ( exists $novaConf{'DEFAULT'}{'xcat_free_space_threshold'} ) { + push( @driverText, "" ); + push( @driverText, "# Expected space available in the xCAT MN image repository" ); + push( @driverText, "# From \'xcat_free_space_threshold\' in $locNovaConf." ); + push( @driverText, "export zxcatIVP_expectedReposSpace=\"$novaConf{'DEFAULT'}{'xcat_free_space_threshold'}G\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Nova Config Properties" ); + + if (( keys %neutronConf ) or ( keys %neutronZvmPluginIni ) or ( keys %ml2ConfIni )) { + push( @driverText, "" ); + push( @driverText, "############## Start of Neutron Config Properties" ); + if ( exists $neutronConf{'DEFAULT'}{'base_mac'} ) { + my $prefix = $neutronConf{'DEFAULT'}{'base_mac'}; + $prefix =~ tr/://d; + $prefix = substr( $prefix, 0, 6 ); + push( @driverText, "" ); + push( @driverText, "# User prefix for MAC Addresses of Linux level 2 interfaces" ); + push( @driverText, "# From \'base_mac\' in $locNeutronConf." ); + push( @driverText, "export zxcatIVP_macPrefix=\"$prefix\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat MN's address on the xCAT management network" ); + push( @driverText, "# From \'xcat_mgt_ip\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_xcatMgtIp=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_ip'}\"" ); + } + if ( exists $neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'} ) { + push( @driverText, "" ); + push( @driverText, "# xCat management interface netmask" ); + push( @driverText, "# From \'xcat_mgt_mask\' in $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_mgtNetmask=\"$neutronZvmPluginIni{'agent'}{'xcat_mgt_mask'}\"" ); + } + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} or + exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + my $list = ''; + if ( exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} and + $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} ne '*' ) { + $list = $ml2ConfIni{'ml2_type_flat'}{'flat_networks'}; + } + if ( exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + if ( $list ne '' ) { + $list = "$list,"; + } + $list = "$list$ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'}"; + } + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Array of networks and possible VLAN ranges" ); + push( @driverText, "# From $locMl2ConfIni,"); + push( @driverText, "# \'flat_networks\' in section \'ml2_type_flat\' and "); + push( @driverText, "# \'network_vlan_ranges\' in section \'ml2_type_vlan\'."); + push( @driverText, "export zxcatIVP_networks=\"$list\"" ); + } + } + if ( exists $commonConf{'xcat_zhcp_nodename'}{'value'} ) { + push( @driverText, "" ); + push( @driverText, "# Node name for xCAT zHCP server" ); + push( @driverText, "# From:"); + push( @driverText, split( '\n', $commonConf{'xcat_zhcp_nodename'}{'fromLines'} ) ); + push( @driverText, "export zxcatIVP_zhcpNode=\"$commonConf{'xcat_zhcp_nodename'}{'value'}\"" ); + } + + # Create the zxcatIVP_vswitchOSAs variable for any networks specified with an rdev list. + my $vswitchOSAs = ''; + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + next if ( $list eq '' ); + $list =~ s/\s+/\,/g; # insert comma between words + $vswitchOSAs = "$section $list $vswitchOSAs"; + } + } + if ( $vswitchOSAs ne '' ) { + push( @driverText, "" ); + push( @driverText, "# Vswitches and their related OSAs" ); + push( @driverText, "# From \'rdev_list\' in vswitch sections of $locNeutronZvmPluginIni." ); + push( @driverText, "export zxcatIVP_vswitchOSAs=\"$vswitchOSAs\"" ); + } + + push( @driverText, "" ); + push( @driverText, "############## End of Neutron Config Properties" ); + } + + push( @driverText, "" ); + push( @driverText, "# Name of user under which nova runs, default is nova." ); + push( @driverText, "# If you system is different then change this property." ); + push( @driverText, "export zxcatIVP_expUser=\"nova\"" ); + push( @driverText, "" ); + push( @driverText, "# Controls whether Warnings/Errors detected by the IVP are" ); + push( @driverText, "# logged in the xCAT MN syslog, 0: do not log, 1: log to syslog." ); + push( @driverText, "export zxcatIVP_syslogErrors=1" ); + push( @driverText, "" ); + push( @driverText, "perl $driverLocation$ivp" ); + + # Write the array to the driver file. + foreach (@driverText) { + #print "$_\n"; + print $fileHandle "$_\n"; # Print each entry in our array to the file + } + + close $fileHandle; + + logResponse( 'BLANK_LINE' ); + logResponse( 'GENERIC_RESPONSE', "$driver was built." ); + return 0; +} +#------------------------------------------------------- + +=head3 buildMsg + + Description : Build a message from a message repository file. + Arguments : Message repository or Repository identifier + 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 - unknown message severity + 2 - bypass message used by automated tests + 3 - information message with "Info" header + 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 ( $repos, $groupId, $msgId, $subs ) = @_; + my @msgSubs = @$subs; + my $sev = 0; + my $recAction = 0; + my $respHash; + my $retMsg = ''; + my $retExtra = ''; + + $Text::Wrap::unexpand = 0; + + # Find the message + if ( $msgId eq 'BLANK_LINE' ) { + $retMsg = "\n"; + } elsif ( !exists $respMsgs{$msgId} ) { + $sev = 3; + $retMsg = "Warning ($msgId): Unknown message id.\n"; + # 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 + $sev = 3; + $msg = $sevInfo[1] . " ($groupId:$msgId) "; + } + + # 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'} ) { + if ( @msgSubs ) { + my $msgText = sprintf( $respMsgs{$msgId}{'text'}, @msgSubs); + $msg = "$msg$msgText"; + } else { + $msg = $msg . $respMsgs{$msgId}{'text'}; + } + } + + # 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( "", "\t", $line ) . "\n"; + } + + if ( $sev >=2 ) { + # 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( "", "\t", $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( "", "\t", $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( "", "\t", $line ) . "\n"; + } + } + } + } + + return ( $recAction, $sev, $retMsg, $retExtra ); +} + +#------------------------------------------------------- + +=head3 hashFile + + Description : Read a file with equal signs designating + key value pairs and create a hash from it. + Arguments : File to read + Reference to hash that should be constructed. + File is required to exist or there is a serious error. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = hashFile( $file, \%novaConf, 1 ); + +=cut + +#------------------------------------------------------- +sub hashFile{ + my ( $file, $hash, $required ) = @_; + my $section = "null"; + my $caseSensitive = 0; # assume section/properties are case insensitive + my @parts; + my $out; + + # Clear the hash + %$hash = (); + + if ( $file eq '' ) { + return 602; + } + + if ( ! -e $file ) { + if ( $required ) { + logResponse( 'FILE01', $file ); + } else { + logResponse( 'FILE02', $file ); + } + return 601; + } + + if (( $file =~ /.conf$/ ) or ( $file =~ /.service$/ ) or ( $file =~ /.COPY$/ )) { + # File is case sensitive, translate sections and property names to uppercase. + $caseSensitive = 1; + } + + # Read the configuration file and construct the hash of values. + if (( $file =~ /.conf$/ ) or ( $file =~ /.ini$/ ) or ( $file =~ /.service$/ )) { + $out = `egrep -v '(^#|^\\s*\\t*#)' $file`; + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^\[/ ) { + # Section header line + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the line + $line =~ s/^\[+|\]+$//g; # trim [] from ends of the line + if ( !$caseSensitive ) { + $section = lc( $line ); + } else { + $section = $line; + } + } else { + # Property line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$section}{$parts[0]} = $parts[1]; + #print "$section $parts[0]" . ": " . $parts[1]. "\n"; + #print $parts[0] . ": " . $$hash{$section}{$parts[0]}. "\n"; + } + } + } else { + # Hash .COPY files + # Read the file and remove comment lines and sequence columns (72-80) + $out = `grep -v ^\$ $file| cut -c1-71`; + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Weed out blank lines + $line =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $line ) == 0 ); + + # Parse the line + @parts = split( "=", $line, 2 ); + next if ( ! exists $parts[0] ); + $parts[0] =~ s/^\s+|\s+$//g; # trim both ends of the string + next if ( length( $parts[0] ) == 0 ); + if ( !$caseSensitive ) { + $parts[0] = lc( $parts[0] ); + } + + # Add the property to the hash if it has data. + if ( exists $parts[1] ) { + chomp( $parts[1] ); + $parts[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + $parts[1] =~ s/^"+|"+$//g; # trim double quotes from both ends of the string + } else { + $parts[1] = ''; + } + + $$hash{$parts[0]} = $parts[1]; + #print $parts[0] . ": " . $$hash{$parts[0]}. "\n"; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 logResponse + + Description : Build and log the response. + Arguments : message ID or special flag: + *ALL* indicates all messages should be displayed. + Returns : 0 - No error, general response or info message detected. + 1 - Non-terminating message detected. + 2 - Terminating message detected. + Example : $rc = logResponse( 'VX01' ); + $rc = logResponse( 'VX03', $nodeName, $sub2); + $rc = logResponse( 'VX03', $nodeName, 'sub2a'); + +=cut + +#------------------------------------------------------- +sub logResponse { + my ( $msgId, @msgSubs ) = @_; + my $rc = 0; + my $extraInfo = ''; + my @ids; + my $msg; + my @msgLines; + my $sev; + my $line; + + if ( $msgId eq 'ALL' ) { + @ids = ( sort keys %respMsgs ); + } else { + $ids[0] = $msgId; + } + + # Process the array of message IDs, a single element array for regular calls + # or an array of all of the keys when "--showmsg all" is specified on the command. + foreach my $id ( @ids ) { + ( $rc, $sev, $msg, $extraInfo ) = buildMsg('VERIFY', 'PREP_ZXCATIVP', $msgId, \@msgSubs); + #print ("rc: $rc, sev: $sev, msg: $msg"); + + if ( defined $msgsToIgnore{$msgId} ) { + # Ignore this message id + $ignored{$msgId} = 1; + $ignoreCnt += 1; + next; + } elsif ( defined $msgsToIgnore{$sev} ) { + # Ignoring all messages of this severity. + $ignored{$msgId} = 1; + $ignoreCnt += 1; + next; + } elsif (( $sev == 2) or ( $sev == 3 )) { + $infoCnt += 1; + } elsif ( $sev >= 4 ) { + $warnErrCnt += 1; + } + if ( $sev >= 3 ) { + print "\n"; + } + print "$msg"; + if ( $extraInfo ne '' ) { + print "$extraInfo"; + } + } + +FINISH_logResponse: + return $rc; +} + +#------------------------------------------------------- + +=head3 obfuscate + + Description : Build or update the driver program with the + data obtained by the scans. + Arguments : string to be processed + direction: 1 - obfuscate (hide it!) + 0 - unobfuscate (unhide it!) + Returns : processed password (either hiden or unhidden)) + Example : $rc = obfuscate( $pw, 1 ); + +=cut + +#------------------------------------------------------- +sub obfuscate{ + my ( $pw, $hide ) = @_; + + if ( $hide == 1 ) { + $pw = encode_base64( $pw, '' ); + } else { + $pw = decode_base64( $pw ); + } + + return $pw; +} + +#------------------------------------------------------- + +=head3 saveCommonOpt + + Description : Save a common option for later use and + verify the value is consistent. + Arguments : option + section that contained the option + configuration file name + name of the option in the commonConf hash + value + Returns : 0 - No error + 1 - Already saved with a different value + Example : $rc = saveCommonOpt( $confFile, $opt, $section, $confFile $commonOptName, $value ); + +=cut + +#------------------------------------------------------- +sub saveCommonOpt { + my ( $opt, $section, $confFile, $commonOptName, $value ) = @_; + my $rc = 0; + + if ( !exists $commonConf{$commonOptName} ) { + $commonConf{$commonOptName}{'value'} = $value; + $commonConf{$commonOptName}{'fromLines'} = '# ' . $opt . ' in ' . $confFile; + $commonConf{$commonOptName}{'firstOpt'} = $opt; + $commonConf{$commonOptName}{'firstSection'} = $section; + $commonConf{$commonOptName}{'firstConf'} = $confFile; + } else { + if ( $commonConf{$commonOptName}{'value'} eq $value ) { + $commonConf{$commonOptName}{'fromLines'} = $commonConf{$commonOptName}{'fromLines'} . + "\n# " . $opt . " in " . $confFile; + } else { + logResponse( 'OPTS01', + $opt, + $section, + $confFile, + $commonConf{$commonOptName}{'firstOpt'}, + $commonConf{$commonOptName}{'firstSection'}, + $commonConf{$commonOptName}{'firstConf'}, + $confFile, + $value, + $commonConf{$commonOptName}{'firstConf'}, + $commonConf{$commonOptName}{'value'} ); + $rc = 1; + } + } + return $rc; +} + +#------------------------------------------------------- + +=head3 scanCeilometer + + Description : Scan the ceilometer configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCeilometer(); + +=cut + +#------------------------------------------------------- +sub scanCeilometer{ + my $rc; + + if ( $locCeilometerConf eq '' ) { + return 602; + } + + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Scanning the Ceilometer configuration files:\n$locCeilometerConf" ); + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCeilometerConf, \%ceilometerConf, 0 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanCinder + + Description : Scan the cinder configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanCinder(); + +=cut + +#------------------------------------------------------- +sub scanCinder{ + my $rc; + + if ( $locCinderConf eq '' ) { + return 602; + } + + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Scanning the Cinder configuration files:\n$locCinderConf" ); + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locCinderConf, \%cinderConf, 0 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanInitScript + + Description : Scan the init script for the + specified configuration file property. + Arguments : Configuration file name + Config property containing the value + Returns : Null - error locating the config file. + Non-null - file specification of the + configuration file. + Example : $confFile = scanInitScript('openstack-cinder-volume-$host', + 'config'); + +=cut + +#------------------------------------------------------- +sub scanInitScript{ + my ( $filename, $property ) = @_; + my $configFile = ''; + my $out; + + if ( -e $filename ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Scanning the $filename file for \'$property\' variable." ); + } + + # Strip out the lines after the desired config property is set and + # remove comment lines then echo the property value. + $out = `awk '{print} /$property=/ {exit}' /etc/init.d/$filename | grep -v ^\# | grep .`; + $out = $out . "echo -n \$$property"; + $configFile = `$out`; + } + + return $configFile; +} + +#------------------------------------------------------- + +=head3 scanNeutron + + Description : Scan the neutron configuration files. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNeutron(); + +=cut + +#------------------------------------------------------- +sub scanNeutron{ + my $rc; + + if ( $locNeutronConf eq '' and $locMl2ConfIni eq '' + and $locNeutronZvmPluginIni eq '' ) { + return 602; + } + + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Scanning the Neutron configuration files:\n $locNeutronConf\n $locMl2ConfIni\n $locNeutronZvmPluginIni" ); + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronConf, \%neutronConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locMl2ConfIni, \%ml2ConfIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + # Read the configuration file and construct the hash of values. + $rc = hashFile( $locNeutronZvmPluginIni, \%neutronZvmPluginIni, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanNova + + Description : Scan the Nova configuration files. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = scanNova(); + +=cut + +#------------------------------------------------------- +sub scanNova{ + my $rc; + + if ( $locNovaConf eq '' ) { + return 602; + } + + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Scanning the Nova configuration file:\n $locNovaConf" ); + } + + # Verify the /etc/nova/nova.conf exists. + $rc = hashFile( $locNovaConf, \%novaConf, 1 ); + if ( $rc == 0 ) { + $configFound = 1; + } + + return $rc; +} + +#------------------------------------------------------- + +=head3 scanServiceUnit + + Description : Scan the service unit for the + specified configuration file property. + Arguments : Service file name + Section containing the desired property + Config property containing the value + Returns : Null - error locating the config file. + Non-null - file specification of the + configuration file. + Example : $confFile = scanServiceUnit( 'openstack-cinder-volume-$host', + 'Service', 'ExecStart' ); + +=cut + +#------------------------------------------------------- +sub scanServiceUnit{ + my ( $filename, $section, $property ) = @_; + my $configFile = ''; + my $out; + my $serviceFile = ''; + my %serviceFileData; + + # verify the file exists + if ( -e "/etc/systemd/system/$filename" ) { + $serviceFile = "/etc/systemd/system/$filename"; + } elsif ( -e "/run/systemd/system/$filename" ) { + $serviceFile = "/run/systemd/system/$filename"; + } elsif ( -e "/usr/lib/systemd/system/$filename" ) { + $serviceFile = "/usr/lib/systemd/system/$filename"; + } else { + return $configFile; # Unit file was not found + } + + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Scanning $filename for \'$property\' variable in section $section." ); + } + + my $rc = hashFile( $serviceFile, \%serviceFileData, 0 ); + if ( $rc != 0 ) { + return $configFile; # Could not build the hash + } + + if ( exists $serviceFileData{$section}{$property} ) { + my @parms = split( ' --config-file ', $serviceFileData{$section}{$property} ); + @parms = split ( ' ', $parms[1] ); + $configFile = $parms[0]; + } + + return $configFile; +} + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares and builds a z/VM xCAT IVP driver program + using the information from the configuration files in the + compute node. + + The default name of the driver program is composed of the following: + '$driverPrefix', and + IP address of the system where driver was prepared, and + (optionally) a hypen and the value specified on --Host operand, and + '.sh'. + For example: + $driverPrefix"."9.123.345.91.sh + $driverPrefix"."9.123.345.91-hostzvm.sh + + $supportString + + The following configuration files are scanned for input: + $locCeilometerConf + $locCinderConf + $locNovaConf + $locNeutronConf + $locMl2ConfIni + $locNeutronZvmPluginIni + + When the --init-files operand is specified, OpenStack startup + scripts are scanned in /etc/init.d. The --host operand should + be specified to indicate the suffix to use for scripts that + are unique to a specific z/VM host. The following startup + scripts are scanned: + $ceilometerStem- + neutron-server + $neutronZvmAgentStem- + $novaComputeStem- + openstack-cinder-volume + + When --init-files is specified without the --host operand, + the following scripts are scanned: + $ceilometerStem + neutron-server + $neutronZvmAgentStem + $novaComputeStem + openstack-cinder-volume + + The constructed driver program can then be uploaded to + the xCAT MN and used to validate the configuration between + the compute node and xCAT.\n\n"; + print $usage_string; + return; +} + +#------------------------------------------------------- + +=head3 validateConfigs + + Description : Compare and validate the configuration + values obtained by the scans. + Arguments : None. + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = validateConfigs(); + +=cut + +#------------------------------------------------------- +sub validateConfigs{ + my $bypassCinder = 0; + my $rc = 0; + my $option; + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', 'Performing a local validation of the configuration files.' ); + } + + #******************************************************* + # Verify required configuration options were specified. + #******************************************************* + if ( keys %ceilometerConf ) { + my @requiredCeilometerOpts = ( + 'DEFAULT','host', + 'DEFAULT','hypervisor_inspector', + 'zvm','zvm_host', + 'zvm','zvm_xcat_master', + 'zvm','zvm_xcat_password', + 'zvm','zvm_xcat_server', + 'zvm','zvm_xcat_username', + ); + for ( my $i = 0; $i < $#requiredCeilometerOpts; $i = $i + 2 ) { + my $section = $requiredCeilometerOpts[$i]; + my $option = $requiredCeilometerOpts[$i+1]; + if ( !exists $ceilometerConf{$section}{$option} ) { + logResponse( 'MISS01', $option, $section, $locCeilometerConf ); + } else { + saveCommonOpt( $option, $section, $locCeilometerConf, $option, $ceilometerConf{$section}{$option} ); + } + } + } + + if ( keys %cinderConf ) { + my @requiredCinderOpts = (); + foreach $option ( @requiredCinderOpts ) { + if ( !exists $cinderConf{'DEFAULT'}{$option} ) { + logResponse( 'MISS01', $option, 'DEFAULT', $locCinderConf ); + } + } + } + + if ( keys %novaConf ) { + my @requiredNovaOpts = ( + 'DEFAULT','compute_driver', + 'DEFAULT','force_config_drive', + 'DEFAULT','host', + 'DEFAULT','instance_name_template', + 'DEFAULT','zvm_diskpool', + 'DEFAULT','zvm_host', + 'DEFAULT','zvm_xcat_master', + 'DEFAULT','zvm_xcat_server', + 'DEFAULT','zvm_xcat_username', + 'DEFAULT','zvm_xcat_password', + ); + for ( my $i = 0; $i < $#requiredNovaOpts; $i = $i + 2 ) { + my $section = $requiredNovaOpts[$i]; + my $option = $requiredNovaOpts[$i+1]; + if ( !exists $novaConf{$section}{$option} ) { + logResponse( 'MISS01', $option, $section, $locNovaConf ); + } elsif ( $commonOpts =~ m/ $option / ) { + saveCommonOpt( $option, $section, $locNovaConf, $option, $novaConf{'DEFAULT'}{$option} ); + } elsif ( $option eq 'host' ) { + saveCommonOpt( $option, $section, $locNovaConf, 'zvm_host', $novaConf{'DEFAULT'}{$option} ); + } + } + } + + if ( keys %neutronConf ) { + my @requiredNeutronConfOpts = ( + 'DEFAULT','core_plugin', + ); + for ( my $i = 0; $i < $#requiredNeutronConfOpts; $i = $i + 2 ) { + my $section = $requiredNeutronConfOpts[$i]; + my $option = $requiredNeutronConfOpts[$i+1]; + if ( !exists $neutronConf{$section}{$option} ) { + logResponse( 'MISS01', $option, $section, "$locNeutronConf/neutron/neutron.conf" ); + } + } + } + + if ( keys %ml2ConfIni ) { + my @requiredMl2ConfIniOpts = ( + 'ml2','mechanism_drivers', + ); + for ( my $i = 0; $i < $#requiredMl2ConfIniOpts; $i = $i + 2 ) { + my $section = $requiredMl2ConfIniOpts[$i]; + my $option = $requiredMl2ConfIniOpts[$i+1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + logResponse( 'MISS01', $option, $section, $locMl2ConfIni ); + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my @requiredNeutronZvmPluginIniOpts = ( + 'agent', 'zvm_host', + 'agent','zvm_xcat_server', + ); + for ( my $i = 0; $i < $#requiredNeutronZvmPluginIniOpts; $i = $i + 2 ) { + my $section = $requiredNeutronZvmPluginIniOpts[$i]; + my $option = $requiredNeutronZvmPluginIniOpts[$i+1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + logResponse( 'MISS01', $option, $section, $locNeutronZvmPluginIni ); + } else { + saveCommonOpt( $option, $section, $locNeutronZvmPluginIni, $option, $neutronZvmPluginIni{$section}{$option} ); + } + } + } + + #****************************************** + # Verify optional operands were specified. + #****************************************** + if ( keys %ceilometerConf ) { + if ( !exists $ceilometerConf{'DEFAULT'}{'host'} and + !exists $ceilometerConf{'zvm'}{'zvm_host'} + ) { + logResponse( 'MISS04' ); + } else { + my %optionalCeilometerConfOpts = ( + 'zvm xcat_zhcp_nodename' => 'A default of \'zhcp\' will be used.', + ); + my %defaultCeilometerConfOpts = ( + 'zvm xcat_zhcp_nodename' => 'zhcp', + ); + foreach my $key ( keys %optionalCeilometerConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ceilometerConf{$section}{$option} ) { + if ( $optionalCeilometerConfOpts{$key} ne '' ) { + logResponse( 'MISS02', $option, $section, $locCeilometerConf, $optionalCeilometerConfOpts{$key} ); + } else { + logResponse( 'MISS03', $option, $section, $locCeilometerConf ); + } + if ( exists $defaultCeilometerConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCeilometerConfOpts{$key}; + } + } + } + } + } + + if ( keys %cinderConf ) { + if ( $cmaSystemRole ne 'CONTROLLER' ) { + logResponse( 'ROLE01', 'CONTROLLER', $cmaSystemRole ); + $bypassCinder = 1; + } elsif ( !exists $cinderConf{'DEFAULT'}{'san_ip'} and + !exists $cinderConf{'DEFAULT'}{'san_private_key'} and + !exists $cinderConf{'DEFAULT'}{'storwize_svc_volpool_name'} + ) { + logResponse( 'MISS05' ); + $bypassCinder = 1; + } else { + my %optionalCinderConfOpts = ( + 'DEFAULT san_ip' => 'This property is necessary when using persistent SAN disks obtained ' . + 'from the Cinder service.', + 'DEFAULT san_private_key' => 'This property is necessary when using persistent SAN disks obtained ' . + 'from the Cinder service.', + 'DEFAULT storwize_svc_connection_protocol' => 'This property is necessary when using StorWize ' . + 'persistent disks obtained from the Cinder service.', + "DEFAULT storwize_svc_volpool_name" => "This property is necessary when using StorWize persistent disks obtained " . + "from the Cinder service. The default is 'volpool'.", + 'DEFAULT storwize_svc_vol_iogrp' => 'This property is necessary when using StorWize persistent ' . + 'disks obtained from the Cinder service. The default is 0.', + 'DEFAULT volume_driver' => 'This property is necessary when using persistent disks obtained ' . + ' from the Cinder service.', + ); + my %defaultCinderConfOpts = ( + 'DEFAULT storwize_svc_volpool_name' => 'volpool', + ); + foreach my $key ( keys %optionalCinderConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $cinderConf{$section}{$option} ) { + if ( $optionalCinderConfOpts{$key} ne '' ) { + logResponse( 'MISS02', $option, $section, $locCinderConf, $optionalCinderConfOpts{$key} ); + } else { + logResponse( 'MISS03', $option, $section, $locCinderConf ); + } + if ( exists $defaultCinderConfOpts{$key} ) { + $cinderConf{$section}{$option} = $defaultCinderConfOpts{$key}; + } + } + } + } + } + + if ( keys %novaConf ) { + my %optionalNovaConfOpts = ( + 'DEFAULT config_drive_format' => 'Default of \'iso9660\' will be used.', + "DEFAULT image_cache_manager_interval" => "Default of 2400 (seconds) will be used.", + "DEFAULT ram_allocation_ratio" => "", + "DEFAULT rpc_response_timeout" => 'zVM Live migration may timeout with the default value '. + '(60 seconds). The recommended value for z/VM is 180 to allow zVM live migration to succeed.', + "DEFAULT xcat_free_space_threshold" => "Default of 50 (G) will be used.", + "DEFAULT xcat_image_clean_period" => "Default of 30 (days) will be used.", + "DEFAULT zvm_config_drive_inject_password" => "This value will default to 'FALSE'.", + "DEFAULT zvm_diskpool_type" => "Default of \'ECKD\' will be used.", + "DEFAULT zvm_fcp_list" => "As a result, Cinder volumes cannot be attached to server instances.", + "DEFAULT zvm_zhcp_fcp_list" => "", + "DEFAULT zvm_image_tmp_path" => "Default of '/var/lib/nova/images' will be used.", + "DEFAULT zvm_multiple_fcp" => "Default of 'false' will be used.", + "DEFAULT zvm_reachable_timeout" => "Default of 300 (seconds) will be used.", + "DEFAULT zvm_scsi_pool" => "Default of \'xcatzfcp\' will be used.", + "DEFAULT zvm_user_default_privilege" => "Default of G will be used.", + "DEFAULT zvm_user_profile" => "Default is 'OSDFLT'.", + "DEFAULT zvm_user_root_vdev" => "Default of 100 will be used.", + "DEFAULT zvm_vmrelocate_force" => "", + "DEFAULT zvm_xcat_connection_timeout" => "Default of 3600 seconds will be used.", + 'DEFAULT zvm_image_compression_level' => 'Image compression is controlled by the xCAT ZHCP ' . + 'server in the /var/opt/zhcp/settings.conf file. Compressing images during image ' . + 'capture is the default.' + ); + my %defaultNovaConfOpts = ( + 'DEFAULT config_drive_format' => 'iso9660', + "DEFAULT xcat_free_space_threshold" => 50, + "DEFAULT xcat_image_clean_period" => 30, + "DEFAULT zvm_user_default_privilege" => "G", + "DEFAULT zvm_user_profile" => 'OSDFLT', + "DEFAULT zvm_user_root_vdev" => 100, + "DEFAULT zvm_diskpool_type" => 'ECKD', + "DEFAULT zvm_scsi_pool" => "xcatzfcp", + ); + foreach my $key ( keys %optionalNovaConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $novaConf{$section}{$option} ) { + if ( $optionalNovaConfOpts{$key} ne '' ) { + logResponse( 'MISS02', $option, $section, $locNovaConf, $optionalNovaConfOpts{$key} ); + } else { + logResponse( 'MISS03', $option, $section, $locNovaConf ); + } + if ( exists $defaultNovaConfOpts{$key} ) { + $novaConf{$section}{$option} = $defaultNovaConfOpts{$key}; + } + } + } + } + + if ( keys %neutronConf) { + my %optionalNeutronConfOpts = ( + 'DEFAULT base_mac' => 'A default value of \'fa:16:3e:00:00:00\' will be used.', + ); + my %defaultNeutronConfOpts = ( + 'DEFAULT base_mac' => 'fa:16:3e:00:00:00', + ); + foreach my $key ( keys %optionalNeutronConfOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronConf{$section}{$option} ) { + if ( $optionalNeutronConfOpts{$key} ne '' ) { + logResponse( 'MISS02', $option, $section, $locNeutronConf, $optionalNeutronConfOpts{$key} ); + } else { + logResponse( 'MISS03', $option, $section, $locNeutronConf ); + } + if ( exists $defaultNeutronConfOpts{$key} ) { + $neutronConf{$section}{$option} = $defaultNeutronConfOpts{$key}; + } + } + } + } + + if ( keys %ml2ConfIni ) { + my %optionalMl2ConfIniOpts = ( + 'ml2 tenant_network_types' => 'This property is an ordered list of '. + 'network types to allocate as tenant (project) networks, separated by commas. '. + 'A default value of \'local\' will be used.', + 'ml2 type_drivers' => 'This property lists the network types to be supported. '. + 'A default value of \'local,flat,vlan\' will be used.', + ); + my %defaultMl2ConfIniOpts = ( + 'ml2 tenant_network_types' => 'local', + 'ml2 type_drivers' => 'local,flat,vlan', + ); + foreach my $key ( keys %optionalMl2ConfIniOpts ) { + my @opts = split( /\s/, $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $ml2ConfIni{$section}{$option} ) { + if ( $optionalMl2ConfIniOpts{$key} ne '' ) { + logResponse( 'MISS02', $option, $section, $locMl2ConfIni, $optionalMl2ConfIniOpts{$key} ); + } else { + logResponse( 'MISS03', $option, $section, $locMl2ConfIni ); + } + if ( exists $defaultMl2ConfIniOpts{$key} ) { + $ml2ConfIni{$section}{$option} = $defaultMl2ConfIniOpts{$key}; + } + } + } + } + + if ( keys %neutronZvmPluginIni ) { + my %optionalNeutronZvmPluginIniOpts = ( + 'agent xcat_mgt_ip' => 'This property is necessary when deploying virtual server ' . + 'instances that do NOT have public IP addresses.', + 'agent xcat_mgt_mask' => 'This property is necessary when deploying virtual server ' . + 'instances that do NOT have public IP addresses.', + "agent polling_interval" => "A default value of '5' will be used.", + "agent xcat_zhcp_nodename" => "A default value of \'zhcp\' will be used.", + "agent zvm_xcat_password" => "A default value of \'admin\' is used.", + "agent zvm_xcat_timeout" => "A default value of 300 seconds is used.", + "agent zvm_xcat_username" => "A default value of \'admin\' is used.", + ); + my %defaultNeutronZvmPluginIniOpts = ( + "agent polling_interval" => 2, + "agent xcat_zhcp_nodename" => "zhcp", + "agent zvm_xcat_password" => "admin", + "agent zvm_xcat_timeout" => 300, + "agent zvm_xcat_username" => "admin", + ); + foreach my $key ( keys %optionalNeutronZvmPluginIniOpts ) { + my @opts = split( '\s', $key ); + my $section = $opts[0]; + my $option = $opts[1]; + if ( !exists $neutronZvmPluginIni{$section}{$option} ) { + if ( $optionalNeutronZvmPluginIniOpts{$key} ne '' ) { + logResponse( 'MISS02', $option, $section, $locNeutronZvmPluginIni, $optionalNeutronZvmPluginIniOpts{$key} ); + } else { + logResponse( 'MISS03', $option, $section, $locNeutronZvmPluginIni ); + } + if ( exists $defaultNeutronZvmPluginIniOpts{$key} ) { + $neutronZvmPluginIni{$section}{$option} = $defaultNeutronZvmPluginIniOpts{$key}; + if ( $commonOpts =~ m/ $option / ) { + saveCommonOpt( $option, $section, $locNeutronZvmPluginIni, $option, $neutronZvmPluginIni{$section}{$option} ); + } + } + } + } + } + + # Verify the instance name template is valid + if ( exists $novaConf{'DEFAULT'}{'instance_name_template'} ) { + # Use sprintf which is close enough to the python % support for formatting to construct a sample. + my $base_name = sprintf( $novaConf{'DEFAULT'}{'instance_name_template'}, 1 ); + if ( length( $base_name ) > 8 ) { + logResponse( 'PROP01', $locNovaConf, 'DEFAULT', 'instance_name_template', $novaConf{'DEFAULT'}{'instance_name_template'} ); + } + if (( $base_name eq $novaConf{'DEFAULT'}{'instance_name_template'} ) || + ( $novaConf{'DEFAULT'}{'instance_name_template'} !~ /%/ )) { + logResponse( 'PROP02', $locNovaConf, 'DEFAULT', 'instance_name_template', $novaConf{'DEFAULT'}{'instance_name_template'} ); + } + my $words; + $words++ while $base_name =~ /\S+/g; + if ( $words != 1 ) { + logResponse( 'PROP03', $locNovaConf, 'DEFAULT', 'instance_name_template', $novaConf{'DEFAULT'}{'instance_name_template'} ); + } + if ( $novaConf{'DEFAULT'}{'instance_name_template'} =~ m/(^RSZ)/i ) { + logResponse( 'PROP04', $locNovaConf, 'DEFAULT', 'instance_name_template', $novaConf{'DEFAULT'}{'instance_name_template'} ); + } + } + + # Verify the compute_driver is for z/VM + if ( exists $novaConf{'DEFAULT'}{'compute_driver'} ) { + if ( $novaConf{'DEFAULT'}{'compute_driver'} ne "nova.virt.zvm.ZVMDriver" and + $novaConf{'DEFAULT'}{'compute_driver'} ne "zvm.ZVMDriver") { + logResponse( 'PROP05', $locNovaConf, 'DEFAULT', 'compute_driver', '\'nova.virt.zvm.ZVMDriver\' or \'zvm.ZVMDriver\'', $novaConf{'DEFAULT'}{'compute_driver'} ); + } + } + + # Check whether the rpc timeout is too small for z/VM + if ( exists $novaConf{'DEFAULT'}{'rpc_response_timeout'} ) { + if ( $novaConf{'DEFAULT'}{'rpc_response_timeout'} < 180 ) { + logResponse( 'PROP06', $locNovaConf, 'DEFAULT', 'rpc_response_timeout', $novaConf{'DEFAULT'}{'rpc_response_timeout'}, '180' ); + } + } + + # Verify all SCSI disk operands are specified, if one exists. + if ( exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} or exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + if ( !exists $novaConf{'DEFAULT'}{'zvm_fcp_list'} ) { + logResponse( 'PROP07', $locNovaConf, 'DEFAULT', 'zvm_fcp_list' ); + } + if ( !exists $novaConf{'DEFAULT'}{'zvm_zhcp_fcp_list'} ) { + logResponse( 'PROP07', $locNovaConf, 'DEFAULT', 'zvm_fcp_list' ); + } + } + + # Verify neutron.conf operands with a fixed set of possible values + if ( exists $neutronConf{'DEFAULT'}{'core_plugin'} ) { + if ( $neutronConf{'DEFAULT'}{'core_plugin'} eq 'neutron.plugins.ml2.plugin.Ml2Plugin' ) { + logResponse( 'TRON01', $locNeutronConf, 'core_plugin', 'DEFAULT', $neutronConf{'DEFAULT'}{'core_plugin'}, 'ml2' ); + } + elsif ( $neutronConf{'DEFAULT'}{'core_plugin'} ne 'ml2' ) { + logResponse( 'TRON02', $locNeutronConf, 'core_plugin', 'DEFAULT', $neutronConf{'DEFAULT'}{'core_plugin'}. 'ml2' ); + } + } + + # Verify any rdev_list in Neutron z/VM Plugin ini file contains a single value and/or not a comma + foreach my $section (sort keys %neutronZvmPluginIni) { + next if ( $section eq 'agent'); + foreach my $property ( keys %{ $neutronZvmPluginIni{$section} } ) { + next if ( $property ne "rdev_list" ); + my $list = $neutronZvmPluginIni{$section}{$property}; + $list =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + if ( $list eq '' ) { + logResponse( 'PROP09', $locNeutronZvmPluginIni, $section, 'rdev_list' ); + } else { + my @vals = split ( /\s/, $list ); + if ( $#vals > 0 ) { + # $#vals is array size - 1. + logResponse( 'PROP10', $locNeutronZvmPluginIni, $section, 'rdev_list' ); + } + foreach my $op ( @vals ) { + if ( $op =~ m/[^0-9a-fA-F]+/ ) { + logResponse( 'PROP11', $locNeutronZvmPluginIni, $section, 'rdev_list', $op ); + } elsif ( length($op) > 4 ) { + logResponse( 'PROP12', $locNeutronZvmPluginIni, $section, 'rdev_list', $op ); + } + } + } + } + } + + # Check whether the storwize_svc_connection_protocol is not 'FC' for z/VM + if ( exists $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ) { + if ( $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'} ne 'FC' ) { + logResponse( 'PROP08', $locCinderConf, 'DEFAULT', 'storwize_svc_connection_protocol', $cinderConf{'DEFAULT'}{'storwize_svc_connection_protocol'}, 'FC' ); + } + } + + # Check whether the volume_driver is correct for z/VM + if ( ! $bypassCinder and exists $cinderConf{'DEFAULT'}{'volume_driver'} ) { + if ( $cinderConf{'DEFAULT'}{'volume_driver'} ne 'cinder.volume.drivers.ibm.storwize_svc.storwize_svc_fc.StorwizeSVCFCDriver' ) { + logResponse( 'PROP08', + $locCinderConf, + 'DEFAULT', + 'volume_driver', + $cinderConf{'DEFAULT'}{'volume_driver'}, + 'cinder.volume.drivers.ibm.storwize_svc.storwize_svc_fc.StorwizeSVCFCDriver' ); + } + } + + # Check that either flat_networks or network_vlan_ranges is specified. + if ( !exists $ml2ConfIni{'ml2_type_flat'}{'flat_networks'} && + !exists $ml2ConfIni{'ml2_type_vlan'}{'network_vlan_ranges'} ) { + logResponse( 'MISS08', + $locMl2ConfIni, + 'ml2_type_flat(flat_networks), ml2_type_vlan(network_vlan_ranges)' ); + } + + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $configOpt; +my $rc = 0; +my $ignoreOpt; +my $obfuscateOpt; +my $out; +my $scanInitOpt; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( + 'c|config=s' => \$configOpt, + 'd|driver=s' => \$driver, + 'h|help' => \$displayHelp, + 'H|host=s' => \$host, + 'i|init-files' => \$scanInitOpt, + 'ignore=s' => \$ignoreOpt, + 'p|password-visible' => \$pwVisible, + 's|scan=s' => \$scan, + 'v' => \$versionOpt, + 'V|verbose' => \$verbose )) { + print $usage_string; + goto FINISH; +} + +if ( $versionOpt ) { + logResponse( 'GENERIC_RESPONSE', "Version: $version\n$supportString" ); +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH; +} + +# Handle messages to ignore. +if ( defined( $ignoreOpt ) ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Operand --ignore $ignoreOpt" ); + } + + # Make hash from the specified ignore operands + $ignoreOpt = uc( $ignoreOpt ); + my @ingoreList; + if ( $ignoreOpt =~ ',' ) { + @ingoreList = split( ',', $ignoreOpt ); + } else { + @ingoreList = split( ' ', $ignoreOpt ); + } + %msgsToIgnore = map { $_ => 1 } @ingoreList; + + # Convert general severity type operands to their numeric value. + if ( $msgsToIgnore{'BYPASS'} ) { + delete $msgsToIgnore{'BYPASS'}; + $msgsToIgnore{'2'} = 1; + } + if ( $msgsToIgnore{'INFO'} ) { + delete $msgsToIgnore{'INFO'}; + $msgsToIgnore{'3'} = 1; + } + if ( $msgsToIgnore{'WARNING'} ) { + delete $msgsToIgnore{'WARNING'}; + $msgsToIgnore{'4'} = 1; + } + if ( $msgsToIgnore{'ERROR'} ) { + delete $msgsToIgnore{'ERROR'}; + $msgsToIgnore{'5'} = 1; + } +} + +# Determine the local IP address for this system. +my $errorLines = ''; +my $hostname = hostname; +if ( $hostname ne '' ) { + $localIpAddress = gethostbyname( $hostname ); + if ( defined $localIpAddress ) { + my $len = length( $localIpAddress ); + if ( $len == 4 ) { + $localIpAddress = inet_ntoa( $localIpAddress ); + } else { + $localIpAddress = ''; + $errorLines = $errorLines . " The IP address obtained from perl gethostbyname function does not\n" . + " appear to be an IPv4 address. An IPv4 address should be used.\n"; + } + } else { + $errorLines = $errorLines . " The IP address related to the following host name was not found:\n" . + " $hostname\n"; + if ( defined $? ) { + $errorLines = $errorLines . " The perl gethostbyname function failed with errno: $?\n"; + } + $localIpAddress = ''; + } +} else { + $errorLines = $errorLines . " The host name was not found.\n"; +} + +if ( $localIpAddress eq '' ) { + if ( $errorLines eq '' ) { + logResponse( 'SYS01' ); + } else { + logResponse( 'SYS01', " Additional Information:\n$errorLines" ); + } +} + +# Detect CMA +if ( -e $locVersionFileCMO ) { + # CMA version file exists, treat this as a CMA node + $cmaAppliance = 1; + $cmaVersionString = `cat $locVersionFileCMO`; + chomp( $cmaVersionString ); + if ( $cmaVersionString eq '' ) { + $cmaVersionString = "version unknown"; + } + + # Determine the role of this CMA system + my %settings; + if ( -e $locApplSystemRole ) { + $rc = hashFile( $locApplSystemRole, \%settings, 0 ); + $cmaSystemRole = uc( $settings{'role'} ); + } + if ( $cmaSystemRole eq '' and -e $locDMSSICMOCopy ) { + $rc = hashFile( $locDMSSICMOCopy, \%settings, 0 ); + if ( exists $settings{'openstack_system_role'} ) { + $cmaSystemRole = uc( $settings{'openstack_system_role'} ); + } + } + if ( $cmaSystemRole eq '' ) { + $cmaSystemRole = 'unknown'; + } +} + +if ( defined( $scan ) ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Operand --scan: $scan" ); + } + if ( 'all cinder neutron nova' !~ $scan ) { + logResponse( 'PARM02', '--scan', $scan, '\'all\', \'cinder\', \'neutron\' or \'nova\'' ); + $rc = 400; + goto FINISH; + } +} else { + $scan = 'all'; +} + +if ( defined( $host ) ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Operand --Host: $host" ); + } + $driverSuffix = "_$host"; + $initSuffix = "-$host"; +} else { + $host = ''; + $driverSuffix = ""; + $initSuffix = ""; +} + +if ( defined( $scanInitOpt ) ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Operand --initFiles" ); + } + + my $configFile = ''; + if ( $scan =~ 'all' or $scan =~ 'nova' ) { + # Look for a System V startup script + $configFile = scanInitScript( "$novaComputeStem$initSuffix", 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$novaComputeStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locNovaConf = $configFile; + } else { + logResponse( 'FILE03', 'Nova' ); + $locNovaConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'ceilometer' ) { + # Look for a System V startup script + $configFile = scanInitScript( "$ceilometerStem$initSuffix", 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$ceilometerStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locCeilometerConf = $configFile; + } else { + logResponse( 'FILE03', 'Ceilometer' ); + $locCeilometerConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + # Look for a System V startup script + $configFile = scanInitScript( 'openstack-cinder-volume', 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( 'openstack-cinder-volume.service', 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locCinderConf = $configFile; + } else { + logResponse( 'FILE03', 'Cinder' ); + $locCinderConf = ''; + } + } + + if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + # Look for a System V startup script + $configFile = scanInitScript( 'neutron-server', 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( 'neutron-server.service', 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locNeutronConf = $configFile; + } else { + logResponse( 'FILE03', 'Neutron' ); + $locNeutronConf = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$neutronZvmAgentStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locNeutronZvmPluginIni = $configFile; + } else { + logResponse( 'FILE04', 'Neutron', 'z/VM agent' ); + $locNeutronZvmPluginIni = ''; + } + + $configFile = scanInitScript( "$neutronZvmAgentStem$initSuffix", 'plugin_config' ); + if ( $configFile eq '' ) { + # Look for systemd unit files + $configFile = scanServiceUnit( "$neutronZvmAgentStem$initSuffix.service", 'Service', 'ExecStart' ); + } + if ( $configFile ne '' ) { + $locMl2ConfIni = $configFile; + } else { + logResponse( 'FILE04', 'Neutron', 'ml2 plugin' ); + $locMl2ConfIni = ''; + } + } +} + +if ( defined( $configOpt ) ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Operand --config $configOpt" ); + } + + my @items = split( ',', $configOpt ); + foreach my $item ( @items ) { + my @parts = split( ':', $item ); + if ( defined $parts[1] ) { + $parts[0] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + if ( defined $parts[1] ) { + $parts[1] =~ s/^\s+|\s+$//g; # trim blanks from both ends of the list + } + + if ( !defined $parts[1] or $parts[1] eq '' ) { + logResponse( 'PARM01', $parts[0] ); + next; + } + + if ( $parts[0] eq $EyeCeilometerConf ) { + $locCeilometerConf = $parts[1]; + } elsif ( $parts[0] eq $EyeCinderConf ) { + $locCinderConf = $parts[1]; + } elsif ( $parts[0] eq $EyeMl2ConfIni ) { + $locMl2ConfIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronConf ) { + $locNeutronConf = $parts[1]; + } elsif ( $parts[0] eq $EyeNeutronZvmPluginIni ) { + $locNeutronZvmPluginIni = $parts[1]; + } elsif ( $parts[0] eq $EyeNovaConf ) { + $locNovaConf = $parts[1]; + } + } +} + +my ( $volume, $directory, $file ) = ''; +if ( defined( $driver ) ) { + if ( $verbose ) { + logResponse( 'GENERIC_RESPONSE', "Operand --driver: $driver" ); + } + + my @dirs; + if ( -d $driver ) { + # Driver operand is a directory name only + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver, 1 ); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); + } else { + # Driver operand is a bad directory or a file in a directory + ( $volume, $directory, $file ) = File::Spec->splitpath( $driver ); + if ( -d $directory ) { + $driver = File::Spec->catpath( $volume, $directory, $file ); + } else { + logResponse( 'DRIV03', $directory ); + $rc = 500; + goto FINISH; + } + } +} else { + $directory = File::Spec->curdir(); + $driver = File::Spec->catpath( $volume, $directory, "$driverPrefix$localIpAddress$driverSuffix.sh" ); +} + +# Validate CMA related information +if ( $cmaSystemRole ne '' ) { + my %cmaRoles = ( 'CONTROLLER'=>1, 'COMPUTE'=>1, 'COMPUTE_MN'=>1, 'MN'=>1, 'ZHCP'=>1 ); + # Validate Controller related information + if ( ! exists $cmaRoles{$cmaSystemRole} ) { + my @k = keys %cmaRoles; + my $keyNames = join( ', ', @k ); + logResponse( 'ROLE02', $cmaSystemRole, $keyNames ); + $rc = 503; + # Allow other processing to continue + } +} + +# Scan the configuration files. +if ( $scan =~ 'all' or $scan =~ 'nova' ) { + scanNova(); +} + +if ( $scan =~ 'all' or $scan =~ 'ceilometer' ) { + scanCeilometer(); +} + +if ( $scan =~ 'all' or $scan =~ 'cinder' ) { + scanCinder(); +} + +if ( $scan =~ 'all' or $scan =~ 'neutron' ) { + scanNeutron(); +} + +# Validate the settings and produce the driver script. +if ( ! $configFound ) { + logResponse( 'MISS06' ); + $rc = 501; + # Allow other processing to continue +} + +!( $rc = validateConfigs() ) or goto FINISH; + +!( $rc = buildDriverProgram() ) or goto FINISH; + +FINISH: + +if ( !$displayHelp and !$versionOpt ) { + # Produce summary messages. + logResponse( 'BLANK_LINE' ); + logResponse( 'GENERIC_RESPONSE', "$infoCnt info or bypass messages were generated." ); + logResponse( 'GENERIC_RESPONSE', "$warnErrCnt warnings or errors were generated." ); + if ( $ignoreCnt != 0 ){ + logResponse( 'GENERIC_RESPONSE', "Ignored messages $ignoreCnt times." ); + my @ignoreArray = sort keys %ignored; + my $ignoreList = join ( ', ', @ignoreArray ); + logResponse( 'GENERIC_RESPONSE', "Message Ids of ignored messages: $ignoreList" ); + } +} + +exit $rc; + diff --git a/xCAT-server/xCAT-server.spec b/xCAT-server/xCAT-server.spec index f4fb5acb9..6299f405f 100644 --- a/xCAT-server/xCAT-server.spec +++ b/xCAT-server/xCAT-server.spec @@ -18,23 +18,38 @@ AutoReqProv: no %define fsm %(if [ "$fsm" = "1" ];then echo 1; else echo 0; fi) +# Define local variable from environment variable %define pcm %(if [ "$pcm" = "1" ];then echo 1; else echo 0; fi) %define notpcm %(if [ "$pcm" = "1" ];then echo 0; else echo 1; fi) +%define s390x %(if [ "$s390x" = "1" ];then echo 1; else echo 0; fi) +%define nots390x %(if [ "$s390x" = "1" ];then echo 0; else echo 1; fi) + +# Define a different location for various httpd configs in s390x mode +%define httpconfigdir %(if [ "$s390x" = "1" ];then echo "xcathttpdsave"; else echo "xcat"; fi) # AIX will build with an arch of "ppc" # also need to fix Requires for AIX %ifos linux BuildArch: noarch -Requires: perl-IO-Socket-SSL perl-XML-Simple perl-XML-Parser perl-Digest-SHA1 perl(LWP::Protocol::https) +# Note: ifarch/ifnarch does not work for noarch package, use environment variable instead + +%if %s390x +Requires: perl-IO-Socket-SSL perl-XML-Simple perl-XML-Parser +%else +Requires: perl-IO-Socket-SSL perl-XML-Simple perl-XML-Parser perl-Digest-SHA1 perl(LWP::Protocol::https) perl-Net-HTTPS-NB perl-HTTP-Async +%endif Obsoletes: atftp-xcat %endif # The aix rpm cmd forces us to do this outside of ifos type stmts %if %notpcm %ifos linux +# # PCM does not use or ship grub2-xcat Requires: grub2-xcat perl-Net-HTTPS-NB perl-HTTP-Async -#%endif +%if %nots390x +Requires: grub2-xcat +%endif %endif %endif @@ -313,7 +328,7 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d cp etc/init.d/xcatd $RPM_BUILD_ROOT/etc/init.d %endif #TODO: the next has to me moved to postscript, to detect /etc/xcat vs /etc/opt/xcat -mkdir -p $RPM_BUILD_ROOT/etc/xcat +mkdir -p $RPM_BUILD_ROOT/etc/%httpconfigdir mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT-server cp LICENSE.html $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT-server @@ -324,7 +339,7 @@ chmod 644 $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT-server/* mkdir -p $RPM_BUILD_ROOT/%{prefix}/ws mkdir -p $RPM_BUILD_ROOT/etc/apache2/conf.d mkdir -p $RPM_BUILD_ROOT/etc/httpd/conf.d -mkdir -p $RPM_BUILD_ROOT/etc/xcat/conf.orig +mkdir -p $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig cp xCAT-wsapi/* $RPM_BUILD_ROOT/%{prefix}/ws @@ -336,18 +351,18 @@ rm -f $RPM_BUILD_ROOT/%{prefix}/ws/xcatws.cgi %if %fsm %else -echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache22 -echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache24 +echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 +echo "ScriptAlias /xcatrhevh %{prefix}/ws/xcatrhevh.cgi" > $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 %if %notpcm -echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache22 -echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache24 +echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 +echo "ScriptAlias /xcatws %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 %endif -cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache22 >> $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache22 -cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache24 >> $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache24 +cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache22 >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 +cat $RPM_BUILD_ROOT/%{prefix}/ws/xcat-ws.conf.apache24 >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 #install lower version(<2.4) apache/httpd conf files by default -cp $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache22 $RPM_BUILD_ROOT/etc/apache2/conf.d/xcat-ws.conf -cp $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat-ws.conf.apache22 $RPM_BUILD_ROOT/etc/httpd/conf.d/xcat-ws.conf +cp $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 $RPM_BUILD_ROOT/etc/apache2/conf.d/xcat-ws.conf +cp $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 $RPM_BUILD_ROOT/etc/httpd/conf.d/xcat-ws.conf %endif @@ -362,12 +377,12 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root) #%doc LICENSE.html %{prefix} -/etc/xcat +/etc/%httpconfigdir %if %fsm %else /etc/init.d/xcatd -#/etc/xcat/conf.orig/xcat-ws.conf.apache24 -#/etc/xcat/conf.orig/xcat-ws.conf.apache22 +#/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 +#/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 /etc/apache2/conf.d/xcat-ws.conf /etc/httpd/conf.d/xcat-ws.conf %endif @@ -434,19 +449,19 @@ fi if [ -n "$(httpd -v 2>&1 |grep -e '^Server version\s*:.*\/2.4')" ] then rm -rf /etc/httpd/conf.d/xcat-ws.conf - cp /etc/xcat/conf.orig/xcat-ws.conf.apache24 /etc/httpd/conf.d/xcat-ws.conf + cp /etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 /etc/httpd/conf.d/xcat-ws.conf fi if [ -n "$(apachectl -v 2>&1 |grep -e '^Server version\s*:.*\/2.4')" ] then rm -rf /etc/apache2/conf.d/xcat-ws.conf - cp /etc/xcat/conf.orig/xcat-ws.conf.apache24 /etc/apache2/conf.d/xcat-ws.conf + cp /etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 /etc/apache2/conf.d/xcat-ws.conf fi if [ -n "$(apache2ctl -v 2>&1 |grep -e '^Server version\s*:.*\/2.4')" ] then rm -rf /etc/apache2/conf.d/xcat-ws.conf - cp /etc/xcat/conf.orig/xcat-ws.conf.apache24 /etc/apache2/conf.d/xcat-ws.conf + cp /etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 /etc/apache2/conf.d/xcat-ws.conf fi exit 0 diff --git a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache2 b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache2 new file mode 100644 index 000000000..7b245776d --- /dev/null +++ b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache2 @@ -0,0 +1,13 @@ +LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so + +# Redirect all http request to https +RewriteEngine On +RewriteCond %{SERVER_PORT} 80 +RewriteCond %{HTTPS} !=on +RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L] + + + Order allow,deny + Allow from all + + diff --git a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 index 8e7326c59..7b245776d 100644 --- a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 +++ b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 @@ -1,8 +1,10 @@ LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so + +# Redirect all http request to https RewriteEngine On RewriteCond %{SERVER_PORT} 80 -RewriteCond %{REQUEST_URI} xcatws -RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R,L] +RewriteCond %{HTTPS} !=on +RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L] Order allow,deny diff --git a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 index b10ccf224..7aa4f20f1 100644 --- a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 +++ b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 @@ -1,8 +1,8 @@ LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so RewriteEngine On RewriteCond %{SERVER_PORT} 80 -RewriteCond %{REQUEST_URI} xcatws -RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R,L] +RewriteCond %{HTTPS} !=on +RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L] Require all granted diff --git a/xCAT-server/xCAT-wsapi/xcat-ws.conf.httpd b/xCAT-server/xCAT-wsapi/xcat-ws.conf.httpd new file mode 100644 index 000000000..10f48ab39 --- /dev/null +++ b/xCAT-server/xCAT-wsapi/xcat-ws.conf.httpd @@ -0,0 +1,4 @@ +RewriteEngine On +RewriteCond %{SERVER_PORT} 80 +RewriteCond %{HTTPS} !=on +RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L]