diff --git a/perl-xCAT/perl-xCAT.spec b/perl-xCAT/perl-xCAT.spec index 86c15f41a..43ae21992 100644 --- a/perl-xCAT/perl-xCAT.spec +++ b/perl-xCAT/perl-xCAT.spec @@ -26,6 +26,7 @@ Includes xCAT::Table, xCAT::NodeRange, among others. %define zvm %(if [ "$zvm" = "1" ];then echo 1; else echo 0; fi) %define fsm %(if [ "$fsm" = "1" ];then echo 1; else echo 0; fi) +%define builddate %(date) %prep %setup -q -n perl-xCAT %build diff --git a/perl-xCAT/xCAT/DSHCLI.pm b/perl-xCAT/xCAT/DSHCLI.pm index 787bb64c7..7e22de2d3 100644 --- a/perl-xCAT/xCAT/DSHCLI.pm +++ b/perl-xCAT/xCAT/DSHCLI.pm @@ -3855,10 +3855,10 @@ sub usage_dsh my $usagemsg1 = " xdsh -h \n xdsh -q \n xdsh -V \n"; my $usagemsg1a = "xdsh [-K] [-l logonuserid]\n"; my $usagemsg2 = " [-B | --bypass ] [-c] [-e] [-E environment_file] - [--devicetype type_of_device] [-f fanout]\n"; + [--devicetype type_of_device] [-f fanout] [--ip ip_addr]\n"; my $usagemsg3 = " [-l user_ID] [-L] "; my $usagemsg4 = "[-m] [-o options][-q] [-Q] [-r remote_shell] - [-i image] [-s] [-S ksh | csh] [-t timeout]\n"; + [-i image] [-s] [-S ksh | csh] [--show key|script] [-t timeout]\n"; my $usagemsg5 = " [-T] [-X environment variables] [-v] [-z] [--sudo]\n"; my $usagemsg6 = " "; my $usagemsg .= $usagemsg1 .= $usagemsg1a .= $usagemsg2 .= $usagemsg3 .= @@ -3962,8 +3962,10 @@ sub parse_and_run_dsh 'c|cleanup' => \$options{'cleanup'}, 'E|environment=s' => \$options{'environment'}, 'I|ignore-sig|ignoresig=s' => \$options{'ignore-signal'}, + 'ip=s' => \$options{'ip'}, 'K|keysetup' => \$options{'ssh-setup'}, 'L|no-locale' => \$options{'no-locale'}, + 'show=s' => \$options{'show'}, 'Q|silent' => \$options{'silent'}, 'S|syntax=s' => \$options{'syntax'}, 'T|trace' => \$options{'trace'}, @@ -4131,7 +4133,7 @@ sub parse_and_run_dsh # with error, if the Management Node is in the Database and in the # noderange my @mname = xCAT::Utils->noderangecontainsMn(@nodelist); - if (@mname) { # MN in the nodelist + if ( @mname and !$options{'ip'} ) { # MN in the nodelist and --ip not specified my $nodes = join(',', @mname); my $rsp = {}; $rsp->{error}->[0] = @@ -4165,7 +4167,7 @@ sub parse_and_run_dsh # password for the key update. This was setup in xdsh client # frontend. - if (!($ENV{'DSH_REMOTE_PASSWORD'})) + if (!($ENV{'DSH_REMOTE_PASSWORD'}) && !(defined $options{'show'})) { my $rsp = {}; $rsp->{error}->[0] = @@ -4239,7 +4241,22 @@ sub parse_and_run_dsh # # setup ssh keys on the nodes or ib switch # - my $rc = xCAT::TableUtils->setupSSH($options{'nodes'}, $options{'timeout'}); + my $rc; + + # If 'show' was specified then pass the value in an environment variable. + if ( $options{'show'} ) { + $ENV{'DSH_SHOW'} = $options{'show'}; + } + + # Go perform the setup of the SSH keys. + if ( $options{'ip'} ) { + # If IPs were passed then use them as the target of the unlock. + $rc = xCAT::TableUtils->setupSSH($options{'ip'} ); + } else { + # Unlock the nodes + $rc = xCAT::TableUtils->setupSSH($options{'nodes'}, $options{'timeout'}); + } + my @results = "return code = $rc"; return (@results); } diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index c38db16d2..f01edfd98 100755 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -276,7 +276,7 @@ qq{ link,ro - The file is readonly, and will be placed in tmpfs on the booted no table_descr => 'Node storage resources', descriptions => { node => 'The node name', - controller => 'The management address to attach/detach new volumes. + controller => 'The management address to attach/detach new volumes. In the scenario involving multiple controllers, this data must be passed as argument rather than by table value', osvolume => "Specification of what storage to place the node OS image onto. Examples include: @@ -664,13 +664,13 @@ passed as argument rather than by table value', servicenode => 'A comma separated list of node names (as known by the management node) that provides most services for this node. The first service node on the list that is accessible will be used. The 2nd node on the list is generally considered to be the backup service node for this node when running commands like snmove.', netboot => 'The type of network booting to use for this node. Valid values: - Arch OS valid netboot options - x86, x86_64 ALL pxe, xnba + Arch OS valid netboot options + x86, x86_64 ALL pxe, xnba ppc64 <=rhel6, <=sles11.3 yaboot ppc64 >=rhels7, >=sles11.4 grub2,grub2-http,grub2-tftp ppc64le NonVirtualize ALL petitboot ppc64le PowerKVM Guest ALL grub2,grub2-http,grub2-tftp - + ', tftpserver => 'The TFTP server for this node (as known by this node). If not set, it defaults to networks.tftpserver.', tftpdir => 'The directory that roots this nodes contents from a tftp and related perspective. Used for NAS offload by using different mountpoints.', @@ -807,16 +807,16 @@ passed as argument rather than by table value', exlist => 'The fully qualified name of the file that stores the file names and directory names that will be excluded from the image during packimage command. It is used for diskless image only.', postinstall => 'Supported in diskless image only. The fully qualified name of the scripts running in non-chroot mode after the package installation but before initrd generation during genimage. If multiple scripts are specified, they should be seperated with comma ",". A set of osimage attributes are exported as the environment variables to be used in the postinstall scripts: - IMG_ARCH(The architecture of the osimage, such as "ppc64le","x86_64"), - IMG_NAME(The name of the osimage, such as "rhels7.3-ppc64le-netboot-compute"), - IMG_OSVER(The os release of the osimage, such as "rhels7.3","sles11.4"), + IMG_ARCH(The architecture of the osimage, such as "ppc64le","x86_64"), + IMG_NAME(The name of the osimage, such as "rhels7.3-ppc64le-netboot-compute"), + IMG_OSVER(The os release of the osimage, such as "rhels7.3","sles11.4"), IMG_KERNELVERSION(the "kernelver" attribute of the osimage), - IMG_PROFILE(the profile of the osimage, such as "service","compute"), - IMG_PKGLIST(the "pkglist" attribute of the osimage), - IMG_PKGDIR(the "pkgdir" attribute of the osimage), - IMG_OTHERPKGLIST(the "otherpkglist" attribute of the osimage), - IMG_OTHERPKGDIR(the "otherpkgdir" attribute of the osimage), - IMG_ROOTIMGDIR(the "rootimgdir" attribute of the osimage)', + IMG_PROFILE(the profile of the osimage, such as "service","compute"), + IMG_PKGLIST(the "pkglist" attribute of the osimage), + IMG_PKGDIR(the "pkgdir" attribute of the osimage), + IMG_OTHERPKGLIST(the "otherpkglist" attribute of the osimage), + IMG_OTHERPKGDIR(the "otherpkgdir" attribute of the osimage), + IMG_ROOTIMGDIR(the "rootimgdir" attribute of the osimage)', rootimgdir => 'The directory name where the image is stored. It is generally used for diskless image. it also can be used in sysclone environment to specify where the image captured from golden client is stored. in sysclone environment, rootimgdir is generally assigned to some default value by xcat, but you can specify your own store directory. just one thing need to be noticed, wherever you save the image, the name of last level directory must be the name of image. for example, if your image name is testimage and you want to save this image under home directoy, rootimgdir should be assigned to value /home/testimage/', kerneldir => 'The directory name where the 3rd-party kernel is stored. It is used for diskless image only.', nodebootif => 'The network interface the stateless/statelite node will boot over (e.g. eth0)', @@ -1069,7 +1069,7 @@ passed as argument rather than by table value', " nodes, use a simple comma-separated list of NICs. To specify different \n" . " NICs for different nodes, use the format: \"xcatmn|eth1,eth2;service|bond0\", \n" . " where xcatmn is the name of the management node, and DNS should listen on\n" . -" the eth1 and eth2 interfaces. All the nods in group 'service' should \n" . +" the eth1 and eth2 interfaces. All the nods in group 'service' should \n" . " listen on the 'bond0' interface.\n\n" . " NOTE: If using this attribute to block certain interfaces, make sure\n" . " the IP maps to your hostname of xCAT MN is not blocked since xCAT needs\n" . @@ -1504,8 +1504,30 @@ passed as argument rather than by table value', }, }, +zvmivp => { + cols => [qw(id ip schedule last_run type_of_run access_user orch_parms prep_parms main_ivp_parms comments disable)], + keys => [qw(id)], + table_desc => 'List of z/VM Installation Verification Procedures (IVPs) to be periodically run.', + descriptions => { + id => 'Unique identifier associated with the IVP run, e.g. 1.', + ip => 'IP address of the target system, either the IP of the OpenStack compute node or the xCAT management node.', + schedule => 'The hours (0-24) that the IVP should be run. Multiple hours are separated by a blank.', + last_run => 'The last time the IVP was run specified as a set of 3 blank delimeted words: year, Julian date, and hour (in 24 hour format).', + type_of_run => 'The type of run requested, \'fullivp\' or \'basicivp\'.', + access_user => 'User on the OpenStack node that is used to: push the IVP preparation script to the OpenStack system, '. + 'drive the preparation script to validate the OpenStack configuration files, and return the created '. + 'driver script to the xCAT MN system for the next part of the IVP. This user should be '. + 'able to access the OpenStack configuration files that are scanned by the IVP.', + orch_parms => 'Parameters to pass to the IVP orchestrator script, verifynode.', + prep_parms => 'Parameters to pass to the phase 1 IVP preparation script.', + main_ivp_parms => 'Parameters to pass to the main IVP script.', + comments => 'Any user provided notes or description of the run.', + disable => "Set to 'yes' or '1' to disable this IVP run.", + }, + }, + zvm => { - cols => [qw(node hcp userid nodetype parent comments disable)], + cols => [qw(node hcp userid nodetype parent comments disable discovered status)], keys => [qw(node)], table_desc => 'List of z/VM virtual servers.', descriptions => { @@ -1516,6 +1538,8 @@ passed as argument rather than by table value', parent => 'The parent node. For LPAR, this specifies the CEC. For z/VM, this specifies the LPAR. For VM, this specifies the z/VM host operating system.', comments => 'Any user provided notes.', disable => "Set to 'yes' or '1' to comment out this row.", + discovered => "Set to '1' to indicate this node was discovered.", + status => "The processing status. Key value pairs (key=value) indicating status of the node. Multiple pairs are separated by semi-colons. Keys include: CLONING, CLONE_ONLY.", }, }, @@ -1551,6 +1575,7 @@ passed as argument rather than by table value', If multiple ip addresses are associated with each NIC: !|,!|,..., for example, eth0!-eth0|-eth0-ipv6,ib0!-ib0|-ib0-ipv6. The xCAT object definition commands support to use nichostnamesuffixes. as the sub attributes. + Note: According to DNS rules a hostname must be a text string up to 24 characters drawn from the alphabet (A-Z), digits (0-9) and minus sign (-). When you are specifying "nichostnamesuffixes" or "nicaliases" make sure the resulting hostnames will conform to this naming convention', nichostnameprefixes => 'Comma-separated list of hostname prefixes per NIC. If only one ip address is associated with each NIC: @@ -1869,6 +1894,7 @@ foreach my $tabname (keys(%xCAT::ExtTab::ext_tabspec)) { osdistro => { attrs => [], attrhash => {}, objkey => 'osdistroname' }, osdistroupdate => { attrs => [], attrhash => {}, objkey => 'osupdatename' }, zone => { attrs => [], attrhash => {}, objkey => 'zonename' }, + zvmivp => { attrs => [], attrhash => {}, objkey => 'id' }, firmware => { attrs => [], attrhash => {}, objkey => 'cfgfile' }, taskstate => { attrs => [], attrhash => {}, objkey => 'node' }, pdu => { attrs => [], attrhash => {}, objkey => 'node' }, diff --git a/perl-xCAT/xCAT/Usage.pm b/perl-xCAT/xCAT/Usage.pm index 6b1dab41c..8175f5522 100755 --- a/perl-xCAT/xCAT/Usage.pm +++ b/perl-xCAT/xCAT/Usage.pm @@ -250,13 +250,16 @@ my %usage = ( lsvm [-a|--all] PPC (using Direct FSP Management) specific: lsvm [-l|--long] --p775 - lsvm + lsvm zVM specific: lsvm noderange lsvm noderange --getnetworknames lsvm noderange --getnetwork network_name lsvm noderange --diskpoolnames - lsvm noderange --diskpool pool_name", + lsvm noderange --diskpool pool_name + lsvm noderange --queryalldisks + lsvm noderange --querypagevolumes + lsvm noderange --queryspoolvolumes", "chvm" => "Usage: Common: @@ -270,7 +273,7 @@ my %usage = ( chvm [lparname=<*|name>] chvm [vmcpus=min/req/max] [vmmemory=min/req/max] [vmothersetting=hugepage:N,bsr:N] - [add_physlots=drc_index1,drc_index2...] + [add_physlots=drc_index1,drc_index2...] [add_vmnics=vlan1,vlan2] [add_vmstorage=] [--vios] chvm [del_physlots=drc_index1,drc_index2...] chvm [del_vadapter=slotid] @@ -448,7 +451,7 @@ Options: [-f|--snsync] Performs File Syncing to the service nodes that service the nodes in the noderange. - [-g|--genmypost] Will generate a new mypostscript file for the + [-g|--genmypost] Will generate a new mypostscript file for the the nodes in the noderange, if site precreatemypostscripts is 1 or YES. [-l|--user] User name to run the updatenode command. It overrides the diff --git a/perl-xCAT/xCAT/Utils.pm b/perl-xCAT/xCAT/Utils.pm index e44a184ee..35b9abea2 100644 --- a/perl-xCAT/xCAT/Utils.pm +++ b/perl-xCAT/xCAT/Utils.pm @@ -2294,11 +2294,19 @@ sub osver $os = $id_like; } + if (!$os ) { + $os = "unknown"; + } + $verrel = $version; if (!$verrel and $version_id) { $verrel = $version_id; } + if (!$verrel ) { + $verrel = "unknown"; + } + if (!$name and $prettyname) { $name = $prettyname; diff --git a/perl-xCAT/xCAT/zvmCPUtils.pm b/perl-xCAT/xCAT/zvmCPUtils.pm index 7c12ab418..f66ba7513 100644 --- a/perl-xCAT/xCAT/zvmCPUtils.pm +++ b/perl-xCAT/xCAT/zvmCPUtils.pm @@ -2,7 +2,7 @@ #------------------------------------------------------- =head1 - + This is a CP utility plugin for z/VM. =cut @@ -23,14 +23,14 @@ use warnings; Node Returns : UserID Example : my $userID = xCAT::zvmCPUtils->getUserId($node); - + =cut #------------------------------------------------------- sub getUserId { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -38,10 +38,15 @@ sub getUserId { } # Get user ID using VMCP - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; - my @results = split(' ', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; + my $cmd = "$sudo /sbin/vmcp q userid"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @results = split( ' ', $out ); - return ($results[0]); + return ( $results[0] ); } #------------------------------------------------------- @@ -53,14 +58,14 @@ sub getUserId { Node Returns : z/VM host Example : my $host = xCAT::zvmCPUtils->getHost($node); - + =cut #------------------------------------------------------- sub getHost { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -68,8 +73,13 @@ sub getHost { } # Get host using VMCP - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; - my @results = split(' ', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q userid"`; + my $cmd = "$sudo /sbin/vmcp q userid"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @results = split( ' ', $out ); my $host = $results[2]; return ($host); @@ -84,14 +94,14 @@ sub getHost { Node Returns : Privilege class Example : my $class = xCAT::zvmCPUtils->getPrivileges($node); - + =cut #------------------------------------------------------- sub getPrivileges { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -99,10 +109,15 @@ sub getPrivileges { } # Get privilege class - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q priv"`; - my @out = split('\n', $out); - $out[1] = xCAT::zvmUtils->trimStr($out[1]); - $out[2] = xCAT::zvmUtils->trimStr($out[2]); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q priv"`; + my $cmd = "$sudo /sbin/vmcp q priv"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @out = split( '\n', $out ); + $out[1] = xCAT::zvmUtils->trimStr( $out[1] ); + $out[2] = xCAT::zvmUtils->trimStr( $out[2] ); my $str = " $out[1]\n $out[2]\n"; return ($str); @@ -116,15 +131,15 @@ sub getPrivileges { Arguments : User (root or non-root) Node Returns : Memory - Example : my $memory = xCAT::zvmCPUtils->getMemory($node); - + Example : my $memory = xCAT::zvmCPUtils->getMemory($user, $node); + =cut #------------------------------------------------------- sub getMemory { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -132,10 +147,15 @@ sub getMemory { } # Get memory - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual storage"`; - my @out = split(' ', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual storage"`; + my $cmd = "$sudo /sbin/vmcp q virtual storage"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + my @out = split( ' ', $out ); - return (xCAT::zvmUtils->trimStr($out[2])); + return ( xCAT::zvmUtils->trimStr( $out[2] ) ); } @@ -148,15 +168,15 @@ sub getMemory { Arguments : User (root or non-root) Node Returns : Processor(s) - Example : my $proc = xCAT::zvmCPUtils->getCpu($node); - + Example : my $proc = xCAT::zvmCPUtils->getCpu( $user, $node ); + =cut #------------------------------------------------------- sub getCpu { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -164,7 +184,12 @@ sub getCpu { } # Get processors - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual cpus"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual cpus"`; + my $cmd = "$sudo /sbin/vmcp q virtual cpus"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } my $str = xCAT::zvmUtils->tabStr($out); return ($str); @@ -179,14 +204,14 @@ sub getCpu { Node Returns : NIC(s) Example : my $nic = xCAT::zvmCPUtils->getNic($node); - + =cut #------------------------------------------------------- sub getNic { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -194,7 +219,12 @@ sub getNic { } # Get NIC - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual nic"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual nic"`; + my $cmd = "$sudo /sbin/vmcp q virtual nic"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } my $str = xCAT::zvmUtils->tabStr($out); return ($str); @@ -209,14 +239,38 @@ sub getNic { Node Returns : Network names Example : my $lans = xCAT::zvmCPUtils->getNetworkNames($user, $node); - + =cut #------------------------------------------------------- sub getNetworkNames { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + my $hcp; + my $hcpUserId; + # Get node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # Get zHCP. If not propVals then the node is probably a zhcp + if (!$propVals) { + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $node); + $hcpUserId =~ tr/a-z/A-Z/; + $hcp = $node; + } else { + $hcp = $propVals->{'hcp'}; + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + } + + if ( !$hcpUserId ) { + xCAT::zvmUtils->printSyslog("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + return ("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + } my $sudo = "sudo"; if ($user eq "root") { @@ -224,28 +278,128 @@ sub getNetworkNames { } # Get network names - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan | egrep 'LAN|VSWITCH'"`; - my @lines = split('\n', $out); - my @parms; + my $out; + my $outmsg; my $names; - foreach (@lines) { + my $count; + my @parms; + my @lines; + my $switchNamesFound = ""; + my $start = 0; + my $switchName; + my $lanType; + my $lanName; + my $lanOwner; + my $rc; - # Trim output - $_ = xCAT::zvmUtils->trimStr($_); - @parms = split(' ', $_); + #If this is zhcp then use SMAPI calls to get network names; otherwise use q lan + if (!$propVals) { + my $retStr; + # First get the VSWITCH information, saving the switch name + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name=*'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name=*'"`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_Vswitch_Query_Extended. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + return ($out); + } + @lines = split( '\n', $out ); + # Just get the lines with "switch_name" + @lines = grep /switch_name/, @lines; + $count = @lines; - # Get the network name - if ($parms[0] eq "LAN") { + # Grep output is 1 line for each item + for (my $i=0; $i < $count; $i++) { + @parms = split( ' ', $lines[$i]); + $switchName = $parms[1]; + $switchNamesFound .= "+" . $switchName . "+"; + $names .= "VSWITCH" . " " . "SYSTEM" . " " . $switchName . "\n"; + } - # Determine if this network is a hipersocket - # Only hipersocket guest LANs are supported - if ($_ =~ m/Type: HIPERS/i) { - $names .= $parms[0] . ":HIPERS " . $parms[1] . " " . $parms[2] . "\n"; + # Next get the LAN information, skipping switches we have + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n '*' -o '*'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n '*' -o '*'"`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_LAN_Query. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + return ($out); + } + @lines = split( '\n', $out ); + # Just get the lines with "Name:|Owner:|LAN type:" + @lines = grep /Name:|Owner:|LAN type:/, @lines; + $count = @lines; + + # Grep output is 3 lines for each item + for (my $i=0; $i < ($count/3); $i++) { + @parms = split( ' ', $lines[$start]); + $lanName = $parms[1]; + $start++; + @parms = split(' ', $lines[$start]); + $lanOwner = $parms[1]; + $start++; + if ( $lines[$start] =~ m/QDIO/i ) { + $lanType = ":QDIO "; } else { - $names .= $parms[0] . ":QDIO " . $parms[1] . " " . $parms[2] . "\n"; + $lanType = ":HIPERS "; + } + $start++; + # Skip any lanNames that were found by VSWITCH query + my $search = "+$lanName+"; + if (index($switchNamesFound, $search) == -1) { + $names .= "LAN" . $lanType . $lanOwner . " " . $lanName . "\n"; + } + } + # use vmcp q lan if this is not the zhcp node + } else { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan | egrep 'LAN|VSWITCH'"`; + my $cmd = $sudo . ' /sbin/vmcp q lan'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i 'LAN|VSWITCH'`; + @lines = split( '\n', $out ); + + foreach (@lines) { + + # Trim output + $_ = xCAT::zvmUtils->trimStr($_); + @parms = split( ' ', $_ ); + + # sample output from q lan + # LAN SYSTEM GLAN1 Type: QDIO Connected: 1 Maxconn: INFINITE + # VSWITCH SYSTEM XCATVSW1 Type: QDIO Connected: 2 Maxconn: INFINITE + # Get the network name + if ( $parms[0] eq "LAN" ) { + + # Determine if this network is a hipersocket + # Only hipersocket guest LANs are supported + if ( $_ =~ m/Type: HIPERS/i ) { + $names .= $parms[0] . ":HIPERS " . $parms[1] . " " . $parms[2] . "\n"; + } else { + $names .= $parms[0] . ":QDIO " . $parms[1] . " " . $parms[2] . "\n"; + } + } elsif ( $parms[0] eq "VSWITCH" ) { + $names .= $parms[0] . " " . $parms[1] . " " . $parms[2] . "\n"; } - } elsif ($parms[0] eq "VSWITCH") { - $names .= $parms[0] . " " . $parms[1] . " " . $parms[2] . "\n"; } } @@ -261,14 +415,14 @@ sub getNetworkNames { Node Returns : Array of networks names Example : my @networks = xCAT::zvmCPUtils->getNetworkNamesArray($user, $node); - + =cut #------------------------------------------------------- sub getNetworkNamesArray { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my @networks; my %netHash; @@ -278,25 +432,29 @@ sub getNetworkNamesArray { } # Get the networks used by the node - my $out = `ssh $user\@$node "$sudo /sbin/vmcp q v nic" | egrep -i "VSWITCH|LAN"`; - my @lines = split('\n', $out); + #my $out = `ssh $user\@$node "$sudo /sbin/vmcp q v nic" | egrep -i "VSWITCH|LAN"`; + my $cmd = $sudo . ' /sbin/vmcp q v nic'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i 'VSWITCH|LAN'`; + my @lines = split( '\n', $out ); # Loop through each line my $line; my @words; my $name; - foreach (@lines) { - + foreach(@lines) { # Get network name # Line should contain: MAC: 02-00-01-00-00-12 VSWITCH: SYSTEM VSW1 - $line = xCAT::zvmUtils->trimStr($_); - @words = split(' ', $line); + $line = xCAT::zvmUtils->trimStr( $_ ); + @words = split( ' ', $line ); if (@words) { - $name = xCAT::zvmUtils->trimStr($words[4]); + $name = xCAT::zvmUtils->trimStr( $words[4] ); # If network is not 'None' if ($name ne 'None') { - # Save network $netHash{$name} = 1; } @@ -304,7 +462,7 @@ sub getNetworkNamesArray { } # Push networks into array - foreach $name (keys %netHash) { + foreach $name ( keys %netHash ) { push(@networks, $name); } @@ -321,26 +479,110 @@ sub getNetworkNamesArray { Network name Returns : Network configuration Example : my $config = xCAT::zvmCPUtils->getNetwork($node, $netName); - + =cut #------------------------------------------------------- sub getNetwork { # Get inputs - my ($class, $user, $node, $netName) = @_; + my ( $class, $user, $node, $netName, $netType ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - # Get network info + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + my $hcp; + my $hcpUserId; my $out; + my $retStr; + my $rc; + + my $netNameQuery = $netName; if ($netName eq "all") { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan"`; + $netNameQuery = "*"; + } + + xCAT::zvmUtils->printSyslog("getNetwork for NetType:$netType NetName:$netName NetNameQuery:$netNameQuery"); + + # Get node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # Get zHCP. If not propVals then the node is probably a zhcp + if (!$propVals) { + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $node); + $hcpUserId =~ tr/a-z/A-Z/; + $hcp = $node; } else { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; + $hcp = $propVals->{'hcp'}; + $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + } + + if ( !$hcpUserId ) { + xCAT::zvmUtils->printSyslog("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + return ("$node: (Error) Missing node HCP. Userid: $hcpUserId"); + } + + #If this is zhcp then use SMAPI calls to get network names; otherwise use q lan + if (!$propVals) { + # If this is a vswitch, use: Virtual_Network_Vswitch_Query_Extended + if (index($netType, "VSWITCH") >= 0) { + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name='$netNameQuery -k 'VEPA_STATUS=YES'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Query_Extended -T $hcpUserId -k 'switch_name='$netNameQuery -k 'VEPA_STATUS=YES'"`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_Vswitch_Query_Extended. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + } + } else { + # Get the LAN information + xCAT::zvmUtils->printSyslog("ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n $netNameQuery -o '*'"); + $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_LAN_Query -T $hcpUserId -n $netNameQuery -o '*' "`; + $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) unable to communicate with the zhcp system: $hcpUserId"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } elsif ($rc) { + $retStr = "(Error) Error trying to execute smcli Virtual_Network_LAN_Query. rc:$rc Output:$out"; + xCAT::zvmUtils->printSyslog($retStr); + return $retStr; + } + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog($out); + } + } + # if not zhcp use q lan output + } else { + # Get network info. + if ( $netName eq "all" ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan"`; + my $cmd = "$sudo /sbin/vmcp q lan"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + } else { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; + my $cmd = "$sudo /sbin/vmcp q lan $netName"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + } } return ($out); @@ -355,14 +597,14 @@ sub getNetwork { Node Returns : Disk(s) Example : my $storage = xCAT::zvmCPUtils->getDisks($node); - + =cut #------------------------------------------------------- sub getDisks { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -370,7 +612,12 @@ sub getDisks { } # Get disks - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual dasd"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q virtual dasd"`; + my $cmd = "$sudo /sbin/vmcp q virtual dasd"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } my $str = xCAT::zvmUtils->tabStr($out); return ($str); @@ -383,16 +630,17 @@ sub getDisks { Description : Load Linux VMCP module on a given node Arguments : User (root or non-root) Node - Returns : Nothing + Returns : Nothing, or error string + Example : xCAT::zvmCPUtils->loadVmcp($node); - + =cut #------------------------------------------------------- sub loadVmcp { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -400,8 +648,8 @@ sub loadVmcp { } # Load Linux VMCP module - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/modprobe vmcp"`; - return; + my $cmd = "$sudo /sbin/modprobe vmcp"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER } #------------------------------------------------------- @@ -413,14 +661,14 @@ sub loadVmcp { Node Returns : VSwitch ID(s) Example : my @vswitch = xCAT::zvmCPUtils->getVswitchId($node); - + =cut #------------------------------------------------------- sub getVswitchId { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -428,13 +676,19 @@ sub getVswitchId { } # Get VSwitch - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v nic" | grep "VSWITCH"`; - my @lines = split('\n', $out); + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v nic" | grep "VSWITCH"`; + my $cmd = $sudo . ' /sbin/vmcp q v nic'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i 'VSWITCH'`; + my @lines = split( '\n', $out ); my @parms; my @vswitch; foreach (@lines) { - @parms = split(' ', $_); - push(@vswitch, $parms[4]); + @parms = split( ' ', $_ ); + push( @vswitch, $parms[4] ); } return @vswitch; @@ -444,21 +698,23 @@ sub getVswitchId { =head3 grantVSwitch - Description : Grant VSwitch access for a given userID + Description : Grant VSwitch access for a given userID Arguments : User (root or non-root) zHCP User ID VSWITCH ID + lan port type ( or '') + vlan id (or '') Returns : Operation results (Done/Failed) - Example : my $out = xCAT::zvmCPUtils->grantVswitch($callback, $hcp, $userId, $vswitchId); - + Example : my $out = xCAT::zvmCPUtils->grantVswitch($callback, $hcp, $userId, $vswitchId, [$vlanporttype, $vlanid]); + =cut #------------------------------------------------------- sub grantVSwitch { # Get inputs - my ($class, $callback, $user, $hcp, $userId, $vswitchId) = @_; + my ( $class, $callback, $user, $hcp, $userId, $vswitchId, $vlanporttype, $vlanid ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -468,23 +724,81 @@ sub grantVSwitch { $sudo = ""; } - # Use SMAPI EXEC - my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId"`; - xCAT::zvmUtils->printSyslog("grantVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId"); + my $lanidparm = ''; + if ($vlanporttype ne "") { + $lanidparm = " -k \'port_type=" + $vlanporttype + "\'"; + } + if ($vlanid ne "") { + $lanidparm += " -k \'user_vlan_id=" + $vlanid + "\'"; + } + + # Use SMAPI EXEC, use new extended SMAPI vs old one + # my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId -u 2"`; + # xCAT::zvmUtils->printSyslog("grantVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -n $vswitchId -I $userId -u 2"); + xCAT::zvmUtils->printSyslog( "grantVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set_Extended -T SYSTEM -k 'switch_name='$vswitchId -k 'grant_userid='$userId -k 'persist=YES '$lanidparm" ); + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set_Extended -T SYSTEM -k 'switch_name='$vswitchId -k 'grant_userid='$userId -k 'persist=YES' $lanidparm"`; + $out = xCAT::zvmUtils->trimStr($out); # If return string contains 'Done' - Operation was successful my $retStr; - if ($out =~ m/Done/i) { + if ( $out =~ m/Done/i ) { $retStr = "Done\n"; } else { - $retStr = "Failed\n"; + $retStr = "Failed " . "Error output: $out\n"; + xCAT::zvmUtils->printSyslog("Error output: $out"); return $retStr; } return $retStr; } +=head3 revokeVSwitch + + Description : Revoke VSwitch access for a given userID + Arguments : User (root or non-root) + zHCP + User ID + VSWITCH ID + Returns : Operation results (Done/Failed) + Example : my $out = xCAT::zvmCPUtils->revokeVswitch($callback, $hcp, $userId, $vswitchId); + +=cut + +#------------------------------------------------------- +sub revokeVSwitch { + + # Get inputs + my ( $class, $callback, $user, $hcp, $userId, $vswitchId ) = @_; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Use SMAPI EXEC, use new extended SMAPI + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Virtual_Network_Vswitch_Set_Extended -T SYSTEM -k 'switch_name='$vswitchId -k 'revoke_userid='$userId -k 'persist=YES'"`; + xCAT::zvmUtils->printSyslog("revokeVSwitch- ssh $user\@$hcp $sudo $dir/smcli Virtual_Network_Vswitch_Set -T SYSTEM -k 'switch_name='$vswitchId -k 'revoke_userid='$userId -k 'persist=YES'"); + $out = xCAT::zvmUtils->trimStr($out); + + xCAT::zvmUtils->printSyslog($out); + + + # If return string contains 'Done' - Operation was successful + my $retStr; + if ( $out =~ m/Done/i ) { + $retStr = "Done\n"; + } else { + $retStr = "Failed " . "Error output: $out\n"; + xCAT::zvmUtils->printSyslog("Error output: $out"); + return $retStr; + } + + return $retStr; +} #------------------------------------------------------- =head3 flashCopy @@ -498,14 +812,14 @@ sub grantVSwitch { Target address Returns : Operation results (Done/Failed) Example : my $results = xCAT::zvmCPUtils->flashCopy($user, $hcp, $srcAddr, $targetAddr); - + =cut #------------------------------------------------------- sub flashCopy { # Get inputs - my ($class, $user, $hcp, $srcAddr, $tgtAddr) = @_; + my ( $class, $user, $hcp, $srcAddr, $tgtAddr ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -516,17 +830,20 @@ sub flashCopy { } # Flash copy using CP + + xCAT::zvmUtils->printSyslog("CP FlashCopy- ssh $user\@$hcp $sudo /sbin/vmcp flashcopy $srcAddr 0 end to $tgtAddr 0 end synchronous"); my $out = `ssh $user\@$hcp "$sudo /sbin/vmcp flashcopy $srcAddr 0 end to $tgtAddr 0 end synchronous"`; $out = xCAT::zvmUtils->trimStr($out); # If return string contains 'Command complete' - Operation was successful my $retStr = ""; - if ($out =~ m/Command complete/i) { + if ( $out =~ m/Command complete/i ) { $retStr = "Copying data via CP FLASHCOPY... Done\n"; } else { $out = xCAT::zvmUtils->tabStr($out); $retStr = "Copying data via CP FLASHCOPY... Failed\n$out"; + xCAT::zvmUtils->printSyslog("$out"); } return $retStr; @@ -545,18 +862,20 @@ sub flashCopy { Target address Returns : Operation results (Done/Failed) Example : my $results = xCAT::zvmCPUtils->smapiFlashCopy($user, $node, $srcId, $srcAddr, $tgtId, $targetAddr); - + =cut #------------------------------------------------------- sub smapiFlashCopy { # Get inputs - my ($class, $user, $hcp, $srcId, $srcAddr, $tgtId, $tgtAddr) = @_; + my ( $class, $user, $hcp, $srcId, $srcAddr, $tgtId, $tgtAddr ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; + my $retStr = ""; + my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; @@ -565,19 +884,23 @@ sub smapiFlashCopy { my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); # Use SMAPI EXEC to flash copy - my $cmd = '\"' . "CMD=FLASHCOPY $srcId $srcAddr 0 END $tgtId $tgtAddr 0 END" . '\"'; - my $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c $cmd"`; + my $cmd = '\"' . "CMD=FLASHCOPY $srcId $srcAddr 0 END $tgtId $tgtAddr 0 END SYNC" . '\"'; xCAT::zvmUtils->printSyslog("smapiFlashCopy- ssh $user\@$hcp $sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c $cmd"); + my $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c $cmd"`; + my $rc = $? >> 8; + if ($rc == 255) { + $retStr = "(Error) Failed to communicate with the zhcp system: $hcp output:$out"; + return $retStr; + } $out = xCAT::zvmUtils->trimStr($out); # If return string contains 'Done' - Operation was successful - my $retStr = ""; - if ($out =~ m/Done/i) { + if (( $out =~ m/Done/i ) or (($out =~ m/Return Code: 592/i) and ($out =~m/Reason Code: 8888/i))) { $retStr = "Copying data via SMAPI FLASHCOPY... Done\n"; } else { $out = xCAT::zvmUtils->tabStr($out); - $retStr = "Copying data via SMAPI FLASHCOPY... $out"; + $retStr = "An Error occurred copying data via SMAPI FLASHCOPY... $out"; } return $retStr; @@ -593,44 +916,145 @@ sub smapiFlashCopy { UserID to receive file Source file Target file to be created by punch (e.g. sles.parm) - Options, e.g. -t (Convert EBCDIC to ASCII) - Returns : Operation results (Done/Failed) - Example : my $rc = xCAT::zvmCPUtils->punch2Reader($hcp, $userId, $srcFile, $tgtFile, $options); - + Options, e.g. -t (Convert EBCDIC to ASCII) or "" for no options + SPOOL file class to be assigned to the punched file + "" means that the current class is to be used + anything else is the class to set on the punched file + Returns : Operation results ("Done" or "Failed" with additional info) + Example : my $response = xCAT::zvmCPUtils->punch2Reader( $user, $hcp, $userId, $srcFile, + $tgtFile, $options, $spoolClass ); + =cut #------------------------------------------------------- sub punch2Reader { - my ($class, $user, $hcp, $userId, $srcFile, $tgtFile, $options) = @_; + my ( $class, $user, $hcp, $userId, $srcFile, $tgtFile, $options, $spoolClass ) = @_; + + my $out = ""; + my $punched = 0; + my $rc = 0; + my $subResp = ""; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } + my $punchTarget = ""; + if ( $spoolClass eq "" ) { + $punchTarget = "-u $userId"; + } + # Get source node OS my $os = xCAT::zvmUtils->getOsVersion($user, $hcp); - # Punch to reader # VMUR located in different directories on RHEL and SLES - my $out; - if ($os =~ m/sles10/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmur punch $options -u $userId -r $srcFile -N $tgtFile"`; - } elsif ($os =~ m/rhel/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /usr/sbin/vmur punch $options -u $userId -r $srcFile -N $tgtFile"`; + my $vmur; + if ( $os =~ m/sles10/i ) { + $vmur = "/sbin/vmur"; } else { - $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /usr/sbin/vmur punch $options -u $userId -r $srcFile -N $tgtFile"`; + $vmur = "/usr/sbin/vmur"; } - # If punch is successful -- Look for this string - my $searchStr = "created and transferred"; - if (!($out =~ m/$searchStr/i)) { - $out = "Failed\n"; - } else { - $out = "Done\n"; + # Punch the file. A loop is done in case the punch is currently in use. + my $done = 0; + my $maxTries = 12; # 12 attempts with 15 second waits for punch to become available + my $maxTime = $maxTries / 4; # Total time: 3 minutes + for ( my $i=0; ( $i < $maxTries and !$done ); $i++ ) { + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo $vmur punch $options $punchTarget -r $srcFile -N $tgtFile" 2>&1`; + $rc = $? >> 8; + if ( $rc == 255 ) { + xCAT::zvmUtils->printSyslog( "(Error) In punch2Reader(), SSH communication with $hcp failed for command: $vmur punch" ); + $subResp = "Failed to communicate with the zHCP system: $hcp"; + $done = 1; + } elsif ( $out =~ m/A concurrent instance of vmur is already active/i ) { + # Recoverable error: retry the command after a delay + xCAT::zvmUtils->printSyslog( "punch2Reader() Punch in use on $hcp, retrying in 15 seconds" ); + $subResp = "Failed, Punch in use on $hcp for over $maxTime minutes."; # Assume it will never become available + sleep( 15 ); + } elsif ( $rc == 0 ) { + # Punch appears successful + $subResp = ''; + $punched = 1; + $done = 1; + } else { + # Punch failed for other than currently in use. + chomp( $out ); + $subResp = "Failed, punch info: '$out'"; + xCAT::zvmUtils->printSyslog( "punch2Reader() Failed punching $srcFile to $userId from $hcp, rc: $rc, out: '$out'" ); + $done = 1; + } } - return $out; + # If we successfully punched the file, we may have some final steps. + if ( $punched == 1 ) { + # If a spool class was specified then we punched to the zHCP's reader instead of + # punching directly to the target virtual machine and now we need to transfer it + # to the target virtual machine. Otherwise, we are done. + if ( $spoolClass ne "" ) { + # Split the punch response line so we can access the spoolid of the created punch file. + # e.g. Reader file with spoolid 0002 created. + my @words = split( / /, $out ); + my $spoolId = $words[4]; + + # Change the class of the spool file that we created so that it is the requested class. + # We could not spool the punch to the desired class because of the possibility of + # multiple threads using the punch that require different classes. Thus, we need to + # change the individual punch files. + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo vmcp change rdr $spoolId class $spoolClass" 2>&1`; + $rc = $? >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. Nothing to do, file remains in zHCP's reader. + xCAT::zvmUtils->printSyslog( "(Error) In punch2Reader(), SSH communication with $hcp failed for command: vmcp change rdr $spoolId class $spoolClass" ); + $subResp = "Failed to communicate with the zHCP system to change the reader file $spoolId to class $spoolClass: $hcp"; + } elsif ( $rc != 0 ) { + # Generic failure of transfer command. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "punch2Reader() Change of spool file $spoolId on $hcp to class $spoolClass failed, rc: $rc, out: $out" ); + $subResp = "Failed, change rdr info rc: $rc, out: '$out'"; + } + + # If we did not have any errors, then transfer the spoolfile to the targer user's reader. + if ( $subResp eq "" ) { + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo vmcp transfer rdr $spoolId to $userId" 2>&1`; + $rc = $? >> 8; + if ( $rc == 0 ) { + # Successful transfer + $subResp = "Done"; + } elsif ( $rc == 255 ) { + # SSH failure to communicate with zHCP. Nothing to do, file remains in zHCP's reader. + xCAT::zvmUtils->printSyslog( "(Error) In punch2Reader(), SSH communication with $hcp failed for command: vmcp transfer rdr $spoolId to $userId" ); + $subResp = "Failed to communicate with the zHCP system to transfer reader file $spoolId: $hcp"; + } else { + # Generic failure of transfer command. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "punch2Reader() Transfer of spool file $spoolId from $hcp to $userId failed, rc: $rc, out: $out" ); + $subResp = "Failed, transfer info rc: $rc, out: '$out'"; + } + } + + # If we had any error then attempt to purge the file from the zHCP machine. + if ( $subResp ne "Done" ) { + $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo vmcp purge reader $spoolId"`; + $rc = $? >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. Nothing to do, file remains in zHCP's reader. + xCAT::zvmUtils->printSyslog( "(Error) In punch2Reader(), SSH communication with $hcp failed for command: vmcp purge reader $spoolId" ); + $subResp = $subResp. "\nFailed to communicate with the zHCP system to purge reader file $spoolId: $hcp"; + } elsif ( $rc != 0 ) { + # Any failure is bad and unrecoverable. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "punch2Reader() Unable to purge spool file $spoolId on $hcp, rc: $rc, out: $out" ); + $subResp = $subResp . "\nUnable to purge reader rc: $rc, out: '$out'"; + } + } + } else { + # Successful punch directly to the target virtual machine. + $subResp = "Done"; + } + } + + return $subResp; } #------------------------------------------------------- @@ -643,12 +1067,12 @@ sub punch2Reader { UserID to purge reader Returns : Nothing Example : my $rc = xCAT::zvmCPUtils->purgeReader($hcp, $userId); - + =cut #------------------------------------------------------- sub purgeReader { - my ($class, $user, $hcp, $userId) = @_; + my ( $class, $user, $hcp, $userId ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -662,13 +1086,11 @@ sub purgeReader { my $out; if (xCAT::zvmUtils->smapi4xcat($user, $hcp)) { - # Use SMAPI EXEC to purge reader my $cmd = '\"' . "CMD=PURGE $userId RDR ALL" . '\"'; $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $userId -c $cmd"`; xCAT::zvmUtils->printSyslog("smcli xCAT_Commands_IUO -T $userId -c $cmd"); } else { - # Purge reader using CP $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp purge $userId rdr all"`; xCAT::zvmUtils->printSyslog("/sbin/vmcp purge $userId rdr all"); @@ -688,12 +1110,12 @@ sub purgeReader { UserID to send CP command Returns : Nothing Example : xCAT::zvmCPUtils->sendCPCmd($hcp, $userId, $cmd); - + =cut #------------------------------------------------------- sub sendCPCmd { - my ($class, $user, $hcp, $userId, $cmd) = @_; + my ( $class, $user, $hcp, $userId, $cmd ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -707,13 +1129,11 @@ sub sendCPCmd { my $out; if (xCAT::zvmUtils->smapi4xcat($user, $hcp)) { - # Use SMAPI EXEC to send command $cmd = '\"' . "CMD=SEND CP $userId " . uc($cmd) . '\"'; $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $userId -c $cmd"`; xCAT::zvmUtils->printSyslog("smcli xCAT_Commands_IUO -T $userId -c $cmd"); } else { - # Send CP command to given user $out = `ssh $user\@$hcp "$sudo /sbin/vmcp send cp $userId $cmd"`; xCAT::zvmUtils->printSyslog("/sbin/vmcp send cp $userId $cmd"); @@ -734,13 +1154,14 @@ sub sendCPCmd { Returns : 2 - Layer 2 3 - Layer 3 -1 - Failed to get network layer + error string if SSH fails Example : my $layer = xCAT::zvmCPUtils->getNetworkLayer($node); - + =cut #------------------------------------------------------- sub getNetworkLayer { - my ($class, $user, $node, $netName) = @_; + my ( $class, $user, $node, $netName ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -748,23 +1169,28 @@ sub getNetworkLayer { } # Exit if the network name is not given - if (!$netName) { + if ( !$netName ) { return -1; } # Get network type (Layer 2 or 3) - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; - if (!$out) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q lan $netName"`; + my $cmd = "$sudo /sbin/vmcp q lan $netName"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput($out) == -1) { + return $out; + } + if ( !$out ) { return -1; } # Go through each line - my $layer = 3; # Default to layer 3 - my @lines = split('\n', $out); + my $layer = 3; # Default to layer 3 + my @lines = split( '\n', $out ); foreach (@lines) { # If the line contains ETHERNET, then it is a layer 2 network - if ($_ =~ m/ETHERNET/i) { + if ( $_ =~ m/ETHERNET/i ) { $layer = 2; } } @@ -780,14 +1206,14 @@ sub getNetworkLayer { Arguments : User (root or non-root) zHCP Name of network - Returns : Network type (VSWITCH/HIPERS/QDIO) + Returns : Network type (VSWITCH/HIPERS/QDIO) or string containing (Error)... Example : my $netType = xCAT::zvmCPUtils->getNetworkType($hcp, $netName); - + =cut #------------------------------------------------------- sub getNetworkType { - my ($class, $user, $hcp, $netName) = @_; + my ( $class, $user, $hcp, $netName ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -795,25 +1221,33 @@ sub getNetworkType { } # Get network details - my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q lan $netName" | grep "Type"`; + my $outmsg; + my $rc; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q lan $netName"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo /sbin/vmcp q lan $netName\"", $hcp, "getNetworkType", $out ); + if ($rc != 0) { + return $outmsg; + } + + $out = `echo "$out" | egrep -a 'Type'`; # Go through each line and determine network type - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); my $netType = ""; foreach (@lines) { # Virtual switch - if ($_ =~ m/VSWITCH/i) { + if ( $_ =~ m/VSWITCH/i ) { $netType = "VSWITCH"; } # HiperSocket guest LAN - elsif ($_ =~ m/HIPERS/i) { + elsif ( $_ =~ m/HIPERS/i ) { $netType = "HIPERS"; } # QDIO guest LAN - elsif ($_ =~ m/QDIO/i) { + elsif ( $_ =~ m/QDIO/i ) { $netType = "QDIO"; } } @@ -828,16 +1262,16 @@ sub getNetworkType { Description : Add processor(s) to given node Arguments : User (root or non-root) Node - Returns : Nothing + Returns : Nothing or error string if failure Example : my $out = xCAT::zvmCPUtils->defineCpu($node, $addr, $type); - + =cut #------------------------------------------------------- sub defineCpu { # Get inputs - my ($class, $user, $node, $addr, $type) = @_; + my ( $class, $user, $node, $addr, $type ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -845,7 +1279,34 @@ sub defineCpu { } # Define processor(s) - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp define cpu $addr type $type"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp define cpu $addr type $type"`; + my $cmd = "$sudo /sbin/vmcp define cpu $addr type $type"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return ($out); } + +#------------------------------------------------------- + +=head3 getIplTime + + Description : Get the IPL time + Arguments : User (root or non-root) + zHCP + Returns : IPL time + Example : my $out = xCAT::zvmCPUtils->getIplTime($user, $hcp); + +=cut + +#------------------------------------------------------- +sub getIplTime { + my ($class, $user, $hcp) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q cplevel"`; + return ((split("\n", $out))[2]); +} diff --git a/perl-xCAT/xCAT/zvmMsgs.pm b/perl-xCAT/xCAT/zvmMsgs.pm new file mode 100644 index 000000000..e36c4cfe7 --- /dev/null +++ b/perl-xCAT/xCAT/zvmMsgs.pm @@ -0,0 +1,1397 @@ +# IBM(c) 2016 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + This is a message utility plugin for z/VM. + +=cut + +#------------------------------------------------------- +package xCAT::zvmMsgs; +use Text::Wrap; +use strict; +use warnings; +1; + + +# Messages +# Severity strings +my @sevInfo = ( '', # No header + 'Bypassing test', # Bypassing message + 'Info', # Information message + 'unknown', # Unknown severity + 'Warning', # Warning message + 'Error' # Error message + ); + +# Hash of message ids. Message ids should be in uppercase. 'ALL' should not +# be used as a message id as this indicates that all messages are wanted. +# For each message id there is another hash with additional info: +# severity - Severity of message and indicate whether it gets a header to the +# message text (e.g. "Error (IVP:MAINT01) " ) +# 0 - no message header, unrated output +# 1 - bypass message used by automated tests +# 2 - information message with "Info" header +# 3 - unknown message severity +# 4 - warning message with "Warning" header +# 5 - error message with "Error" header +# recAction - Recommended action: +# 0 - non-fatal message, continue processing +# 1 - fatal message, end further processing +# explain - Further explanation of the message. +# subTab - (optional) Indicates the tabbing characters to use. This is +# used to control whether subsequent lines of a message are +# indented. The default is '\t', to indent the lines. +# sysAct - System action to be performed. Normally, this is not specified +# because the severity generates a default system action. +# userResp - Suggested user response. + +#****************************************************************************** +# verifynode messages +#****************************************************************************** +my %verifyMsgs = ( + 'FATAL_DEFAULTS' => + { + 'sysAct' => 'No further verification will be performed.' + }, + 'NONFATAL_DEFAULTS' => + { + 'sysAct' => 'Verification continues.' + }, + 'GENERIC_RESPONSE' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + }, + 'GENERIC_RESPONSE_NOINDENT' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + 'subTab' => '', + }, + 'CLNUP01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to remove the temporary directory, %s, from the'. + 'from the compute node system at %s. '. + 'The following command failed: %s with rc: %s, out: %s', + 'explain' => 'The IVP created a temporary directory on the target system. '. + 'It was unable to remove the directory when it was finished with it. '. + 'The message indicate the command, return code and output of the command.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. You may want '. + 'to access the system and remove the directory.', + }, + 'DFLT01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '%s so the function will use \'%s\' as the default value. %s', + 'explain' => 'A value was not specified so the function will use the indicated default.', + 'userResp' => 'You can specify the missing value in order to avoid this message in the future. ' . + 'If the value is acceptable then you can ingnore this message.', + }, + 'DRIV01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'The driver script, %s, failed, rc: %s, out: %s', + 'explain' => 'The driver script failed with the indicated return code and output. '. + 'The IVP is unable to run the full installation verification test.', + 'sysAct' => 'IVP processing stops.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the IVP after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'DRIV02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The driver script, %s, did not specify the z/VM host node information in the %s property', + 'explain' => 'The driver script did not specify the z/VM host node property. '. + 'The IVP will default to notifying the user on the system where the '. + 'xCAT management node runs.', + 'userResp' => 'Determine why the property was not defined in OpenStack and correct the error. Run the IVP after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'DRIV03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The ZHCP agent related to the host, \'%s\', could not be determined from the xCAT node information.', + 'explain' => 'The driver script specified a z/VM host. However, the IVP was not able to determine the ZHCP '. + 'agent associated with the host.', + 'sysAct' => 'IVP processing continues. The IVP will attempt to determine the ZHCP agent using some other information.', + 'userResp' => 'Verify that the host node in xCAT has an hcp property and that hosttype is \'zvm\'. '. + 'Run the IVP after you have corected the error.', + }, + 'GNRL01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'An unexpected command failure occurred, cmd: \'%s\', rc: %s, out: %s', + 'explain' => 'An unexpected failure was encountered during processing. The command that '. + 'failed is shown along with the return code and output of the command.', + 'sysAct' => 'This failure will cause some processing to be bypassed but the IVP run will continue.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the IVP after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'GNRL02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'An error occurred attempting to punch \'%s\' from %s to user %s on %s. '. + 'The file remains on the source system as \'%s\'. Output from the punch invocation: %s', + 'explain' => 'An unexpected failure occurred while attempting to punch a file.', + 'sysAct' => 'An attempt will be made to remove the file but this may fail. '. + 'The IVP will try to send the file to an alternate user and will indicate if this was successful. '. + 'This failure may prevent the IVP from sending the log file to the notify user on the target system.', + 'userResp' => 'The original log file exists on the xCAT management node in the /var/log/xcat/ivp directory. '. + 'You may access it on that system if you did not receive the file on an alternate userid and system.', + }, + 'GNRL04' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'An unexpected command failure occurred, cmd: \'%s\', rc: %s, out: %s', + 'explain' => 'An unexpected failure was encountered during processing. The command that '. + 'failed is shown along with the return code and output of the command.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the command again after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'GOSL01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The command \'%s\' was sent to %s to determine the OpenStack level but '. + 'did not return an expected response. Instead it returned rc: %s and out: %s.', + 'explain' => 'The value returned by the command was not an expected value. '. + 'This is expected to be a single word, e.g. \'14.0.2\', with the '. + 'components of the version separated by a period. There should be '. + 'at least two components.', + 'userResp' => 'Determine the reason that the OpenStack level is not a recognized value. '. + 'Run the command again after you have corrected the error. '. + 'Otherwise, consult the support team.', + }, + 'GOSL02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The command \'%s\' was sent to %s to determine the OpenStack level but '. + 'did not return one of the recognized versions. '. + 'Instead, it returned rc: %s and out: %s.', + 'explain' => 'The value returned by the command was not an expected value. '. + 'The expected values are listed in /opt/xcat/openstack.versions.', + 'userResp' => 'Determine the reason that the OpenStack level is not a recognized value. '. + 'Run the command again after you have corrected the error. '. + 'Otherwise, consult the support team.', + }, + 'ID01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'The system attempted to generate a unique IVP id but failed to do so after 10000 attempts.', + 'explain' => 'An IVP id was not specified so the system attempted to generate a unique IVP id. '. + 'This begins with an id of 10 and increments by 1 upto 10010. Unfortunately, '. + 'a unique unused Id was not found in the zvmivp table. You may have a corrupted '. + 'zvmivp table or have somehow filled the table with 10000 IVPs which is highly abnormal.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Determine the cause of the error and correct the error. Run the command again after '. + 'you have corrected the error. Otherwise, consult the support team.', + }, + 'ID02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The IVP id, %s, does not exist. A new IVP will be scheduled using that id.', + 'explain' => 'The specified IVP id was not found in the zvmivp table.', + 'sysAct' => 'Processing continues and a new IVP will be added to the table of scheduled IVPs using the '. + 'specified id.', + 'userResp' => 'If you incorrectly specified the IVP id and did not want to add a new ID, then you should '. + 'remove the IVP that you do not want. '. + 'From the xCAT GUI, go to Help->Verify xCAT to remove the IVP.', + }, + 'ID03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The IVP id does not exist in the zvmivp table: %s', + 'explain' => 'The specified IVP id was not found in the zvmivp table.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the correct id and retry.', + }, + 'MN01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'Could not find an xCAT management node which had an IP address of %s, cmd: \'%s\', rc: %s, out: %s', + 'explain' => 'An xCAT node that represents the xCAT management node could not be found. '. + 'The xCAT management node is not required by the IVP but some processing '. + 'such as default notification of the z/VM host on which the xCAT management '. + 'node runs will not occur.', + 'userResp' => 'If you expected the xCAT management node to exist, please review the '. + 'command that was issued and the return code and output to determine '. + 'why the xCAT management node was not found and correct the issue. '. + 'Run the IVP or other command that you issuedd after you have corrected the issue.', + }, + 'MN02' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'The xCAT management node is not defined as a node in xCAT and the function will not '. + 'be performed.', + 'explain' => 'The issued command requires the existence of the xCAT MN as an xCAT node. '. + 'The requrested processing cannot be performed.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Determine the cause of the error and correct the error.', + }, + 'MN03' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => 'The xCAT management node is not defined as a node so only a basic IVP will be run.', + 'explain' => 'The xCAT MN should exist as an xCAT node. The failure to find the node indicates '. + 'a larger failure. An automated IVP will be performed to verify the general '. + 'functioning of the environment but it will not be added to the zvmivp table. '. + 'We expect the installation to define the xCAT node upon seeing this message '. + 'and that will allow the automated IVP to create default IVPs in the zvmivp table.', + 'sysAct' => 'Processing continues.', + 'userResp' => 'Define the xCAT management node in xCAT.', + }, + 'MSG01' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '%s on %s is unable to receive messages at this time, status: \'%s\'. '. + 'They will not be sent the IVP results message.', + 'explain' => 'The IVP attempted to send a message to the user on the target system '. + 'to notify them of the status of the IVP run. The user was '. + 'unavailable to receive the message. '. + 'Status of \'SSI\' indicates they are logged on another system in the SSI '. + 'cluster while \'DSC\' indicates the machine is running disconnected. '. + 'The userid is defined by the XCAT_notify property in the '. + 'DMSSICNF COPY file or the zvmnotify property in the xCAT site table.', + 'sysAct' => 'Processing continues. The log file from the run was '. + 'sent to the userid as a spool file.', + 'userResp' => 'If the userid is not the userid that you expected to receive the '. + 'IVP status message then correct the XCAT_notify property in '. + 'the DMSSICNF COPY file and recycle SMAPI to pick up the new value or '. + 'as a temporary measure until the next recycle of SMAPI, update the '. + 'zvmnotify property in the xCAT site table.', + }, + 'OPER01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Required operand %s is missing.', + 'explain' => 'The indicated operand is required but missing.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the command with the required operand.', + }, + 'OPER02' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Operand %s has a value of \'%s\' which is not one of the recognized values: %s.', + 'explain' => 'The value for the operand is not recognized.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the command with the correct operand value.', + }, + 'OPER03' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Conflicting operands were specified: %s.', + 'sysAct' => 'Processing terminates.', + 'userResp' => 'Specify the command with the correct operand value.', + }, + 'PERL01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'A perl script error was detected in %s.\n'. + ' Calling parms: %s\n'. + ' Reason: %s', + 'explain' => 'The indicated perl script encountered a perl scripting error '. + 'that prevents it from operating correctly. The script is '. + 'ending the current run. This error message is a restatement of '. + 'a previous message so that you are ensured to see the error.', + 'userResp' => 'Review the previous messages to determine whether the error '. + 'is caused by bad invocation parameters or an error within '. + 'the script. Please report this problem if it is not caused '. + 'by a user error.', + }, + 'PREP01' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Unable to create the %s directory in the system running the xCAT management node. rc: %s, out: %s', + 'explain' => 'The specified directory is used to contain the driver script created '. + 'by the IVP preparation script. This directory exists in the virtual '. + 'server that runs the xCAT management node. The directory '. + 'could not be created.'. + 'The current OpenStack properties cannot be validated. '. + 'A saved version of the driver script from a previous run will be used, if it exists.', + 'userResp' => 'Determine the cause of the error. It could be caused by running the '. + 'verifynode script under a user that does not have root authority. '. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to send the IVP preparation script, %s, to the OpenStack system at %s. '. + 'The script was intended to be used to create a driver script for the IVP '. + 'on the system running the xCAT management node at %s. '. + 'A previous version of the driver script will be used. '. + 'The following command failed: %s with rc: %s, out: %s', + 'explain' => 'The xCAT Management Node was unable to send the IVP preparation script to the '. + 'specified system in order to analyze the OpenStack properties and '. + 'construct the driver script for the next phase of the IVP. The command failed '. + 'with the indicated return code and output. '. + 'The current OpenStack properties cannot be validated. '. + 'A saved version of the driver script from a previous run will be used, if it exists.', + 'userResp' => 'Determine the cause of the error and correct it, if possible.'. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP04' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to retrieve the driver script created by the IVP preparation script, %s, '. + 'from the OpenStack system at %s. '. + 'The driver script was intended to be used to drive the IVP '. + 'on the system running the xCAT management node at %s. '. + 'The following command failed: %s with rc: %s, out: %s', + 'explain' => 'The xCAT Management Node was unable to retrieve the driver script from the '. + 'specified system for the next phase of the IVP. '. + 'A saved version of the driver script from a previous run will be used, it it exists.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. '. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP06' => + { 'severity' => 5, + 'recAction' => 1, + 'text' => 'Unable to determine the OpenStack level of the OpenStack system at %s. '. + 'The full IVP cannot be run.', + 'explain' => 'The xCAT Management Node was unable to level of OpenStack running the in the indicated system. '. + 'This is necessary so that the correct level of analysis code can be run '. + 'to validate the system. '. + 'A saved version of the driver script from a previous run will be used, if it exists.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. If OpenStack '. + 'Nova services are not actively running on the system then please start them '. + 'and reattempt the IVP.'. + 'If you cannot correct the problem then you can download the IVP preparation script '. + 'to the target system and run it as discussed in the Enabling z/VM for OpenStack manual. '. + 'The driver script may then be uploaded to the xCAT management node system and '. + 'moved to the indicated location so that it can be used in a subsequent IVP.', + }, + 'PREP07' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'Unable to reduce the file permissions on %s with command: %s,\nrc: %s, out: %s', + 'explain' => 'The xCAT Management Node was unable to lower the file permission on the indicated file.', + 'userResp' => 'Determine the cause of the error and correct it, if possible. You may want to change the '. + 'permissions on the file.', + }, + 'SITE01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The \'%s\' property from the site table has a value that is not valid. Value: \'%s\', '. + 'bad portion of the value: \'%s\'', + 'explain' => 'The site table contains a property that is used in the IVP processing. The value '. + 'of the property has the following format:'. + "\n". + 'zvmnotify: \'hostnode(userid_on_the_host)\' If additional host/userid combinations are specified '. + 'then they are connected by a semicolon and no blanks are allowed between the '. + 'components that make up the value. '. + 'For example, \'host1(usera);host2(userb)\'', + 'userResp' => 'Correct the error and rerun the IVP. Otherwise, consult the support team.', + }, + 'SITE02' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The \'%s\' property from the site table is missing or has an empty string value.', + 'explain' => 'The site table contains a property that is used in the IVP processing. The property '. + 'is missing from the table or has a value that is an empty string. The \'master\' '. + 'property should specify the IP address of the xCAT management node that matches the value '. + 'specified for the \'ip\' property of the node that represents the xCAT management node.', + 'userResp' => 'Correctly specify the master property in the site table and rerun the IVP. '. + 'Otherwise, consult the support team.', + }, + 'VSTN01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to SSH to %s.', + 'explain' => 'The xCAT management node uses SSH to obtain information '. + 'from the target system or to make changes within Linux on the '. + 'target system. This does not appear to be working. '. + 'This may be due to the system not being unlocked to the xCAT management node for '. + 'communication or a TCP/IP error.', + 'sysAct' => 'The system action depends upon the severity of the problem. '. + 'It will attempt to continue, if possible.', + 'userResp' => 'First, attempt to unlock the target system from the xCAT GUI by selecting the ' . + 'node on the Nodes->Nodes panel and choosing \'unlock\' in ' . + 'the \'Configuration\' pulldown. If that does not work then investigate '. + 'TCP/IP errors or network configuration errors within the target system.', + }, + 'TP01' => + { 'severity' => 5, + 'recAction' => 0, + 'text' => 'The \'%s\' property from the %s table is %s: \'%s\'. A default of \'%s\' will be used instead of that property.', + 'explain' => 'A property in the indicated table has a value that is not valid. '. + 'The default will be used instead. ', + 'userResp' => 'Correctly specify the property in the table.', + }, + 'VA01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'xCAT MN is unable to issue a simple \'pwd\' command on the target system.', + 'explain' => 'The xCAT management node uses SSH to obtain information '. + 'from the target system or to make changes within Linux on the '. + 'target system. The xCAT MN can access the system but is unable to issue '. + 'a simple command.', + 'userResp' => 'Unlock the target node from the xCAT GUI by selecting the ' . + 'node on the Nodes->Nodes panel and choosing \'unlock\' in ' . + 'the \'Configuration\' pulldown. If this does not work then log onto the '. + 'virtual machine to see why simple commands are failing.', + }, + 'VD01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => '%s is not a version supported by xCAT.', + 'explain' => 'The Linux distribution name and version was compared to the list of '. + 'versions that xCAT supports and was not found in the list.', + 'sysAct' => 'Some xCAT functions may be unavailable such as image capture and deploy '. + 'or work incorrectly.', + 'userResp' => 'Either update the OS running in the target node or monitor the xCAT ' . + 'responses to commands directed to the node to ensure that it is doing '. + 'what you desire.', + }, + 'VPF01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to determine the power status of %s, rpower rc: %s, msg: %s', + 'explain' => 'An xCAT rpower command with the stat option was issued to determine '. + 'the power status of the specified node and returned a non-zero return code.', + 'userResp' => 'The return code and the message returned by rpower (shown in the message '. + 'after msg:) should be used to determine the cause of the error. '. + 'Please correct the error and retry the test.', + }, + 'VP02' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => '%s is not logged on.', + 'explain' => 'The virtual machine related to the node is not currently logged on.', + 'userResp' => 'Log the virtual machine on to the z/VM host and retry the verification.', + }, + 'VS05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Linux is not configured for the service: %s.', + 'explain' => 'The specified service should be configured on the target system. '. + 'This ensures that images '. + 'captured from this system will have the service started when the '. + 'new system boots to allow xCAT and/or OpenStack to complete the configuration of the '. + 'system the first time it boots.', + 'userResp' => 'Configure Linux to start service on boot of the server.' + }, + 'VS06' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The %s service is not configured for the following run levels: %s', + 'explain' => 'The specified service should be configured on the target '. + 'system for the specified run levels. This ensures that images '. + 'captured from this system will have the service started when the '. + 'new system boots to allow xCAT and/or OpenStack to complete the configuration of the '. + 'system the first time it boots.', + 'userResp' => 'Configure the service to be \'on\' for indicated run levels.' + }, + 'VX01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to determine the version number of the xcatconf4z that is shipped in the xCAT MN.', + 'explain' => 'The verification code runs xcatconf4z with the \'version\' operand ' . + 'on the target node. The script did not return a version string. ' . + 'This normally indicates that xcatconf4z is a very early version.', + 'userResp' => 'xcatconf4z should be updated to the latest level.', + }, + 'VX02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xcatconf4z on the system is back level. Unable to determine the version installed.', + 'explain' => 'xcatconf4z exists on the system but is very old and does not respond to the version query.', + 'userResp' => 'xcatconf4z should be updated to the latest level.', + }, + 'VX03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xcatconf4z on the system is back level at %s. It should be at %s.', + 'explain' => 'The xcatconf4z on that target node is compared to the one shipped with ' . + 'the xCAT MN. The xcatconf4z should be at the same level as the one ' . + 'on xCAT MN or at a later level.', + 'userResp' => 'xcatconf4z should be updated to the latest level.' + }, + 'VX04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xcatconf4z is not set up to receive configuration directive files from its reader.', + 'explain' => 'The xcatconf4z script reads configuration directives sent as reader files to it ' . + 'from authorized userids. The script contains a variable, authorized_senders, that ' . + 'lists the names of authorized virtual machines.', + 'userResp' => 'Configure the authorized_senders variable in the script to contain the userids '. + 'of ZHCP virtual machines that will send configuration directives to this node or '. + 'to images create from this node.' + }, + 'VX05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Linux is not setup to start xcatconf4z on boot.', + 'explain' => 'The xcatconf4z script should be configured on the target '. + 'system to start when the system boots. This ensures that images '. + 'captured from this system will have the service started when the '. + 'new system boots to allow xCAT to complete the configuration of the '. + 'system the first time it boots.', + 'userResp' => 'Configure Linux to start xcatconf4z on boot of the server.' + }, + 'VX06' => + { 'severity' => 4, + 'recAction'=> 1, + 'text' => 'xcatconf4z was not found in the /opt directory on the target node.', + 'explain' => 'The xcatconf4z script should exist in the /opt directory on the target node.', + 'userResp' => 'Obtain xcatconf4z and put it in the /opt directory on the target node.' + }, + 'VX07' => + { 'severity' => 4, + 'recAction'=> 0, + 'text' => '/opt/bin/mkisofs was not found on the target system.', + 'explain' => 'The xcatconf4z script invokes the /opt/bin/mkisofs to create ' . + 'an ISO9660 disk in which it stores configuration '. + 'data used by the activation engine. The mkisofs file is missing '. + 'and will prevent proper operation of the xcatconf4z script during '. + 'deploy of an image.', + 'userResp' => 'This function should be obtained from the Linux distribution and '. + 'installed.' + }, + ); + +#****************************************************************************** +# zxcatIVP messages +#****************************************************************************** +my %zxcativpMsgs = ( + 'FATAL_DEFAULTS' => + { + 'sysAct' => 'No further verification will be performed.' + }, + 'NONFATAL_DEFAULTS' => + { + 'sysAct' => 'Verification continues.' + }, + 'GENERIC_RESPONSE' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + }, + 'GENERIC_RESPONSE_NOINDENT' => + { 'severity' => 0, + 'recAction' => 0, + 'text' => '%s', + 'subTab' => '', + }, + 'BPVMN01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'The node id for the xCAT MN was not specified, so the IVP will not verify that it exists.', + 'explain' => 'The node name of the xCAT MN is a configuration property, zvm_xcat_master '. + 'in /etc/nova/nova.conf file, that is used by z/VM OpenStack plugin code. '. + 'The driver script created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_mnNode environment variable. '. + 'The IVP perform tests related to the value. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'sysAct' => 'Some tests which use the node name of the xCAT MN will not be run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.' + }, + 'BPVDP01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Disk pool names were not specified, so IVP will verify the pools associated with each host.', + 'explain' => 'Directory manager disk pools are used for obtaining OpenStack ephemeral '. + 'disks. The zvm_diskpool property in /etc/nova/nova.conf file specifies the disk pools. '. + 'The driver script created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_diskpools environment variable. '. + 'The IVP perform tests related to the value. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.' + }, + 'BPVDS01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Directory space tests related to ZHCP agent will not be run.', + 'explain' => 'The IVP detected that the ZHCP agent is on the same system as the xCAT management node. ' . + 'The space related tests would have already be run for the xCAT MN so similar tests ' . + 'will not be run for the ZHCP agent.', + 'userResp' => 'If you want to verify additional directories for the ZHCP running on the xCAT MN\'s system '. + 'then specify the directory information as part of the xcatDiskSpace command line operand or ' . + 'zxcatIVP_xcatDiskSpace environment variable. Please remember to include directories '. + 'that are defaults for the xCAT MN\'s directory space tests. You can see the defaults in the ' . + 'help output for zxcatIVP.' + }, + 'BPVN01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Networks were not specified, so IVP will not verify them.', + 'explain' => 'Virtual switches are used for internet access by the deployed '. + 'virtual servers. The flat_networks property in section ml2_type_flat '. + 'and the network_vlan_ranges property in section ml2_type_vlan '. + 'in the /etc/neutron/plugins/ml2/ml2_conf.ini file specify the vswitches. '. + 'The driver script created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_networks environment variable. '. + 'The IVP perform tests related to the value. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.' + }, + 'BPVCNC01' => + { 'severity' => 1, + 'recAction' => 0, + 'text' => 'Compute Node address or export user was not specified, so the IVP will '. + 'not verify that the xCAT MN can communicate with the compute node.', + 'explain' => 'The address of the virtual server where the nova compute services '. + 'are running is configured in the '. + 'my_ip property in the /etc/nova/nova.conf file. The driver script '. + 'created by the IVP preparation script normally passes this value '. + 'in the zxcatIVP_cNAddress environment variable. '. + "\n\n". + 'The export user is normally set to \'nova\' by the IVP preparation script '. + 'and is passed in the zxcatIVP_expUser environment variable.'. + 'The IVP will perform tests related to these values. This bypass message is '. + 'expected when the IVP is driven directly without a driver script; for '. + 'example, in basic test mode but is not expected when a full IVP is run.', + 'userResp' => 'If you did not run the preparation script to create a driver script, then '. + 'consider doing so and rerunning the test with the driver script. '. + 'If you intended to run the basic IVP which is run without a driver script '. + 'then you can ignore this message.', + }, + 'MAIN01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Significant error detected, no further verification will be performed.', + 'explain' => 'A significant warning was detected, which prevents further tests '. + 'from accurately validating the system.', + 'sysAct' => 'No further verification will be performed for this run.', + 'userResp' => 'You should correct the situation indicated by previous warning '. + 'messages and then rerun the IVP.', + }, + 'STN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to SSH to %s.', + 'explain' => 'The xCAT management node uses SSH to obtain information '. + 'from the target node or to make changes within Linux on the '. + 'target node. This does not appear to be working.', + 'userResp' => 'You should investigate TCP/IP errors or network configuration errors within the target node.', + }, + 'VCMAP01' => + { 'severity' => 4, + 'recAction' => 1, + 'text' => 'Unable to determine the role of the Cloud Manager Appliance from the %s file.', + 'explain' => 'The Cloud Manager Appliance has a /var/lib/sspmod/appliance_system_role '. + 'file that cannot be read or does not contain the expected role property '. + 'in its expected form of "role=value" where the value is either: '. + 'CONTROLLER, COMPUTE, COMPUTE_MN, or MN.'. + 'This indicates corruption of the file and possibly other files, or a failed '. + 'install.', + 'userResp' => 'You should correct the corruption of the /var/lib/sspmod/appliance_system_role '. + 'file and then rerun the IVP.', + }, + 'VCMAP02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the version of Cloud Manager Appliance from the %s file.', + 'explain' => 'The Cloud Manager Appliance contains a /opt/ibm/cmo/version file '. + 'that cannot be read or does not contain a line indicating the '. + 'version of the appliance. This indicates a possible corruption of '. + 'the file system or a failed install.', + 'userResp' => 'You should verify that the file exists on the appliance and has the proper '. + 'permissions set to allow user root to read the file.', + }, + 'VCNC01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xCAT MN is unable to SSH to %s with user %s.', + 'explain' => 'The xCAT MN could not SSH into the compute node. This can occur for the '. + 'following reasons:'. + "\n". + '* the wrong user name was specified,'. + "\n". + '* the wrong IP address was specified,'. + "\n". + '* or the SSH keys were not set up on the compute '. + 'node to allow the xCAT MN to push data to that node.'. + "\n\n". + 'The error message indicates the IP address and user name that the IVP thinks '. + 'xCAT MN will use.', + 'userResp' => 'Verify the my_ip property, if it was specified in '. + '/etc/nova/nova.conf, or verify the local IP address of the OpenStack system. '. + 'The local IP address is used by the IVP preparation script as a default '. + 'when the my_ip property is not set. The IVP uses this '. + 'address to verify that the xCAT MN can access the compute node. xCAT MN '. + 'communicates with the compute node using this IP address. An address '. + 'which is not accessible by the xCAT MN will cause various xCAT functions '. + 'to fail. If the my_ip is not set, an incorrect default for your compute '. + 'node might have been chosen. You may wish to specify the my_ip property '. + 'with a valid value for your environment. Also, verify the value of the '. + 'zxcatIVP_expUser specified in the driver script by the IVP preparation script '. + 'script. This value defaults to "nova", which is the user name under '. + 'which the compute node will allow xCAT MN to access the system.', + }, + 'VDP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Disk pool %s was not found as a disk pool.', + 'explain' => 'The specified disk pool was not found. The disk pool is specified in '. + '/etc/nova/nova.conf and is passed to the IVP in the driver script '. + 'by the zxcatIVP_diskpools environment variable.', + 'userResp' => 'Verify the zvm_diskpool property in /etc/nova/nova.conf is correct.' + }, + 'VDP02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Disk pool %s has no free space', + 'explain' => 'The indicated disk pool has no space for minidisk creation. '. + 'You may need to add disks to the directory manager disk pool '. + 'or specify a different disk pool for the zvm_diskpool property in '. + '/etc/nova/nova.conf.'. + "\n\n". + 'If you are running a basic IVP and see this message for disk pool '. + 'XCAT1, you can ignore the warning. Disk pool XCAT1 is a special '. + 'disk pool that normally has no available space. This disk pool '. + 'is not intended to be used as a disk pool by the compute node.', + 'userResp' => 'Add space to the disk pool in the directory manager.' + }, + 'VDP03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to obtain a list of disk pools for host %s.', + 'explain' => 'The list of disk pools defined in the directory manager could not be obtained for '. + 'the specified host.', + 'userResp' => 'This can be caused by a few different problems. They include: '. + "\n". + '* No disk pools/regions defined in the directory manager - Use directory manager '. + 'commands to verify that disk pools have been defined, or to define them. '. + "\n". + '* The ZHCP server is unresponsive - Ensure that the ZHCP server is running. Other '. + 'error messages will occur if it is not running. '. + "\n". + '* SMAPI is unable to communicate with the directory manager - Use the SMAPI '. + 'SMSTATUS command to collect SMAPI debug information and verify the configuration '. + 'between SMAPI and the directory manager. '. + "\n". + '* If the name of the host node is incorrect in the message, then the xCAT server '. + 'may have been previously started prior to modifying the properties in '. + 'DMSSICNF COPY. This can cause an invalid xCAT node to be created for the host. Subsequent '. + 'restarts with a correctly configured DMSSICNF COPY file will not remove the '. + 'incorrect node but only add the new one. When the IVP attempts to verify the '. + 'host, it fails. See "zxcatIVP Issues" in the Enabling Manual for information on how to '. + 'remove the invalid node.', + }, + 'VDS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The file system related to the %s directory on the %s system has %s percent space '. + 'in use which is more than the expected maximum of %s percent.', + 'explain' => 'The file system is over the recommended percentage of space in use. This can cause processing errors.', + 'userResp' => 'You can eliminate this warning by freeing up space in the directories related to the file system or '. + 'adding space to the CMA\'s root disk. '. + 'See Appendix G, "Increasing the Size of the CMA\'s Root Disk using LVM Commands" section '. + 'in the Enabling z/VM for OpenStack Manual for instructions on increasing the size of the root disk.' + }, + 'VDS02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The file system related to the %s directory on the %s system has %s available space '. + 'which is less than the expected minimum of %s.', + 'explain' => 'The file system has less than the minimum available space. This can cause processing errors.', + 'userResp' => 'You can eliminate this warning by freeing up space in the directories related to the file system or '. + 'adding space to the CMA\'s root disk. '. + 'See Appendix G, "Increasing the Size of the CMA\'s Root Disk using LVM Commands" section '. + 'in the Enabling z/VM for OpenStack Manual for instructions on increasing the size of the root disk.' + }, + 'VDS03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the percentage of disk space used by the %s system\'s file system '. + 'related to the %s directory.', + 'explain' => 'The IVP attempts to determine the size of the file system disk space related to the specified directory '. + 'using the df -h command and is unable to do so. This error is normally '. + 'related to disk corruption issue or problems with a logical volume associated with the directory.', + 'userResp' => 'Please review the messages log file on the affected system for errors related to the disks.' + }, + 'VDS04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The following files are larger than the expected maximum of \'%s\': %s', + 'explain' => 'Very large files in the indicated directory and it subdirectories can consume space needed '. + 'for normal operations. The IVP program warns of very large files.', + 'userResp' => 'You should consider removing the identified files. Please note that '. + 'the IVP program compiles a simple '. + 'list of large files in the directories. It only removes some files from the list such as '. + 'files ending in .so or with .so. in the name or .jar at the end of the filename. For this reason, '. + 'you should only remove files which you have identified as log files or which you know '. + 'are files which can be safely removed. See "Space Issues on persistent '. + 'Directory Can Lead to xCAT MN Issues" in the Enabling Manual for '. + 'instructions on addressing space issues.' + }, + 'VDS05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The size of the logical volume on which the xCAT MN image repository resides (%s) '. + 'is less than image pruning goal (%s).', + 'explain' => 'The xCAT MN image repository is a subdirectory of the /install '. + 'directory. The IVP compares the size of the logical volume '. + 'providing storage for the directory to the value specified in the '. + 'xcat_free_space_threshold property in the /etc/install/nova.conf '. + 'file in the OpenStack systen (passed using the '. + 'xcatIVP_expectedReposSpace property in the driver script). When '. + 'the OpenStack performs automated pruning of older OpenStack '. + 'images from the xCAT image repository (images that still exist in '. + 'Glance), it will attempt to free up enough space to match the '. + 'xcat_free_space_threshold. An xcat_free_space_threshold value '. + 'that is greater than the size of the logical volume can result in '. + 'more images being removed than necessary when automatic pruning '. + 'occurs. This will result in images being transferred between '. + 'OpenStack and xCAT unnecessarily.', + 'userResp' => 'You should consider adding additional volumes the logical volume '. + 'by updating the XCAT_iso property in the DMSSICNF COPY file for '. + 'an xCAT MN system, or to the cmo_data_disk property in the '. + 'DMSSICMO COPY file for a CMA in controller role.' + }, + 'VHN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The host node (%s) was not defined.', + 'explain' => 'The specified host node was not defined to xCAT. ', + 'userResp' => 'Verify that the zvm_host property is specified '. + 'correctly in /etc/nova/nova.conf. This property is passed by the driver '. + 'script in the zxcatIVP_hostNode environment variable.' + }, + 'VHN02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Host node does not have an ZHCP associated with it.', + 'explain' => 'The specified host node, specified with the zvm_host property in '. + '/etc/nova/nova.conf, does not have a ZHCP agent associated with it. '. + 'This property is passed by the driver script in the zxcatIVP_hostNode '. + 'environment variable.', + 'userResp' => 'You should correct the host node in xCAT by associating a ZHCP agent '. + 'using the hcp property so that it can be managed by xCAT and the services '. + 'services that use xCAT.' + }, + 'VMN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'MN node(%s) was not defined.', + 'explain' => 'The xCAT management node specified on the zvm_xcat_master '. + 'property in the /etc/nova/nova.conf file was not in the list of '. + 'defined xCAT nodes. The probable cause of this message is an '. + 'incorrectly specified property value for the xCAT node that '. + 'represents the xCAT MN.', + 'userResp' => 'You can view the list of nodes using the xCAT GUI in the '. + 'Nodes->Nodes tab. The xCAT MN node should be in the list of '. + 'nodes for the "all" group. You can view other xCAT node groups '. + 'by selecting the group name in the "Groups" frame on the left '. + 'side of the web page.' + }, + 'VMNI01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xCAT MN does not have an interface defined for %s with address: %s.', + 'explain' => 'The xCAT MN does not have the specified IP address defined. This '. + 'is most likely caused by a typo in the configuration files. The '. + 'IVP driver script contains the values used in the test, and which '. + 'configuration file and property provided the value.', + 'userResp' => 'Use the information to identify the property in error and correct '. + 'the address in the configuration file.' + }, + 'VMNI02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the routing prefix information for %s %s.', + 'explain' => 'While attempting to verify the information specified for the IVP, '. + 'the routing prefix for the xCAT MN IP address could not be determined. '. + 'This could indicate a problem with the IP interface for xCAT MN.', + 'userResp' => 'Use the information to identify the property in error and correct '. + 'the address in the configuration file.' + }, + 'VMNI03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Subnet mask specified as input is not valid: %s.', + 'explain' => 'The subnet mask specified as input to the IVP is not a valid subnet mask.', + 'userResp' => 'Please verify the subnet mask used as input to the installation '. + 'verification program.' + }, + 'VMNI04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'xCAT MN subnet mask for %s has a routing prefix of %s which '. + 'does not match the calculated routing prefix of %s for the subnet '. + 'mask passed as input: %s.', + 'explain' => 'The routing prefix for the xCAT MN does not match the calculated '. + 'routing prefix for the subnet mask that was specified as input '. + 'to the IVP.', + 'userResp' => 'Please verify the subnet mask used as input to the installation verification program.' + }, + 'VMHS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the signal shutdown time on %s. Query returned, rc: %s, output: %s', + 'explain' => 'The IVP program attempted to determine the signal shutdown duration on the z/VM host '. + 'and either encountered an unexpected return code or response from the command. '. + 'The response is expected to be in English.', + 'userResp' => 'Determine the cause of the error and correct it.' + }, + 'VMHS02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'The signal shutdown time on %s is %s seconds which is less than the tested minimum of %s seconds.', + 'explain' => 'The signal shutdown time defines the time CP will wait for a virtual machine to respond to '. + 'a shutdown signal sent to it by SIGNAL, FORCE, or SHUTDOWN commands before forcing a virtual '. + 'machine off the z/VM system. A value of 0 causes CP to immediately force a virtual machine '. + 'without sending it a shutdown signal. xCAT and SMAPI use one or more of these commands in the '. + 'process of powering off a virtual machine.'. + "\n\n". + 'Linux systems cache their disk reads and writes to improve performance. For this reason, a sufficient '. + 'delay is recommended to allow the Linux operating system to clear the disk cache. '. + 'Failure to clear the cache can cause disk problems when the virtual machine logs back on '. + 'the z/VM system.', + 'userResp' => 'You should change the signal shutdown time for the z/VM host. The system configuration '. + 'file supports a SET SIGNAL SHUTDOWNTIME statement that allows you to set the time. '. + 'In addition, the CP class A and C SET SIGNAL SHUTDOWNTIME command can be used to '. + 'change the time for the current system IPL. '. + "\n\n". + 'The test run by the IVP checks for a minimum amount of time. You should choose a time that '. + 'allows sufficient time for the Linux virtual machines that are running on your system. This '. + 'will depend upon the performance of your system and the activity performed by the Linux virtual '. + 'machines. In general, setting a larger time interval such as 300 seconds (5 minutes) is safer '. + 'than an interval that is too short. In most cases, the virtual machines will shutdown upon '. + 'receiving the signal in a much shorter time and thus avoid the forced log off that results '. + 'from the time interval expiring.' + }, + 'VMS01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to determine the virtual storage size.', + 'explain' => 'An attempt to verify the virtual storage size of the z/VM virtual '. + 'machine running xCAT MN using the z/VM CP QUERY VIRTUAL STORAGE '. + 'command failed. This can indicate a command authorization problem.', + 'userResp' => 'Please ensure that the virtual machine is permitted to run the command.' + }, + 'VMS02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Virtual machine storage size (%s) is less than the recommended '. + 'size of %s.', + 'explain' => 'The virtual storage size of the z/VM virtual machine which is '. + 'running xCAT MN is less than the recommended size.', + 'userResp' => 'Please update the user directory for that virtual machine to '. + 'increase its virtual storage.' + }, + 'VMUP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'MACID user prefix for the %s is \'%s\' and is not the expected value of \'%s\'', + 'explain' => 'The MACADDR user prefix portion of the base_mac property in '. + '/etc/neutron/neutron.conf does not match the value specified '. + 'on the z/VM VMLAN system configuration property in the z/VM '. + 'host. The IVP uses the z/VM CP Query VMLAN '. + 'command along with the information in the "VMLAN MAC address '. + 'assignment" portion of the command response, specifically the '. + 'user prefix information.', + 'userResp' => 'This error is most often caused by an error in the OpenStack configuration '. + 'property. Either the OpenStack property should be changed to match the z/VM '. + 'system or the z/VM system\'s value should be changed.', + }, + 'VN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Network %s was not found as a network.', + 'explain' => 'The specified network is not a known network on the host. The most '. + 'likely cause of this problem is a typo in the flat_networks and/or '. + 'network_vlan_ranges properties of the /etc/neutron/plugins/ml2/ml2_conf.ini file.', + 'userResp' => 'Correct the OpenStack configuration files.' + }, + 'VN02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Network %s is not %s as expected.', + 'explain' => 'The specified network on the host is not configured with the '. + 'expected VLAN awareness. The most likely cause of this problem '. + 'is an error in the flat_networks and/or network_vlan_ranges '. + 'properties of the /etc/neutron/plugins/ml2/ml2_conf.ini file.', + 'userResp' => 'Correct the OpenStack configuration files.', + }, + 'VNE01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Node %s is not defined to xCAT.', + 'explain' => 'The indicated node is not defined to xCAT. '. + 'This most often indicates a typo in a configuration file.', + 'userResp' => 'If using a driver script created by the IVP preparation script, '. + 'please review the driver script to determine the OpenStack '. + 'property and configuration file which specified the node. You can '. + 'do this by locating the node name in the driver script and then '. + 'reading the comments which indicate what property was used to set '. + 'the value in the driver script.' + }, + 'VP01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to get the directory statements for profile %s', + 'explain' => 'The profile specified as input is not in the z/VM directory. '. + 'The cause of this error is either a typo in the zvm_user_profile '. + 'property in the /etc/nova/nova.conf file or that the profile was not'. + 'defined to z/VM.', + 'userResp' => 'Correct the OpenStack configuration file.', + }, + 'VR01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'REST API failed to successfully respond to request.\n' . + ' Response code: %s, Message: %s\n' . + ' Response content: %s', + 'explain' => 'A REST communication to the xCAT Management Node from the same '. + 'server on which the management node was running failed. This '. + 'can be caused by a typo in the zvm_xcat_username, zvm_xcat_password, '. + 'and/or zvm_xcat_server properties in /etc/nova/nova.conf file. The xCAT '. + 'management node obtains the value that it recognizes for the user name from the '. + 'XCAT_MN_admin property in DMSSICNF COPY. The user name properties '. + 'should match. It can also occur if there are configuration errors '. + 'or TCP/IP errors in the xCAT management node.', + 'userResp' => 'Review the REST response data that is provided with the message. '. + 'It should help isolate the problem.' + }, + 'VU01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'user %s is not in the policy table or has no rule defined in the table.', + 'explain' => 'The specified xCAT user is not a known xCAT user. This is most likely '. + 'caused by a typo in the zvm_xcat_username property in the '. + '/etc/nova/nova.conf file. The xCAT management node obtains the value that it '. + 'recognizes for the user name from the XCAT_MN_admin property in DMSSICNF COPY. '. + "\n". + 'Another cause is if the user is defined in the policy table but does not '. + 'have a rule defined.', + 'userResp' => 'Correct the OpenStack configuration file, if it is incorrect. '. + 'Correct the xCAT POLICY table, if it is incorrect.', + }, + 'VU02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'user %s is in the xCAT POLICY table but the rule property is not \'accept\' or '. + '\'allow\'. It is \'%s\'.', + 'explain' => 'The specified xCAT user is not defined in the xCAT POLICY table with the '. + 'correct rule. ', + 'userResp' => 'Correct the xCAT POLICY table entry for the user.', + }, + 'VVO01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'vswitch %s does not exist.', + 'explain' => 'The specified virtual switch does not exist in the z/VM system.', + 'userResp' => 'The vswitch name was specified as the section name in the '. + '/etc/neutron/plugins/zvm/neutron_zvm_plugin.ini file before the '. + 'rdev_list property. The neutron agent creates the virtual switches '. + 'when it starts up. If the switch is not defined, you should determine '. + 'whether the neutron agent was started and is successfully communicating '. + 'with xCAT.' + }, + 'VVO02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'vswitch %s does not use real device %s', + 'explain' => 'The /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini file indicated that '. + 'the virtual switch has a real device associated with it at the specified '. + 'address which does not match the actual vswitch definition in z/VM.', + 'userResp' => 'This is most likely an error in the rdev_list property for the section '. + 'indicated by the vswitch name in the /etc/neutron/plugins/zvm/neutron_zvm_plugin.ini file. '. + 'Correct the OpenStack configuration file.', + }, + 'VZN01' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'zHCP node %s did not respond to a ping.', + 'explain' => 'The indicated ZHCP node did not respond to a ping. '. + 'The virtual machine may be logged off the z/VM system '. + 'or there may be problems in the configuration of this server causing it '. + 'to fail to IPL, or configure its TCP/IP interfaces. '. + 'ZHCP is necessary to manage the z/VM host and its virtual servers. '. + 'The ZHCP node is associated with the host specified by the zvm_host property in the '. + '/etc/nova/nova.conf file.', + 'userResp' => 'There are a number of possibilities that you should consider: '. + "\n". + '* Verify that the virtual machine is logged on. '. + 'If it is not logged on then verify that you have performed the necessary steps to '. + 'tell SMAPI to start the server when SMAPI starts. '. + 'This is discussed in the z/VM Systems Management Application Programming manual.'. + "\n". + '* Obtain the console log from the virtual server and verify that the system has '. + 'not stopped itself due to an error. This is not a normal condition and '. + 'usually indicates a serious configuration error.', + }, + 'VZN02' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'zHCP node %s is either not powered on or responding.', + 'explain' => 'The indicated ZHCP node did not respond to simple power status request. '. + 'There may be problems with the set up of this ZHCP.', + 'userResp' => 'Pay particular attention to whether there are SSL key problems. Another '. + 'possible cause is an error preventing ZHCP from communicating with the '. + 'z/VM CP. The ZHCP node is associated with the host specified by the zvm_host '. + 'property in the /etc/nova/nova.conf file.', + }, + 'VZN03' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to get the directory statements for %s. The statements should be for userid %s.', + 'explain' => 'The indicated ZHCP node did not provide the proper response to a request for '. + 'information on a user in the z/VM directory. '. + 'The IVP checks the directory statements to verify that a USER or IDENTITY statement '. + 'was returned with the userid that was specified in the xCAT zvm table. '. + 'In addition, the SMAPI servers or the directory manager '. + 'may not be configured to communicate with the ZHCP agent. '. + "\n". + 'The ZHCP node is associated with the host specified by the zvm_host property in the '. + '/etc/nova/nova.conf file.', + 'userResp' => 'Verify that:'. + "\n". + '* the zvm table has the userid property correctly listed for the '. + 'virtual machine that is running the ZHCP node. If not then correct it.'. + "\n". + '* the Directory manager is operational. This can be accomplished by issuing '. + 'a command to review a userid\'s directory entries to see if you get a valid response.'. + "\n". + '* the Directory manager is properly configuring to communicate with the SMAPI servers. '. + 'This is discussed in the z/VM Systems Management Application Programming manual.', + }, + 'VZN04' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'zHCP node %s is encountering errors communicating with SMAPI, out: %s', + 'explain' => 'The indicated ZHCP node was not able to execute a simple query to the SMAPI '. + 'servers and receive an expected result. This indicates a possible error '. + 'in the SMAPI configuration.', + 'userResp' => 'You should review the SMAPI configuration to ensure all steps were '. + 'accomplished and that the virtual machine running the ZHCP agent is '. + 'authorized to communicate with the SMAPI servers and that '. + 'the SMAPI servers are running. For example, verify that '. + 'the VSMWORK1 AUTHLIST file in \'VMSYS:VSMWORK1.\' SFS directory lists '. + 'the virtual machine that is running the ZHCP agent and that the information in the file '. + 'is in the correct columns.', + }, + 'VZN05' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => 'Unable to find a zHCP node related to host %s.', + 'explain' => 'The indicated host node does not have a ZHCP node which is defined and '. + 'associated with it. The IVP program expects each host to have an hcp property '. + 'defined and that property to be related to an xCAT node that represents '. + 'the ZHCP server and has the same host name as specified for the host\'s hcp property.', + 'userResp' => 'Define a node in xCAT for the ZHCP server.' + }, + ); + +my %reposList = ( + 'VERIFYNODE' => \%verifyMsgs, + 'ZXCATIVP' => \%zxcativpMsgs, + ); + + + + +#------------------------------------------------------- + +=head3 buildMsg + + Description : Build a message from a message repository file. + Arguments : Group identifier + Message identifier + 'BLANK_LINE' is a special identifier that does not appear in + the message repository but instead causes a blank line to + be printed to the display. + Message substitutions + Returns : Recommended action: + 0: Continue processing + 1: Fatal, end processing + severity - Severity of message and indicate whether it gets + a header to the message text (e.g. "Error (IVP:MAINT01) " ) + 0 - no message header, unrated output + 1 - bypass message used by automated tests + 2 - information message with "Info" header + 3 - unknown message severity + 4 - warning message with "Warning" header + 5 - error message with "Error" header + Constructed message + Additional information (e.g. explanation, system action, user action) + Example : ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'IVP', $msgInfo, \@msgSubs); + +=cut + +#------------------------------------------------------- +sub buildMsg { + my ( $class, $groupId, $msgId, $subs ) = @_; + my @msgSubs = (); + my $recAction = 0; + my %respMsgs; + my $respHash; + my $retMsg = ''; + my $retExtra = ''; + my $sev = 0; + my ( $init_tab, $subsequent_tab ); + + $Text::Wrap::unexpand = 0; + + if ( defined $subs ) { + @msgSubs = @$subs; + } + + # Get the hash of the messages. + if ( exists $reposList{$groupId} ) { + $respHash = $reposList{$groupId}; + } else { + $respHash = \%verifyMsgs; + } + %respMsgs = %$respHash; + + # Find the message + if ( $msgId eq 'BLANK_LINE' ) { + $retMsg = "\n"; + } elsif ( !exists $respMsgs{$msgId} ) { + $retMsg = "Warning ($groupId:$msgId): Message was not found! Unable to find " . + "\'$msgId\' in \'$groupId\' messages.\n"; + $sev = 3; + # Recommended Action is 'continue'. + } else { + # Build severity and message Id portion of the message. + my $msg = ''; + if ( exists $respMsgs{$msgId}{'severity'} ) { + $sev = $respMsgs{$msgId}{'severity'}; + if ( $respMsgs{$msgId}{'severity'} != 0 ) { + $msg = $sevInfo[ $respMsgs{$msgId}{'severity'} ] . " ($groupId:$msgId) "; + } + } else { + # Unknown severity + $msg = $sevInfo[1] . " ($groupId:$msgId) "; + $sev = 3; + } + + # Determine the recommended action to return to the caller. + if ( exists $respMsgs{$msgId}{'recAction'} ) { + $recAction = $respMsgs{$msgId}{'recAction'}; + } + + # Build text portion of the message. + if ( exists $respMsgs{$msgId}{'text'} ) { + # Determine the number of '%s' in the message and pad @msgSubs + # so that we do not get a sprintf error due to having to few + # substitution values. + my $numSubs = 0; + while ( $respMsgs{$msgId}{'text'} =~ /\%s/g ) { $numSubs++ } + if ( $numSubs > 0 and $numSubs > ( scalar @msgSubs ) ) { + # Too few subs. Push on some empty strings so that sprintf does not complain. + for ( my $i = (scalar @msgSubs); $i <= $numSubs; $i++ ) { + push @msgSubs, ''; + } + } + + if ( @msgSubs ) { + # Ensure all elements in @msgSubs are defined. + for ( my $i = 0; $i < (scalar @msgSubs); $i++) { + if ( ! defined $msgSubs[$i] ) { + $msgSubs[$i] = ''; + } + } + + # Insert the substitutions + my $msgText = sprintf( $respMsgs{$msgId}{'text'}, @msgSubs); + $msg = "$msg$msgText"; + } else { + $msg = $msg . $respMsgs{$msgId}{'text'}; + } + } + + # Set the subsequent wrap/tab value for formatting. + if ( exists $respMsgs{$msgId}{'initTab'} ) { + $init_tab = $respMsgs{$msgId}{'initTab'}; + } else { + $init_tab = ""; + } + if ( exists $respMsgs{$msgId}{'subTab'} ) { + $subsequent_tab = $respMsgs{$msgId}{'subTab'}; + } else { + $subsequent_tab = "\t"; + } + + # Format the messages lines with proper indentation. + my $line; + chomp $msg; + my @msgLines = split( /\\n/, $msg ); + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + $retMsg = $retMsg . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + + if ( $respMsgs{$msgId}{'severity'} >= 1 ) { + # Build explanation portion of the known messages that are bypass, + # info, warning or error. These can have extra information. + if ( exists $respMsgs{$msgId}{'explain'} ) { + my $expLines = " Explanation: $respMsgs{$msgId}{'explain'}"; + @msgLines = split( /\\n/, $expLines ); + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + if ( $i != 0 ) { + $line = "\t$line"; + } + $retExtra = $retExtra . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + } + + # Build system action portion of the message. + my $sysAction; + if ( exists $respMsgs{$msgId}{'sysAct'} ) { + $sysAction = " System Action: $respMsgs{$msgId}{'sysAct'}"; + } else { + if ( $recAction == 0 and exists $respMsgs{'NONFATAL_DEFAULTS'}{'sysAct'} ) { + $sysAction = " System Action: $respMsgs{'NONFATAL_DEFAULTS'}{'sysAct'}"; + } elsif ( $recAction == 1 and exists $respMsgs{'FATAL_DEFAULTS'}{'sysAct'} ) { + $sysAction = " System Action: $respMsgs{'FATAL_DEFAULTS'}{'sysAct'}"; + } + } + if ( defined $sysAction ) { + #@msgLines = split( /\\n/, $sysAction ); + #$retMsg = $retMsg . wrap( "", "\t", @msgLines ) . "\n"; + + @msgLines = split( /\\n/, $sysAction ); + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + if ( $i != 0 ) { + $line = "\t$line"; + } + $retExtra = $retExtra . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + } + + # Build user response portion of the message. + if ( exists $respMsgs{$msgId}{'userResp'} ) { + @msgLines = split( /\\n/, " User Response: $respMsgs{$msgId}{'userResp'}" ); + #$retMsg = $retMsg . wrap( "", "\t", @msgLines ) . "\n"; + for ( my $i = 0; $i < scalar @msgLines; $i++ ) { + $line = "$msgLines[$i]"; + if ( $i != 0 ) { + $line = "\t$line"; + } + $retExtra = $retExtra . wrap( $init_tab, $subsequent_tab, $line ) . "\n"; + } + } + } + } + + return ( $recAction, $sev, $retMsg, $retExtra ); +} \ No newline at end of file diff --git a/perl-xCAT/xCAT/zvmUtils.pm b/perl-xCAT/xCAT/zvmUtils.pm index 2ef8880bd..e191be899 100644 --- a/perl-xCAT/xCAT/zvmUtils.pm +++ b/perl-xCAT/xCAT/zvmUtils.pm @@ -1,4 +1,4 @@ -# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html +# IBM(c) 2013-2016 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 @@ -14,10 +14,37 @@ use xCAT::Utils; use xCAT::Table; use xCAT::NetworkUtils; use File::Basename; +use Net::Ping; use strict; use warnings; +use Encode; +use JSON; +use Data::Dumper; +use Cwd; 1; +my $locOpenStackUpdateName = '/var/lib/sspmod/setnewname.py'; + +# Files which contain OS distribution and version information. +my $locEtcDebianVersion = '/etc/debian_version'; +my $locEtcFedoraRelease = '/etc/fedora-release'; +my $locEtcIssue = '/etc/issue'; +my $locEtcLsbRelease = '/etc/lsb-release'; +my $locEtcOsRelease = '/etc/os-release'; +my $locEtcRedhatRelease = '/etc/redhat-release'; +my $locEtcStarRelease = '/etc/*-release'; +my $locEtcSuseRelease = '/etc/SuSE-release'; +my $locEtcUnitedLinux = '/etc/UnitedLinux-release'; +my $locAllEtcVerFiles = "/etc/*-release /etc/issue /etc/debian_version"; + +# Supported Operating System distros and versions +my %supportedVersions = ( + rhel5 => 1, + rhel6 => 1, + sles10 => 1, + sles11 => 1, + ); + #------------------------------------------------------- =head3 getNodeProps @@ -27,20 +54,20 @@ use warnings; Properties Returns : Node properties from given table Example : my $propVals = xCAT::zvmUtils->getNodeProps($tabName, $node, $propNames); - + =cut #------------------------------------------------------- sub getNodeProps { # Get inputs - my ($class, $tabName, $node, @propNames) = @_; + my ( $class, $tabName, $node, @propNames ) = @_; # Get table my $tab = xCAT::Table->new($tabName); # Get property values - my $propVals = $tab->getNodeAttribs($node, [@propNames]); + my $propVals = $tab->getNodeAttribs( $node, [@propNames] ); return ($propVals); } @@ -55,21 +82,21 @@ sub getNodeProps { Requested properties Returns : Table entry properties Example : my $propVals = xCAT::zvmUtils->getTabPropsByKey($tabName, $key, $keyValue, @reqProps); - + =cut #------------------------------------------------------- sub getTabPropsByKey { # Get inputs - my ($class, $tabName, $key, $keyVal, @propNames) = @_; + my ( $class, $tabName, $key, $keyVal, @propNames ) = @_; # Get table my $tab = xCAT::Table->new($tabName); my $propVals; # Get table attributes matching given key - $propVals = $tab->getAttribs({ $key => $keyVal }, @propNames); + $propVals = $tab->getAttribs( { $key => $keyVal }, @propNames ); return ($propVals); } @@ -80,14 +107,14 @@ sub getTabPropsByKey { Arguments : Table name Returns : All table entries Example : my $entries = xCAT::zvmUtils->getAllTabEntries($tabName); - + =cut #------------------------------------------------------- sub getAllTabEntries { # Get inputs - my ($class, $tabName) = @_; + my ( $class, $tabName ) = @_; # Get table my $tab = xCAT::Table->new($tabName); @@ -109,20 +136,20 @@ sub getAllTabEntries { Property value Returns : Nothing Example : xCAT::zvmUtils->setNodeProp($tabName, $node, $propName, $propVal); - + =cut #------------------------------------------------------- sub setNodeProp { # Get inputs - my ($class, $tabName, $node, $propName, $propVal) = @_; + my ( $class, $tabName, $node, $propName, $propVal ) = @_; # Get table - my $tab = xCAT::Table->new($tabName, -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Set property - $tab->setAttribs({ 'node' => $node }, { $propName => $propVal }); + $tab->setAttribs( { 'node' => $node }, { $propName => $propVal } ); # Save table $tab->commit; @@ -140,20 +167,20 @@ sub setNodeProp { Reference to property name/value hash Returns : Nothing Example : xCAT::zvmUtils->setNodeProps($tabName, $node, \%propHash); - + =cut #------------------------------------------------------- sub setNodeProps { # Get inputs - my ($class, $tabName, $node, $propHash) = @_; + my ( $class, $tabName, $node, $propHash ) = @_; # Get table - my $tab = xCAT::Table->new($tabName, -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Set property - $tab->setAttribs({ 'node' => $node }, $propHash); + $tab->setAttribs( { 'node' => $node }, $propHash ); # Save table $tab->commit; @@ -168,24 +195,24 @@ sub setNodeProps { Description : Delete a table entry Arguments : Table Key name - Key value + Key value Returns : Nothing Example : xCAT::zvmUtils->delTabEntry($tabName, $keyName, $keyVal); - + =cut #------------------------------------------------------- sub delTabEntry { # Get inputs - my ($class, $tabName, $keyName, $keyVal) = @_; + my ( $class, $tabName, $keyName, $keyVal ) = @_; # Get table - my $tab = xCAT::Table->new($tabName, -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Delete entry from table - my %key = ($keyName => $keyVal); - $tab->delEntries(\%key); + my %key = ( $keyName => $keyVal ); + $tab->delEntries( \%key ); # Save table $tab->commit; @@ -201,15 +228,15 @@ sub delTabEntry { Arguments : String Returns : Tabbed string Example : my $str = xCAT::zvmUtils->tabStr($str); - + =cut #------------------------------------------------------- sub tabStr { # Get inputs - my ($class, $inStr) = @_; - my @lines = split("\n", $inStr); + my ( $class, $inStr ) = @_; + my @lines = split( "\n", $inStr ); # Tab output my $outStr; @@ -228,14 +255,14 @@ sub tabStr { Arguments : String Returns : Trimmed string Example : my $str = xCAT::zvmUtils->trimStr($str); - + =cut #------------------------------------------------------- sub trimStr { # Get string - my ($class, $str) = @_; + my ( $class, $str ) = @_; # Trim right $str =~ s/\s*$//; @@ -254,14 +281,14 @@ sub trimStr { Arguments : String Returns : New string Example : my $str = xCAT::zvmUtils->replaceStr($str, $pattern, $replacement); - + =cut #------------------------------------------------------- sub replaceStr { # Get string - my ($class, $str, $pattern, $replacement) = @_; + my ( $class, $str, $pattern, $replacement ) = @_; # Replace string $str =~ s/$pattern/$replacement/g; @@ -277,25 +304,24 @@ sub replaceStr { Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printLn($callback, $str); - + =cut #------------------------------------------------------- sub printLn { # Get inputs - my ($class, $callback, $str) = @_; + my ( $class, $callback, $str ) = @_; # Print string my $rsp; my $type = "I"; - if ($str =~ m/error/i) { # Set to print error if the string contains error + if ($str =~ m/(\(error\)|\s*failed)/i) { # Set to print error if the string contains (error) or starts with failed $type = "E"; } $rsp->{data}->[0] = "$str"; - xCAT::MsgUtils->message($type, $rsp, $callback); - + xCAT::MsgUtils->message( $type, $rsp, $callback ); # xCAT::MsgUtils->message( "S", $str ); # Print to syslog return; @@ -309,20 +335,20 @@ sub printLn { Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printSyslog($str); - + =cut #------------------------------------------------------- sub printSyslog { # Get inputs - my ($class, $str) = @_; + my ( $class, $str ) = @_; # Prepend where this message came from $str = $class . " " . $str; # Print string - xCAT::MsgUtils->message("S", $str); + xCAT::MsgUtils->message( "S", $str ); return; } @@ -336,19 +362,19 @@ sub printSyslog { Returns : TRUE Node exists FALSE Node does not exists Example : my $out = xCAT::zvmUtils->isZvmNode($node); - + =cut #------------------------------------------------------- sub isZvmNode { # Get inputs - my ($class, $node) = @_; + my ( $class, $node ) = @_; # Look in 'zvm' table - my $tab = xCAT::Table->new('zvm', -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); - my @results = $tab->getAllAttribsWhere("node like '%" . $node . "%'", 'userid'); + my @results = $tab->getAllAttribsWhere( "node = '" . $node . "'", 'userid' ); foreach (@results) { # Return 'TRUE' if given node is in the table @@ -370,14 +396,14 @@ sub isZvmNode { Node Returns : Hardware configuration file path Example : my $hwcfg = xCAT::zvmUtils->getHwcfg($user, $node); - + =cut #------------------------------------------------------- sub getHwcfg { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -392,10 +418,15 @@ sub getHwcfg { my @parms; # If it is SUSE - hwcfg-qeth file is in /etc/sysconfig/hardware - if ($os =~ m/SUSE/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"`; - @parms = split('\n', $out); - return ($parms[0]); + if ( $os =~ m/SUSE/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"`; + my $cmd = "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If no file is found - Return nothing @@ -410,20 +441,20 @@ sub getHwcfg { Arguments : Node Returns : IP address of given node Example : my $ip = xCAT::zvmUtils->getIp($node); - + =cut #------------------------------------------------------- sub getIp { # Get inputs - my ($class, $node) = @_; + my ( $class, $node ) = @_; # Get IP address # You need the extra space in the pattern, # else it will confuse gpok2 with gpok21 - my $out = `cat /etc/hosts | egrep -i "$node | $node."`; - my @parms = split(' ', $out); + my $out = `cat /etc/hosts | egrep -i "$node | $node."`; + my @parms = split( ' ', $out ); return $parms[0]; } @@ -439,14 +470,14 @@ sub getIp { Node Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfg($user, $node); - + =cut #------------------------------------------------------- sub getIfcfg { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -461,17 +492,27 @@ sub getIfcfg { my @parms; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts - if ($os =~ m/Red Hat/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; - @parms = split('\n', $out); - return ($parms[0]); + if ( $os =~ m/Red Hat/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If it is SUSE - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($os =~ m/SUSE/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"`; - @parms = split('\n', $out); - return ($parms[0]); + elsif ( $os =~ m/SUSE/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If no file is found - Return nothing @@ -488,14 +529,14 @@ sub getIfcfg { NIC address Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfgByNic($user, $node, $nic); - + =cut #------------------------------------------------------- sub getIfcfgByNic { # Get inputs - my ($class, $user, $node, $nic) = @_; + my ( $class, $user, $node, $nic ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -510,42 +551,71 @@ sub getIfcfgByNic { my @parms; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts - if ($os =~ m/Red Hat/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; - @parms = split('\n', $out); + if ( $os =~ m/Red Hat/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + @parms = split( '\n', $out ); # Go through each line foreach (@parms) { + my $filename = $_; # If the network file contains the NIC address - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | egrep -i "$nic"`; + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | egrep -i "$nic"`; + my $cmd = $sudo . ' cat $filename'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i "$nic"`; if ($out) { # Return network file path - return ($_); + return ($filename); } } } # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($os =~ m/SUSE Linux Enterprise Server 10/i) { - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*" | grep -i "$nic"`; - @parms = split('\n', $out); - return ($parms[0]); + elsif ( $os =~ m/SUSE Linux Enterprise Server 10/i ) { + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*" | grep -i "$nic"`; + my $cmd = $sudo . ' ls /etc/sysconfig/network/ifcfg-qeth*'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i "$nic"`; + @parms = split( '\n', $out ); + return ( $parms[0] ); } # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ($os =~ m/SUSE Linux Enterprise Server 11/i) { + elsif ( $os =~ m/SUSE Linux Enterprise Server 11/i ) { # Get a list of ifcfg-eth files found - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-eth*"`; - my @file = split('\n', $out); + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-eth*"`; + my $cmd = "$sudo ls /etc/sysconfig/network/ifcfg-eth*"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + my @file = split( '\n', $out ); # Go through each ifcfg-eth file foreach (@file) { # If the network file contains the NIC address - $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | grep -i "$nic"`; + #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | grep -i "$nic"`; + my $cmd = $sudo . ' cat $_'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i "$nic"`; if ($out) { # Return ifcfg-eth file path @@ -567,24 +637,36 @@ sub getIfcfgByNic { Node Source file Target file - Returns : Nothing - Example : xCAT::zvmUtils->sendFile($user, $node, $srcFile, $trgtFile); - + Returns : Return code from SCP + Example : $rc = xCAT::zvmUtils->sendFile($user, $node, $srcFile, $trgtFile); + =cut #------------------------------------------------------- sub sendFile { # Get inputs - my ($class, $user, $node, $srcFile, $trgtFile) = @_; + my ( $class, $user, $node, $srcFile, $trgtFile ) = @_; + + my $out; + my $rc; # Create destination string my $dest = "$user\@$node"; # SCP directory entry file over to HCP - my $out = `/usr/bin/scp $srcFile $dest:$trgtFile`; + foreach my $wait ( 1, 2, 3, 5, 8, 15, 22, 34, 60 ) { + $out = `/usr/bin/scp $srcFile $dest:$trgtFile 2>&1`; + $rc = $?; + if ( $rc == 0 ) { + last; + } - return; + xCAT::zvmUtils->printSyslog("SCP $srcFile $dest:$trgtFile - failed with rc: $rc, out: $out"); + sleep( $wait ); + } + + return $rc; } #------------------------------------------------------- @@ -596,26 +678,45 @@ sub sendFile { Node Returns : Root device address Example : my $deviceAddr = xCAT::zvmUtils->getRootDeviceAddr($user, $node); - + =cut #------------------------------------------------------- sub getRootDeviceAddr { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } # Get the root device node # LVM is not supported - my $out = `ssh $user\@$node "mount" | grep "/ type" | sed 's/1//'`; - my @parms = split(" ", $out); - @parms = split("/", xCAT::zvmUtils->trimStr($parms[0])); + #my $out = `ssh $user\@$node "mount" | grep "/ type" | sed 's/1//'`; + my $cmd = $sudo . ' mount'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i "/ type" | sed \'s/1//\'`; + + my @parms = split( " ", $out ); + @parms = split( "/", xCAT::zvmUtils->trimStr( $parms[0] ) ); my $devNode = $parms[0]; # Get disk address - $out = `ssh $user\@$node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; - @parms = split(" ", $out); - return ($parms[0]); + #$out = `ssh $user\@$node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; + $cmd = $sudo . ' cat /proc/dasd/devices'; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i "$devNode" | sed "s/(ECKD)//" | sed "s/(FBA )//" | sed \'s/0.0.//\'`; + + @parms = split( " ", $out ); + return ( $parms[0] ); } #------------------------------------------------------- @@ -629,14 +730,14 @@ sub getRootDeviceAddr { Option (-d|-e) Returns : Nothing Example : my $out = xCAT::zvmUtils->disableEnableDisk($callback, $user, $node, $option, $devAddr); - + =cut #------------------------------------------------------- sub disableEnableDisk { # Get inputs - my ($class, $user, $node, $option, $devAddr) = @_; + my ( $class, $user, $node, $option, $devAddr ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -645,11 +746,23 @@ sub disableEnableDisk { # Disable/enable disk my $out; - if ($option eq "-d" || $option eq "-e") { - $out = `ssh $user\@$node "$sudo /sbin/chccwdev $option $devAddr"`; + if ( $option eq "-d" || $option eq "-e" ) { + # Can't guarantee the success of online/offline disk, need to wait + # Until it's done because we may detach the disk after -d option + # or use the disk after the -e option + my @sleepTime = (1,2,3,5,8,15,22,34,60,60,60,60,60,90,120); + foreach (@sleepTime) { + #$out = system("ssh $user\@$node '$sudo /sbin/chccwdev $option $devAddr'"); + my $cmd = "$sudo /sbin/chccwdev $option $devAddr"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { # try again if an error + sleep($_); + } else { + return "ssh $user\@$node $sudo /sbin/chccwdev $option $devAddr... Done"; + } + } } - - return ($out); + return "Error: failed to ssh $user\@$node $sudo /sbin/chccwdev $option $devAddr"; } #------------------------------------------------------- @@ -659,16 +772,16 @@ sub disableEnableDisk { Description : Get the MDISK statements in the user entry of a given node Arguments : User (root or non-root) Node - Returns : MDISK statements + Returns : MDISK statements array or or string containing (Error)... Example : my @mdisks = xCAT::zvmUtils->getMdisks($callback, $user, $node); - + =cut #------------------------------------------------------- sub getMdisks { # Get inputs - my ($class, $callback, $user, $node) = @_; + my ( $class, $callback, $user, $node ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -679,23 +792,34 @@ sub getMdisks { } # Get HCP - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userID my $userId = $propVals->{'userid'}; - my $out = `ssh $user\@$hcp "$sudo $dir/getuserentry $userId" | grep "MDISK"`; + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo $dir/getuserentry $userId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/getuserentry $userId\"", $hcp, "getMdisks", $out, $node ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "MDISK"`; # Get MDISK statements - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); my @disks; foreach (@lines) { + # skip comment lines that start with * or whitespace * + if ( $_ =~ m/^\s*\*/) { + next; + } $_ = xCAT::zvmUtils->trimStr($_); # Save MDISK statements - push(@disks, $_); + push( @disks, $_ ); } return (@disks); @@ -708,16 +832,16 @@ sub getMdisks { Description : Get the DEDICATE statements in the user entry of a given node Arguments : User (root or non-root) Node - Returns : DEDICATE statements + Returns : DEDICATE statements array or string containing (Error)... Example : my @dedicates = xCAT::zvmUtils->getDedicates($callback, $user, $node); - + =cut #------------------------------------------------------- sub getDedicates { # Get inputs - my ($class, $callback, $user, $node) = @_; + my ( $class, $callback, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -728,23 +852,30 @@ sub getDedicates { my $dir = '/opt/zhcp/bin'; # Get zHCP - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userId my $userId = $propVals->{'userid'}; - my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId" | egrep -i "DEDICATE"`; + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "commandString", $hcp, "getMdisks", $out, $node ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "DEDICATE"`; # Get DEDICATE statements - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); my @dedicates; foreach (@lines) { $_ = xCAT::zvmUtils->trimStr($_); # Save statements - push(@dedicates, $_); + push( @dedicates, $_ ); } return (@dedicates); @@ -752,23 +883,79 @@ sub getDedicates { #------------------------------------------------------- +=head3 getCommands + + Description : Get the COMMAND statements in the user entry of a given node + Arguments : User (root or non-root) + Node + Returns : COMMAND statements array or or string containing (Error)... + Example : my @commands = xCAT::zvmUtils->getCommands($callback, $user, $node); + +=cut + +#------------------------------------------------------- +sub getCommands { + + # Get inputs + my ( $class, $callback, $user, $node ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + # Get zHCP + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + my $hcp = $propVals->{'hcp'}; + + # Get node userId + my $userId = $propVals->{'userid'}; + + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli Image_Query_DM -T $userId\"", $hcp, "getCommands", $out, $node ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "COMMAND"`; + + # Get COMMAND statements + my @lines = split( '\n', $out ); + my @commands; + foreach (@lines) { + $_ = xCAT::zvmUtils->trimStr($_); + + # Save statements + push( @commands, $_ ); + } + + return (@commands); +} + +#------------------------------------------------------- + =head3 getUserEntryWODisk - Description : Get the user entry of a given node without MDISK statments, + Description : Get the user entry of a given node without MDISK statments, and save it to a file Arguments : User (root or non-root) Node File name to save user entry under - Returns : Nothing + Returns : Nothing, or string containing (Error)... Example : my $out = xCAT::zvmUtils->getUserEntryWODisk($callback, $user, $node, $file); - + =cut #------------------------------------------------------- sub getUserEntryWODisk { # Get inputs - my ($class, $callback, $user, $node, $file) = @_; + my ( $class, $callback, $user, $node, $file ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -779,30 +966,37 @@ sub getUserEntryWODisk { my $dir = '/opt/zhcp/bin'; # Get node properties from 'zvm' table - my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { - xCAT::zvmUtils->printLn($callback, "Error: Missing node HCP"); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; - if (!$userId) { - xCAT::zvmUtils->printLn($callback, "Error: Missing node ID"); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); return; } - my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId" | sed '\$d' | grep -v "MDISK"`; + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli Image_Query_DM -T $userId\"", $hcp, "getUserEntryWODisk", $out, $node ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | sed '\$d' | grep -a -i -v "MDISK"`; # Create a file to save output - open(DIRENTRY, ">$file"); + open( DIRENTRY, ">$file" ); # Save output - my @lines = split('\n', $out); + my @lines = split( '\n', $out ); foreach (@lines) { # Trim line @@ -825,15 +1019,15 @@ sub getUserEntryWODisk { String Returns : String appended with hostname Example : my $str = xCAT::zvmUtils->appendHostname($hostname, $str); - + =cut #------------------------------------------------------- sub appendHostname { - my ($class, $hostname, $str) = @_; + my ( $class, $hostname, $str ) = @_; # Append hostname to every line - my @outLn = split("\n", $str); + my @outLn = split( "\n", $str ); $str = ""; foreach (@outLn) { $str .= "$hostname: " . $_ . "\n"; @@ -850,20 +1044,20 @@ sub appendHostname { Arguments : Output string Returns : 0 Good output -1 Bad output - Example : my $rtn = xCAT::zvmUtils->checkOutput($callback, $out); - + Example : my $rtn = xCAT::zvmUtils->checkOutput( $out ); + =cut #------------------------------------------------------- sub checkOutput { - my ($class, $callback, $out) = @_; + my ( $class, $out ) = @_; # Check output string - my @outLn = split("\n", $out); + my @outLn = split( "\n", $out ); foreach (@outLn) { - # If output contains 'Failed', return -1 - if ($_ =~ m/Failed/i || $_ =~ m/Error/i) { + # If output contains 'Failed', or Error return -1 + if ( $_ =~ m/Failed/i || $_ =~ m/Error/i ) { return -1; } } @@ -880,18 +1074,17 @@ sub checkOutput { Reason (passed as a reference) Returns : 0 Good output -1 Bad output - Example : my $rtn = xCAT::zvmUtils->checkOutput($callback, $out, \$reason); - + Example : my $rtn = xCAT::zvmUtils->checkOutput( $out, \$reason ); + =cut #------------------------------------------------------- sub checkOutputExtractReason { - my ($class, $callback, $out, $reason) = @_; + my ( $class, $out, $reason ) = @_; # Check output string my @outLn = split("\n", $out); foreach (@outLn) { - # If output contains 'ERROR: ', return -1 and pass back the reason. if ($_ =~ /(.*?ERROR: )/) { $$reason = substr($_, index($_, "ERROR: ") + length("ERROR: ")); @@ -902,6 +1095,79 @@ sub checkOutputExtractReason { return 0; } + +#------------------------------------------------------- + +=head3 checkSSH_Rc + + Description : Check for SSH errors, and command return code failure + + Arguments : $? + command that was issued (do not pass in a password in cmd string, just mask it out) + node the SSH is targeting + function name the SSH call was done in + optional command output + optional final target node (used to prefix "Node: " to error message. Helps in tracing hcp calls on behalf of a node) + optional syslog supression, "NONE", "SSHONLY" defaults to syslog message of any SSH or command error + + Returns : rc = 0 Good output + rc = 255 Error occurred in SSH + rc > 0 Command error + + $outmsg = error message string if $rc !=0 + plus syslog entry for non zero errors + + + Example : ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "Command being issued", $hcp, "testfunc", $out, $node, "NONE"); + +=cut + +#------------------------------------------------------- +sub checkSSH_Rc { + my ( $class, $rc, $cmd, $tgtNode, $functionName, $cmdOutput, $finalTargetNode, $syslogOptions ) = @_; + + my $msgTxt = ''; + my $logit = 2; + + if (!defined $cmdOutput) { + $cmdOutput = ''; + } else { + $cmdOutput = " Output: " . $cmdOutput . " "; + } + + if (!defined $finalTargetNode) { + $finalTargetNode = ''; + } else { + $finalTargetNode = "Node: " . $finalTargetNode . " "; + } + if (defined $syslogOptions) { + if ( $syslogOptions =~ m/NONE/i ) { + $logit = 0; + } elsif ( $syslogOptions =~ m/SSHONLY/i ) { + $logit = 1; + } + } + + + $rc = $rc >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with $tgtNode. + $msgTxt = "$finalTargetNode" . "(Error) In $functionName(), SSH communication to $tgtNode failed for command: $cmd"; + if ($logit > 0 ) { + xCAT::zvmUtils->printSyslog("$msgTxt"); + } + return ($rc, $msgTxt); + } elsif ( $rc != 0 ) { + # Generic failure of the command. + $msgTxt = "$finalTargetNode" . "(Error) In $functionName(), command to $tgtNode failed with return code $rc for: $cmd" . "$cmdOutput"; + if ($logit > 1 ) { + xCAT::zvmUtils->printSyslog("$msgTxt"); + } + return ($rc, $msgTxt); + } + return 0; +} + #------------------------------------------------------- =head3 getDeviceNode @@ -912,12 +1178,12 @@ sub checkOutputExtractReason { Disk address Returns : Device node Example : my $devNode = xCAT::zvmUtils->getDeviceNode($user, $node, $tgtAddr); - + =cut #------------------------------------------------------- sub getDeviceNode { - my ($class, $user, $node, $tgtAddr) = @_; + my ( $class, $user, $node, $tgtAddr ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -925,7 +1191,13 @@ sub getDeviceNode { } # Determine device node - my $out = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep ".$tgtAddr("`; + #my $out = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep ".$tgtAddr("`; + my $cmd = $sudo . ' cat /proc/dasd/devices'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i ".$tgtAddr("`; my @words = split(' ', $out); my $tgtDevNode; @@ -951,12 +1223,12 @@ sub getDeviceNode { Device node Returns : Virtual device address Example : my $addr = xCAT::zvmUtils->getDeviceNodeAddr($user, $node, $deviceNode); - + =cut #------------------------------------------------------- sub getDeviceNodeAddr { - my ($class, $user, $node, $deviceNode) = @_; + my ( $class, $user, $node, $deviceNode ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -966,7 +1238,13 @@ sub getDeviceNodeAddr { # Find device node and determine virtual address # /proc/dasd/devices look similar to this: # 0.0.0100(ECKD) at ( 94: 0) is dasda : active at blocksize: 4096, 1802880 blocks, 7042 MB - my $addr = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep -i "is $deviceNode"`; + #my $addr = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep -i "is $deviceNode"`; + my $cmd = $sudo . ' cat /proc/dasd/devices'; + my $addr = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $addr ) == -1) { + return $addr; + } + $addr = `echo "$addr" | egrep -a -i "is $deviceNode"`; $addr =~ s/ +/ /g; $addr =~ s/^0.0.([0-9a-f]*).*/$1/; chomp($addr); @@ -985,12 +1263,12 @@ sub getDeviceNodeAddr { Returns : 0 Address used -1 Address not used Example : my $ans = xCAT::zvmUtils->isAddressUsed($user, $node, $address); - + =cut #------------------------------------------------------- sub isAddressUsed { - my ($class, $user, $node, $address) = @_; + my ( $class, $user, $node, $address ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -998,7 +1276,13 @@ sub isAddressUsed { } # Search for disk address - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v dasd" | grep "DASD $address"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v dasd" | grep "DASD $address"`; + my $cmd = $sudo . ' /sbin/vmcp q v dasd'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i "DASD $address"`; if ($out) { return 0; } @@ -1015,12 +1299,12 @@ sub isAddressUsed { zHCP Returns : MACID Example : my $macId = xCAT::zvmUtils->getMacID($user, $hcp); - + =cut #------------------------------------------------------- sub getMacID { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1029,11 +1313,11 @@ sub getMacID { # Check /opt/zhcp/conf directory on HCP my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -d /opt/zhcp/conf && echo 'Directory exists'"`; - if ($out =~ m/Directory exists/i) { + if ( $out =~ m/Directory exists/i ) { # Check next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -e /opt/zhcp/conf/next_macid && echo 'File exists'"`; - if ($out =~ m/File exists/i) { + if ( $out =~ m/File exists/i ) { # Do nothing } else { @@ -1060,17 +1344,17 @@ sub getMacID { =head3 generateMacId - Description : Generate a new MACID + Description : Generate a new MACID Arguments : User (root or non-root) zHCP Returns : Nothing Example : my $macId = xCAT::zvmUtils->generateMacId($user, $hcp); - + =cut #------------------------------------------------------- sub generateMacId { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1079,11 +1363,11 @@ sub generateMacId { # Check /opt/zhcp/conf directory on HCP my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -d /opt/zhcp/conf && echo 'Directory exists'"`; - if ($out =~ m/Directory exists/i) { + if ( $out =~ m/Directory exists/i ) { # Check next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -e /opt/zhcp/conf/next_macid && echo 'File exists'"`; - if ($out =~ m/File exists/i) { + if ( $out =~ m/File exists/i ) { # Do nothing } else { @@ -1109,14 +1393,14 @@ sub generateMacId { if ($macId) { # Convert hexadecimal - decimal - $int = hex($macId); - $macId = sprintf("%d", $int); + $int = hex($macId); + $macId = sprintf( "%d", $int ); # Generate new MAC suffix $macId = $macId - 1; # Convert decimal - hexadecimal - $macId = sprintf("%X", $macId); + $macId = sprintf( "%X", $macId ); # Save new MACID $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo $macId > /opt/zhcp/conf/next_macid"`; @@ -1133,14 +1417,14 @@ sub generateMacId { Arguments : User (root or non-root) Node MAC suffix - Returns : MAC address + Returns : MAC address or string containing (Error)... Example : my $mac = xCAT::zvmUtils->createMacAddr($user, $node, $suffix); - + =cut #------------------------------------------------------- sub createMacAddr { - my ($class, $user, $node, $suffix) = @_; + my ( $class, $user, $node, $suffix ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1149,23 +1433,31 @@ sub createMacAddr { # Get node properties from 'zvm' table my @propNames = ('hcp'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; - if (!$hcp) { + if ( !$hcp ) { return -1; } # Get USER Prefix - my $prefix = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q vmlan" | egrep -i "USER Prefix:"`; + my $outmsg; + my $rc; + my $prefix; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q vmlan"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo /sbin/vmcp q vmlan\"", $hcp, "createMacAddr", $out, $node ); + if ($rc != 0) { + return $outmsg; + } + $prefix = `echo "$out" | 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 + # Get MACADDR Prefix instead if USER Prefix is not defined, use saved out data if (!$prefix) { - $prefix = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q vmlan" | egrep -i "MACADDR Prefix:"`; + $prefix = `echo "$out" | egrep -a -i "MACADDR Prefix:"`; $prefix =~ s/(.*?)MACADDR Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; @@ -1179,18 +1471,18 @@ sub createMacAddr { my $mac = $prefix . $suffix; # If length is less than 12, append a zero - if (length($mac) != 12) { + if ( length($mac) != 12 ) { $mac = "0" . $mac; } # Format MAC address $mac = - substr($mac, 0, 2) . ":" - . substr($mac, 2, 2) . ":" - . substr($mac, 4, 2) . ":" - . substr($mac, 6, 2) . ":" - . substr($mac, 8, 2) . ":" - . substr($mac, 10, 2); + substr( $mac, 0, 2 ) . ":" + . substr( $mac, 2, 2 ) . ":" + . substr( $mac, 4, 2 ) . ":" + . substr( $mac, 6, 2 ) . ":" + . substr( $mac, 8, 2 ) . ":" + . substr( $mac, 10, 2 ); return $mac; } @@ -1204,14 +1496,14 @@ sub createMacAddr { Node Returns : Operating system name Example : my $osName = xCAT::zvmUtils->getOs($user, $node); - + =cut #------------------------------------------------------- sub getOs { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1219,9 +1511,15 @@ sub getOs { } # Get operating system - my $out = `ssh -o ConnectTimeout=10 $user\@$node "$sudo cat /etc/*release" | egrep -v "LSB_VERSION"`; - my @results = split('\n', $out); - return (xCAT::zvmUtils->trimStr($results[0])); + #my $out = `ssh -o ConnectTimeout=10 $user\@$node "$sudo cat /etc/*release" | egrep -v "LSB_VERSION"`; + my $cmd = $sudo . ' cat /etc/*release'; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } + $out = `echo "$out" | egrep -a -i -v "LSB_VERSION"`; + my @results = split( '\n', $out ); + return ( xCAT::zvmUtils->trimStr( $results[0] ) ); } #------------------------------------------------------- @@ -1233,14 +1531,14 @@ sub getOs { Node Returns : Architecture of node Example : my $arch = xCAT::zvmUtils->getArch($user, $node); - + =cut #------------------------------------------------------- sub getArch { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1248,9 +1546,14 @@ sub getArch { } # Get host using VMCP - my $arch = `ssh $user\@$node "$sudo uname -p"`; + #my $arch = `ssh $user\@$node "$sudo uname -m"`; + my $cmd = "$sudo uname -m"; + my $arch = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $arch ) == -1) { + return $arch; + } - return (xCAT::zvmUtils->trimStr($arch)); + return ( xCAT::zvmUtils->trimStr($arch) ); } #------------------------------------------------------- @@ -1262,14 +1565,14 @@ sub getArch { Profile name Returns : User profile Example : my $profile = xCAT::zvmUtils->getUserProfile($user, $hcp, $name); - + =cut #------------------------------------------------------- sub getUserProfile { # Get inputs - my ($class, $user, $hcp, $profile) = @_; + my ( $class, $user, $hcp, $profile ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1283,10 +1586,8 @@ sub getUserProfile { # Set directory for cache my $cache = '/var/opt/zhcp/cache'; - # If the cache directory does not exist if (!(`ssh $user\@$hcp "$sudo test -d $cache && echo Exists"`)) { - # Create cache directory $out = `ssh $user\@$hcp "$sudo mkdir -p $cache"`; } @@ -1305,7 +1606,7 @@ sub getUserProfile { # If the current time is greater than 5 minutes of the file timestamp my $interval = 300; # 300 seconds = 5 minutes * 60 seconds/minute - if ($curTime > $fileTime + $interval) { + if ( $curTime > $fileTime + $interval ) { # Get user profiles and save it in a file $out = `ssh $user\@$hcp "$sudo $hcpDir/smcli Profile_Query_DM -T $profile > $file"`; @@ -1323,6 +1624,67 @@ sub getUserProfile { #------------------------------------------------------- +=head3 getVswitchIdsFromDirectory + + Description : Get the nicdef switch names of a given node + Arguments : User (root or non-root) + zHCP + Userid + Returns : Vswitch names array or or string containing (Error)... + Example : my $vSwitchNamers = xCAT::zvmCPUtils->getVswitchIdsFromDirectory($user, $hcp, $userId); + +=cut + +#------------------------------------------------------- +sub getVswitchIdsFromDirectory { + + # Get inputs + my ( $class, $user, $hcp ,$userId ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + my @vswitch; + + # Get VSwitchs in directory + # only get lines that have SYSTEM switchname in them + # smcli Image_Definition_Query_DM -T xcat -k nicdef + # NICDEF=VDEV=0600 TYPE=QDIO LAN=SYSTEM SWITCHNAME=XCATVSW1 + # NICDEF=VDEV=0700 TYPE=QDIO LAN=SYSTEM SWITCHNAME=XCATVSW2 + # + + my $outmsg; + my $rc; + xCAT::zvmUtils->printSyslog("Calling: ssh $user\@$hcp $sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF | egrep -i 'SWITCHNAME'"); + my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF\"", $hcp, "getVswitchIdsFromDirectory", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i 'SWITCHNAME'`; + # if there is nothing found, log that and return; + if ( !length($out) ) { + xCAT::zvmUtils->printSyslog("No SWITCHNAME found in NICDEF statement for userid $userId"); + return (@vswitch); + } + xCAT::zvmUtils->printSyslog("$userId output: $out"); + my @lines = split( '\n', $out ); + my @parms; + my $vswitchToken = ''; + + foreach (@lines) { + @parms = split( ' ', $_ ); + $vswitchToken = $parms[3]; + @parms = split( '=', $vswitchToken ); + push( @vswitch, $parms[1] ); + } + + return (@vswitch); +} + +#------------------------------------------------------- + =head3 inArray Description : Checks if a value exists in an array @@ -1330,15 +1692,15 @@ sub getUserProfile { Search array Returns : The searched expression Example : my $rtn = xCAT::zvmUtils->inArray($needle, @haystack); - + =cut #------------------------------------------------------- sub inArray { # Get inputs - my ($class, $needle, @haystack) = @_; - return grep { $_ eq $needle } @haystack; + my ( $class, $needle, @haystack ) = @_; + return grep{ $_ eq $needle } @haystack; } #------------------------------------------------------- @@ -1347,54 +1709,352 @@ sub inArray { Description : Get the operating system of a given node Arguments : User (root or non-root) - Node - Returns : Operating system name + Node or IP Address + Callback handle (optional). Allows the routine, + to write error messages when SSH fails. + Returns : Operating system name & version as a single word. + Otherwise, an empty string. Example : my $os = xCAT::zvmUtils->getOsVersion($user, $node); - + my $os = xCAT::zvmUtils->getOsVersion($user, $node, $callback); + =cut #------------------------------------------------------- sub getOsVersion { # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node, $callback ) = @_; + + my $osVer = ''; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $os = ''; - my $version = ''; + # Contact the system to extract the possible files which contain pertinent data. + #my $releaseInfo = `ssh -qo ConnectTimeout=2 $user\@$node "$sudo ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; + my $cmd = $sudo . ' ls /dev/null '. $locAllEtcVerFiles . ' 2>/dev/null'; + my $releaseInfo = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); + if (xCAT::zvmUtils->checkOutput( $releaseInfo ) == -1) { + return ''; + } + $releaseInfo = `echo "$releaseInfo" | xargs grep ''`; + $osVer = buildOsVersion( $callback, $releaseInfo, 'all' ); + + return $osVer; +} + +#------------------------------------------------------- + +=head3 getOSFromIP + + Description : Get the operating system name and + version for the system at the specified + IP Address. + Arguments : Callback handle + z/VM userid of the system + IP Address + Type of IP address (not currently used) + Returns : Operating system name & version as a single word. + Otherwise, an empty string. + Example : my $os = xCAT::zvmUtils->getOSFromIP( $callback, $userid, $ipAddr, $ipVersion }; + +=cut + +#------------------------------------------------------- +sub getOSFromIP { + my ( $class, $callback, $activeSystem, $ipAddr, $ipVersion ) = @_; + + my $osVer = ''; + my $rc = 0; # Get operating system - my $release = `ssh -o ConnectTimeout=2 $user\@$node "$sudo cat /etc/*release"`; - my @lines = split('\n', $release); - if (grep(/SUSE Linux Enterprise Server/, @lines)) { - $os = 'sles'; - $version = `echo "$release" | grep "VERSION ="`; - $version =~ s/\s*$//; - $version =~ s/^\s*//; - $version =~ tr/\.//; - $version =~ s/[^0-9]*([0-9]+).*/$1/; - $os = $os . $version; - - # Append service level - $version = `echo "$release" | grep "LEVEL ="`; - $version =~ s/\s*$//; - $version =~ s/^\s*//; - $version =~ tr/\.//; - $version =~ s/[^0-9]*([0-9]+).*/$1/; - $os = $os . 'sp' . $version; - } elsif (grep(/Red Hat Enterprise Linux Server/, @lines)) { - $os = 'rhel'; - $version = $lines[0]; - $version =~ tr/\.//; - $version =~ s/([A-Za-z\s\(\)]+)//g; - $os = $os . $version; + my $releaseInfo = `ssh -qo ConnectTimeout=2 $ipAddr "ls /dev/null $locAllEtcVerFiles 2>/dev/null"`; + $rc = $? >> 8; + if ( $rc == 0 ) { + $releaseInfo = `echo "$releaseInfo" | xargs grep ''`; + $osVer = buildOsVersion( $callback, $releaseInfo, 'all' ); + } else { + if ( $callback ) { + # We have a callback so we can write an error message. + if ( $rc == 255 ) { + my $rsp; + push @{$rsp->{data}}, "Unable to communicate with $activeSystem at $ipAddr using ssh."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } else { + my $rsp; + push @{$rsp->{data}}, "Received an unexpected ssh return code $rc from $activeSystem at $ipAddr."; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + } } - return xCAT::zvmUtils->trimStr($os); + return $osVer; +} + +#------------------------------------------------------- + +=head3 stripHeader + + Description : Scans an input string to find only + lines that match a specified header string + and return an array of those lines without + the prefix. + Arguments : Multi-line string to scan + String to match + Returns : Array of matching lines. + Example : my @lines = stripHeader( $releaseInfo, '^/etc/os-release:' ); + ($ver) = stripHeader( $releaseInfo, "^$locEtcUnitedLinux:" ); + +=cut + +#------------------------------------------------------- +sub stripHeader { + my ( $inputLines, $matchString ) = @_; + my @outputLines; + + my @lines = split( /\n/, $inputLines ); + foreach my $line ( @lines ){ + if ( $line =~ m/$matchString/ ) { + $line =~ s/$matchString//g; + chomp $line; + if ( $line eq '' ) { next } + push @outputLines, $line; + } + } + + return @outputLines; +} + + +#------------------------------------------------------- + +=head3 buildOsVersion + + Description : Build the OS version string from the + provided data files. + + Note: Only RHEL and SLES are supported + z/VM xCAT supported distributions. + This routine has code for other distributions + supported by common xCAT. If z/VM + ever supports another distribution + then we need to verify that the code + gets the correct version information. + Arguments : Callback (or undef) + Operating system data file(s) output + Type of data to return: + 'all' - $os$ver.$rel + 'all_comma' - $os,$ver.$rel + 'os' - $os + 'version' - $ver + 'release' - $rel + '' - $os$ver + Returns : Operating system name & version as a + single word. + Example : my $osVer = buildOsVersion( $callback, $releaseInfo, $type ); + +=cut + +#------------------------------------------------------- +sub buildOsVersion { + my $callback = shift; + my $releaseInfo = shift; + my $type = shift; + + my $os = 'unknown'; # OS indicator, e.g. "rhel", "SLES" + my $ver = ''; # Version indicator, e.g. "7.1", + my $version = ''; + my $rel = ''; + my $line = ''; + my @lines; + + # Test strings that were used when we added the original support. These + # strings are what we used to simulate the other common Linux distros. + #$releaseInfo = "$locEtcSuseRelease:SUSE Linux Enterprise Server 11 (s390x)\n$locEtcSuseRelease:VERSION = 11\n$locEtcSuseRelease:PATCHLEVEL = 4.32"; + #$releaseInfo = "$locEtcDebianVersion:1.0\n$locEtcIssue:debian release"; + #$releaseInfo = "$locEtcUnitedLinux:2.1a"; + #$releaseInfo = "$locEtcLsbRelease:Ubuntu\n$locEtcLsbRelease: DISTRIB_ID=Ubuntu\n$locEtcLsbRelease: DISTRIB_RELEASE=4.1"; + + my @relOut = split('\n', $releaseInfo); + if ( grep( /^$locEtcOsRelease:/, @relOut ) ) { + my $version = ''; + my $version_id; + my $id; + my $id_like; + my $name; + my $prettyname; + my $verrel = ''; + + my @text = stripHeader( $releaseInfo, "^$locEtcOsRelease:" ); + foreach my $line ( @text ) { + if ( $line =~ /^\s*VERSION=\"?([0-9\.]+).*/ ) { + $version = $1; + } + if($line =~ /^\s*VERSION_ID=\"?([0-9\.]+).*/){ + $version_id = $1; + } + + if ( $line =~ /^\s*Base release\s?([0-9\.]+).*/ ) { + $version = $1; + $id = 'BASE'; + } + + if ( $line =~ /^\s*ID=\"?([0-9a-z\_\-\.]+).*/ ) { + $id = $1; + } + if ( $line =~ /^\s*ID_LIKE=\"?([0-9a-z\_\-\.]+).*/ ) { + $id_like = $1; + } + + if ( $line =~ /^\s*NAME=\"?(.*)/ ) { + $name = $1; + } + if($line =~ /^\s*PRETTY_NAME=\"?(.*)/){ + $prettyname = $1; + } + } + + $os = $id; + if ( !$os and $id_like ) { + $os = $id_like; + } + + $verrel = $version; + if ( !$verrel and $version_id ) { + $verrel = $version_id; + } + + if( !$name and $prettyname ){ + $name = $prettyname; + } + + # Note: xcat::Utils->osver() sets this value with an 's' but zvm.pm + # does not use it. So for now, we don't set 's'. + #if ( $os =~ /rhel/ and $name =~ /Server/i ) { + # # $os = "rhels"; + #} + + if ( $verrel =~ /([0-9]+)\.?(.*)/ ) { + $ver = $1; + $rel = $2; + } + } elsif ( grep( /^$locEtcRedhatRelease:/, @relOut ) ) { + my @text = stripHeader( $releaseInfo, "^$locEtcRedhatRelease:" ); + my $line = $text[0]; + chomp( $line ); + $os = "rh"; + my $verrel = $line; + $ver = $line; + + if ( $type ) { + $verrel =~ s/[^0-9]*([0-9.]+).*/$1/; + ($ver,$rel) = split /\./, $verrel; + } else { + $ver=~ tr/\.//; + $ver =~ s/[^0-9]*([0-9]+).*/$1/; + } + + if ( $line =~ /AS/ ) { $os = 'rhas' } + elsif ( $line =~ /ES/ ) { $os = 'rhes' } + elsif ( $line =~ /WS/ ) { $os = 'rhws' } + elsif ( $line =~ /Server/ ) { + if ( $type ) { + $os = 'rhel'; + # Note: xcat::Utils->osver() sets this value with an 's' but zvm.pm + # does not use it. So for now, we don't set 's'. + #$os = 'rhels'; + } else { + $os = 'rhserver'; + } + } elsif ( $line =~ /Client/ ) { + if ( $type ) { + $os = 'rhel'; + } else { + $os = 'rhclient'; + } + } elsif ( grep( /$locEtcFedoraRelease:/, @relOut ) ) { $os = 'rhfc' } + } elsif ( grep( /^$locEtcSuseRelease:/, @relOut ) ) { + my @lines = stripHeader( $releaseInfo, "^$locEtcSuseRelease:" ); + if ( grep /SLES|Enterprise Server/, @lines ) { $os = "sles" } + if ( grep /SLEC/, @lines ) { $os = "slec" } + $ver = $lines[0]; + $ver =~ tr/\.//; + $ver =~ s/[^0-9]*([0-9]+).*/$1/; + + $rel = $lines[2]; + $rel =~ tr/\.//; + $rel =~ s/[^0-9]*([0-9]+).*/$1/; + } elsif ( grep( /^$locEtcUnitedLinux:/, @relOut ) ) { + # Note: Not a z/VM xCAT supported distribution. + # If we ever support this then we need to verify this code + # gets the correct version information. + ($ver) = stripHeader( $releaseInfo, "^$locEtcUnitedLinux:" ); + $os = "ul"; + $ver =~ tr/\.//; + $ver =~ s/[^0-9]*([0-9]+).*/$1/; + } elsif ( grep( /$locEtcLsbRelease:/, @relOut ) ) { + # Ubuntu release + my @text = stripHeader( $releaseInfo, "^$locEtcLsbRelease:" ); + chomp( @text ); + my $distrib_id = ''; + my $distrib_rel = ''; + + foreach ( @text ) { + if ( $_ =~ /^\s*DISTRIB_ID=(.*)$/ ) { + $distrib_id = $1; # last DISTRIB_ID value in file used + } elsif ( $_ =~ /^\s*DISTRIB_RELEASE=(.*)$/ ) { + $distrib_rel = $1; # last DISTRIB_RELEASE value in file used + } + } + + if ( $distrib_id =~ /^(Ubuntu|"Ubuntu")\s*$/ ) { + $os = "ubuntu"; + + if ( $distrib_rel =~ /^(.*?)\s*$/ ) { # eliminate trailing blanks, if any + $distrib_rel = $1; + } + if ( $distrib_rel =~ /^"(.*?)"$/ ) { # eliminate enclosing quotes, if any + $distrib_rel = $1; + } + $ver = $distrib_rel; + } + } elsif ( grep( /^$locEtcDebianVersion:/, @relOut ) ) { + # Debian release + if ( grep( /^$locEtcIssue:/, @relOut ) ) { + ($line) = stripHeader( $releaseInfo, "^$locEtcIssue:" ); + if ( $line =~ /debian.*/i ) { + $os = "debian"; + ($ver) = stripHeader( $releaseInfo, "^$locEtcDebianVersion:" ); + } + } + } + + my $outString = ''; + if ( $type eq 'all_comma' ) { + if ( $rel ne "") { + $outString = "$os,$ver.$rel"; + } else { + $outString = "$os,$ver"; + } + } elsif ( $type eq 'all' ) { + if ( $rel ne "") { + $outString = "$os$ver.$rel"; + } else { + $outString = "$os$ver"; + } + } elsif ( $type eq 'os' ) { + $outString = $os; + } elsif ( $type eq 'version' ) { + $outString = $ver; + } elsif ( $type eq 'release' ) { + $outString = $os; + } else { + $outString = "$os$ver"; + } + + return $outString; } #------------------------------------------------------- @@ -1406,14 +2066,13 @@ sub getOsVersion { Node Returns : zFCP device info Example : my $info = xCAT::zvmUtils->getZfcpInfo($user, $node); - + =cut #------------------------------------------------------- sub getZfcpInfo { - # Get inputs - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1421,14 +2080,22 @@ sub getZfcpInfo { } # Get zFCP device info - my $info = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/lszfcp -D"`; + #my $info = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/lszfcp -D"`; + my $cmd = "$sudo /sbin/lszfcp -D"; + my $info = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + # will not check for connection errors here my @zfcp = split("\n", $info); if (!$info || $info =~ m/No zfcp support/i || $info =~ m/No fcp devices found/i) { return; } # Get SCSI device and their attributes - my $scsi = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/lsscsi"`; + #my $scsi = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/lsscsi"`; + $cmd = "$sudo /usr/bin/lsscsi"; + my $scsi = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $info ) == -1) { + return $info; + } $info = ""; my @args; @@ -1441,16 +2108,16 @@ sub getZfcpInfo { foreach (@zfcp) { @args = split(" ", $_); - $id = $args[1]; + $id = $args[1]; @args = split("/", $args[0]); $device = $args[0]; - $wwpn = $args[1]; - $lun = $args[2]; + $wwpn = $args[1]; + $lun = $args[2]; # Make sure WWPN and LUN do not have 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); # Find the device name $tmp = `echo "$scsi" | egrep -i $id`; @@ -1458,7 +2125,13 @@ sub getZfcpInfo { chomp($tmp); # Find the size in MiB - $size = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/sg_readcap $tmp" | egrep -i "Device size:"`; + #$size = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/sg_readcap $tmp" | egrep -i "Device[[:space:]]size:"`; + my $cmd = $sudo . ' /usr/bin/sg_readcap ' . $tmp; + $size = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $size ) == -1) { + return $size; + } + $size = `echo "$size" | egrep -a -i "Device[[:space:]]size:"`; $size =~ s/Device size: //g; @args = split(",", $size); $size = xCAT::zvmUtils->trimStr($args[1]); @@ -1479,19 +2152,19 @@ sub getZfcpInfo { Returns : 1 Node exists 0 Node does not exists Example : my $out = xCAT::zvmUtils->isHypervisor($node); - + =cut #------------------------------------------------------- sub isHypervisor { # Get inputs - my ($class, $node) = @_; + my ( $class, $node ) = @_; # Look in 'zvm' table - my $tab = xCAT::Table->new("hypervisor", -create => 1, -autocommit => 0); + my $tab = xCAT::Table->new( "hypervisor", -create => 1, -autocommit => 0 ); - my @results = $tab->getAllAttribsWhere("node like '%" . $node . "%'", 'type'); + my @results = $tab->getAllAttribsWhere( "node = '" . $node . "'", 'type' ); foreach (@results) { # Return 'TRUE' if given node is in the table @@ -1503,6 +2176,37 @@ sub isHypervisor { return 0; } +#------------------------------------------------------- + +=head3 isOSVerSupported + + Description : Determine if the specified OS version supported. + Arguments : OS version string (e.g. RHEL6, RHEL6.1, SLES11sp2) + Returns : 1 - version is supported + 0 - version is not supported + Example : my $supported = xCAT::zvmUtils->isOSVerSupported( $os ); + +=cut + +#------------------------------------------------------- +sub isOSVerSupported { + my ( $class, $osVer ) = @_; + + # Keep just the OS distro name and the version, ie. drop any release info. + $osVer = lc( $osVer ); + if ( $osVer =~ /([a-z]+[0-9]+)/ ) { + $osVer = $1; + } + + # Check against our list of supported versions. + if ( $supportedVersions{$osVer} ) { + return 1; + } else { + return 0; + } +} + + #------------------------------------------------------- =head3 getSudoer @@ -1512,18 +2216,17 @@ sub isHypervisor { Returns : Sudoer user name Sudo keyword Example : my ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); - + =cut #------------------------------------------------------- sub getSudoer { - # Get inputs - my ($class) = @_; + my ( $class ) = @_; # Use sudo or not on zHCP my @propNames = ('username'); - my $propVals = xCAT::zvmUtils->getTabPropsByKey('passwd', 'key', 'sudoer', @propNames); + my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'passwd', 'key', 'sudoer', @propNames ); my $sudo = "sudo"; my $user = $propVals->{'username'}; @@ -1549,12 +2252,12 @@ sub getSudoer { Returns : vdev An address which is free to use -1 No free address is left Example : my $vdev = xCAT::zvmUtils->getFreeAddress($user, $node, $type); - + =cut #------------------------------------------------------- sub getFreeAddress { - my ($class, $user, $node, $type) = @_; + my ( $class, $user, $node, $type ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -1566,43 +2269,47 @@ sub getFreeAddress { my $freeAddressHex = sprintf('%04X', $freeAddress); # All device type names in VM, do not contain CPU - my $deviceTypesVm = 'CONS|CTCA|DASD|FCP|GRAF|LINE|MSGD|OSA|PRT|PUN|RDR|SWCH|TAPE'; - + #my $deviceTypesVm = 'CONS|CTCA|DASD|FCP|GRAF|LINE|MSGD|OSA|PRT|PUN|RDR|SWCH|TAPE'; + my $deviceTypesVm = '^CONS|^CTCA|^DASD|^FCP|^GRAF|^LINE|^MSGD|^OSA|^PRT|^PUN|^RDR|^SWCH|^TAPE'; # All device type names in user directory, do not contain CPU my $deviceTypesUserDir = 'CONSOLE|MDISK|NICDEF|SPOOL|RDEVICE'; + # Search for all address that is in use my $allUsedAddr; if ($type eq 'vmcp') { - # When the node is up, vmcp can be used - $allUsedAddr = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v all | awk '$1 ~/^($deviceTypesVm)/ {print $2}' | sort"`; + #$allUsedAddr = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v all | awk '\$1 ~/^($deviceTypesVm)/ {print \$2}' | sort"`; + my $cmd = $sudo . '/sbin/vmcp q v all'; + my $allUsedAddr = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $allUsedAddr ) == -1) { + return -1; + } + $allUsedAddr = `echo '$allUsedAddr' | egrep -a -i "$deviceTypesVm"`; } else { - # When the node is down, use zHCP to get its user directory entry # Get HCP my @propNames = ('hcp', 'userid'); - my $propVals = xCAT::zvmUtils->getNodeProps('zvm', $node, @propNames); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userID my $userId = $propVals->{'userid'}; # Get user directory entry - my $userDirEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"'`; + my $userDirEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`; # Get profile if user directory entry include a profile if ($userDirEntry =~ "INCLUDE ") { - my $profileName = `cat $userDirEntry | awk '$1 ~/^(INCLUDE)/ {print $2}`; + my $profileName = `cat $userDirEntry | awk '\$1 ~/^(INCLUDE)/ {print \$2}'`; $profileName = xCAT::zvmUtils->trimStr($profileName); $userDirEntry .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $profileName"`; } # Get all defined device address - $allUsedAddr = `cat $userDirEntry | awk '$1 ~/^($deviceTypesUserDir)/ {print $2}' | sort`; - + $allUsedAddr = `cat $userDirEntry | awk '\$1 ~/^($deviceTypesUserDir)/ {print \$2}' | sort`; # Get all linked device address - $allUsedAddr .= `cat $userDirEntry | awk '$1 ~/^(LINK)/ {print $4}' | sort`; + $allUsedAddr .= `cat $userDirEntry | awk '\$1 ~/^(LINK)/ {print \$4}' | sort`; } # Loop to get the lowest free address @@ -1626,14 +2333,14 @@ sub getFreeAddress { Arguments : User (root or non-root) zHCP (to query on) node - Returns : In nanoseconds for used CPU time + Returns : In nanoseconds for used CPU time or string containing (Error)... Example : my $out = xCAT::zvmUtils->getUsedCpuTime($hcp, $node); - + =cut #------------------------------------------------------- sub getUsedCpuTime { - my ($class, $user, $hcp, $node) = @_; + my ( $class, $user, $hcp , $node ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -1646,7 +2353,14 @@ sub getUsedCpuTime { my $userId = xCAT::zvmCPUtils->getUserId($user, $node); # Call IUO function to query CPU used time - my $time = `ssh $user\@$hcp "$sudo $dir/smcli Image_Performance_Query -T $userId -c 1" | egrep -i "Used CPU time:"`; + my $outmsg; + my $rc; + my $time = `ssh $user\@$hcp "$sudo $dir/smcli Image_Performance_Query -T $userId -c 1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli Image_Performance_Query -T $userId -c 1\"", $hcp, "getUsedCpuTime", $time, $node ); + if ($rc != 0) { + return $outmsg; + } + $time = `echo "$time" | egrep -a -i "Used CPU time:"`; $time =~ s/^Used CPU time:(.*)/$1/; $time =~ s/"//g; $time =~ s/^\s+//; @@ -1669,30 +2383,33 @@ sub getUsedCpuTime { Node Returns : Running time Example : my $out = xCAT::zvmUtils->getUpTime($user, $node); - + =cut #------------------------------------------------------- sub getUpTime { - my ($class, $user, $node) = @_; + my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo uptime"`; + #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo uptime"`; + my $cmd = "$sudo uptime"; + my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return $out; + } $out = xCAT::zvmUtils->trimStr($out); $out =~ /.*up +(?:(\d+) days?,? +)?(\d+):(\d+),.*/; my $uptime; if (!$1 && !$2) { - # Special case for less than 1 hour, will display X min $out =~ /.*up +(\d+) min,.*/; $uptime = "0 days $3 min"; } elsif (!$1) { - # Special case for less than 1 day, will display X hr X min $uptime = "0 days $2 hr $3 min"; } else { @@ -1710,24 +2427,22 @@ sub getUpTime { Arguments : Bytes Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromByte($bytes); - + =cut #------------------------------------------------------- sub getSizeFromByte { - my ($class, $bytes) = @_; + my ( $class, $bytes ) = @_; - my $size = ($bytes) / (1024 * 1024); - if ($size > (1024 * 5)) { + my $size = ($bytes)/(1024*1024); + if ($size > (1024*5)) { $size = ($size / 1024); - # If the size > 5G, will use G to represent - $size = sprintf("%.1f", $size); + $size = sprintf("%.1f",$size); $size = $size . 'G'; } else { - # If the size < 5G, will use M to represent - $size = sprintf("%d", $size); + $size = sprintf("%d",$size); $size = $size . 'M'; } @@ -1743,7 +2458,7 @@ sub getSizeFromByte { Arguments : Node Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromCyl($cyl); - + =cut #------------------------------------------------------- @@ -1751,7 +2466,7 @@ sub getSizeFromCyl { my ($class, $cyl) = @_; my $bytes = ($cyl * 737280); - my $size = xCAT::zvmUtils->getSizeFromByte($bytes); + my $size = xCAT::zvmUtils->getSizeFromByte($bytes); return ($size); } @@ -1764,15 +2479,15 @@ sub getSizeFromCyl { Arguments : Page Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromPage($page); - + =cut #------------------------------------------------------- sub getSizeFromPage { - my ($class, $page) = @_; + my ( $class, $page ) = @_; my $bytes = ($page * 4096); - my $size = xCAT::zvmUtils->getSizeFromByte($bytes); + my $size = xCAT::zvmUtils->getSizeFromByte($bytes); return ($size); } @@ -1785,9 +2500,9 @@ sub getSizeFromPage { Description : Get total count of logical CPUs in the LPAR Arguments : User (root or non-root) zHCP - Returns : Total CPU count + Returns : Total CPU count or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparCpuTotal($user, $hcp); - + =cut #------------------------------------------------------- @@ -1799,7 +2514,14 @@ sub getLparCpuTotal { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo" | grep "LPAR CPUs Total"`; + my $outmsg; + my $rc; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getLparCpuTotal", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "LPAR CPUs Total"`; my @results = split(' ', $out); return ($results[3]); @@ -1812,9 +2534,9 @@ sub getLparCpuTotal { Description : Get count of used logical CPUs in the LPAR Arguments : User (root or non-root) zHCP - Returns : Used CPU count + Returns : Used CPU count or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparCpuUsed($user, $hcp); - + =cut #------------------------------------------------------- @@ -1826,7 +2548,14 @@ sub getLparCpuUsed { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo" | grep "LPAR CPUs Configured"`; + my $outmsg; + my $rc; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getLparCpuUsed", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "LPAR CPUs Configured"`; my @results = split(' ', $out); return ($results[3]); @@ -1839,9 +2568,9 @@ sub getLparCpuUsed { Description : Get the model of this CEC (LPAR) Arguments : User (root or non-root) zHCP - Returns : Model of this CEC + Returns : Model of this CEC or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getCecModel($user, $hcp); - + =cut #------------------------------------------------------- @@ -1853,7 +2582,14 @@ sub getCecModel { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo" | grep "^Type:"`; + my $outmsg; + my $rc; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getCecModel", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "^Type:"`; my @results = split(' ', $out); return ($results[1]); @@ -1866,21 +2602,28 @@ sub getCecModel { Description : Get the vendor of this CEC (LPAR) Arguments : User (root or non-root) zHCP - Returns : Vendor of this CEC + Returns : Vendor of this CEC or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getCecVendor($user, $hcp); - + =cut #------------------------------------------------------- sub getCecVendor { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo" | grep "Manufacturer"`; + my $outmsg; + my $rc; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getCecVendor", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "Manufacturer"`; my @results = split(' ', $out); return ($results[1]); @@ -1893,9 +2636,9 @@ sub getCecVendor { Description : Get the info(name & version) for this hypervisor Arguments : User (root or non-root) zHCP - Returns : Name & version of this hypervisor + Returns : Name & version of this hypervisor or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getHypervisorInfo($user, $hcp); - + =cut #------------------------------------------------------- @@ -1907,7 +2650,14 @@ sub getHypervisorInfo { $sudo = ""; } - my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo" | grep "VM00 Control Program"`; + my $outmsg; + my $rc; + my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getHypervisorInfo", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "VM00 Control Program"`; my @results = split(' ', $out); my $str = "$results[3] $results[4]"; @@ -1922,9 +2672,9 @@ sub getHypervisorInfo { Description : Get the total physical memory of this LPAR Arguments : User (root or non-root) zHCP - Returns : Total physical memory + Returns : Total physical memory or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparMemoryTotal($user, $hcp); - + =cut #------------------------------------------------------- @@ -1936,7 +2686,14 @@ sub getLparMemoryTotal { $sudo = ""; } - my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Info_Query" | grep "real storage"`; + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Info_Query"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli System_Info_Query\"", $hcp, "getLparMemoryTotal", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "real storage"`; my @results = split(' ', $out); return ($results[5]); @@ -1949,9 +2706,9 @@ sub getLparMemoryTotal { Description : Get the offline physical memory of this LPAR Arguments : User (root or non-root) zHCP - Returns : Offline physical memory + Returns : Offline physical memory or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparMemoryOffline($user, $hcp); - + =cut #------------------------------------------------------- @@ -1963,7 +2720,14 @@ sub getLparMemoryOffline { $sudo = ""; } - my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Info_Query" | grep "real storage"`; + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Info_Query"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli System_Info_Query\"", $hcp, "getLparMemoryOffline", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "real storage"`; my @results = split(' ', $out); return ($results[14]); @@ -1976,9 +2740,9 @@ sub getLparMemoryOffline { Description : Get the used physical memory of this LPAR Arguments : User (root or non-root) zHCP - Returns : Used physical memory + Returns : Used physical memory or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparMemoryUsed($user, $hcp); - + =cut #------------------------------------------------------- @@ -1990,11 +2754,18 @@ sub getLparMemoryUsed { $sudo = ""; } - my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Performance_Info_Query " | grep "Used memory pages:"`; + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Performance_Info_Query "`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli System_Performance_Info_Query \"", $hcp, "getLparMemoryUsed", $out ); + if ($rc != 0) { + return $outmsg; + } + $out = `echo "$out" | egrep -a -i "Used memory pages:"`; my @results = split(':', $out); - my $page = xCAT::zvmUtils->trimStr($results[1]); - my $size = xCAT::zvmUtils->getSizeFromPage($page); + my $page = xCAT::zvmUtils->trimStr( $results[1] ); + my $size = xCAT::zvmUtils->getSizeFromPage( $page ); return ($size); } @@ -2003,13 +2774,13 @@ sub getLparMemoryUsed { =head3 getDiskPoolUsed - Description : Get the used size of specified disk pool + Description : Get the used size of specified disk pool Arguments : User (root or non-root) zHCP Disk pool - Returns : Used size of specified disk pool + Returns : Used size of specified disk pool Example : my $out = xCAT::zvmCPUtils->getDiskPoolUsed($user, $hcp, $diskpool); - + =cut #------------------------------------------------------- @@ -2031,9 +2802,8 @@ sub getDiskPoolUsed { foreach (@lines) { @results = split(' ', $_); if ($results[1] =~ '^9336') { - # Change the format from blocks (512 byte) to cylinder (737280) - my $cyls = ($results[3] * 512) / (737280); + my $cyls = ($results[3] * 512)/(737280); $used += $cyls; } elsif ($results[1] =~ '^3390') { $used += $results[3]; @@ -2047,13 +2817,13 @@ sub getDiskPoolUsed { =head3 getDiskPoolFree - Description : Get the free size of specified disk pool + Description : Get the free size of specified disk pool Arguments : User (root or non-root) - zHCP + zHCP Disk pool - Returns : Free size of specified disk pool + Returns : Free size of specified disk pool Example : my $out = xCAT::zvmCPUtils->getDiskPoolFree($user, $hcp, $diskpool); - + =cut #------------------------------------------------------- @@ -2075,9 +2845,8 @@ sub getDiskPoolFree { foreach (@lines) { @results = split(' ', $_); if ($results[1] =~ '^9336') { - # Change the format from blocks (512 byte) to cylinder (737280) - my $cyls = ($results[3] * 512) / (737280); + my $cyls = ( $results[3] * 512 ) / ( 737280 ); $free += $cyls; } elsif ($results[1] =~ '^3390') { $free += $results[3]; @@ -2093,23 +2862,23 @@ sub getDiskPoolFree { Description : Get the max memory of a given node Arguments : User (root or non-root) - zHCP + zHCP Node Returns : Max memory Example : my $maxMemory = xCAT::zvmCPUtils->getMaxMemory($user, $hcp, $node); - + =cut #------------------------------------------------------- sub getMaxMemory { - my ($class, $user, $hcp, $node) = @_; + my ($class, $user, $hcp , $node) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $userId = xCAT::zvmCPUtils->getUserId($user, $node); + my $userId = xCAT::zvmCPUtils->getUserId( $user, $node ); # Query the maximum memory allowed in user directory entry my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k STORAGE_MAXIMUM"`; @@ -2118,6 +2887,436 @@ sub getMaxMemory { return ($results[1]); } + +#------------------------------------------------------- + +=head3 handlePowerUp + + Description : Handle power up of nodes whose IP information + may change on power up. The routine will weed out + non-s390x architectures and do processing for nodes whose + status in the zvm table is "POWER_UP=1". + Arguments : Callback + Nodes that are to be handled. + Returns : None + Example : xCAT::zvmUtils->handlePowerUp( $callback, $nodes, \%args ); + +=cut + +#------------------------------------------------------- +sub handlePowerUp { + my ( $class, $callback, $nodes, $argsRef ) = @_; + my %propHash; # Property hash used to fill in various tables + my %sysInfo; # Hash to hold system information + my %args = %$argsRef; # Command arguments, verbose is one such operand + + my $nodetypeTab = xCAT::Table->new('nodetype'); + my $nodetypeHash = $nodetypeTab->getNodesAttribs( $nodes, ['arch'] ); + my $zvmTab = xCAT::Table->new('zvm'); + my $zvmHash = $zvmTab->getNodesAttribs( $nodes, ['hcp', 'status', 'userid'] ); + + foreach my $node ( keys %{$nodetypeHash} ) { + next if ( $nodetypeHash->{$node}->[0]->{'arch'} !~ 's390x' ); + my $status = $zvmHash->{$node}->[0]->{'status'}; + if ( $status =~ 'POWER_UP=1' ) { + my $userid = $zvmHash->{$node}->[0]->{'userid'}; + my $rc = xCAT::zvmUtils->findAccessIP( $callback, + $userid, + $zvmHash->{$node}->[0]->{'hcp'}, + \%sysInfo, + \%args ); + if ( $rc == 0 ) { + # Got what we needed so we can update tables with the current information. + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Updating xCAT tables with IP information for $node:\n" . + " ip: $sysInfo{$userid}{'ipAddr'}, hostname: $sysInfo{$userid}{'hostname'}\n". + " macAddr: $sysInfo{$userid}{'macAddr'}, switch: $sysInfo{$userid}{'switch'}\n". + " Network Adapter VDEV: $sysInfo{$userid}{'adapterAddr'}"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + + substr( $sysInfo{$userid}{'macAddr'}, 10, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 8, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 6, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 4, 0 ) = ':'; + substr( $sysInfo{$userid}{'macAddr'}, 2, 0 ) = ':'; + %propHash = ( + 'disable' => 0, + 'interface' => $sysInfo{$userid}{'vdev'}, + 'mac' => $sysInfo{$userid}{'macAddr'}, + ); + xCAT::zvmUtils->setNodeProps( 'mac', $node, \%propHash ); + + %propHash = ( + 'disable' => 0, + 'hostnames' => $sysInfo{$userid}{'hostname'}, + 'ip' => $sysInfo{$userid}{'ipAddr'}, + ); + xCAT::zvmUtils->setNodeProps( 'hosts', $node, \%propHash ); + + $status =~ s/POWER_UP=1/POWER_UP=0/g; + %propHash = ( + 'status' => $status, + ); + xCAT::zvmUtils->setNodeProps( 'zvm', $node, \%propHash ); + + my $out = `/opt/xcat/sbin/makehosts $node 2>&1`; + if ( $out ne '' ) { + my $rsp; + push @{$rsp->{data}}, "'makehosts' failed for $node. " . + "'makehosts' response: $out"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + + # Inform OpenStack of the hostname. + if ( -e $locOpenStackUpdateName ) { + # Call the python change instance name command + my $renamed = 0; + my $args = "--nodename $node --hostname $sysInfo{$userid}{'hostname'}"; + xCAT::MsgUtils->message( "S", "Invoking $locOpenStackUpdateName $args" ); + my $out = `python $locOpenStackUpdateName $args`; + xCAT::MsgUtils->message( "S", "Returned from OpenStack node name update for $node with $out" ); + if ( $out ) { + if ( $out =~ m/^Success!/ ) { + $renamed = 1; + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "Renamed the OpenStack instance."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + } + + if ( !$renamed ) { + # Return an information message but do not fail the nodeset with an error + # message. This error is minor to the overall operation. + my $rsp; + push @{$rsp->{data}}, "Unable to update the OpenStack node name: $node"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + } else { + my $rsp; + push @{$rsp->{data}}, "Did not find sufficient IP information for $node."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + } + } +} + + +#------------------------------------------------------- + +=head3 findAccessIP + + Description : Obtain TCP/IP and hostname information about + the virtual machine related to the node. + Arguments : Callback in case we want to produce a message + Virtual machine userid + ZHCP node handling the z/VM host node + Hash to contain information on the system + Command invocation argument hash. 'ipfilter' and + 'verbose' keys are used in this routine. + SUDO issuer ($sudoer) or 'root' user for the SSH into ZHCP. + This parameter avoids the call to getsudoer() to obtain + the sudo and sudoer value. 'root' user does not use 'sudo'. + Returns : 0 - No error + non-zero - Error detected or filtered out as a usable IP address + Example : $rc = findAccessIP( $callback, 'linux001', $hcp + \%sysInfo, \%args ); + +=cut + +#------------------------------------------------------- +sub findAccessIP +{ + my $class = shift; # Perl class + my $callback = shift; # Callback for messaging + my $activeSystem = shift; # Userid of the system being queried + my $hcp = shift; # HCP + my $sysInfoRef = shift; # Hash reference for system IP information + my $argsRef = shift; # Command arguments, verbose, ipFilter + my %args = %$argsRef; # Access hash for easier reference + my $sudoer = shift; # SUDO issuer + + my %adapter; # Adapter hash info, used mainly for verbose processing + my @failureInfo; # Information on IP contact failures + my $hostname = ''; # Hostname from the target node + my @hostnameCmds = ( # List of host name resolution commands to be issued in a virtual OS. + 'hostname --fqdn', + 'hostname --long', + 'hostname', + ); + my %ips; # Hash of IP info obtained from the various calls + my $out; # Output buffer work area + my $rc; # Return code + my $rsp; # Message work buffer + my $sudo = ''; # Assume we are not going to use SUDO on ZHCP call. + + # Use sudo or not + if ( $sudoer eq '' ) { + # Looks in the passwd table for a key = sudoer. + ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); + } elsif ( $sudoer ne 'root' ) { + # Non-root user specified so we will invoke 'sudo' on the SSH call to ZHCP. + $sudo = 'sudo'; + } + + # Get the list of IP addresses currently in use by the virtual machine. + $out = `ssh -q $sudoer\@$hcp $sudo /opt/zhcp/bin/smcli "Virtual_Network_Adapter_Query_Extended -T '$activeSystem' -k 'image_device_number=*'"`; + $rc = $? >> 8; + if ($rc == 255) { + push @{$rsp->{data}}, "Unable to communicate with the zhcp system: $hcp"; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_findAccessIP; + } elsif ( $rc == 1 ) { + my ( $smcliRC, $smcliRS, $smcliDesc ); + my $errorOut = `echo "$out" | egrep -i '(^ Return Code: |^ Reason Code: |^ Description: )'`; + my @errorLines = split( "\n", $errorOut ); + foreach my $errorLine ( @errorLines ) { + if ( $errorLine =~ /^ Return Code: / ) { + ($smcliRC) = $errorLine =~ /^ Return Code: (.*)/; + } + if ( $errorLine =~ /^ Reason Code: / ) { + ($smcliRS) = $errorLine =~ /^ Reason Code: (.*)/; + } + if ( $errorLine =~ /^ Description: / ) { + ($smcliDesc) = $errorLine =~ /^ Description: (.*)/; + } + } + if (( $smcliRC == 212 ) && ( $smcliRS == 8 )) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, the virtual machine does not have any network adapters."; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + push @failureInfo, "The virtual machine does not have any network adapters"; + goto FINISH_findAccessIP; + } else { + push @{$rsp->{data}}, "An unexpected return code $smcliRC and reason code $smcliRS was received from " . + "the zhcp server $hcp for an smcli Virtual_Network_Adapter_Query_Extended " . + "request. Error description: $smcliDesc"; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_findAccessIP; + } + + } elsif ( $rc != 0 ) { + push @{$rsp->{data}}, "An unexpected return code $rc was received from " . + "the zhcp server $hcp for an smcli Virtual_Network_Adapter_Query_Extended " . + "request. SMAPI servers may be unavailable. " . + "Received response: $out"; + xCAT::MsgUtils->message("E", $rsp, $callback); + goto FINISH_findAccessIP; + } + + my $filteredOut = `echo "$out" | egrep -i '(adapter_address=|adapter_status=|mac_address=|mac_ip_address=|mac_ip_version=|lan_name=|lan_owner=)'`; + my @ipOut = split( "\n", $filteredOut ); + my ($adapterAddr, $adapterStatus, $ipAddr, $ipVersion, $lanName, $lanOwner, $macAddr ); + foreach my $ipLine ( @ipOut ) { + if ( $ipLine =~ /adapter_address=/ ) { + ($adapterAddr) = $ipLine =~ /adapter_address=(.*)/; + $adapter{$adapterAddr}{'ipCnt'} = 0; + $adapter{$adapterAddr}{'macCnt'} = 0; + ($lanName, $lanOwner) = ''; + } elsif ( $ipLine =~ /adapter_status=/ ) { + ($adapterStatus) = $ipLine =~ /adapter_status=(.*)/; + $adapter{$adapterAddr}{'status'} = $adapterStatus; + } elsif ( $ipLine =~ /^lan_name=/ ) { + ($lanName) = $ipLine =~ /lan_name=(.*)/; + #} elsif ( $ipLine =~ /^lan_owner=/ ) { + # ($lanOwner) = $ipLine =~ /lan_owner=(.*)/; + } elsif ( $ipLine =~ /^mac_address=/ ) { + ($macAddr) = $ipLine =~ /mac_address=(.*)/; + ($ipVersion, $ipAddr) = ''; + $adapter{$adapterAddr}{'macCnt'} += 1; + } elsif ( $ipLine =~ /^mac_ip_address=/ ) { + ($ipAddr) = $ipLine =~ /mac_ip_address=(.*)/; + } elsif ( $ipLine =~ /^mac_ip_version=/ ) { + ($ipVersion) = $ipLine =~ /mac_ip_version=(.*)/; + } + if ( $ipVersion ne '' and $ipAddr ne '' ) { + $ips{$ipAddr}{'adapterAddr'} = $adapterAddr; + $ips{$ipAddr}{'ipVersion'} = $ipVersion; + $ips{$ipAddr}{'lanName'} = $lanName if $lanName; + #$ips{$ipAddr}{'lanOwner'} = $lanOwner if $lanOwner; + $ips{$ipAddr}{'macAddr'} = $macAddr; + $adapter{$adapterAddr}{'ipCnt'} += 1; + } + } + + my $adapterCnt = keys %adapter; + push @{$rsp->{data}}, "For userid $activeSystem, $adapterCnt adapters were detected." if ( $args{'verbose'} == 1 ); + foreach $adapterAddr ( keys %adapter ) { + if ( $adapter{$adapterAddr}{'macCnt'} > 0 ) { + if ( $adapter{$adapterAddr}{'ipCnt'} != 0 ) { + push @{$rsp->{data}}, " Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs with $adapter{$adapterAddr}{'ipCnt'} associated IP address(es)" if ( $args{'verbose'} == 1 ); + } else { + push @{$rsp->{data}}, " Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs but no associated IP addresses" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs but no associated IP addresses"; + } + } elsif ( $adapter{$adapterAddr}{'status'} eq '00' ) { + push @{$rsp->{data}}, " Adapter $adapterAddr: Not coupled" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: Not coupled"; + } elsif ( $adapter{$adapterAddr}{'status'} eq '01' ) { + push @{$rsp->{data}}, " Adapter $adapterAddr: Not active" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: Not active"; + } else { + push @{$rsp->{data}}, " Adapter $adapterAddr: No MACs with associated IP addresses" if ( $args{'verbose'} == 1 ); + push @failureInfo, "Adapter $adapterAddr: No MACs with associated IP addresses"; + } + } + xCAT::MsgUtils->message( "I", $rsp, $callback ) if ( $args{'verbose'} == 1 ); + + if ( !%ips ) { + if ( keys %adapter eq 0 ) { + push @failureInfo, "No adapters were found"; + } else { + push @failureInfo, "No IP addresses were detected"; + } + + goto FINISH_findAccessIP; + } + + # Contact the IPs to see which one, if any, lets us in. + foreach $ipAddr ( keys %ips ) { + $rc = 0; + if ( $ips{$ipAddr}{'ipVersion'} eq '6' ) { + # IPv6 is not currently supported. + next; + } + + if ( $args{'ipfilter'} ) { + if ( $ipAddr !~ m/$args{'ipfilter'}/i ) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, filtered out IP: $ipAddr"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + } + push @failureInfo, "$ipAddr - filtered out by the specified IP filter"; + next; + } + } + + # Ping the address to see if it is responsive. + if ( $ips{$ipAddr}{'ipVersion'} eq '4' ) { + $out = `ping -c1 $ipAddr`; + $rc = $?; + } elsif ( $ips{$ipAddr}{'ipVersion'} eq '6' ) { + # IPv6 is not currently supported. + $rc = 3; + #$out = `ping6 -c1 $ipAddr`; + #$rc = $?; + next; + } else { + push @{$rsp->{data}}, "Userid $activeSystem, IP address: $ipAddr has an unsupported IP version: $ips{$ipAddr}"; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + next; + } + if ( $rc != 0 or $out !~ / 0% packet loss,/ ) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, ping failed for $ipAddr"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + push @failureInfo, "$ipAddr - Unable to ping, rc: $rc"; + next; + } + + # SSH into the system to verify access. + my $line = `ssh -q $ipAddr pwd 2>/dev/null`; + $rc = $? >> 8; + if ( $rc == 255 ) { + if ( $args{'verbose'} == 1 ) { + push @{$rsp->{data}}, "For userid $activeSystem, Unable to ssh into: $ipAddr"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + push @failureInfo, "$ipAddr - Unable to ssh into system"; + next; + } + + # Determine the fully qualified host name to use. + my $fqdn = ''; + + # Attempt to get it from the system's OS using one of a number of commands. + # If we can't get a fully qualified DNS names (with periods) then accept the short name. + my $shortName = ''; + foreach my $cmd ( @hostnameCmds ) { + my $hostname = `ssh -q $ipAddr $cmd 2>/dev/null`; + my $rc = $? >> 8; + if ( $rc == 255 ) { + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "For userid $activeSystem, Unable to ssh into: $ipAddr"; + xCAT::MsgUtils->message("I", $rsp, $callback); + push @failureInfo, "$ipAddr - Unable to ssh into system"; + last; + } + last; + } elsif ( $rc == 0 ) { + # verify the hostname is a fully qualified name. + chomp $hostname; + if ( $hostname =~ /\./ ) { + $fqdn = $hostname; + last; + } else { + $hostname =~ s/^\s+//; + if (( $hostname ne '' ) && ( $hostname !~ /\s/ )) { + # Single word returned without periods. Must be a short name. + $shortName = $hostname; + } + # Keep looking for a long name but we will remember the short name + # in case we can't find a long name. + } + } else { + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "For userid $activeSystem, \'$cmd\' returned, rc: $rc."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + } + } + if (( $shortName eq '' ) && ( $rc != 255 )) { + push @failureInfo, "$ipAddr - hostname query commands failed to return required information"; + } + + if (( $fqdn eq '' ) && ( $shortName ne '' )) { + if ( $args{'verbose'} == 1 ) { + my $rsp; + push @{$rsp->{data}}, "For userid $activeSystem, Unable to determine the fully qualified domain name but found a short name. The short name will be used."; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + $fqdn = $shortName; + } + + # Found the last piece of info needed. + # Note: If hostname is empty because we could not find it, the ultimate response will + # be a failing return code. + $sysInfoRef->{$activeSystem}{'adapterAddr'} = $ips{$ipAddr}{'adapterAddr'};; + $sysInfoRef->{$activeSystem}{'ipAddr'} = $ipAddr; + $sysInfoRef->{$activeSystem}{'ipVersion'} = $ips{$ipAddr}; + $sysInfoRef->{$activeSystem}{'macAddr'} = $ips{$ipAddr}{'macAddr'}; + $sysInfoRef->{$activeSystem}{'switch'} = $ips{$ipAddr}{'lanName'}; + $sysInfoRef->{$activeSystem}{'hostname'} = $fqdn; + last; + } + +FINISH_findAccessIP: + if ( $sysInfoRef->{$activeSystem}{'hostname'} ) { + $rc = 0; + } else { + $rc = 1; + if ( @failureInfo ) { + my $rsp; + my $failureString = join( ',\n', @failureInfo ); + push @{$rsp->{data}}, "Unable to access $activeSystem for the following reasons:\n$failureString"; + xCAT::MsgUtils->message("I", $rsp, $callback); + + $failureString = join( ', ', @failureInfo ); + xCAT::zvmUtils->printSyslog( "findAccessIP() Unable to access $activeSystem for the following reasons: $failureString" ); + } + } + return $rc; +} + + #------------------------------------------------------- =head3 smapi4xcat @@ -2128,12 +3327,12 @@ sub getMaxMemory { Returns : 0 EXEC not found 1 EXEC found Example : my $out = xCAT::zvmUtils->smapi4xcat($user, $hcp); - + =cut #------------------------------------------------------- sub smapi4xcat { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -2151,7 +3350,7 @@ sub smapi4xcat { # Levels 621 and greater support SMAPI EXEC my $out = `ssh $user\@$hcp "$sudo $dir/smcli Query_API_Functional_Level -T $hcpUserId"`; $out = xCAT::zvmUtils->trimStr($out); - if (!($out =~ m/V6.2/i || $out =~ m/V6.1/i || $out =~ m/V5.4/i)) { + if ( !($out =~ m/V6.2/i || $out =~ m/V6.1/i || $out =~ m/V5.4/i) ) { return 1; } @@ -2159,7 +3358,7 @@ sub smapi4xcat { # EXEC found if RC = 8 and RS = 3002 $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c ''"`; $out = xCAT::zvmUtils->trimStr($out); - if ($out =~ m/Return Code: 8/i && $out =~ m/Reason Code: 3002/i) { + if ( $out =~ m/Return Code: 8/i && $out =~ m/Reason Code: 3002/i ) { return 1; } @@ -2179,12 +3378,12 @@ sub smapi4xcat { Cpu Returns : If successful, return file path. Otherwise, return -1 Example : my $out = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount); - + =cut #------------------------------------------------------- sub generateUserEntryFile { - my ($class, $userId, $password, $memorySize, $privilege, $profileName, $cpuCount) = @_; + my ( $class, $userId, $password, $memorySize, $privilege, $profileName, $cpuCount, $ipl) = @_; # If a file of this name already exists, just override it my $file = "/tmp/$userId.txt"; @@ -2192,8 +3391,13 @@ sub generateUserEntryFile { # Add additional CPUs my $i; - for ($i = 1 ; $i < $cpuCount ; $i++) { - $content = $content . sprintf("CPU %02X\n", $i) + for ( $i = 1; $i < $cpuCount; $i++ ) { + $content = $content.sprintf("CPU %02X\n", $i); + } + + if ( $ipl != "") { + # the caller need validate this $ipl param + $content = $content.sprintf("IPL %04s\n", $ipl); } unless (open(FILE, ">$file")) { @@ -2213,14 +3417,14 @@ sub generateUserEntryFile { Description : Obtain the SSI and system status Arguments : User (root or non-root) zHCP - Returns : SSI cluster name + Returns : SSI cluster name or string containing (Error)... Example : my $out = xCAT::zvmUtils->querySSI($user, $hcp); - + =cut #------------------------------------------------------- sub querySSI { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; @@ -2230,7 +3434,14 @@ sub querySSI { $sudo = ""; } - my $ssi = `ssh -o ConnectTimeout=10 $user\@$hcp "$sudo $dir/smcli SSI_Query" | egrep -i "ssi_name"`; + my $outmsg; + my $rc; + my $ssi = `ssh -o ConnectTimeout=10 $user\@$hcp "$sudo $dir/smcli SSI_Query"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=10 $user\@$hcp \"$sudo $dir/smcli SSI_Query\"", $hcp, "querySSI", $ssi ); + if ($rc != 0) { + return $outmsg; + } + $ssi = `echo "$ssi" | egrep -a -i "ssi_name"`; $ssi =~ s/ssi_name = //; $ssi =~ s/\s*$//; $ssi =~ s/^\s*//; @@ -2248,25 +3459,27 @@ sub querySSI { Command to execute Returns : Output returned from executing command Example : my $out = xCAT::zvmUtils->rExecute($user, $node, $cmd); - + =cut #------------------------------------------------------- sub rExecute { - my ($class, $user, $node, $cmd) = @_; + my ( $class, $user, $node, $cmd ) = @_; my $out; my $sudo = "sudo"; - if ($user eq "root") { + if ($user eq "root") { # Just execute the command if root - $out = `ssh $user\@$node "$cmd"`; + #$out = `ssh $user\@$node "$cmd"`; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return $out; } - # Encapsulate command in single quotes $cmd = "'" . $cmd . "'"; - $out = `ssh $user\@$node "$sudo sh -c $cmd"`; + #$out = `ssh $user\@$node "$sudo sh -c $cmd"`; + $cmd = "$sudo sh -c $cmd"; + $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return $out; } @@ -2277,14 +3490,14 @@ sub rExecute { Description : Get a list of used FCP devices in the zFCP pools Arguments : User (root or non-root) zHCP - Returns : List of known FCP devices + Returns : List of known FCP devices, or hash with key "Error" containing error message Example : my %devices = xCAT::zvmUtils->getUsedFcpDevices($user, $zhcp); - + =cut #------------------------------------------------------- sub getUsedFcpDevices { - my ($class, $user, $hcp) = @_; + my ( $class, $user, $hcp ) = @_; # Directory where zFCP pools are my $pool = "/var/opt/zhcp/zfcp"; @@ -2297,7 +3510,16 @@ sub getUsedFcpDevices { # Grep the pools for used or allocated zFCP devices my %usedDevices; my @args; - my @devices = split("\n", `ssh $user\@$hcp "$sudo cat $pool/*.conf" | egrep -i "used|allocated"`); + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo cat $pool/*.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $pool/*.conf\"", $hcp, "getUsedFcpDevices", $out ); + if ($rc != 0) { + $usedDevices{"Error"} = $outmsg; + return %usedDevices; + } + $out = `echo "$out" | egrep -a -i "used|allocated"`; + my @devices = split("\n", $out); foreach (@devices) { @args = split(",", $_); @@ -2310,7 +3532,7 @@ sub getUsedFcpDevices { # Push used or allocated devices into hash if ($args[6]) { - $usedDevices{ uc($args[6]) } = 1; + $usedDevices{uc($args[6])} = 1; } } @@ -2325,58 +3547,78 @@ sub getUsedFcpDevices { Arguments : Sudoer user name Sudo keyword zHCP hostname + Install root directory Local directory to remotely mount Mount access ('ro' for read only, 'rw' for read write) Directory as known to zHCP (out) Returns : 0 - Mounted, or zHCP and MN are on the same system - 1 - Mount failed - Example : establishMount( $callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$provMethod", "ro", \$remoteDeployDir ); - + 1 - Mount failed, errors returned in $callback + Example : establishMount( $callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $provMethod, "ro", \$remoteDeployDir ); + =cut #------------------------------------------------------- sub establishMount { - # Get inputs - my ($class, $callback, $sudoer, $sudo, $hcp, $localDir, $access, $mountedPt) = @_; + my ($class, $callback, $sudoer, $sudo, $hcp, $installRoot, $localDir, $access, $mountedPt) = @_; my $out; # If the target system is not on this system then establish the NFS mount point. - my $hcpIP = xCAT::NetworkUtils->getipaddr($hcp); - if (!defined $hcpIP) { - xCAT::zvmUtils->printLn($callback, "(Error) Unable to obtain the IP address of the hcp node"); + my $hcpIP = xCAT::NetworkUtils->getipaddr( $hcp ); + if (! defined $hcpIP) { + xCAT::zvmUtils->printLn( $callback, "(Error) Unable to obtain the IP address of the hcp node" ); return 1; } - my $masterIp = xCAT::TableUtils->get_site_Master(); + # Get internal master IP if xcat and zhcp are on a 10. network + my $masterIp = xCAT::TableUtils->get_site_attribute("internalmaster"); + + # Use "internalmaster", if it is set. Otherwise, look at "master" property. if (!defined $masterIp) { - xCAT::zvmUtils->printLn($callback, "$hcp: (Error) Unable to obtain the management node IP address from the site table"); - return 1; + + $masterIp = xCAT::TableUtils->get_site_attribute("master"); + if (! defined $masterIp) { + xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to obtain the management node IP address from the site table" ); + return 1; + } } if ($masterIp eq $hcpIP) { - # xCAT MN and zHCP are on the same box and will use the same directory without the need for an NFS mount. - $$mountedPt = $localDir; + $$mountedPt = "$installRoot/$localDir"; } else { - # Determine the hostname for this management node my $masterHostname = Sys::Hostname::hostname(); - if (!defined $masterHostname) { - + if (! defined $masterHostname) { # For some reason, the xCAT MN's hostname is not known. We pass along the IP address instead. $masterHostname = $masterIp; } - xCAT::zvmUtils->printSyslog("establishMount() Preparing the NFS mount point on zHCP ($hcpIP) to xCAT MN $masterHostname($masterIp) for $localDir"); + $$mountedPt = "/mnt/$masterHostname$installRoot/$localDir"; + + # If the mount point already exists then return because we are done. + my $outmsg; + my $rc; + $out = `ssh $sudoer\@$hcp "$sudo mount"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $sudoer\@$hcp \"$sudo mount\"", $hcp, "establishMount", $out ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return 1; + } + $rc = `echo "$out" | egrep -a -i $$mountedPt > /dev/null; echo \\\$?"`; + if ($rc == 0) { + return 0; + } + + xCAT::zvmUtils->printSyslog( "establishMount() Preparing the NFS mount point on zHCP ($hcpIP) to xCAT MN $masterHostname($masterIp) for $localDir" ); # Prepare the staging mount point on zHCP, if they need to be established - $$mountedPt = "/mnt/$masterHostname$localDir"; - my $rc = `ssh $sudoer\@$hcp "$sudo mkdir -p $$mountedPt && mount -t nfs -o $access $masterIp:$localDir $$mountedPt; echo \\\$?"`; + $rc = `ssh $sudoer\@$hcp "$sudo mkdir -p $$mountedPt && mount -t nfs -o $access $masterIp:/$localDir $$mountedPt; echo \\\$?"`; - # Return code = 0 (mount succeeded) or 32 (mount already exists) - if ($rc != '0' && $rc != '32') { - xCAT::zvmUtils->printLn($callback, "$hcp: (Error) Unable to establish zHCP mount point: $$mountedPt"); + # Return code = 0 (mount succeeded) + if ($rc != '0') { + xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to establish zHCP mount point: $$mountedPt" ); + xCAT::zvmUtils->printSyslog( "establishMount() Unable to establish zHCP mount point: $$mountedPt, rc: $rc" ); return 1; } } @@ -2388,16 +3630,18 @@ sub establishMount { =head3 getFreeRepoSpace - Description : Get the free space of image repository under /install + Description : Get the free space of image repository under /install. Arguments : Node - Returns : The available space for /install + Returns : The available space for /install (e.g. "2.1G "). + The value is returned as a perl string (e.g. "0 ") to + avoid perl returning null instead of "0" in the case + of no space available. Example : my $free = getFreeRepoSpace($callback, $node); - + =cut #------------------------------------------------------- sub getFreeRepoSpace { - # Get inputs my ($class, $user, $node) = @_; @@ -2408,18 +3652,22 @@ sub getFreeRepoSpace { # Check if node is the management node my @entries = xCAT::TableUtils->get_site_attribute("master"); - my $master = xCAT::zvmUtils->trimStr($entries[0]); - my $ip = xCAT::NetworkUtils->getipaddr($node); + my $master = xCAT::zvmUtils->trimStr($entries[0]); + my $ip = xCAT::NetworkUtils->getipaddr($node); $ip = xCAT::zvmUtils->trimStr($ip); my $mn = 0; if ($master eq $ip) { - # If the master IP and node IP match, then it is the management node my $out = `$sudo /bin/df -h /install | sed 1d`; - # causing problems on other platforms $out =~ s/\h+/ /g;$out =~ s/\h+/ /g; + # Comment out the horizontal whitespace escape, it was causing "Restarting xCATd Unrecognized escape" + # $out =~ s/\h+/ /g; + my @results = split(' ', $out); - return ($results[3]); + if ( $results[3] eq "0" ) { + $results[3] = "0M"; + } + return $results[3]; } return; @@ -2442,22 +3690,24 @@ sub getFreeRepoSpace { - LUN - Size requested - Owner - - Tag + - Tag Returns : Results hash including: - - Return code (0 = Success, -1 = Failure) + - Return code (0 = Success, -1 = Failure, errors returned in $callback) - zFCP device (if one is requested) - WWPN - LUN Example : my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $header, $user, $hcp, $pool, $criteriaRef); - + =cut #------------------------------------------------------- sub findAndUpdatezFcpPool { - # Get inputs my ($class, $callback, $header, $user, $hcp, $pool, $criteriaRef) = @_; + my $outmsg; + my $rc; + my $out; # Determine if sudo is used my $sudo = "sudo"; if ($user eq "root") { @@ -2470,22 +3720,22 @@ sub findAndUpdatezFcpPool { # Directory where FCP disk pools are on zHCP my $zfcpDir = "/var/opt/zhcp/zfcp"; - my %results = ('rc' => -1); # Default to error + my %results = ('rc' => -1); # Default to error # Extract criteria - my %criteria = %$criteriaRef; - my $status = defined($criteria{status}) ? $criteria{status} : ""; + my %criteria = %$criteriaRef; + my $status = defined($criteria{status}) ? $criteria{status} : ""; my $fcpDevice = defined($criteria{fcp}) ? $criteria{fcp} : ""; - my $wwpn = defined($criteria{wwpn}) ? $criteria{wwpn} : ""; - my $lun = defined($criteria{lun}) ? $criteria{lun} : ""; - my $size = defined($criteria{size}) ? $criteria{size} : ""; - my $owner = defined($criteria{owner}) ? $criteria{owner} : ""; - my $tag = defined($criteria{tag}) ? $criteria{tag} : ""; + my $wwpn = defined($criteria{wwpn}) ? $criteria{wwpn} : ""; + my $lun = defined($criteria{lun}) ? $criteria{lun} : ""; + my $size = defined($criteria{size}) ? $criteria{size} : ""; + my $owner = defined($criteria{owner}) ? $criteria{owner} : ""; + my $tag = defined($criteria{tag}) ? $criteria{tag} : ""; # Check required arguments: pool, status # If you do not know what to update, why update! if (!$pool && !$status) { - return \%results; + return \%results; } # Check status @@ -2495,17 +3745,17 @@ sub findAndUpdatezFcpPool { } # Check FCP device syntax - if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f]/i)) { + if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f;]/i)) { xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid FCP channel address $fcpDevice."); return \%results; } - # Check WWPN and LUN syntax - if ($wwpn && ($wwpn =~ /[^0-9a-f;"]/i)) { - xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid world wide portname $wwpn."); + # Owner must be specified if status is used + if ($status =~ m/used/i && !$owner) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Owner must be specified if status is used." ); return \%results; - } if ($lun && ($lun =~ /[^0-9a-f]/i)) { - xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid logical unit number $lun."); + } elsif ($status =~ m/free/i && $owner) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Owner must not be specified if status is free." ); return \%results; } @@ -2513,15 +3763,13 @@ sub findAndUpdatezFcpPool { my $originSize = $size; if ($size) { if ($size =~ m/G/i) { - # Convert to MegaBytes $size =~ s/\D//g; $size = int($size) * 1024 } elsif ($size =~ m/M/i || !$size) { - # Do nothing } else { - xCAT::zvmUtils->printLn($callback, "$header: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)."); + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)." ); return \%results; } } @@ -2535,20 +3783,26 @@ sub findAndUpdatezFcpPool { # Make sure WWPN and LUN do not have 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + + # Check WWPN and LUN syntax + if ( $wwpn && ($wwpn =~ /[^0-9a-f;]/i) ) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid world wide portname $wwpn." ); + return \%results; + } if ( $lun && ($lun =~ /[^0-9a-f]/i) ) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid logical unit number $lun." ); + return \%results; + } } # Find disk pool (create one if non-existent) - my $out; if (!(`ssh $user\@$hcp "$sudo test -d $zfcpDir && echo Exists"`)) { - # Create pool directory $out = `ssh $user\@$hcp "$sudo mkdir -p $zfcpDir"`; } # Find if disk pool exists if (!(`ssh $user\@$hcp "$sudo test -e $zfcpDir/$pool.conf && echo Exists"`)) { - # Return if xCAT is expected to find a FCP device, but no disk pool exists. xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP storage pool does not exist"); return \%results; @@ -2556,30 +3810,34 @@ sub findAndUpdatezFcpPool { # Find a free disk in the pool # FCP devices are contained in /var/opt/zhcp/zfcp/.conf - my $range = ""; + my $range = ""; my $sizeFound = "*"; my @info; if (!$useWwpnLun) { - # Find a suitable pair of WWPN and LUN in device pool based on requested size # Sample pool configuration file: # #status,wwpn,lun,size,range,owner,channel,tag # used,1000000000000000,2000000000000110,8g,3B00-3B3F,ihost1,1a23,$root_device$ # free,1000000000000000,2000000000000111,,3B00-3B3F,,, # free,1230000000000000;4560000000000000,2000000000000112,,3B00-3B3F,,, - my @devices = split("\n", `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | egrep -i ^free`); + $out = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $zfcpDir/$pool.conf\"", $hcp, "findAndUpdatezFcpPool", $out ); + if ($rc != 0) { + xCAT::zvmUtils->printLn($callback, "$outmsg"); + return \%results; + } + $out = `echo "$out" | egrep -a -i ^free`; + my @devices = split("\n", $out); $sizeFound = 0; foreach (@devices) { @info = split(',', $_); # Check if the size is sufficient. Convert size into MB. if ($info[3] =~ m/G/i) { - # Convert to MegaBytes $info[3] =~ s/\D//g; $info[3] = int($info[3]) * 1024 } elsif ($info[3] =~ m/M/i) { - # Do nothing $info[3] =~ s/\D//g; } else { @@ -2589,42 +3847,47 @@ sub findAndUpdatezFcpPool { # Find optimal disk based on requested size if ($sizeFound && $info[3] >= $size && $info[3] < $sizeFound) { $sizeFound = $info[3]; - $wwpn = $info[1]; - $lun = $info[2]; - $range = $info[4]; + $wwpn = $info[1]; + $lun = $info[2]; + $range = $info[4]; } elsif (!$sizeFound && $info[3] >= $size) { $sizeFound = $info[3]; - $wwpn = $info[1]; - $lun = $info[2]; - $range = $info[4]; + $wwpn = $info[1]; + $lun = $info[2]; + $range = $info[4]; } } # Do not continue if no devices can be found - if (!$wwpn && !$lun) { + if (!$wwpn || !$lun) { xCAT::zvmUtils->printLn($callback, "$header: (Error) A suitable device of $size" . "M or larger could not be found"); return \%results; } } else { - # Find given WWPN and LUN. Do not continue if device is used - my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$wwpn,$lun"`; + my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $zfcpDir/$pool.conf\"", $hcp, "findAndUpdatezFcpPool", $select ); + if ($rc != 0) { + xCAT::zvmUtils->printLn($callback, "$outmsg"); + return \%results; + } + $select = `echo "$select" | egrep -a -i "$wwpn,$lun"`; chomp($select); + if (!$select) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) zFCP device 0x$wwpn/0x$lun could not be found in zFCP pool $pool"); + return \%results; + } @info = split(',', $select); if ($size) { if ($info[3] =~ m/G/i) { - # Convert to MegaBytes $info[3] =~ s/\D//g; $info[3] = int($info[3]) * 1024 - } elsif ($info[3] =~ m/M/i) { - + } else { # Do nothing $info[3] =~ s/\D//g; - } else { - next; } # Do not continue if specified device does not have enough capacity @@ -2638,15 +3901,104 @@ sub findAndUpdatezFcpPool { $range = $info[4]; } - # If there are multiple paths, take the 1st one - # Handle multi-pathing in postscript because autoyast/kickstart does not support it. - my $origWwpn = $wwpn; - if ($wwpn =~ m/;/i) { - @info = split(';', $wwpn); - $wwpn = xCAT::zvmUtils->trimStr($info[0]); + xCAT::zvmUtils->printLn($callback, "$header: Found FCP device 0x$wwpn/0x$lun"); + + if ( ($status =~ m/used/i) && ($fcpDevice =~ /^auto/i) ) { + # select an eligible FCP device + $fcpDevice = xCAT::zvmUtils->selectFcpDevice($callback, $header, $user, $hcp, $fcpDevice, $range, $owner); + if (!$fcpDevice) { + return \%results; + } + } elsif ($status =~ m/free/i) { + # Owner and FCP channel make no sense when status is free + $fcpDevice = ""; + $owner = ""; } - xCAT::zvmUtils->printLn($callback, "$header: Found FCP device 0x$wwpn/0x$lun"); + # Mark WWPN and LUN as used, free, or reserved and set the owner/channel appropriately + # This config file keeps track of the owner of each device, which is useful in nodeset + $size = $size . "M"; + my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $zfcpDir/$pool.conf\"", $hcp, "findAndUpdatezFcpPool", $select ); + if ($rc != 0) { + xCAT::zvmUtils->printLn($callback, "$outmsg"); + return \%results; + } + $select = `echo "$select" | egrep -a -i "$lun"`; + chomp($select); + if ($select) { + @info = split(',', $select); + + if (!$info[3]) { + $info[3] = $size; + } + + # Do not update if WWPN/LUN pair is specified but the pair does not exist + if (!($info[1] =~ m/$wwpn/i)) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun does not exists"); + return \%results; + } + + # Entry order: status,wwpn,lun,size,range,owner,channel,tag + # The following are never updated: wwpn, lun, size, and range + my $update = "$status,$info[1],$info[2],$info[3],$info[4],$owner,$fcpDevice,$tag"; + my $expression = "'s#" . $select . "#" .$update . "#i'"; + $out = `ssh $user\@$hcp "$sudo sed --in-place -e $expression $zfcpDir/$pool.conf"`; + } else { + # Insert device entry into file + $out = `ssh $user\@$hcp "$sudo echo \"$status,$wwpn,$lun,$size,,$owner,$fcpDevice,$tag\" >> $zfcpDir/$pool.conf"`; + } + + # Generate results hash + %results = ( + 'rc' => 0, + 'fcp' => $fcpDevice, + 'wwpn' => $wwpn, + 'lun' => $lun + ); + return \%results; +} + +#------------------------------------------------------- + +=head3 selectFcpDevice + + Description : Select an eligible FCP device for attaching a zFCP device to a node + Arguments : Message header + User (root or non-root) + zHCP + candidate FCP devices or auto + FCP device range + zFCP device owner + Returns : selected FCP device or empty if no one is selected, errors returned in $callback + Example : my $fcpDevice = xCAT::zvmUtils->selectFcpDevice($callback, $header, $user, $hcp, $fcpDevice, $range, $owner); + +=cut + +#------------------------------------------------------- +sub selectFcpDevice { + # Get inputs + my ($class, $callback, $header, $user, $hcp, $fcpDevice, $range, $owner) = @_; + + # Determine if sudo is used + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where executables are on zHCP + my $dir = "/opt/zhcp/bin"; + + # Directory where FCP disk pools are on zHCP + my $zfcpDir = "/var/opt/zhcp/zfcp"; + + my %results = ('rc' => -1); # Default to error + + # Check FCP device syntax + if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f]/i)) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid FCP channel address $fcpDevice."); + return; + } # Find a free FCP device based on the given range if ($fcpDevice =~ m/^auto/i) { @@ -2662,18 +4014,16 @@ sub findAndUpdatezFcpPool { } if (!$found) { - # If the node has an eligible FCP device, use it my @deviceList = xCAT::zvmUtils->getDedicates($callback, $user, $owner); foreach (@deviceList) { - # Check if this devide is eligible (among the range specified for disk $lun) - @info = split(' ', $_); + my @info = split(' ', $_); my $candidate = $info[2]; foreach (@ranges) { ($min, $max) = split('-', $_); if (hex($candidate) >= hex($min) && hex($candidate) <= hex($max)) { - $found = 1; + $found = 1; $fcpDevice = uc($candidate); last; @@ -2688,18 +4038,28 @@ sub findAndUpdatezFcpPool { } if (!$found) { - # If the node has no eligible FCP device, find a free one for it. my %usedDevices = xCAT::zvmUtils->getUsedFcpDevices($user, $hcp); + if (exists $usedDevices{"Error"}) { + xCAT::zvmUtils->printLn($callback, "$header: $usedDevices{Error}"); + return; + } my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); $hcpUserId =~ tr/a-z/A-Z/; # Find a free FCP channel - $out = `ssh $user\@$hcp "$sudo $dir/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; - my @devices = split("\n", $out); - for (my $i = 0 ; $i < @devices ; $i++) { - + my $outmsg; + my $rc; + my $out = `ssh $user\@$hcp "$sudo $dir/smcli System_WWPN_Query -T $hcpUserId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli System_WWPN_Query -T $hcpUserId\"", $hcp, "selectFcpDevice", $out ); + if ($rc != 0) { + xCAT::zvmUtils->printLn($callback, "$outmsg"); + return; + } + $out = `echo "$out" | egrep -a -i "FCP device number|Status"`; + my @devices = split( "\n", $out ); + for (my $i = 0; $i < @devices; $i++) { # Extract the device number and status $fcpDevice = $devices[$i]; $fcpDevice =~ s/^FCP device number:(.*)/$1/; @@ -2714,7 +4074,6 @@ sub findAndUpdatezFcpPool { # Only look at free FCP devices if ($fcpStatus =~ m/free/i) { - # If the device number is within the specified range, exit out of loop # Range: 3B00-3C00;4B00-4C00;5E12-5E12 foreach (@ranges) { @@ -2722,7 +4081,7 @@ sub findAndUpdatezFcpPool { if (hex($fcpDevice) >= hex($min) && hex($fcpDevice) <= hex($max)) { $fcpDevice = uc($fcpDevice); - # Used found FCP channel if not in use or allocated + # Use found FCP channel if not in use or allocated if (!$usedDevices{$fcpDevice}) { $found = 1; last; @@ -2742,14 +4101,14 @@ sub findAndUpdatezFcpPool { # Do not continue if no FCP channel is found if (!$found) { xCAT::zvmUtils->printLn($callback, "$header: (Error) A suitable FCP channel could not be found"); - return \%results; + return; } } # If there are multiple devices (multipathing), take the 1st one if ($fcpDevice) { if ($fcpDevice =~ m/;/i) { - @info = split(';', $fcpDevice); + my @info = split(';', $fcpDevice); $fcpDevice = xCAT::zvmUtils->trimStr($info[0]); } @@ -2759,43 +4118,7 @@ sub findAndUpdatezFcpPool { } } - # Mark WWPN and LUN as used, free, or reserved and set the owner/channel appropriately - # This config file keeps track of the owner of each device, which is useful in nodeset - $size = $size . "M"; - my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$lun" | grep -i "$wwpn"`; - chomp($select); - if ($select) { - @info = split(',', $select); - - if (!$info[3]) { - $info[3] = $size; - } - - # Do not update if WWPN/LUN pair is specified but the pair does not exist - if (!($info[1] =~ m/$wwpn/i)) { - xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun does not exists"); - return \%results; - } - - # Entry order: status,wwpn,lun,size,range,owner,channel,tag - # The following are never updated: wwpn, lun, size, and range - my $update = "$status,$info[1],$info[2],$info[3],$info[4],$owner,$fcpDevice,$tag"; - my $expression = "'s#" . $select . "#" . $update . "#i'"; - $out = `ssh $user\@$hcp "$sudo sed --in-place -e $expression $zfcpDir/$pool.conf"`; - } else { - - # Insert device entry into file - $out = `ssh $user\@$hcp "$sudo echo \"$status,$origWwpn,$lun,$size,,$owner,$fcpDevice,$tag\" >> $zfcpDir/$pool.conf"`; - } - - # Generate results hash - %results = ( - 'rc' => 0, - 'fcp' => $fcpDevice, - 'wwpn' => $wwpn, - 'lun' => $lun - ); - return \%results; + return $fcpDevice; } #------------------------------------------------------- @@ -2807,16 +4130,16 @@ sub findAndUpdatezFcpPool { zHCP WWPN LUN - Returns : Storage pool where zFCP device resides + Returns : Storage pool where zFCP device resides, or string containing (Error)... Example : my $pool = xCAT::zvmUtils->findzFcpDevicePool($user, $hcp, $wwpn, $lun); - + =cut #------------------------------------------------------- sub findzFcpDevicePool { # Get inputs - my ($class, $user, $hcp, $wwpn, $lun) = @_; + my ( $class, $user, $hcp, $wwpn, $lun ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -2827,11 +4150,19 @@ sub findzFcpDevicePool { my $zfcpDir = "/var/opt/zhcp/zfcp"; # Find the pool that contains the SCSI/FCP device - my @pools = split("\n", `ssh $user\@$hcp "$sudo grep -i -l \"$wwpn,$lun\" $zfcpDir/*.conf"`); + my $out; + my $outmsg; + my $rc; + $out = `ssh $user\@$hcp "$sudo grep -i -l \\\"$wwpn,$lun\\\" $zfcpDir/*.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo grep -i -l \\\"$wwpn,$lun\\\" $zfcpDir/*.conf\"", $hcp, "findzFcpDevicePool", $out ); + if ($rc != 0) { + return $outmsg; + } + my @pools = split("\n", $out); my $pool = ""; if (scalar(@pools)) { $pool = basename($pools[0]); - $pool =~ s/\.[^.]+$//; # Do not use extension + $pool =~ s/\.[^.]+$//; # Do not use extension } return $pool; @@ -2847,16 +4178,16 @@ sub findzFcpDevicePool { Storage pool WWPN LUN - Returns : Architecture of node - Example : my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($user, $hcp, $wwpn, $lun); - + Returns : Architecture of node or string containing (Error)... + Example : my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($user, $hcp, $pool, $wwpn, $lun); + =cut #------------------------------------------------------- sub findzFcpDeviceAttr { # Get inputs - my ($class, $user, $hcp, $pool, $wwpn, $lun) = @_; + my ( $class, $user, $hcp, $pool, $wwpn, $lun ) = @_; my $sudo = "sudo"; if ($user eq "root") { @@ -2868,7 +4199,15 @@ sub findzFcpDeviceAttr { # Find the SCSI/FCP device # Entry order: status,wwpn,lun,size,range,owner,channel,tag - my @info = split("\n", `ssh $user\@$hcp "$sudo grep \"$wwpn,$lun\" $zfcpDir/$pool.conf"`); + my $out; + my $outmsg; + my $rc; + $out = `ssh $user\@$hcp "$sudo grep -i \"$wwpn,$lun\" $zfcpDir/$pool.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo grep -i \"$wwpn,$lun\" $zfcpDir/$pool.conf\"", $hcp, "findzFcpDeviceAttr", $out ); + if ($rc != 0) { + return $outmsg; + } + my @info = split("\n", $out); my $entry = $info[0]; chomp($entry); @@ -2881,13 +4220,13 @@ sub findzFcpDeviceAttr { @info = split(',', $entry); %attrs = ( 'status' => $info[0], - 'wwpn' => $info[1], - 'lun' => $info[2], - 'size' => $info[3], - 'range' => $info[4], - 'owner' => $info[5], - 'fcp' => $info[6], - 'tag' => $info[7] + 'wwpn' => $info[1], + 'lun' => $info[2], + 'size' => $info[3], + 'range' => $info[4], + 'owner' => $info[5], + 'fcp' => $info[6], + 'tag' => $info[7] ); return \%attrs; @@ -2904,22 +4243,21 @@ sub findzFcpDeviceAttr { DHCP is used or not (0 or 1) Returns : NIC, device channel, and layer (2 or 3) Example : my ($nic, $channel, $layer) = xCAT::zvmUtils->findUsablezHcpNetwork($user, $hcp, $userId, $dhcp); - + =cut #------------------------------------------------------- sub findUsablezHcpNetwork { - - # Get inputs - my ($class, $user, $hcp, $userId, $dhcp) = @_; + # Get inputs + my ( $class, $user, $hcp, $userId, $dhcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } - my $nic = ''; # Usuable NIC on zHCP - my $channel = ''; # Device channel where NIC is attached + my $nic = ''; # Usuable NIC on zHCP + my $channel = ''; # Device channel where NIC is attached my $layer; my $i; my @words; @@ -2936,26 +4274,23 @@ sub findUsablezHcpNetwork { my @lines = split('\n', $out); # Go through each line - for ($i = 0 ; $i < @lines ; $i++) { - + for ($i = 0; $i < @lines; $i++) { # Go through each network device attached to zHCP foreach (@hcpNetworks) { # If network device is found if ($lines[$i] =~ m/ $_/i) { - # Get network layer $layer = xCAT::zvmCPUtils->getNetworkLayer($user, $hcp, $_); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() NIC:$_ layer:$layer"); # If template using DHCP, layer must be 2 if ((!$dhcp && $layer != 2) || (!$dhcp && $layer == 2) || ($dhcp && $layer == 2)) { - # Save network name $nic = $_; # Get network virtual address - @words = split(' ', $lines[$i]); + @words = split(' ', $lines[$i]); # Get virtual address (channel) # Convert subchannel to decimal @@ -2964,7 +4299,6 @@ sub findUsablezHcpNetwork { xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() Candidate found NIC:$nic channel:$channel layer:$layer"); return ($nic, $channel, $layer); } else { - # Go to next network available $nic = ''; } @@ -2974,7 +4308,6 @@ sub findUsablezHcpNetwork { # If network device is not found if (!$nic) { - # Check for user profile my $profileName = `echo "$userEntry" | grep "INCLUDE"`; if ($profileName) { @@ -2984,31 +4317,28 @@ sub findUsablezHcpNetwork { my $userProfile = xCAT::zvmUtils->getUserProfile($user, $hcp, $words[1]); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() $userProfile"); - # Get the NICDEF statement containing the HCP network + # Get the NICDEF statement $out = `echo "$userProfile" | grep "NICDEF"`; @lines = split('\n', $out); # Go through each line - for ($i = 0 ; $i < @lines ; $i++) { - + for ($i = 0; $i < @lines; $i++) { # Go through each network device attached to zHCP foreach (@hcpNetworks) { # If network device is found if ($lines[$i] =~ m/ $_/i) { - # Get network layer $layer = xCAT::zvmCPUtils->getNetworkLayer($user, $hcp, $_); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() NIC:$_ layer:$layer"); # If template using DHCP, layer must be 2 if ((!$dhcp && $layer != 2) || (!$dhcp && $layer == 2) || ($dhcp && $layer == 2)) { - # Save network name $nic = $_; # Get network virtual address - @words = split(' ', $lines[$i]); + @words = split(' ', $lines[$i]); # Get virtual address (channel) # Convert subchannel to decimal @@ -3017,15 +4347,848 @@ sub findUsablezHcpNetwork { xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() Candidate found NIC:$nic channel:$channel layer:$layer"); return ($nic, $channel, $layer); } else { - # Go to next network available $nic = ''; } } - } # End of foreach - } # End of for - } # End of if + } # End of foreach + } # End of for + } # End of if } return; } + +#------------------------------------------------------- + +=head3 printInfo + + Description : Print a long string to stdout as information without checking anything + Arguments : String + Returns : Nothing + Example : xCAT::zvmUtils->printInfo($callback, $str); + +=cut + +#------------------------------------------------------- +sub printInfo { + + # Get inputs + my ( $class, $callback, $str ) = @_; + + # Print string + my $rsp; + + $rsp->{data}->[0] = "$str"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + + return; +} + +#------------------------------------------------------- + +=head3 getSpecialCloneInfo + + Description : Look in the /var/opt/xcat/doclone.txt file (if exists) and return a + hash of the keys and values found that match the image name parameter + Arguments : User friendly image name + + Returns : hash of keys and values found or empty hash + Example : my %cloneinfo = xCAT::zvmUtils->getSpecialCloneInfo($callback, $user, $node); + if (%cloneinfo) { + %cloneinfo has at least one key + } else { + %cloneinfo empty, no keys + } + +=cut + +#------------------------------------------------------- +sub getSpecialCloneInfo { + + # Get inputs + my ( $class, $imagename ) = @_; + my %cloneInfoHash = (); # create empty hash + + # Directory where doclone.txt is + my $dir = '/var/opt/xcat/'; + my $clonefile = 'doclone.txt'; + my $out; + + # Does the file exist? If so read and look for this image name + if (-e "$dir$clonefile") { + # look for this image name and ignore case + $out = `cat $dir$clonefile | grep -v '^\\s*/[*]'| grep -v '^\\s*[*]'| grep -E -i -w "IMAGE_NAME[[:blank:]]*=[[:blank:]]*$imagename"`; + + my @lines = split( '\n', $out ); + my $count = @lines; + + # loop for any lines found + for (my $i=0; $i < $count; $i++) { + # Break out each key=value; item + my @parms = split( ';', $lines[$i]); + my $parmcount = @parms; + # get the key and value for this item, store in hash + for (my $j=0; $j < $parmcount; $j++) { + my @keyvalue = split('=', $parms[$j]); + my $key = $keyvalue[0]; + $key =~ s/^\s+|\s+$//g; # get rid of leading and trailing blanks + next if ( length( $key ) == 0 ); # Skip incorrect key=value data + + my $value = $keyvalue[1]; + $value =~ s/^\s+|\s+$//g; + next if ( length( $value ) == 0 ); # Skip incorrect key=value data + #uppercase both key and value; + $key = uc $key; + $value = uc $value; + $cloneInfoHash{ $key } = $value; + } + } + } + return (%cloneInfoHash); +} + +#------------------------------------------------------- + +=head3 pingNode + + Description : Execute a Perl ping for this node + Arguments : Node name + + Returns : "ping" if found; or "noping" (if not found) + Example : my $out = xCAT::zvmUtils->pingNode($node); + +=cut + +#------------------------------------------------------- +sub pingNode { + + # Get input node + my ( $class, $node ) = @_; + + my $timeout = 2; # how many seconds to wait for response. Default was 5 + # call system ping and max count of pings 2 + my $out = `ping -W $timeout -c 2 -q $node`; + if ($? != 0) { + # Ping failed, try to get result with execcmdonVM. + my $result = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, 'date'); + if (xCAT::zvmUtils->checkOutput( $result ) == -1) { + return $result; + } + if ($result) { + return ("ping"); + } + return ("noping"); + } + return ("ping"); +} + +#------------------------------------------------------- + +=head3 onlineZhcpPunch + + Description : Online punch device and load VMCP module on zHCP + Arguments : User (root or non-root) + zHCP + Returns : Operation results (Done/Failed) + Example : my $out = xCAT::zvmUtils->onlineZhcpPunch($user, $hcp); + +=cut + +#------------------------------------------------------- +sub onlineZhcpPunch { + + # Get input node + my ( $class, $user, $hcp ) = @_; + + my $out = ""; + my $subResp = ""; + my $rc = ""; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Online zHCP's punch + $out = `ssh $user\@$hcp "$sudo cat /sys/bus/ccw/drivers/vmur/0.0.000d/online" 2>&1`; + $rc = $? >> 8; + if ( $rc == 255 ) { + # SSH failure to communicate with zHCP. + $subResp = "Failed to communicate with the zHCP system to get the punch device status"; + } elsif ( $rc != 0 ) { + # Generic failure of the command. + chomp( $out ); + xCAT::zvmUtils->printSyslog( "onlineZhcpPunch() Failed to get the punch device status on zHCP rc: $rc, out: $out" ); + $subResp = "Failed to online the punch device on zHCP rc: $rc, out: $out"; + } + + if ( $subResp eq "" ) { + if ($out != 1) { + chomp( $out = `ssh $user\@$hcp "$sudo /sbin/cio_ignore -r 000d; /sbin/chccwdev -e 000d"`); + $rc = $? >> 8; + if ( $rc == 0 ) { + $subResp = "Done"; + } elsif ( $rc == 255 ) { + # SSH failure to communicate with zHCP. + $subResp = "Failed to communicate with the zHCP system to online the punch device"; + } else { + if ( !( $out =~ m/Done$/i ) ) { + xCAT::zvmUtils->printSyslog("onlineZhcpPunch() failed to online the zHCP's punch, cmd output: $out."); + $subResp = "Failed to online the zHCP's punch rc: $rc, out: $out"; + } + } + `ssh $user\@$hcp "$sudo which udevadm &> /dev/null && udevadm settle || udevsettle"`; + } else { + $subResp = "Done"; + } + } + return $subResp +} + +#------------------------------------------------------- +=head3 genCfgdrive + + Description : Generate a final config drive to punch + Arguments : Configure file directory + + Returns : Generated config drive file path + Example : my $out = xCAT::zvmUtils->genCfgdrive($path); + +=cut + +#------------------------------------------------------- +sub genCfgdrive { + + # Get input node + my ( $class, $cfgpath ) = @_; + my $node = basename($cfgpath); + + my $out = xCAT::zvmUtils->injectMNKey($cfgpath); + if ( $out =~ m/Failed/i ) { + xCAT::zvmUtils->printSyslog("genCfgdrive() Failed to generate the final cfgdrive.tgz for target node: $node, out: $out"); + return ""; + } else { + xCAT::zvmUtils->printSyslog("genCfgdrive() Successfully generated the final cfgdrive.tgz for target node: $node"); + return "$cfgpath/cfgdrive.tgz"; + } +} + +#------------------------------------------------------- +=head3 injectMNKey + + Description : Inject xCAT MN's public key to the meta_data.json for target vm + Arguments : Configure file directory + + Returns : A message indicate whether the MN's key in injected success or not + Example : my $out = xCAT::zvmUtils->injectMNKey($path); + +=cut + +#------------------------------------------------------- + +sub injectMNKey { + + # Get input node + my ( $class, $cfgpath ) = @_; + my $subResp = ""; + + if ( -e "$cfgpath/cfgdrive.tgz" ) { + system("tar -zxf $cfgpath/cfgdrive.tgz -C $cfgpath "); + } else { + $subResp = "injectMNKey() Failed to find the cfgdrive.tgz under $cfgpath for target node"; + return $subResp; + } + + # Get xcat key, store it to a hash var for later use + open(my $keyFile, '<', "/root/.ssh/id_rsa.pub"); + my $mnKey = <$keyFile>; + close($keyFile); + my @set = ('0' ..'9', 'A' .. 'F'); + my $mnKeyName = join '' => map $set[rand @set], 1 .. 8; + my %mnKeyHash = ("name" => $mnKeyName, "type" => "ssh", "data" => $mnKey,); + + # Read the file content to a variable named md_json,and close the source file + my $jsonText; + my $MDfile; + if(open($MDfile, '<', "$cfgpath/openstack/latest/meta_data.json")) { + while(<$MDfile>) { + $jsonText .= "$_"; + } + } else { + $subResp = "injectMNKey() Failed to open the meta data file for processing"; + close($MDfile); + return $subResp; + } + close($MDfile); + + # Get the public_keys from meta_data.json, if it not exist, add xCAT's key to meta_data.json directly, + # if already exist, compare if the xCAT's key is same or not with existing one, append xCAT key if not same + my $md_json = decode_json($jsonText); + if (exists $md_json->{"public_keys"}) { + my $publicKeys = $md_json->{"public_keys"}; + # Check if xCAT key already exist , append it if not exist. + foreach my $pubkey ( keys %$publicKeys ) { + if ( $publicKeys->{$pubkey} eq $mnKey ) { + last; + } + $publicKeys->{$mnKeyName} = $mnKey; + my @tkeys = $md_json->{"keys"}; + push @tkeys, {%mnKeyHash}; + #push $md_json->{"keys"}, {%mnKeyHash}; + } + } else { + # Set the public_keys and keys with xCAT's key info in meta_data.json + $md_json->{"public_keys"}->{$mnKeyName} = $mnKey; + $md_json->{"keys"}[0] = {%mnKeyHash}; + } + + # Save the changed meta_data.json to new file + open my $fh, ">", "$cfgpath/meta_data.json"; + print $fh encode_json($md_json); + close $fh; + + # Replace the meta_data.json file in original config drive with the modified one + system( "find $cfgpath/openstack -name meta_data.json -print | xargs -i cp $cfgpath/meta_data.json {}"); + `rm -f $cfgpath/meta_data.json`; + + # Tar the file generate the final one + my $oldpath=cwd(); + chdir($cfgpath); + system ( "tar -zcf cfgdrive.tgz openstack ec2"); + chdir($oldpath); + + $subResp = "Done"; + return $subResp; +} + +#------------------------------------------------------- + +=head3 execcmdthroughIUCV + + Description : Execute a command to node with IUCV client. + Arguments : User (root or non-root). + zHCP (opencloud user) + VM's userid + command [parms..] the comands and parms with the command which need to execute. + callback + + Returns : command result, if success. + if an error: + and $callback then $callback gets error message + returns with string containing (Error) and message + Example : my $out = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $command); + +=cut + +#------------------------------------------------------- +sub execcmdthroughIUCV { + my ($class, $user, $hcp, $userid, $commandwithparm, $callback) = @_; + my $result = ''; + my $rsp; + my $msg; + my $iucvpath = '/opt/zhcp/bin/IUCV'; + my $isCallback = 0; + if (defined $callback) { + $isCallback = 1; + } + $result= `ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid "\'$commandwithparm\'" 2>&1`; + + my $rc = $? >> 8; + $result = xCAT::zvmUtils->trimStr( $result ); + if ( $isCallback || $rc == 0 ){ + xCAT::zvmUtils->printSyslog("$userid: IUCV command: ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid $commandwithparm. return $rc\n $result"); + } else { + xCAT::zvmUtils->printSyslog("$userid: IUCV command: ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid $commandwithparm."); + } + if ( $rc == 0 ) { + if ($result eq ''){ + return "Done"; + } + return $result; + } elsif ( $rc == 1 ) { + $msg = "Issued command was not authorized or a generic Linux error occurred. error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 2 ) { + $msg = "parameter to iucvclient error, $result"; + push @{$rsp->{data}}, $msg + } elsif ( $rc == 4 ) { + $msg = "IUCV socket error, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 8 ) { + $msg = "Command executed failed, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 16 ) { + $msg = "File Transport failed, error details $result"; + push @{$rsp->{data}}, $msg; + } elsif ( $rc == 32 ) { + $msg = "File Transport failed, error details $result"; + push @{$rsp->{data}}, $msg; + } + + # Error occurred + if ($isCallback){ + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + return "(Error) $msg"; +} + +#------------------------------------------------------- + +=head3 cleanIUCV + + Description : rollback IUCV to clean all the files that copy to it. + Arguments : User (root or non-root). + VM's node + VM's system + + Returns : Nothing + Example : xCAT::zvmUtils->cleanIUCV( $user, $hcp, $userid); + +=cut + +#------------------------------------------------------- +sub cleanIUCV { + my ($class, $user, $node, $os) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + my $result = ''; + my $outmsg = ''; + my $cmd = ''; + my $rc; + my $trgtiucvpath = "/usr/bin/iucvserv"; + my $trgtiucvservicepath_rh6_sl11 = "/etc/init.d/iucvserd"; + my $trgtiucvservicepath_rh7 = "/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_ubuntu16 = "/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_sl12 = "/usr/lib/systemd/system/iucvserd.service"; + + #clean iucv server file + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $result, $node ); + # Continue processing even if an error. + + #clean iucv server service file + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ) { + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh6_sl11 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh6_sl11 2>&1`; + } + elsif ( $os =~ m/sles12/i ) { + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_sl12 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_sl12 2>&1`; + } + elsif ( $os =~ m/rhel7/i){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh7 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh7 2>&1`; + } elsif ( $os =~ m/ubuntu16/i){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_ubuntu16 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_ubuntu16 2>&1`; + } + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); + # Continue processing even if an error. + + #clean iucv server authorized file + $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); + # Continue processing even if an error. + + #clean iucv server service start + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"chkconfig --del iucvserd && service iucvserd stop 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node "chkconfig --del iucvserd && service iucvserd stop 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); + # Continue processing even if an error. + }else{ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"systemctl disable iucvserd.service && systemctl stop iucvserd.service 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node "systemctl disable iucvserd.service && systemctl stop iucvserd.service 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); + # Continue processing even if an error. + } +} + +#------------------------------------------------------- + +=head3 setsshforvm + + Description : If IUCV communication failed, try to use ssh to make communication. + Arguments : User (root or non-root). + VM's node + VM's linux system type + command [parms..] the comands and parms with the command which need to execute. + error message which is got in setup IUCV + current VM's status in zvm table + callback + + Returns : command result, if success. + if an error: + and $callback then $callback gets error message and routine returns with 1 + if no $callback then routine returns with string containing Error: and message + Example : my $out = xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + +=cut + +#------------------------------------------------------- + +sub setsshforvm { + my ($class, $user, $node, $os, $commandwithparm, $msg, $status, $callback) = @_; + my $result =''; + my $rsp; + my $isCallback = 0; + my $outmsg = ''; + my $rc; + if (defined $callback) { + $isCallback = 1; + } + + #clean IUCV server first. + $result = xCAT::zvmUtils->cleanIUCV($user, $node, $os); + if (xCAT::zvmUtils->checkOutput( $result ) == -1) { + return $result; + } + # check whether the vm can be ping, if so then set ssh to zvm table, + # to indicate that it use ssh. + my $ping = `ping -W 2 -c 2 -q $node`; + if ($? == 0) { + my $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "setsshforvm", $result, $node ); + if ($rc == 255) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + # Continue processing even if an error. + } + + if ($status){ + $status = "$status;SSH=1"; + }else{ + $status = "SSH=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + xCAT::zvmUtils->printSyslog("$node: Set SSH=1 for node $node"); + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$node: Set SSH=1 for node $node."); + } + return $result; + } + + # Error occurred on ping + if ($callback){ + push @{$rsp->{data}}, $msg; + xCAT::MsgUtils->message( "E", $rsp, $callback ); + } + xCAT::zvmUtils->printSyslog("$node: $msg"); + return "$msg"; +} + +#------------------------------------------------------- + +=head3 execcmdonVM + + Description : Execute a command to node. + Arguments : User (root or non-root). + VM's node + command [parms..] the comands and parms with the command which need to execute. + callback + + Returns : command result, if success. + if an error: + and $callback then $callback gets error message + routine returns with string containing Error: and message + + Example : my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $commandwithparm, $callback); + +=cut + +#------------------------------------------------------- +sub execcmdonVM { + my ($class, $user, $node, $commandwithparm, $callback) = @_; + + # get HCP and z/VM userid + my @propNames = ( 'hcp', 'userid', 'status' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + my $status = $propVals->{'status'}; + if(!(defined($status))){ + $status = ''; + } + my $hcp = $propVals->{'hcp'}; + my $userid = $propVals->{'userid'}; + my $isCallback = 0; + if (defined $callback) { + $isCallback = 1; + } + + my $result = ''; + my $outmsg = ''; + my $rsp; + my $rc; + my $msg = ''; + my $cmd = ''; + my $opnclouduserid='OPNCLOUD'; + my $simplecmd = 'date'; + + # Create path string + my $dest = "$user\@$node"; + my $srciucvpath = '/opt/zhcp/bin/IUCV'; + my $trgtiucvpath = "/usr/bin/iucvserv"; + my $trgtiucvservicepath_rh6_sl11 = "/etc/init.d/iucvserd"; + my $trgtiucvservicepath_rh7 = "/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_sl12 = "/usr/lib/systemd/system/iucvserd.service"; + my $trgtiucvservicepath_ubuntu16 = "/lib/systemd/system/iucvserd.service"; + my $authorizedfilepath = "/etc/iucv_authorized_userid"; + my $xcatuserid = `vmcp q userid | awk '{print \$1}'`; + chomp($xcatuserid); + + # Add escape for IUCV and SSH commands. + if ($commandwithparm =~ '\\\"'){ + $commandwithparm =~ s/"/\\"/g; + } + $commandwithparm =~ s/"/\\"/g; + + # For not xcat deployed node, use SSH to make communication. + if (!(defined($userid)) || !(defined($hcp))){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); + if ($rc == 255) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + } + # Remove IUCV=1 if it has been set + if ($status =~ /IUCV=1/){ + $status =~ s/IUCV=1/SSH=1/g; + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } + return $result; + } + + $userid =~ tr/a-z/A-Z/; + # For normal managed nodes, ask zhcp to query the power state + if (($userid ne $xcatuserid) && !($hcp =~ /$node/) && ($hcp ne '')){ + # Get VM's power stat first, if power stat is off, return error. + my $max = 0; + while ( !$result && $max < 10 ) { + $cmd = "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q user $userid 2>/dev/null\" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/'"; + $result = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userid 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/'`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $hcp, "execcmdonVM", $result, $node ); + if ($rc == 255) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + return $outmsg; + } + $max++; + } + #xCAT::zvmUtils->printSyslog("$node: ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q user $userid 2>/dev/null\" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/' ##$result##"); + if ("off" =~ $result) { + my $msgText = "$node: (Error) VM $userid is powered off"; + xCAT::zvmUtils->printSyslog("$msgText"); + if ($isCallback) { + xCAT::zvmUtils->printLn( $callback, "$msgText"); + } + return "$msgText"; + } + + if (!($status =~ /SSH=1/) && !($status =~ /IUCV=1/)){ + # if zhcp direct entry does set "IUCV ANY", will set to SSH directly. + my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); + @propNames = ( 'status' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $hcp, @propNames ); + my $iucvanystatus = $propVals->{'status'}; + if (!($iucvanystatus =~ m/IUCVANY/i)){ + xCAT::zvmUtils->printSyslog("$node: zhcp's IUCVANY status is not set"); + my $out = `ssh $user\@$hcp "$::SUDO /opt/zhcp/bin/smcli Image_Query_DM -T $hcpUserId"| egrep -i "IUCV ANY"`; + if ( $out =~ m/IUCV ANY/i){ + if ($iucvanystatus){ + $iucvanystatus = "$status;IUCVANY=1"; + }else{ + $iucvanystatus = "IUCVANY=1"; + } + }else{ + if ($iucvanystatus){ + $iucvanystatus = "$status;IUCVANY=0"; + }else{ + $iucvanystatus = "IUCVANY=0"; + } + } + xCAT::zvmUtils->setNodeProp( 'zvm', $hcp, 'status', $iucvanystatus ); + xCAT::zvmUtils->printSyslog("$node: zhcp's status is $iucvanystatus"); + } + if ($iucvanystatus =~ m/IUCVANY=0/i){ + xCAT::zvmUtils->printSyslog("$node: zhcp doesn't support to make communication with IUCV, set SSH=1 for $node"); + if ($status){ + $status = "$status;SSH=1"; + }else{ + $status = "SSH=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + } + } + } + + # If node userid is xcat or zhcp, only use SSH. + if (($status =~ /SSH=1/) || ($userid eq $xcatuserid) || ($hcp =~ /$node/) || ($hcp eq '')){ + # SSH=1, Use ssh to make communication. + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); + if ($rc == 255) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + } + return $result; + }elsif ($status =~ /IUCV=1/){ + #xCAT::zvmUtils->printSyslog("$node: IUCV command: $commandwithparm"); + # IUCV=1, Use IUCV to make communication. + $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); + return $result; + } + + xCAT::zvmUtils->printSyslog("$node: VM $userid doesn't set communicate type, will set it first." ); + my $releaseInfo = `ssh -qo ConnectTimeout=2 $user\@$node "$::SUDO ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; + if (xCAT::zvmUtils->checkOutput( $releaseInfo ) == -1) { + return $releaseInfo; + } + my $os = buildOsVersion( $callback, $releaseInfo, 'all' ); + + # For the existed VMs which are deployed with SSH, will try to copy IUCV files to them. + # These VMs are not set communication type, try IUCV first and set the type after communication. + # if IUCV server doesn't exist on xcat /var/lib/sspmod, first to copy from OPNCLOUD. + if ( not (-e "$srciucvpath/iucvserv" and -e "$srciucvpath/iucvserd" + and -e "$srciucvpath/iucvserd.service" )) { + $result= `mkdir -p $srciucvpath && scp -p $user\@$hcp:$srciucvpath/iucvser* $srciucvpath 2>&1`; + $rc = $? >>8 ; + xCAT::zvmUtils->printSyslog("$node: IUCV server files doesn't exist on xcat $srciucvpath, copy from OPNCLOUD.return $rc, $result"); + if ($rc != 0) { + $msg = "Failed to copy $user\@$hcp:$srciucvpath/iucvser* to $srciucvpath. $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } + # Check whether IUCV server is installed. + $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: try to execute command through IUCV. $result return $?" ); + if ( $rc != 0 ){ + #IUCV server doesn't exist on node, copy file to it and restart service + if ($result =~ "ERROR connecting socket") { + xCAT::zvmUtils->printSyslog("$node: start to set iucv, first to copy iucv server files and start iucv server service" ); + #copy IUCV server files. + $result = `/usr/bin/scp -p $srciucvpath/iucvserv $dest:$trgtiucvpath 2>&1`; + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: /usr/bin/scp -p $srciucvpath/iucvserv $dest:$trgtiucvpath 2>&1 return $rc\n $result"); + if ($rc != 0) { + $msg = "Failed to copy $srciucvpath/iucvserv to $dest:$trgtiucvpath. $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ) { + $result = `/usr/bin/scp -p $srciucvpath/iucvserd $dest:$trgtiucvservicepath_rh6_sl11 2>&1`; + } + elsif ( $os =~ m/sles12/i ) { + $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_sl12 2>&1`; + } + elsif ( $os =~ m/rhel7/i){ + $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_rh7 2>&1`; + } elsif ( $os =~ m/ubuntu16/i){ + # Note: we should not encounter this line as we don't have ubuntu support before IUCV enablement + $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_ubuntu16 2>&1`; + } + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: /usr/bin/scp -p iucv service file return $rc $result"); + if ($rc != 0) { + $msg = "Failed to copy iucvservice file. $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + $opnclouduserid = xCAT::zvmCPUtils->getUserId($user, $hcp); + $opnclouduserid =~ tr/a-z/A-Z/; + if ($rc !=0) { + $msg = "failed to get OPNCLOUD userid. return $? \n$result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"echo -n $opnclouduserid >$authorizedfilepath\" 2>&1"; + $result = `ssh -o ConnectTimeout=5 $user\@$node "echo -n $opnclouduserid >$authorizedfilepath" 2>&1`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); + if ($rc != 0) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + + # Start service of IUCV server + if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ){ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"chkconfig --add iucvserd && service iucvserd start 2>&1\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "chkconfig --add iucvserd && service iucvserd start 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); + if ($rc != 0) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + }else{ + $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"systemctl enable iucvserd.service && systemctl start iucvserd.service 2>&1\""; + $result = `ssh -o ConnectTimeout=5 $user\@$node "systemctl enable iucvserd.service && systemctl start iucvserd.service 2>&1"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); + if ($rc != 0) { + if ($isCallback){ + xCAT::zvmUtils->printLn( $callback, "$outmsg"); + } + $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } + $rc = $? >> 8; + xCAT::zvmUtils->printSyslog("$node: start iucvserver service return $rc. $result"); + # Now that the IUCV server has started successfully, send a simple command + if ($rc == 0) { + $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $simplecmd, $callback); + # The simple command worked! Update the zvm table so we always communicate via IUCV + if ($? == 0){ + xCAT::zvmUtils->printSyslog("$node: successfully initialized IUCV, Set IUCV=1 for $user"); + if ($callback){ + xCAT::zvmUtils->printLn( $callback, "$node: successfully initialized IUCV, Set IUCV=1 for $user"); + } + if ($status){ + $status = "$status;IUCV=1"; + }else{ + $status = "IUCV=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + return xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); + }else{ + $msg = "$node: Failed to start iucvserver, result is $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } else { + $msg = "$node: Failed to start iucvserver, result is $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + # If our command failed, just return the error + } elsif ($result =~ /Command executed failed/) { + return $result; + } else { + $msg = "$node: IUCV server on VM got another error that is not a socket error, result is $result"; + return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); + } + } else { + $msg = "IUCV has worked well, set IUCV=1 for $user ."; + if ($status) { + $status = "$status;IUCV=1"; + } else { + $status = "IUCV=1"; + } + xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); + return $result; + } + +} diff --git a/xCAT-UI/js/configure/configure.js b/xCAT-UI/js/configure/configure.js index 33cac8c41..79cff6f25 100644 --- a/xCAT-UI/js/configure/configure.js +++ b/xCAT-UI/js/configure/configure.js @@ -1,560 +1,558 @@ -/** - * Global variables - */ -var configTabs; // Config tabs -var configDatatables = new Object(); // Datatables on the config page - -/** - * Set the datatable - * - * @param id The ID of the datatable - * @param obj Datatable object - */ -function setConfigDatatable(id, obj) { - configDatatables[id] = obj; -} - -/** - * Get the datatable with the given ID - * - * @param id The ID of the datatable - * @return Datatable object - */ -function getConfigDatatable(id) { - return configDatatables[id]; -} - -/** - * Set the configure tab - * - * @param obj Tab object - */ -function setConfigTab(obj) { - configTabs = obj; -} - -/** - * Get the configure tab - * - * @param Nothing - * @return Tab object - */ -function getConfigTab() { - return configTabs; -} - -/** - * Load configure page - */ -function loadConfigPage() { - // If the configure page has already been loaded - if ($('#content').children().length) { - // Do not reload configure page - return; - } - - // Create configure tab - var tab = new Tab(); - setConfigTab(tab); - tab.init(); - $('#content').append(tab.object()); - - // Create loader - var loader = $('
').append(createLoader()); - - // Add tab to configure xCAT tables - tab.add('configTablesTab', 'Tables', loader, false); - - // Add the update tab -// tab.add('updateTab', 'Update', '', false); - - // Add the Users tab - tab.add('usersTab', 'Users', '', false); - - // Add the discover tab -// tab.add('discoverTab', 'Discover', '', false); - - // Add the self-service tab -// tab.add('serviceTab', 'Service', '', false); - - // Add the files tab - tab.add('filesTab', 'Files', '', false); - - // Get list of tables and their descriptions - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : '-d', - msg : '' - }, - - success : loadTableNames - }); - - // Do not load everything at once - // Load when tab is shown - tab.object().bind('tabsshow', function(event, ui) { - if ($(ui.panel).children().length) { - return; - } - - if (ui.index == 1) { - loadUserPage(); - } else if (ui.index == 2) { - loadServicePage(); - } else if (ui.index == 3) { - loadFilesPage(); -// loadDiscoverPage(); -// } else if (ui.index == 4) { -// loadServicePage(); - } - }); -} - -/** - * Load xCAT database table names and their descriptions - * - * @param data Data returned from HTTP request - */ -function loadTableNames(data) { - // Get output - var tables = data.rsp; - - // Remove loader - var tabId = 'configTablesTab'; - $('#' + tabId).find('img').hide(); - - // Create a groups division - var tablesDIV = $('
'); - $('#' + tabId).append(tablesDIV); - - // Create info bar - var infoBar = createInfoBar('Select a table to view or edit.'); - tablesDIV.append(infoBar); - - // Create a list for the tables - var list = $('
    '); - // Loop through each table - for ( var i = 0; i < tables.length; i++) { - // Create a link for each table - var args = tables[i].split(':'); - var link = $('' + args[0] + ''); - - // Open table on click - link.bind('click', function(e) { - // Get table ID that was clicked - var id = (e.target) ? e.target.id : e.srcElement.id; - - // Create loader - var loader = $('
    ').append(createLoader()); - - // Add a new tab for this table - var configTab = getConfigTab(); - if (!$('#' + id + 'Tab').length) { - configTab.add(id + 'Tab', id, loader, true); - - // Get contents of selected table - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : id, - msg : id - }, - - success : loadTable - }); - } - - // Select new tab - configTab.select(id + 'Tab'); - }); - - var item = $('
  • '); - item.append(link); - - // Append the table description - item.append(': ' + args[1]); - - // Append item to list - list.append(item); - } - - tablesDIV.append(list); -} - -/** - * Load a given database table - * - * @param data Data returned from HTTP request - */ -function loadTable(data) { - // Get response - var rsp = data.rsp; - // Get table ID - var id = data.msg; - - // Remove loader - var tabId = id + 'Tab'; - $('#' + tabId).find('img').remove(); - - // Create info bar - var infoBar = createInfoBar('Click on a cell to edit. Click outside the table to write to the cell. Once you are satisfied with how the table looks, click on Save.'); - $('#' + tabId).append(infoBar); - - // Create action bar - var actionBar = $('
    '); - $('#' + tabId).append(actionBar); - - // Get table headers - var args = rsp[0].replace('#', ''); - var headers = args.split(','); - - // Create container for original table contents - var origCont = new Array(); // Original table content - origCont[0] = rsp[0].split(','); // Headers - - // Create container for new table contents - var newCont = new Object(); - var tmp = new Object(); - tmp[0] = '#' + headers[0]; // Put a # in front of the header - for ( var i = 1; i < headers.length; i++) { - tmp[i] = headers[i]; - } - newCont[0] = tmp; - - // Create a new datatable - var tableId = id + 'Datatable'; - var table = new DataTable(tableId); - - // Add column for the remove row button - headers.unshift(''); - table.init(headers); - headers.shift(); - - // Append datatable to tab - $('#' + tabId).append(table.object()); - - // Add table rows - // Start with the 2nd row (1st row is the headers) - for ( var i = 1; i < rsp.length; i++) { - // Split into columns - var cols = rsp[i].split(','); - - // Go through each column - for ( var j = 0; j < cols.length; j++) { - - // If the column is not complete - if (cols[j].count('"') == 1) { - while (cols[j].count('"') != 2) { - // Merge this column with the adjacent one - cols[j] = cols[j] + "," + cols[j + 1]; - - // Remove merged row - cols.splice(j + 1, 1); - } - } - - // Replace quote - cols[j] = cols[j].replace(new RegExp('"', 'g'), ''); - } - - // Add remove button - cols.unshift(''); - - // Add row - table.add(cols); - - // Save original table content - origCont[i] = cols; - } - - // Turn table into datatable - var dTable = $('#' + id + 'Datatable').dataTable({ - 'iDisplayLength': 50, - 'bLengthChange': false, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - - /** - * Enable editable columns - */ - // Do not make 1st column editable - $('#' + tableId + ' td:not(td:nth-child(1))').editable( - function(value, settings) { - // Get column index - var colPos = this.cellIndex; - // Get row index - var rowPos = dTable.fnGetPosition(this.parentNode); - - // Update datatable - dTable.fnUpdate(value, rowPos, colPos); - - return (value); - }, { - onblur : 'submit', // Clicking outside editable area submits changes - type : 'textarea', - placeholder: ' ', - height : '30px' // The height of the text area - }); - - // Create action bar - var actionBar = $('
    '); - - var saveLnk = $('Save'); - saveLnk.click(function() { - // Get table ID and name - var tableId = $(this).parents('.dataTables_wrapper').attr('id').replace('_wrapper', ''); - var tableName = tableId.replace('Datatable', ''); - - // Get datatable - var dTable = $('#' + tableId).dataTable(); - // Get the nodes from the table - var dRows = dTable.fnGetNodes(); - - // Go through each row - for ( var i = 0; i < dRows.length; i++) { - // If there is row with values - if (dRows[i]) { - // Go through each column - // Ignore the 1st column because it is a button - var cols = dRows[i].childNodes; - var vals = new Object(); - for ( var j = 1; j < cols.length; j++) { - var val = cols.item(j).firstChild.nodeValue; - - // Insert quotes - if (val == ' ') { - vals[j - 1] = ''; - } else { - vals[j - 1] = val; - } - } - - // Save row - newCont[i + 1] = vals; - } - } - - // Update xCAT table - $.ajax({ - type : 'POST', - url : 'lib/tabRestore.php', - dataType : 'json', - data : { - table : tableName, - cont : newCont - }, - success : function(data) { - // Create info message - var dialog = $('
    ').append(createInfoBar('Changes saved!')); - - // Open dialog - dialog.dialog({ - modal: true, - title: 'Info', - width: 400, - buttons: { - "Ok": function(){ - $(this).dialog("close"); - } - } - }); - } - }); - }); - - var undoLnk = $('Undo'); - undoLnk.click(function() { - // Get table ID - var tableId = $(this).parents('.dataTables_wrapper').attr('id').replace('_wrapper', ''); - - // Get datatable - var dTable = $('#' + tableId).dataTable(); - - // Clear entire datatable - dTable.fnClearTable(); - - // Add original content back into datatable - for ( var i = 1; i < origCont.length; i++) { - dTable.fnAddData(origCont[i], true); - } - - // Enable editable columns (again) - // Do not make 1st column editable - $('#' + tableId + ' td:not(td:nth-child(1))').editable( - function(value, settings) { - // Get column index - var colPos = this.cellIndex; - // Get row index - var rowPos = dTable.fnGetPosition(this.parentNode); - - // Update datatable - dTable.fnUpdate(value, rowPos, colPos); - - return (value); - }, { - onblur : 'submit', // Clicking outside editable area submits changes - type : 'textarea', - placeholder: ' ', - height : '30px' // The height of the text area - }); - }); - - var addLnk = $('Add row'); - addLnk.click(function() { - // Create an empty row - var row = new Array(); - - /** - * Remove button - */ - row.push(''); - for ( var i = 0; i < headers.length; i++) { - row.push(''); - } - - // Get table ID and name - var tableId = $(this).parents('.dataTables_wrapper').attr('id').replace('_wrapper', ''); - - // Get datatable - var dTable = $('#' + tableId).dataTable(); - - // Add the row to the data table - dTable.fnAddData(row); - - // Enable editable columns (again) - // Do not make 1st column editable - $('#' + tableId + ' td:not(td:nth-child(1))').editable( - function(value, settings) { - // Get column index - var colPos = this.cellIndex; - // Get row index - var rowPos = dTable.fnGetPosition(this.parentNode); - - // Update datatable - dTable.fnUpdate(value, rowPos, colPos); - - return (value); - }, { - onblur : 'submit', // Clicking outside editable area submits changes - type : 'textarea', - placeholder: ' ', - height : '30px' // The height of the text area - }); - }); - - // Create an action menu - var actionsMenu = createMenu([saveLnk, undoLnk, addLnk]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + id + 'Datatable_wrapper').prepend(menuDiv); - menuDiv.append(actionBar); - $('#' + id + 'Datatable_filter').appendTo(menuDiv); -} - -/** - * Delete a row in the data table - * - * @param obj The object that was clicked - */ -function deleteRow(obj) { - // Get table ID - var tableId = $(obj).parents('table').attr('id'); - - // Get datatable - var dTable = $('#' + tableId).dataTable(); - - // Get all nodes within the datatable - var rows = dTable.fnGetNodes(); - // Get target row - var tgtRow = $(obj).parent().parent().get(0); - - // Find the target row in the datatable - for ( var i in rows) { - // If the row matches the target row - if (rows[i] == tgtRow) { - // Remove row - dTable.fnDeleteRow(i, null, true); - break; - } - } -} - -/** - * Count the number of occurrences of a specific character in a string - * - * @param c Character to count - * @return The number of occurrences - */ -String.prototype.count = function(c) { - return (this.length - this.replace(new RegExp(c, 'g'), '').length)/c.length; -}; - -/** - * Update dialog - * - * @param data HTTP request data - */ -function updatePanel(data) { - var dialogId = data.msg; - var infoMsg; - - // Create info message - if (jQuery.isArray(data.rsp)) { - infoMsg = ''; - for (var i in data.rsp) { - infoMsg += data.rsp[i] + '
    '; - } - } else { - infoMsg = data.rsp; - } - - // Create info bar with close button - var infoBar = $('
    ').css('margin', '5px 0px'); - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px' - }); - - // Create close button to close info bar - var close = $('').css({ - 'display': 'inline-block', - 'float': 'right' - }).click(function() { - $(this).parent().remove(); - }); - - var msg = $('
    ' + infoMsg + '
    ').css({ - 'display': 'inline-block', - 'width': '85%' - }); - - infoBar.append(icon, msg, close); - infoBar.prependTo($('#' + dialogId)); +/** + * Global variables + */ +var configTabs; // Config tabs +var configDatatables = new Object(); // Datatables on the config page + +/** + * Set the datatable + * + * @param id The ID of the datatable + * @param obj Datatable object + */ +function setConfigDatatable(id, obj) { + configDatatables[id] = obj; +} + +/** + * Get the datatable with the given ID + * + * @param id The ID of the datatable + * @return Datatable object + */ +function getConfigDatatable(id) { + return configDatatables[id]; +} + +/** + * Set the configure tab + * + * @param obj Tab object + */ +function setConfigTab(obj) { + configTabs = obj; +} + +/** + * Get the configure tab + * + * @param Nothing + * @return Tab object + */ +function getConfigTab() { + return configTabs; +} + +/** + * Load configure page + */ +function loadConfigPage() { + // If the configure page has already been loaded + if ($('#content').children().length) { + // Do not reload configure page + return; + } + + // Create configure tab + var tab = new Tab(); + setConfigTab(tab); + tab.init(); + $('#content').append(tab.object()); + + // Create loader + var loader = $('
    ').append(createLoader()); + + // Add tab to configure xCAT tables + tab.add('configTablesTab', 'Tables', loader, false); + + // Add the self-service tab + tab.add('usersTab', 'Users', '', false); + + // Add the self-service tab + tab.add('serviceTab', 'Service', '', false); + + // Add the files tab + tab.add('filesTab', 'Files', '', false); + + // Get list of tables and their descriptions + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : '-d', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadTableNames(data); + } + }); + + // Do not load everything at once + // Load when tab is shown + tab.object().bind('tabsshow', function(event, ui) { + if ($(ui.panel).children().length) { + return; + } + + if (ui.index == 1) { + loadUserPage(); + } else if (ui.index == 2) { + loadServicePage(); + } else if (ui.index == 3) { + loadFilesPage(); + } + }); +} + +/** + * Load xCAT database table names and their descriptions + * + * @param data Data returned from HTTP request + */ +function loadTableNames(data) { + // Get output + var tables = data.rsp; + + // Remove loader + var tabId = 'configTablesTab'; + $('#' + tabId).find('img').hide(); + + // Create a groups division + var tablesDIV = $('
    '); + $('#' + tabId).append(tablesDIV); + + // Create info bar + var infoBar = createInfoBar('Select a table to view or edit.'); + tablesDIV.append(infoBar); + + // Create a list for the tables + var list = $('
      '); + // Loop through each table + for ( var i = 0; i < tables.length; i++) { + // Create a link for each table + var args = tables[i].split(':'); + var link = $('' + args[0] + ''); + + // Open table on click + link.bind('click', function(e) { + // Get table ID that was clicked + var id = (e.target) ? e.target.id : e.srcElement.id; + + // Create loader + var loader = $('
      ').append(createLoader()); + + // Add a new tab for this table + var configTab = getConfigTab(); + if (!$('#' + id + 'Tab').length) { + configTab.add(id + 'Tab', id, loader, true); + + // Get contents of selected table + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : id, + msg : id + }, + + success : function(data) { + data = decodeRsp(data); + loadTable(data); + } + }); + } + + // Select new tab + configTab.select(id + 'Tab'); + }); + + var item = $('
    • '); + item.append(link); + + // Append the table description + item.append(': ' + args[1]); + + // Append item to list + list.append(item); + } + + tablesDIV.append(list); +} + +/** + * Load a given database table + * + * @param data Data returned from HTTP request + */ +function loadTable(data) { + // Get response + var rsp = data.rsp; + // Get table ID + var id = data.msg; + + // Remove loader + var tabId = id + 'Tab'; + $('#' + tabId).find('img').remove(); + + // Create info bar + var infoBar = createInfoBar('Click on a cell to edit. Click outside the table to write to the cell. Once you are satisfied with how the table looks, click on Save.'); + $('#' + tabId).append(infoBar); + + // Create action bar + var actionBar = $('
      '); + $('#' + tabId).append(actionBar); + + // Get table headers + var args = rsp[0].replace('#', ''); + var headers = args.split(','); + + // Create container for original table contents + var origCont = new Array(); // Original table content + origCont[0] = rsp[0].split(','); // Headers + + // Create container for new table contents + var newCont = new Object(); + var tmp = new Object(); + tmp[0] = '#' + headers[0]; // Put a # in front of the header + for ( var i = 1; i < headers.length; i++) { + tmp[i] = headers[i]; + } + newCont[0] = tmp; + + // Create a new datatable + var tableId = id + 'Datatable'; + var table = new DataTable(tableId); + + // Add column for the remove row button + headers.unshift(''); + table.init(headers); + headers.shift(); + + // Append datatable to tab + $('#' + tabId).append(table.object()); + + // Add table rows + // Start with the 2nd row (1st row is the headers) + for ( var i = 1; i < rsp.length; i++) { + // Split into columns + var cols = rsp[i].split(','); + + // Go through each column + for ( var j = 0; j < cols.length; j++) { + + // If the column is not complete + if (cols[j].count('"') == 1) { + while (cols[j].count('"') != 2) { + // Merge this column with the adjacent one + cols[j] = cols[j] + "," + cols[j + 1]; + + // Remove merged row + cols.splice(j + 1, 1); + } + } + + // Replace quote + cols[j] = cols[j].replace(new RegExp('"', 'g'), ''); + } + + // Add remove button + cols.unshift(''); + + // Add row + table.add(cols); + + // Save original table content + origCont[i] = cols; + } + + // Turn table into datatable + var dTable = $('#' + id + 'Datatable').dataTable({ + 'iDisplayLength': 50, + 'bLengthChange': false, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + + /** + * Enable editable columns + */ + // Do not make 1st column editable + $('#' + tableId + ' td:not(td:nth-child(1))').editable( + function(value, settings) { + // Get column index + var colPos = this.cellIndex; + // Get row index + var rowPos = dTable.fnGetPosition(this.parentNode); + + // Update datatable + dTable.fnUpdate(value, rowPos, colPos); + + return (value); + }, { + onblur : 'submit', // Clicking outside editable area submits changes + type : 'textarea', + placeholder: ' ', + height : '30px' // The height of the text area + }); + + // Create action bar + var actionBar = $('
      '); + + var saveLnk = $('Save'); + saveLnk.click(function() { + // Get table ID and name + var tableId = $(this).parents('.dataTables_wrapper').attr('id').replace('_wrapper', ''); + var tableName = tableId.replace('Datatable', ''); + + // Get datatable + var dTable = $('#' + tableId).dataTable(); + // Get the nodes from the table + var dRows = dTable.fnGetNodes(); + + // Go through each row + for ( var i = 0; i < dRows.length; i++) { + // If there is row with values + if (dRows[i]) { + // Go through each column + // Ignore the 1st column because it is a button + var cols = dRows[i].childNodes; + var vals = new Object(); + for ( var j = 1; j < cols.length; j++) { + var val = cols.item(j).firstChild.nodeValue; + + // Insert quotes + if (val == ' ') { + vals[j - 1] = ''; + } else { + vals[j - 1] = val; + } + } + + // Save row + newCont[i + 1] = vals; + } + } + + // Update xCAT table + $.ajax({ + type : 'POST', + url : 'lib/tabRestore.php', + dataType : 'json', + data : { + table : tableName, + cont : newCont + }, + success : function(data) { + // data = decodeRsp(data); Do not do this here until tabRestore.php is analyzed + // Create info message + var dialog = $('
      ').append(createInfoBar('Changes saved!')); + + // Open dialog + dialog.dialog({ + modal: true, + title: 'Info', + width: 400, + buttons: { + "Ok": function(){ + $(this).dialog("close"); + } + } + }); + } + }); + }); + + var undoLnk = $('Undo'); + undoLnk.click(function() { + // Get table ID + var tableId = $(this).parents('.dataTables_wrapper').attr('id').replace('_wrapper', ''); + + // Get datatable + var dTable = $('#' + tableId).dataTable(); + + // Clear entire datatable + dTable.fnClearTable(); + + // Add original content back into datatable + for ( var i = 1; i < origCont.length; i++) { + dTable.fnAddData(origCont[i], true); + } + + // Enable editable columns (again) + // Do not make 1st column editable + $('#' + tableId + ' td:not(td:nth-child(1))').editable( + function(value, settings) { + // Get column index + var colPos = this.cellIndex; + // Get row index + var rowPos = dTable.fnGetPosition(this.parentNode); + + // Update datatable + dTable.fnUpdate(value, rowPos, colPos); + + return (value); + }, { + onblur : 'submit', // Clicking outside editable area submits changes + type : 'textarea', + placeholder: ' ', + height : '30px' // The height of the text area + }); + }); + + var addLnk = $('Add row'); + addLnk.click(function() { + // Create an empty row + var row = new Array(); + + /** + * Remove button + */ + row.push(''); + for ( var i = 0; i < headers.length; i++) { + row.push(''); + } + + // Get table ID and name + var tableId = $(this).parents('.dataTables_wrapper').attr('id').replace('_wrapper', ''); + + // Get datatable + var dTable = $('#' + tableId).dataTable(); + + // Add the row to the data table + dTable.fnAddData(row); + + // Enable editable columns (again) + // Do not make 1st column editable + $('#' + tableId + ' td:not(td:nth-child(1))').editable( + function(value, settings) { + // Get column index + var colPos = this.cellIndex; + // Get row index + var rowPos = dTable.fnGetPosition(this.parentNode); + + // Update datatable + dTable.fnUpdate(value, rowPos, colPos); + + return (value); + }, { + onblur : 'submit', // Clicking outside editable area submits changes + type : 'textarea', + placeholder: ' ', + height : '30px' // The height of the text area + }); + }); + + // Create an action menu + var actionsMenu = createMenu([saveLnk, undoLnk, addLnk]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + id + 'Datatable_wrapper').prepend(menuDiv); + menuDiv.append(actionBar); + $('#' + id + 'Datatable_filter').appendTo(menuDiv); +} + +/** + * Delete a row in the data table + * + * @param obj The object that was clicked + */ +function deleteRow(obj) { + // Get table ID + var tableId = $(obj).parents('table').attr('id'); + + // Get datatable + var dTable = $('#' + tableId).dataTable(); + + // Get all nodes within the datatable + var rows = dTable.fnGetNodes(); + // Get target row + var tgtRow = $(obj).parent().parent().get(0); + + // Find the target row in the datatable + for ( var i in rows) { + // If the row matches the target row + if (rows[i] == tgtRow) { + // Remove row + dTable.fnDeleteRow(i, null, true); + break; + } + } +} + +/** + * Count the number of occurrences of a specific character in a string + * + * @param c Character to count + * @return The number of occurrences + */ +String.prototype.count = function(c) { + return (this.length - this.replace(new RegExp(c, 'g'), '').length)/c.length; +}; + +/** + * Update dialog + * + * @param data HTTP request data + */ +function updatePanel(data) { + var dialogId = data.msg; + var infoMsg; + + // Create info message + if (jQuery.isArray(data.rsp)) { + infoMsg = ''; + for (var i in data.rsp) { + infoMsg += data.rsp[i] + '
      '; + } + } else { + infoMsg = data.rsp; + } + + // Create info bar with close button + var infoBar = $('
      ').css('margin', '5px 0px'); + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px' + }); + + // Create close button to close info bar + var close = $('').css({ + 'display': 'inline-block', + 'float': 'right' + }).click(function() { + $(this).parent().remove(); + }); + + var msg = $('
      ' + infoMsg + '
      ').css({ + 'display': 'inline-block', + 'width': '85%' + }); + + infoBar.append(icon, msg, close); + infoBar.prependTo($('#' + dialogId)); } \ No newline at end of file diff --git a/xCAT-UI/js/configure/files.js b/xCAT-UI/js/configure/files.js new file mode 100644 index 000000000..4c79af81f --- /dev/null +++ b/xCAT-UI/js/configure/files.js @@ -0,0 +1,336 @@ +/** + * Load the files page + */ +function loadFilesPage() { + var tabId = 'filesTab'; + $('#' + tabId).empty(); + + // Set padding for page + $('#' + tabId).css('padding', '10px 30px'); + + // Create info bar + var info = $('#' + tabId).find('.ui-state-highlight'); + // If there is no info bar + if (!info.length) { + var infoBar = createInfoBar('Below is a listing of the xCAT repository. ' + + 'Upload any file or package into the repository using the Upload button. ' + + 'Go into any subdirectories by specifying the directory path and clicking on Go.'); + + var directoryFS = $('
      '); + var dirLegend = $('Directory'); + directoryFS.append(dirLegend); + + // Division to hold directory actions + var actions = $('
      '); + directoryFS.append(actions); + + // Create button to create a directory + var folderBtn = createButton('New folder'); + folderBtn.click(function() { + var deleteFolderBtn = $(''); + var createFolderBtn = createButton('Create'); + + // Create a new directory + var newFolder = $('
    • '); + newFolder.prepend(deleteFolderBtn); + newFolder.append(createFolderBtn); + $('#repo_content ul').append(newFolder); + + // Delete new folder on-click + deleteFolderBtn.click(function() { + $(this).parents('li').remove(); + }); + + // Create folder on-click + createFolderBtn.click(function() { + var directory = $('#' + tabId + ' input[name="repo_directory"]'); + var newFolderPath = $('#' + tabId + ' input[name="new_folder"]').val(); + if (newFolderPath) { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'createfolder;' + directory.val() + '/' + newFolderPath, + msg : '' + }, + + success:function(data) { + data = decodeRsp(data); + openDialog('info', data.rsp[0]); + } + }); + + $(this).parents('li').remove(); + } else { + openDialog('warn', 'You must specify the folder name'); + } + }); + }); + + // Create button to upload files + var uploadBtn = createButton('Upload'); + uploadBtn.click(function() { + var directory = $('#' + tabId + ' input[name="repo_directory"]'); + openUploadDialog(directory.val()); + }); + + // Create button to go into a directory path + var dirPath = $(''); + var goBtn = createButton('Go'); + goBtn.click(function() { + var directory = $('#' + tabId + ' input[name="repo_directory"]'); + loadPath(directory.val()); + }); + goBtn.attr('id', 'go_to_path'); + + var space = $('
      '); + var content = $('
      '); + actions.append(folderBtn, uploadBtn, dirPath, goBtn); + directoryFS.append(space, content); + + $('#' + tabId).append(infoBar, directoryFS); + } + + // Retrieve repository space + getRepositorySpace(); + + // Retrieve files from /install + loadPath('/install'); +} + +/** + * Get the repository space + */ +function getRepositorySpace() { + // Grab repository space + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + async: false, + data : { + cmd : 'webrun', + tgt : '', + args : 'getrepospace', + msg : '' + }, + success: function(data) { + data = decodeRsp(data); + $('#repo_space').children().remove(); + + // Data returned is: size, used, available, used %, mount + // Data could be in a different format in CMO, where it puts the directory on the line + // "rsp":["\/data\/xcat\/install 28G 6.0G 20G 24% \/install"],"msg":null} + var space = data.rsp[0].split(' '); + if (space.length == 6) { + space.splice(0,1); + } + var spaceLabel = $(''); + $('#repo_space').append(spaceLabel); + } + }); +} + +/** + * Open a dialog to upload files into the repository + * + * @param destDirectory The destination directory + */ +function openUploadDialog(destDirectory) { + // Create info bar + var info = createInfoBar('Select a file to upload onto ' + destDirectory + '.'); + var dialog = $('
      '); + dialog.append(info); + + // Upload file + var upload = $('
      '); + var label = $(''); + var file = $(''); + var subBtn = createButton('Upload'); + upload.append(label, file, subBtn); + dialog.append(upload); + + upload.submit(function() { + // Create status bar, hide on load + var statBarId = 'uploadStatusBar'; + var statBar = createStatusBar(statBarId); + var loader = createLoader(''); + statBar.find('div').append('Do not close this dialog while the file is being uploaded '); + statBar.find('div').append(loader); + statBar.prependTo($('#upload_file_dg')); + + var data = new FormData($('#upload_file')[0]); + $.ajax({ + type: 'POST', + url : 'lib/uploadfile.php?destination=' + destDirectory, + data: data, + success: function(data) { + $('#uploadStatusBar').find('img').hide(); + $('#uploadStatusBar').find('div').empty(); + $('#uploadStatusBar').find('div').append(data); + + // Refresh directory contents + $('#go_to_path').click(); + getRepositorySpace(); + }, + cache: false, + contentType: false, + processData: false + }); + + return false; + }); + + // Create dialog + dialog.dialog({ + modal: true, + title: 'Upload', + width: 500, + close: function() {$(this).remove();} + }); +} + +/** + * Load the directory path structure + * + * @path The directory path + */ +function loadPath(path) { + // Limit access to only /install + if (path.substring(0, 9).indexOf("install") == -1) { + openDialog('warn', 'You are not authorized to browse outside the repository'); + return; + } + + var tabId = 'filesTab'; + var directory = $('#' + tabId + ' input[name="repo_directory"]'); + directory.val(path); + + // Un-ordered list containing directories and files + var contentId = 'repo_content'; + $('#' + contentId).empty(); + var itemsList = $('
        '); + $('#' + contentId).append(itemsList); + + // Back button to go up a directory + var item = $('
      • ..
      • '); + itemsList.append(item); + item.dblclick(function() { + if (path.lastIndexOf('/') > 1) + path = path.substring(0, path.lastIndexOf('/')); + loadPath(path); + }); + + $.ajax({ + type: 'POST', + url : 'lib/getpath.php', + dataType : 'json', + data: { + action: 'browse', + path: path, + time: new Date().getTime() + }, + beforeSend: function() { + // Show loading image + }, + success: function(files) { + $.each(files, function(index, file) { + if (!file.path || file.path.indexOf("undefined")) + file.path = ""; + + var fullPath = file.path + "/" + file.name; + + // Create a list showing the directories and files + var item; + if (file.isFolder) { + var deleteFolderBtn = $(''); + + item = $('
      • ' + file.name + '
      • '); + item.prepend(deleteFolderBtn); + itemsList.append(item); + item.dblclick(function() { + loadPath(directory.val() + fullPath); + }); + + // Delete file on click + deleteFolderBtn.click(function() { + deleteFile($(this).parents('li'), directory.val() + fullPath); + }); + } else { + var icon = $(''); + var deleteFileBtn = $(''); + + item = $('
      • ' + file.name + '
      • '); + item.append(deleteFileBtn, icon); + + // Delete file on click + deleteFileBtn.click(function() { + deleteFile($(this).parents('li'), directory.val() + fullPath); + }); + + itemsList.append(item); + } + }); + } + }); +} + +/** + * Prompt user to confirm deletion of file + * + * @param container The element container + * @param file The file name to delete + */ +function deleteFile(container, file) { + // Open dialog to confirm + var confirmDialog = $('
        '); + var warn = createWarnBar('Are you sure you want to delete ' + file + '?'); + confirmDialog.append(warn); + confirmDialog.dialog({ + title: "Confirm", + modal: true, + width: 400, + close: function() {$(this).remove();}, + buttons: { + "Ok": function() { + var loader = createLoader('').css({'margin': '5px'}); + $(this).append(loader); + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close':function() { + $(this).dialog('destroy').remove(); + } + }); + + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + async: false, + data : { + cmd : 'webrun', + tgt : '', + args : 'deletefile;' + file, + msg : '' + }, + success: function(data) { + data = decodeRsp(data); + $('#confirm_delete').children().remove(); + var info = createInfoBar(data.rsp[0]); + $('#confirm_delete').append(info); + getRepositorySpace(); + } + }); + + // Delete folder from the list + container.remove(); + }, + "Cancel": function() { + $(this).dialog('destroy').remove(); + } + } + }); +} \ No newline at end of file diff --git a/xCAT-UI/js/configure/service.js b/xCAT-UI/js/configure/service.js index 515b86296..036de8dce 100644 --- a/xCAT-UI/js/configure/service.js +++ b/xCAT-UI/js/configure/service.js @@ -1,1280 +1,1314 @@ -/** - * Global variables - */ -var topPriority = 0; - -/** - * Load the service portal's provision page - * - * @param tabId Tab ID where page will reside - */ -function loadServicePage(tabId) { - // Create info bar - var infoBar = createInfoBar('Select a platform to configure, then click Ok.'); - - // Create self-service portal page - var tabId = 'serviceTab'; - var servicePg = $('
        '); - $('#' + tabId).append(infoBar, servicePg); - - // Create radio buttons for platforms - var hwList = $('
          Platforms available:
        '); - var esx = $('
      • ESX
      • '); - var kvm = $('
      • KVM
      • '); - var zvm = $('
      • z\/VM
      • '); - - hwList.append(esx); - hwList.append(kvm); - hwList.append(zvm); - servicePg.append(hwList); - - /** - * Ok - */ - var okBtn = createButton('Ok'); - okBtn.bind('click', function(event) { - var configTabs = getConfigTab(); - - // Get hardware that was selected - var hw = $(this).parent().find('input[name="hw"]:checked').val(); - var newTabId = hw + 'ProvisionTab'; - - if ($('#' + newTabId).size() > 0){ - configTabs.select(newTabId); - } else { - var title = ''; - - // Create an instance of the plugin - var plugin = null; - switch (hw) { - case "kvm": - plugin = new kvmPlugin(); - title = 'KVM'; - break; - case "esx": - plugin = new esxPlugin(); - title = 'ESX'; - break; - case "zvm": - plugin = new zvmPlugin(); - title = 'z/VM'; - - // Get zVM host names - if (!$.cookie('xcat_zvms')){ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webportal', - tgt : '', - args : 'lszvm', - msg : '' - }, - - success : function(data) { - setzVMCookies(data); - } - }); - } - - break; - } - - // Select tab - configTabs.add(newTabId, title, '', true); - configTabs.select(newTabId); - plugin.loadConfigPage(newTabId); - } - }); - - servicePg.append(okBtn); -} - -/** - * Round a floating point to a given precision - * - * @param value Floating point - * @param precision Decimal precision - * @returns Floating point number - */ -function toFixed(value, precision) { - var power = Math.pow(10, precision || 0); - return String(Math.round(value * power) / power); -} - -/** - * Query the images that exists - * - * @param panelId Panel ID - */ -function queryImages(panelId) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'osimage', - msg : panelId - }, - - success : configImagePanel - }); -} - -/** - * Panel to configure OS images - * - * @param data Data from HTTP request - */ -function configImagePanel(data) { - var panelId = data.msg; - var rsp = data.rsp; - - // Wipe panel clean - $('#' + panelId).empty(); - - // Add info bar - $('#' + panelId).append(createInfoBar('Create, edit, and delete operating system images for the self-service portal.')); - - // Create table - var tableId = panelId + 'Datatable'; - var table = new DataTable(tableId); - table.init(['', 'Name', 'Selectable', 'OS Version', 'OS Arch', 'OS Name', 'Type', 'Profile', 'Method', 'Description']); - - // Insert images into table - var imagePos = 0; - var profilePos = 0; - var osversPos = 0; - var osarchPos = 0; - var osnamePos = 0; - var imagetypePos = 0; - var provMethodPos = 0; - var comments = 0; - var desc, selectable, tmp; - // Get column index for each attribute - var colNameArray = rsp[0].substr(1).split(','); - for (var i in colNameArray){ - switch (colNameArray[i]){ - case 'imagename': { - imagePos = i; - } - break; - - case 'profile':{ - profilePos = i; - } - break; - - case 'osvers':{ - osversPos = i; - } - break; - - case 'osarch':{ - osarchPos = i; - } - break; - - case 'osname':{ - osnamePos = i; - } - break; - - case 'imagetype':{ - imagetypePos = i; - } - break; - - case 'comments':{ - comments = i; - } - break; - - case 'provmethod':{ - provMethodPos = i; - } - break; - - default : - break; - } - } - - // Go through each index - for (var i = 1; i < rsp.length; i++) { - // Get image name - var cols = rsp[i].split(','); - var name = cols[imagePos].replace(new RegExp('"', 'g'), ''); - var profile = cols[profilePos].replace(new RegExp('"', 'g'), ''); - var provMethod = cols[provMethodPos].replace(new RegExp('"', 'g'), ''); - var osVer = cols[osversPos].replace(new RegExp('"', 'g'), ''); - var osArch = cols[osarchPos].replace(new RegExp('"', 'g'), ''); - var osName = cols[osnamePos].replace(new RegExp('"', 'g'), ''); - var imageType = cols[imagetypePos].replace(new RegExp('"', 'g'), ''); - var osComments = cols[comments].replace(new RegExp('"', 'g'), ''); - - // Only save install boot and s390x architectures - if (osArch == "s390x") { - // Set default description and selectable - selectable = "no"; - desc = "No description"; - - if (osComments) { - tmp = osComments.split('|'); - for (var j = 0; j < tmp.length; j++) { - // Save description - if (tmp[j].indexOf('description:') > -1) { - desc = tmp[j].replace('description:', ''); - desc = jQuery.trim(desc); - } - - // Is the image selectable? - if (tmp[j].indexOf('selectable:') > -1) { - selectable = tmp[j].replace('selectable:', ''); - selectable = jQuery.trim(selectable); - } - } - } - - // Columns are: name, selectable, OS version, OS arch, OS name, type, profile, method, and description - var cols = new Array(name, selectable, osVer, osArch, osName, imageType, profile, provMethod, desc); - - // Add remove button where id = user name - cols.unshift(''); - - // Add row - table.add(cols); - } - } - - // Append datatable to tab - $('#' + panelId).append(table.object()); - - // Turn into datatable - $('#' + tableId).dataTable({ - 'iDisplayLength': 50, - 'bLengthChange': false, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - - // Create action bar - var actionBar = $('
        ').css("width", "400px"); - - // Create a profile - var createLnk = $('Create'); - createLnk.click(function() { - imageDialog(); - }); - - // Edit a profile - var editLnk = $('Edit'); - editLnk.click(function() { - var images = $('#' + tableId + ' input[type=checkbox]:checked'); - for (var i in images) { - var image = images.eq(i).attr('name'); - if (image) { - // Columns are: name, selectable, OS version, OS arch, OS name, type, profile, method, and description - var cols = images.eq(i).parents('tr').find('td'); - var selectable = cols.eq(2).text(); - var osVersion = cols.eq(3).text(); - var osArch = cols.eq(4).text(); - var osName = cols.eq(5).text(); - var type = cols.eq(6).text(); - var profile = cols.eq(7).text(); - var method = cols.eq(8).text(); - var description = cols.eq(9).text(); - - editImageDialog(image, selectable, osVersion, osArch, osName, type, profile, method, description); - } - } - }); - - // Delete a profile - var deleteLnk = $('Delete'); - deleteLnk.click(function() { - var images = getNodesChecked(tableId); - if (images) { - deleteImageDialog(images); - } - }); - - // Refresh profiles table - var refreshLnk = $('Refresh'); - refreshLnk.click(function() { - queryImages(panelId); - }); - - // Create an action menu - var actionsMenu = createMenu([refreshLnk, createLnk, editLnk, deleteLnk]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + tableId + '_wrapper').prepend(menuDiv); - menuDiv.append(actionBar); - $('#' + tableId + '_filter').appendTo(menuDiv); - - // Resize accordion - $('#' + tableId).parents('.ui-accordion').accordion('resize'); -} - -/** - * Open image dialog - */ -function imageDialog() { - // Create form to add profile - var dialogId = 'createImage'; - var imageForm = $('
        '); - - // Create info bar - var info = createInfoBar('Provide the following attributes for the image. The image name will be generated based on the attributes you will give.'); - imageForm.append(info); - - var imageName = $('
        '); - var selectable = $('
        '); - var imageType = $('
        '); - var architecture = $('
        '); - var osName = $('
        '); - var osVersion = $('
        '); - var profile = $('
        '); - var provisionMethod = $('
        '); - var provisionSelect = $(''); - provisionMethod.append(provisionSelect); - var comments = $('
        '); - imageForm.append(imageName, selectable, imageType, architecture, osName, osVersion, profile, provisionMethod, comments); - - // Generate tooltips - imageForm.find('div input[title],textarea[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add image - imageForm.dialog({ - title:'Create image', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get image attributes - var imageType = $(this).find('input[name="imagetype"]'); - var selectable = $(this).find('input[name="selectable"]'); - var architecture = $(this).find('input[name="osarch"]'); - var osName = $(this).find('input[name="osname"]'); - var osVersion = $(this).find('input[name="osvers"]'); - var profile = $(this).find('input[name="profile"]'); - var provisionMethod = $(this).find('select[name="provmethod"]'); - var comments = $(this).find('input[name="comments"]'); - - // Check that image attributes are provided before continuing - var ready = 1; - var inputs = new Array(imageType, architecture, osName, osVersion, profile, provisionMethod); - for (var i in inputs) { - if (!inputs[i].val()) { - inputs[i].css('border-color', 'red'); - ready = 0; - } else - inputs[i].css('border-color', ''); - } - - // If inputs are not complete, show warning message - if (!ready) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - // Override image name - $(this).find('input[name="imagename"]').val(osVersion.val() + '-' + architecture.val() + '-' + provisionMethod.val() + '-' + profile.val()); - var imageName = $(this).find('input[name="imagename"]'); - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Set default description - if (!comments.val()) - comments.val('No description'); - - // Create arguments to send via AJAX - var args = 'updateosimage;' + imageName.val() + ';' + - imageType.val() + ';' + - architecture.val() + ';' + - osName.val() + ';' + - osVersion.val() + ';' + - profile.val() + ';' + - provisionMethod.val() + ';'; - - if (selectable.attr('checked')) - args += '"description:' + comments.val() + '|selectable:yes"'; - else - args += '"description:' + comments.val() + '|selectable:no"'; - - // Add image to xCAT - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : args, - msg : dialogId - }, - - success : updatePanel - }); - } - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Edit image dialog - * - * @param iName Image name - * @param iSelectable Is image selectable from service page - * @param iOsVersion OS version - * @param iProfile Profile name - * @param iMethod Provisioning method - * @param iComments Image description - */ -function editImageDialog(iName, iSelectable, iOsVersion, iOsArch, iOsName, iType, iProfile, iMethod, iComments) { - var inst = 0; - var dialogId = 'editImage' + inst; - while ($('#' + dialogId).length) { - // If one already exists, generate another one - inst = inst + 1; - dialogId = 'editImage' + inst; - } - - // Create form to add profile - var imageForm = $('
        '); - - // Create info bar - var info = createInfoBar('Provide the following attributes for the image. The image name will be generated based on the attributes you will give.'); - imageForm.append(info); - - var imageName = $('
        '); - var selectable = $('
        '); - var imageType = $('
        '); - var architecture = $('
        '); - var osName = $('
        '); - var osVersion = $('
        '); - var profile = $('
        '); - var provisionMethod = $('
        '); - var provisionSelect = $(''); - provisionMethod.append(provisionSelect); - var comments = $('
        '); - imageForm.append(imageName, selectable, imageType, architecture, osName, osVersion, profile, provisionMethod, comments); - - // Fill in image attributes - imageForm.find('input[name="imagename"]').val(iName); - imageForm.find('input[name="osvers"]').val(iOsVersion); - imageForm.find('input[name="osarch"]').val(iOsArch); - imageForm.find('input[name="osname"]').val(iOsName); - imageForm.find('input[name="imagetype"]').val(iType); - imageForm.find('input[name="profile"]').val(iProfile); - imageForm.find('select[name="provmethod"]').val(iMethod); - imageForm.find('input[name="comments"]').val(iComments); - if (iSelectable == "yes") - imageForm.find('input[name="selectable"]').attr('checked', 'checked'); - - // Generate tooltips - imageForm.find('div input[title],textarea[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "toggle", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add image - imageForm.dialog({ - title:'Edit image', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get image attributes - var imageType = $(this).find('input[name="imagetype"]'); - var selectable = $(this).find('input[name="selectable"]'); - var architecture = $(this).find('input[name="osarch"]'); - var osName = $(this).find('input[name="osname"]'); - var osVersion = $(this).find('input[name="osvers"]'); - var profile = $(this).find('input[name="profile"]'); - var provisionMethod = $(this).find('select[name="provmethod"]'); - var comments = $(this).find('input[name="comments"]'); - - // Check that image attributes are provided before continuing - var ready = 1; - var inputs = new Array(imageType, architecture, osName, osVersion, profile, provisionMethod); - for (var i in inputs) { - if (!inputs[i].val()) { - inputs[i].css('border-color', 'red'); - ready = 0; - } else - inputs[i].css('border-color', ''); - } - - // If inputs are not complete, show warning message - if (!ready) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - // Override image name - $(this).find('input[name="imagename"]').val(osVersion.val() + '-' + architecture.val() + '-' + provisionMethod.val() + '-' + profile.val()); - var imageName = $(this).find('input[name="imagename"]'); - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Set default description - if (!comments.val()) - comments.val('No description'); - - // Create arguments to send via AJAX - var args = 'updateosimage;' + imageName.val() + ';' + - imageType.val() + ';' + - architecture.val() + ';' + - osName.val() + ';' + - osVersion.val() + ';' + - profile.val() + ';' + - provisionMethod.val() + ';'; - - if (selectable.attr('checked')) - args += '"description:' + comments.val() + '|selectable:yes"'; - else - args += '"description:' + comments.val() + '|selectable:no"'; - - // Add image to xCAT - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : args, - msg : dialogId - }, - - success : updatePanel - }); - } - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to confirm image delete - * - * @param images Images to delete - */ -function deleteImageDialog(images) { - // Create form to delete disk to pool - var dialogId = 'deleteImage'; - var deleteForm = $('
        '); - - // Create info bar - var info = createInfoBar('Are you sure you want to delete ' + images.replace(new RegExp(',', 'g'), ', ') + '?'); - deleteForm.append(info); - - // Open dialog to delete user - deleteForm.dialog({ - title:'Delete image', - modal: true, - width: 400, - close: function(){ - $(this).remove(); - }, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Delete user - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'rmosimage;' + images, - msg : dialogId - }, - success : updatePanel - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Query the groups that exists - * - * @param panelId Panel ID - */ -function queryGroups(panelId) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'hosts', - msg : panelId - }, - - success : configGroupPanel - }); -} - -/** - * Panel to configure groups - * - * @param data Data from HTTP request - */ -function configGroupPanel(data) { - var panelId = data.msg; - var rsp = data.rsp; - - // Wipe panel clean - $('#' + panelId).empty(); - - // Add info bar - $('#' + panelId).append(createInfoBar('Create, edit, and delete groups for the self-service portal.')); - - // Create table - var tableId = panelId + 'Datatable'; - var table = new DataTable(tableId); - table.init(['', 'Name', 'Selectable', 'IP', 'Hostname', 'Description']); - - // Insert groups into table - var nodePos = 0; - var ipPos = 0; - var hostnamePos = 0; - var commentsPos = 0; - var desc, selectable, tmp; - // Get column index for each attribute - var colNameArray = rsp[0].substr(1).split(','); - for (var i in colNameArray){ - switch (colNameArray[i]){ - case 'node': - nodePos = i; - break; - - case 'ip': - ipPos = i; - break; - - case 'hostnames': - hostnamePos = i; - break; - - case 'comments': - commentsPos = i; - break; - - default : - break; - } - } - - // Go through each index - for (var i = 1; i < rsp.length; i++) { - // Get image name - var cols = rsp[i].split(','); - var name = cols[nodePos].replace(new RegExp('"', 'g'), ''); - var ip = cols[ipPos].replace(new RegExp('"', 'g'), ''); - var hostname = cols[hostnamePos].replace(new RegExp('"', 'g'), ''); - var comments = cols[commentsPos].replace(new RegExp('"', 'g'), ''); - - // Set default description and selectable - selectable = "no"; - network = ""; - desc = "No description"; - - if (comments) { - tmp = comments.split('|'); - for (var j = 0; j < tmp.length; j++) { - // Save description - if (tmp[j].indexOf('description:') > -1) { - desc = tmp[j].replace('description:', ''); - desc = jQuery.trim(desc); - } - - // Is the group selectable? - if (tmp[j].indexOf('selectable:') > -1) { - selectable = tmp[j].replace('selectable:', ''); - selectable = jQuery.trim(selectable); - } - } - } - - // Columns are: name, selectable, network, and description - var cols = new Array(name, selectable, ip, hostname, desc); - - // Add remove button where id = user name - cols.unshift(''); - - // Add row - table.add(cols); - } - - // Append datatable to tab - $('#' + panelId).append(table.object()); - - // Turn into datatable - $('#' + tableId).dataTable({ - 'iDisplayLength': 50, - 'bLengthChange': false, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - - // Create action bar - var actionBar = $('
        ').css("width", "400px"); - - // Create a group - var createLnk = $('Create'); - createLnk.click(function() { - groupDialog(); - }); - - // Edit a group - var editLnk = $('Edit'); - editLnk.click(function() { - var groups = $('#' + tableId + ' input[type=checkbox]:checked'); - for (var i in groups) { - var group = groups.eq(i).attr('name'); - if (group) { - // Column order is: name, selectable, network, and description - var cols = groups.eq(i).parents('tr').find('td'); - var selectable = cols.eq(2).text(); - var ip = cols.eq(3).text(); - var hostnames = cols.eq(4).text(); - var description = cols.eq(5).text(); - - editGroupDialog(group, selectable, ip, hostnames, description); - } - } - }); - - // Delete a profile - var deleteLnk = $('Delete'); - deleteLnk.click(function() { - var groups = getNodesChecked(tableId); - if (groups) { - deleteGroupDialog(groups); - } - }); - - // Refresh profiles table - var refreshLnk = $('Refresh'); - refreshLnk.click(function() { - queryGroups(panelId); - }); - - // Create an action menu - var actionsMenu = createMenu([refreshLnk, createLnk, editLnk, deleteLnk]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + tableId + '_wrapper').prepend(menuDiv); - menuDiv.append(actionBar); - $('#' + tableId + '_filter').appendTo(menuDiv); - - // Resize accordion - $('#' + tableId).parents('.ui-accordion').accordion('resize'); -} - -/** - * Open group dialog - */ -function groupDialog() { - // Create form to add profile - var dialogId = 'createGroup'; - var groupForm = $('
        '); - - // Create info bar - var info = createInfoBar('Provide the following attributes for the group.'); - groupForm.append(info); - - var group = $('
        '); - var selectable = $('
        '); - var ip = $('
        '); - var hostnames = $('
        '); - var comments = $('
        '); - var ipPool = $('
        '); - logOpt.hide(); - optsList.append(logOpt); - - // Create clear log checkbox - var clearChkBox = $('
      • '); - optsList.append(clearChkBox); - clearChkBox.append('Clear log'); - - retrieveChkBox.bind('click', function(event) { - tgtLog.toggle(); - }); - - setChkBox.find('input').bind('click', function(event) { - logOpt.toggle(); - }); - - // Generate tooltips - logForm.find('div input[title]').tooltip({ - position : "center right", - offset : [ -2, 10 ], - effect : "fade", - opacity : 0.7, - predelay: 800, - events : { - def : "mouseover,mouseout", - input : "mouseover,mouseout", - widget : "focus mouseover,blur mouseout", - tooltip : "mouseover,mouseout" - } - }); - - /** - * Run node - */ - var runBtn = createButton('Run'); - runBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parent().parent().find('.ui-state-error').remove(); - - var ready = true; - var errMsg = ''; - - // Verify required inputs are provided - var inputs = $('#' + newTabId + ' input'); - for ( var i = 0; i < inputs.length; i++) { - if (!inputs.eq(i).val() - && inputs.eq(i).attr('name') != 'tgtLog' - && inputs.eq(i).attr('name') != 'logOpt') { - inputs.eq(i).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - inputs.eq(i).css('border', 'solid #BDBDBD 1px'); - } - } - - // Write error message - if (!ready) { - errMsg = errMsg + 'Please provide a value for each missing field.
        '; - } - - var tgts = $('#' + newTabId + ' input[name=tgtNode]').val(); - var srcLog = $('#' + newTabId + ' input[name=srcLog]').val(); - - var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); - var optStr = '-s;' + srcLog + ';'; - var opt; - for ( var i = 0; i < chkBoxes.length; i++) { - opt = chkBoxes.eq(i).attr('name'); - optStr += '-' + opt; - - // If it is the retrieve log - if (opt == 't') { - // Append log destination - optStr += ';' + $('#' + newTabId + ' input[name=tgtLog]').val(); - } - - // If it is set options - if (opt == 'o') { - // Append options - optStr += ';' + $('#' + newTabId + ' textarea[name=logOpt]').val(); - } - - // Append ; to end of string - if (i < (chkBoxes.length - 1)) { - optStr += ';'; - } - } - - // If a value is given for every input - if (ready) { - // Do not disable all inputs - //var inputs = $('#' + newTabId + ' input'); - //inputs.attr('disabled', 'disabled'); - - /** - * (1) Retrieve, clear, or set options for event logs - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'reventlog', - tgt : tgts, - args : optStr, - msg : 'out=' + statBarId + ';cmd=reventlog;tgt=' + tgts - }, - - success : updateStatusBar - }); - - // Create loader - $('#' + statBarId).find('div').append(createLoader()); - $('#' + statBarId).show(); - - // Do not disable run button - //$(this).attr('disabled', 'true'); - } else { - // Show warning message - var warn = createWarnBar(errMsg); - warn.prependTo($(this).parent().parent()); - } - }); - logForm.append(runBtn); - - // Add clone tab - tab.add(newTabId, 'Logs', logForm, true); - } - - tab.select(newTabId); -}; +/** + * Execute when the DOM is fully loaded + */ +$(document).ready(function() { + // Load utility scripts + includeJs("js/custom/zvmUtils.js"); +}); + +/** + * Constructor + */ +var zvmPlugin = function() { + +}; + +/** + * Configure self-service configure page + */ +zvmPlugin.prototype.loadConfigPage = function(tabId) { + var configAccordion = $('
        '); + + // Create accordion panel for profiles + var profileSection = $('
        '); + var profileLnk = $('

        Profiles

        ').click(function () { + // Do not load panel again if it is already loaded + if ($('#zvmConfigProfile').find('.dataTables_wrapper').length) + return; + else + $('#zvmConfigProfile').append(createLoader('')); + + queryProfiles('zvmConfigProfile'); + }); + + // Create accordion panel for images + var imgSection = $('
        '); + var imgLnk = $('

        Templates

        ').click(function () { + // Do not load panel again if it is already loaded + if ($('#zvmConfigImages').find('.dataTables_wrapper').length) + return; + else + $('#zvmConfigImages').append(createLoader('')); + + queryImages('zvmConfigImages'); + }); + + // Create accordion panel for groups + var groupsSection = $('
        '); + var groupsLnk = $('

        Groups

        ').click(function () { + // Do not load panel again if it is already loaded + if ($('#zvmConfigGroups').find('.dataTables_wrapper').length) + return; + else + $('#zvmConfigGroups').append(createLoader('')); + + queryGroups('zvmConfigGroups'); + }); + + configAccordion.append(profileLnk, profileSection, imgLnk, imgSection, groupsLnk, groupsSection); + $('#' + tabId).append(configAccordion); + configAccordion.accordion(); + + profileLnk.trigger('click'); +}; + +/** + * Clone node (service page) + * + * @param node Node to clone + */ +zvmPlugin.prototype.serviceClone = function(node) { + var owner = $.cookie('xcat_username'); + var group = getUserNodeAttr(node, 'groups'); + + // Submit request to clone VM + // webportal clonezlinux [src node] [group] [owner] + var iframe = createIFrame('lib/srv_cmd.php?cmd=webportal&tgt=&args=clonezlinux;' + node + ';' + group + ';' + owner + '&msg=&opts=flush'); + iframe.prependTo($('#manageTab')); +}; + +/** + * Load provision page (service page) + * + * @param tabId Tab ID where page will reside + */ +zvmPlugin.prototype.loadServiceProvisionPage = function(tabId) { + // Create provision form + var provForm = $('
        '); + + // Create info bar + var infoBar = createInfoBar('Provision a Linux virtual machine on IBM z Systems by selecting the appropriate choices below. Once you are ready, click on Provision to provision the virtual machine.'); + provForm.append(infoBar); + + // Append to provision tab + $('#' + tabId).append(provForm); + + // Create provision table + var provTable = $(''); + var provBody = $(''); + var provFooter = $(''); + provTable.append(provHeader, provBody, provFooter); + provForm.append(provTable); + + provHeader.children('th').css({ + 'font': 'bold 12px verdana, arial, helvetica, sans-serif' + }); + + // Create row to contain selections + var provRow = $(''); + provBody.append(provRow); + // Create columns for zVM, group, template, and image + var zvmCol = $(''); + provRow.append(zvmCol); + var groupCol = $(''); + provRow.append(groupCol); + var tmplCol = $(''); + provRow.append(tmplCol); + var imgCol = $(''); + provRow.append(imgCol); + + provRow.children('td').css({ + 'min-width': '200px' + }); + + /** + * Provision VM + */ + var provisionBtn = createButton('Provision'); + provisionBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parent().find('.ui-state-error').remove(); + + var hcp = $('#select-table tbody tr:eq(0) td:eq(0) input[name="hcp"]:checked').val(); + var group = $('#select-table tbody tr:eq(0) td:eq(1) input[name="group"]:checked').val(); + var tmpl = $('#select-table tbody tr:eq(0) td:eq(2) input[name="image"]:checked').val(); + var img = $('#select-table tbody tr:eq(0) td:eq(3) input[name="master"]:checked').val(); + var owner = $.cookie('xcat_username'); + + if (img && !group) { + // Show warning message + var warn = createWarnBar('You need to select a group'); + warn.prependTo($(this).parent()); + } else if (!img && (!hcp || !group || !tmpl)) { + // Show warning message + var warn = createWarnBar('You need to select a zHCP, group, and image'); + warn.prependTo($(this).parent()); + } else { + if (img) { + // Begin by clonning VM + + // Submit request to clone VM + // webportal clonezlinux [src node] [group] [owner] + var iframe = createIFrame('lib/srv_cmd.php?cmd=webportal&tgt=&args=clonezlinux;' + img + ';' + group + ';' + owner + '&msg=&opts=flush'); + iframe.prependTo($('#zvmProvisionTab')); + } else { + // Begin by creating VM + createzVM(tabId, group, hcp, tmpl, owner); + } + } + }); + provForm.append(provisionBtn); + + // Load zVMs, groups, template, and image into their respective columns + loadSrvGroups(groupCol); + loadOSImages(tmplCol); + loadGoldenImages(imgCol); + + // Get zVM host names + if (!$.cookie('xcat_zvms')){ + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webportal', + tgt : '', + args : 'lszvm', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setzVMCookies(data); + loadzVMs(zvmCol); + } + }); + } else { + loadzVMs(zvmCol); + } +}; + +/** + * Show node inventory (service page) + * + * @param data Data from HTTP request + */ +zvmPlugin.prototype.loadServiceInventory = function(data) { + var args = data.msg.split(','); + + // Get tab ID + var tabId = args[0].replace('out=', ''); + // Get node + var node = args[1].replace('node=', ''); + + // Remove loader + $('#' + tabId).find('img').remove(); + + // Do not continue if error is found + if (data.rsp[0].indexOf('Error') > -1) { + var warn = createWarnBar(data.rsp[0]); + $('#' + tabId).append(warn); + return; + } + + // Get node inventory + var inv = data.rsp[0].split(node + ':'); + + // Create array of unique property keys for this node (VM) + // The keys must match all possible keys that zvm.pm rinv could return. Can add new ones to the end. + var keys = new Array('userId', 'host', 'os', 'arch', 'uptime', 'cpuusedtime', 'hcp', 'priv', 'memory', 'maxmemory', 'proc', 'disk', 'zfcp', 'nic', 'hypervisornode'); + + // These two arrays show what keys and what order the data is displayed in the GUI + var guikeysGeneral = new Array('userId', 'host', 'hypervisornode', 'os', 'arch', 'uptime'); + var guikeysHardware = new Array('priv', 'memory', 'maxmemory', 'proc', 'disk', 'zfcp', 'nic'); + + // Create hash table for property names (VM) + var attrNames = new Object(); + attrNames['userId'] = 'z/VM UserID:'; + attrNames['host'] = 'z/VM Host:'; + attrNames['os'] = 'Operating System:'; + attrNames['arch'] = 'Architecture:'; + attrNames['uptime'] = 'Uptime:'; + attrNames['cpuusedtime'] = 'CPU Used Time:'; + attrNames['hcp'] = 'HCP:'; + attrNames['priv'] = 'Privileges:'; + attrNames['memory'] = 'Total Memory:'; + attrNames['maxmemory'] = 'Max Memory:'; + attrNames['proc'] = 'Processors:'; + attrNames['disk'] = 'Disks:'; + attrNames['zfcp'] = 'zFCP:'; + attrNames['nic'] = 'NICs:'; + attrNames['hypervisornode'] = 'xCAT Hypervisor Node:'; + + // Create hash table for node attributes + var attrs = getAttrs(keys, attrNames, inv); + + // Create division to hold inventory + var invDivId = node + 'Inventory'; + var invDiv = $('
        '); + + var infoBar = createInfoBar('Below is the inventory for the virtual machine you selected.'); + invDiv.append(infoBar); + + /** + * General info section + */ + var fieldSet = $('
        '); + var legend = $('General'); + fieldSet.append(legend); + var oList = $('
          '); + var item, label, args; + + // Loop through each general property + for ( var k = 0; k < guikeysGeneral.length; k++) { + // Create a list item for each property + item = $('
        1. '); + + // Create a label - Property name. Change old "z/VM Host label" + if (guikeysGeneral[k] == 'host') { + label = ''; + } else { + label = $(''); + } + item.append(label); + + // Insert the data stored in attr hash using the key + for ( var l = 0; l < attrs[guikeysGeneral[k]].length; l++) { + // Create a input - Property value(s) + // Handle each property uniquely + item.append(attrs[guikeysGeneral[k]][l]); + } + + oList.append(item); + } + // Append to inventory form + fieldSet.append(oList); + invDiv.append(fieldSet); + + /** + * Monitoring section + */ + fieldSet = $('
          '); + legend = $('Monitoring [Refresh]'); + fieldSet.append(legend); +// var info = createInfoBar('No data available'); +// fieldSet.append(info.css('width', '300px')); + + getMonitorMetrics(node); + + // Refresh monitoring charts on-click + legend.find('a').click(function() { + getMonitorMetrics(node); + }); + + // Append to inventory form + invDiv.append(fieldSet); + + /** + * Hardware info section + */ + var hwList, hwItem; + fieldSet = $('
          '); + legend = $('Hardware'); + fieldSet.append(legend); + oList = $('
            '); + + // Loop through each property + var label; + for (k = 0; k < guikeysHardware.length; k++) { + // Create a list item + item = $('
          1. '); + + // Create a list to hold the property value(s) + hwList = $('
              '); + hwItem = $('
            • '); + + /** + * Privilege section + */ + if (guikeysHardware[k] == 'priv') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Loop through each line + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + // Create a new list item for each line + hwItem = $('
            • '); + + // Determine privilege + args = attrs[guikeysHardware[k]][l].split(' '); + if (args[0] == 'Directory:') { + label = $(''); + hwItem.append(label); + hwItem.append(args[1]); + } else if (args[0] == 'Currently:') { + label = $(''); + hwItem.append(label); + hwItem.append(args[1]); + } + + hwList.append(hwItem); + } + + item.append(hwList); + } + + /** + * Memory section + */ + else if (guikeysHardware[k] == 'memory') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Loop through each value line + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + // Create a new list item for each line + hwItem = $('
            • '); + hwItem.append(attrs[guikeysHardware[k]][l]); + hwList.append(hwItem); + } + + item.append(hwList); + } + + /** + * Processor section + */ + else if (guikeysHardware[k] == 'proc') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold processor data + var procTable = $('
              zVM Group Template Image
              '); + var procBody = $(''); + + // Table columns - Type, Address, ID, Base, Dedicated, and Affinity + var procTabRow = $(' Type Address ID Base Dedicated Affinity '); + procTable.append(procTabRow); + var procType, procAddr, procId, procAff; + + // Loop through each processor + var n, temp; + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l]) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get processor type, address, ID, and affinity + n = 3; + temp = args[args.length - n]; + while (!jQuery.trim(temp)) { + n = n + 1; + temp = args[args.length - n]; + } + procType = $('' + temp + ''); + procAddr = $('' + args[1] + ''); + procId = $('' + args[5] + ''); + procAff = $('' + args[args.length - 1] + ''); + + // Base processor + if (args[6] == '(BASE)') { + baseProc = $('' + true + ''); + } else { + baseProc = $('' + false + ''); + } + + // Dedicated processor + if (args[args.length - 3] == 'DEDICATED') { + dedicatedProc = $('' + true + ''); + } else { + dedicatedProc = $('' + false + ''); + } + + // Create a new row for each processor + procTabRow = $(''); + procTabRow.append(procType); + procTabRow.append(procAddr); + procTabRow.append(procId); + procTabRow.append(baseProc); + procTabRow.append(dedicatedProc); + procTabRow.append(procAff); + procBody.append(procTabRow); + } + } + + procTable.append(procBody); + item.append(procTable); + } + + /** + * Disk section + */ + else if (guikeysHardware[k] == 'disk') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold disk (DASD) data + var dasdTable = $('
              '); + var dasdBody = $(''); + + // Table columns - Virtual Device, Type, VolID, Type of Access, and Size + var dasdTabRow = $(' Virtual Device # Type VolID Type of Access Size '); + dasdTable.append(dasdTabRow); + var dasdVDev, dasdType, dasdVolId, dasdAccess, dasdSize; + + // Loop through each DASD + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l]) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get DASD virtual device, type, volume ID, access, and size + dasdVDev = $('' + args[1] + ''); + dasdType = $('' + args[2] + ''); + dasdVolId = $('' + args[3] + ''); + dasdAccess = $('' + args[4] + ''); + dasdSize = $('' + args[args.length - 9] + ' ' + args[args.length - 8] + ''); + + // Create a new row for each DASD + dasdTabRow = $(''); + dasdTabRow.append(dasdVDev); + dasdTabRow.append(dasdType); + dasdTabRow.append(dasdVolId); + dasdTabRow.append(dasdAccess); + dasdTabRow.append(dasdSize); + dasdBody.append(dasdTabRow); + } + } + + dasdTable.append(dasdBody); + item.append(dasdTable); + } + + /** + * zFCP section + */ + else if (guikeysHardware[k] == 'zfcp') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold NIC data + var zfcpTable = $('
              '); + var zfcpBody = $(''); + + // Table columns - Virtual device, Adapter Type, Port Name, # of Devices, MAC Address, and LAN Name + var zfcpTabRow = $(' Virtual Device # Port Name Unit Number Size'); + zfcpTable.append(zfcpTabRow); + var zfcpVDev, zfcpPortName, zfcpLun, zfcpSize; + + // Loop through each zFCP device + if (attrs[guikeysHardware[k]]) { + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l]) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get zFCP virtual device, port name (WWPN), unit number (LUN), and size + zfcpVDev = $('' + args[1].replace('0.0.', '') + ''); + zfcpPortName = $('' + args[4] + ''); + zfcpLun = $('' + args[7] + ''); + zfcpSize = $('' + args[args.length - 2] + ' ' + args[args.length - 1] + ''); + + // Create a new row for each zFCP device + zfcpTabRow = $(''); + zfcpTabRow.append(zfcpVDev); + zfcpTabRow.append(zfcpPortName); + zfcpTabRow.append(zfcpLun); + zfcpTabRow.append(zfcpSize); + + zfcpBody.append(zfcpTabRow); + } + } + } + + zfcpTable.append(zfcpBody); + item.append(zfcpTable); + } + + /** + * NIC section + */ + else if (guikeysHardware[k] == 'nic') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold NIC data + var nicTable = $('
              '); + var nicBody = $(''); + + // Table columns - Virtual device, Adapter Type, Port Name, # of Devices, MAC Address, and LAN Name + var nicTabRow = $('Virtual Device # Adapter Type Port Name # of Devices LAN Name'); + nicTable.append(nicTabRow); + var nicVDev, nicType, nicPortName, nicNumOfDevs, nicLanName; + + // Loop through each NIC (Data contained in 2 lines) + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l].indexOf('Adapter') != -1) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get NIC virtual device, type, port name, and number of devices + nicVDev = $('' + args[1] + ''); + nicType = $('' + args[3] + ''); + nicPortName = $('' + args[10] + ''); + nicNumOfDevs = $('' + args[args.length - 1] + ''); + + args = attrs[guikeysHardware[k]][l + 1].split(' '); + nicLanName = $('' + args[args.length - 2] + ' ' + args[args.length - 1] + ''); + + // Create a new row for each DASD + nicTabRow = $(''); + nicTabRow.append(nicVDev); + nicTabRow.append(nicType); + nicTabRow.append(nicPortName); + nicTabRow.append(nicNumOfDevs); + nicTabRow.append(nicLanName); + + nicBody.append(nicTabRow); + } + } + + nicTable.append(nicBody); + item.append(nicTable); + } + + // Ignore any fields not in key + else { + continue; + } + + oList.append(item); + } + + // Append inventory to division + fieldSet.append(oList); + invDiv.append(fieldSet); + invDiv.find('th').css({ + 'padding': '5px 10px', + 'font-weight': 'bold' + }); + + // Append to tab + $('#' + tabId).append(invDiv); +}; + +/** + * Load clone page + * + * @param node Source node to clone + */ +zvmPlugin.prototype.loadClonePage = function(node, nodeOS, nodeArch) { + // Get nodes tab + if (typeof console == "object"){ + console.log("Entering loadClonePage....."); + } + var tab = getNodesTab(); + var newTabId = node + 'CloneTab'; + + // If there is no existing clone tab + if (!$('#' + newTabId).length) { + // Get table headers + var tableId = $('#' + node).parents('table').attr('id'); + var headers = $('#' + tableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); + var cols = new Array(); + for ( var i = 0; i < headers.length; i++) { + var col = headers.eq(i).text(); + cols.push(col); + } + + // Get hardware control point column + var hcpCol = $.inArray('hcp', cols); + + // Get hardware control point + var nodeRow = $('#' + node).parent().parent(); + var datatable = $('#' + getNodesTableId()).dataTable(); + var rowPos = datatable.fnGetPosition(nodeRow.get(0)); + var aData = datatable.fnGetData(rowPos); + var hcp = aData[hcpCol]; + + // Create status bar and hide it + var statBarId = node + 'CloneStatusBar'; + var statBar = createStatusBar(statBarId).hide(); + + // Create info bar + var infoBar = createInfoBar('Clone a zVM node.'); + + // Create clone form + var cloneForm = $('
              '); + cloneForm.append(statBar); + cloneForm.append(infoBar); + + // Create VM fieldset + var vmFS = $('
              '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + cloneForm.append(vmFS); + + var vmAttr = $('
              '); + vmFS.append($('
              ')); + vmFS.append(vmAttr); + + // Create hardware fieldset + var storageFS = $('
              '); + var storageLegend = $('Storage'); + storageFS.append(storageLegend); + cloneForm.append(storageFS); + + var storageAttr = $('
              '); + storageFS.append($('
              ')); + storageFS.append(storageAttr); + + vmAttr.append('
              '); + vmAttr.append('
              '); + vmAttr.append('
              '); + vmAttr.append('
              '); + + // Create group input + var group = $('
              '); + var groupLabel = $(''); + var groupInput = $(''); + groupInput.one('focus', function(){ + var groupNames = $.cookie('xcat_groups'); + if (groupNames) { + // Turn on auto complete + $(this).autocomplete({ + source: groupNames.split(',') + }); + } + }); + group.append(groupLabel); + group.append(groupInput); + vmAttr.append(group); + + // Create an advanced link to set IP address and hostname + var advancedLnk = $(''); + vmAttr.append(advancedLnk); + var advanced = $('
              ').hide(); + vmAttr.append(advanced); + + var ip = $('
              '); + advanced.append(ip); + var hostname = $('
              '); + advanced.append(hostname); + + // Show IP address and hostname inputs on-click + advancedLnk.click(function() { + advanced.toggle(); + }); + + if (typeof console == "object"){ + console.log(" loadClonePage hcp data:<"+hcp+">"); + } + // Get list of disk pools + var temp = hcp.split('.'); + var diskPools = $.cookie('xcat_' + temp[0] + 'diskpools'); + + // Create disk pool input + var poolDiv = $('
              '); + var poolLabel = $(''); + var poolInput = $('').autocomplete({ + source: diskPools.split(',') + }); + poolDiv.append(poolLabel); + poolDiv.append(poolInput); + storageAttr.append(poolDiv); + + storageAttr.append('
              '); + + // Generate tooltips + cloneForm.find('div input[title]').tooltip({ + position : "center right", + offset : [ -2, 10 ], + effect : "fade", + opacity : 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Clone node + */ + var cloneBtn = createButton('Clone'); + cloneBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + var ready = true; + var errMsg = ''; + + // Check node name, userId, hardware control point, group, and password + var inputs = $('#' + newTabId + ' input'); + for ( var i = 0; i < inputs.length; i++) { + if (!inputs.eq(i).val() + && inputs.eq(i).attr('name') != 'diskPw' + && inputs.eq(i).attr('name') != 'diskPool' + && inputs.eq(i).attr('name') != 'ip' + && inputs.eq(i).attr('name') != 'hostname') { + inputs.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + inputs.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + // Write error message + if (!ready) { + errMsg = errMsg + 'Please provide a value for each missing field.
              '; + } + + // Get target node + var nodeRange = $('#' + newTabId + ' input[name=tgtNode]').val(); + // Get target user ID + var userIdRange = $('#' + newTabId + ' input[name=tgtUserId]').val(); + // Get IP address range + var ipRange = $('#' + newTabId + ' input[name=ip]').val(); + // Get hostname range + var hostnameRange = $('#' + newTabId + ' input[name=hostname]').val(); + + // Check node range and user ID range + if (nodeRange.indexOf('-') > -1 || userIdRange.indexOf('-') > -1 || ipRange.indexOf('-') > -1 || hostnameRange.indexOf('-') > -1) { + if (nodeRange.indexOf('-') < 0 || userIdRange.indexOf('-') < 0) { + errMsg = errMsg + 'A user ID range and node range needs to be given.
              '; + ready = false; + } else { + var tmp = nodeRange.split('-'); + + // Get node base name + var nodeBase = tmp[0].match(/[a-zA-Z]+/); + // Get starting index + var nodeStart = parseInt(tmp[0].match(/\d+/)); + // Get ending index + var nodeEnd = parseInt(tmp[1].match(/\d+/)); + + tmp = userIdRange.split('-'); + + // Get user ID base name + var userIdBase = tmp[0].match(/[a-zA-Z]+/); + // Get starting index + var userIdStart = parseInt(tmp[0].match(/\d+/)); + // Get ending index + var userIdEnd = parseInt(tmp[1].match(/\d+/)); + + var ipStart = "", ipEnd = ""; + if (ipRange) { + tmp = ipRange.split('-'); + + // Get starting IP address + ipStart = tmp[0].substring(tmp[0].lastIndexOf(".") + 1); + // Get ending IP address + ipEnd = tmp[1].substring(tmp[1].lastIndexOf(".") + 1); + } + + var hostnameStart = "", hostnameEnd = ""; + if (hostnameRange) { + tmp = hostnameRange.split('-'); + + // Get starting hostname + hostnameStart = parseInt(tmp[0].substring(0, tmp[0].indexOf(".")).match(/\d+/)); + // Get ending hostname + hostnameEnd = parseInt(tmp[1].substring(0, tmp[1].indexOf(".")).match(/\d+/)); + } + + // If starting and ending index do not match + if (!(nodeStart == userIdStart) || !(nodeEnd == userIdEnd)) { + // Not ready to provision + errMsg = errMsg + 'The node range and user ID range does not match.
              '; + ready = false; + } + + // If an IP address range is given and the starting and ending index do not match + if (ipRange && (!(nodeStart == ipStart) || !(nodeEnd == ipEnd))) { + errMsg = errMsg + 'The node range and IP address range does not match. '; + ready = false; + } + + // If a hostname range is given and the starting and ending index do not match + if (hostnameRange && (!(nodeStart == hostnameStart) || !(nodeEnd == hostnameEnd))) { + errMsg = errMsg + 'The node range and hostname range does not match. '; + ready = false; + } + } + } + + // Get source node, hardware control point, group, disk pool, and disk password + var srcNode = $('#' + newTabId + ' input[name=srcNode]').val(); + var hcp = $('#' + newTabId + ' input[name=newHcp]').val(); + var group = $('#' + newTabId + ' input[name=newGroup]').val(); + var diskPool = $('#' + newTabId + ' input[name=diskPool]').val(); + var diskPw = $('#' + newTabId + ' input[name=diskPw]').val(); + + // If a value is given for every input + if (ready) { + // Disable all inputs + var inputs = $('#' + newTabId + ' input'); + inputs.attr('disabled', 'disabled'); + + // If a node range is given + if (nodeRange.indexOf('-') > -1) { + var tmp = nodeRange.split('-'); + + // Get node base name + var nodeBase = tmp[0].match(/[a-zA-Z]+/); + // Get starting index + var nodeStart = parseInt(tmp[0].match(/\d+/)); + // Get ending index + var nodeEnd = parseInt(tmp[1].match(/\d+/)); + + tmp = userIdRange.split('-'); + + // Get user ID base name + var userIdBase = tmp[0].match(/[a-zA-Z]+/); + + var ipBase = ""; + if (ipRange) { + tmp = ipRange.split('-'); + + // Get network base + ipBase = tmp[0].substring(0, tmp[0].lastIndexOf(".") + 1); + } + + var domain = ""; + if (hostnameRange) { + tmp = hostnameRange.split('-'); + + // Get domain name + domain = tmp[0].substring(tmp[0].indexOf(".")); + } + + // Loop through each node in the node range + for ( var i = nodeStart; i <= nodeEnd; i++) { + var node = nodeBase + i.toString(); + var userId = userIdBase + i.toString(); + var inst = i + '/' + nodeEnd; + + var args = node + + ';zvm.hcp=' + hcp + + ';zvm.userid=' + userId + + ';nodehm.mgt=zvm' + + ';nodetype.os=' + nodeOS + + ';nodetype.arch=' + nodeArch + + ';groups=' + group; + + if (ipRange) { + var ip = ipBase + i.toString(); + args += ';hosts.ip=' + ip; + } + + if (hostnameRange) { + var hostname = node + domain; + args += ';hosts.hostnames=' + hostname; + } + + /** + * (1) Define node + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : args, + msg : 'cmd=nodeadd;inst=' + inst + + ';out=' + statBarId + + ';node=' + node + }, + + success : function(data) { + data = decodeRsp(data); + updateZCloneStatus(data); + } + }); + } + } else { + var args = nodeRange + + ';zvm.hcp=' + hcp + + ';zvm.userid=' + userIdRange + + ';nodehm.mgt=zvm' + + ';nodetype.os=' + nodeOS + + ';nodetype.arch=' + nodeArch + + ';groups=' + group; + + if (ipRange) + args += ';hosts.ip=' + ipRange; + + if (hostnameRange) + args += ';hosts.hostnames=' + hostnameRange; + + /** + * (1) Define node + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : args, + msg : 'cmd=nodeadd;inst=1/1;out=' + statBarId + + ';node=' + nodeRange + }, + + success : function(data) { + data = decodeRsp(data); + updateZCloneStatus(data); + } + }); + } + + // Create loader + $('#' + statBarId).find('div').append(createLoader()); + $('#' + statBarId).show(); + + // Disable clone button + $(this).attr('disabled', 'true'); + } else { + // Show warning message + var warn = createWarnBar(errMsg); + warn.prependTo($(this).parent().parent()); + } + }); + cloneForm.append(cloneBtn); + + // Add clone tab + tab.add(newTabId, 'Clone', cloneForm, true); + } + + tab.select(newTabId); +}; + +/** + * Load node inventory + * + * @param data Data from HTTP request + */ +zvmPlugin.prototype.loadInventory = function(data) { + var args = data.msg.split(','); + + // Get tab ID + var tabId = args[0].replace('out=', ''); + // Get node + var node = args[1].replace('node=', ''); + // Clear any existing cookie + $.cookie('xcat_' + node + 'processes', null, {path: '/xcat', secure:true }); + + // Remove loader + $('#' + tabId).find('img').remove(); + + // Check for error + var error = false; + if (data.rsp.length && data.rsp[0].indexOf('Error') > -1) { + error = true; + + var warn = createWarnBar(data.rsp[0]); + $('#' + tabId).append(warn); + } + + // Determine the node type + if (data.rsp.length && data.rsp[0].indexOf('Hypervisor OS:') > -1) { + loadHypervisorInventory(data); + return; + } + + // Create status bar + var statBarId = node + 'StatusBar'; + var statBar = createStatusBar(statBarId); + + // Add loader to status bar and hide it + var loader = createLoader(node + 'StatusBarLoader').hide(); + statBar.find('div').append(loader); + statBar.hide(); + + // Create division to hold user entry + var ueDivId = node + 'UserEntry'; + var ueDiv = $('
              '); + + // Create division to hold inventory + var invDivId = node + 'Inventory'; + var invDiv = $('
              '); + + /** + * Show user entry + */ + var toggleLinkId = node + 'ToggleLink'; + var toggleLink = $('Show directory entry'); + toggleLink.one('click', function(event) { + // Toggle inventory division + $('#' + invDivId).toggle(); + + // Create loader + var loader = createLoader(node + 'TabLoader'); + loader = $('
              ').append(loader); + ueDiv.append(loader); + + // Get user entry + var msg = 'out=' + ueDivId + ';node=' + node; + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : node, + args : '', + msg : msg + }, + + success : function(data) { + data = decodeRsp(data); + loadUserEntry(data); + } + }); + + // Change text + $(this).text('Show inventory'); + + // Disable toggle link + $(this).unbind(event); + }); + + // Align toggle link to the right + var toggleLnkDiv = $('
              ').css({ + 'text-align' : 'right' + }); + toggleLnkDiv.append(toggleLink); + + // Append to tab + $('#' + tabId).append(statBar); + $('#' + tabId).append(toggleLnkDiv); + $('#' + tabId).append(ueDiv); + $('#' + tabId).append(invDiv); + + // Do not load inventory if no inventory is returned + if (data.rsp.length && data.rsp[0].indexOf('z/VM UserID:') > -1) { + // Do nothing + } else { + return; + } + + // Create array of unique property keys for this node (VM) + // The keys must match all possible keys that zvm.pm rinv could return. Can add new ones to the end. + var keys = new Array('userId', 'host', 'os', 'arch', 'uptime', 'cpuusedtime', 'hcp', 'priv', 'memory', 'maxmemory', 'proc', 'disk', 'zfcp', 'nic', 'hypervisornode'); + + // These two arrays show what keys and what order the data is displayed in the GUI + var guikeysGeneral = new Array('userId', 'host', 'hypervisornode', 'os', 'arch', 'uptime', 'cpuusedtime'); + var guikeysHardware = new Array('priv', 'memory', 'maxmemory', 'proc', 'disk', 'zfcp', 'nic'); + + + // Create hash table for property names (VM) + var attrNames = new Object(); + attrNames['userId'] = 'z/VM UserID:'; + attrNames['host'] = 'z/VM Host:'; + attrNames['os'] = 'Operating System:'; + attrNames['arch'] = 'Architecture:'; + attrNames['uptime'] = 'Uptime:'; + attrNames['cpuusedtime'] = 'CPU Used Time:'; + attrNames['hcp'] = 'HCP:'; + attrNames['priv'] = 'Privileges:'; + attrNames['memory'] = 'Total Memory:'; + attrNames['maxmemory'] = 'Max Memory:'; + attrNames['proc'] = 'Processors:'; + attrNames['disk'] = 'Disks:'; + attrNames['zfcp'] = 'zFCP:'; + attrNames['nic'] = 'NICs:'; + attrNames['hypervisornode'] = 'xCAT Hypervisor Node:'; + + // Create hash table for node attributes + var inv = data.rsp[0].split(node + ':'); + var attrs; + if (!error) { + attrs = getAttrs(keys, attrNames, inv); + } + + // Do not continue if error + if (error) { + return; + } + + // Find the hcp node from the host name + // Start off an ajax request to save the zhcp node name + // in a cookie for possible later use by addNic dialog + var hcpHostname = attrs['hcp']; + if (!$.cookie('xcat_' + node+'_hcpnodename')){ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodels', + tgt : 'mgt==zvm', + args : 'hosts.hostnames', + msg : node+';'+hcpHostname + }, + + success : function(data) { + data = decodeRsp(data); + setNodeZhcpNodename(data); + } + }); + } + + /** + * General info section + */ + var fieldSet = $('
              '); + var legend = $('General'); + fieldSet.append(legend); + var oList = $('
                '); + var item, label, args; + + // Loop through each property + for (var k = 0; k < guikeysGeneral.length; k++) { + // Create a list item for each property + item = $('
              1. '); + + // Create a label - Property name. Change old "z/VM Host label" + if (guikeysGeneral[k] == 'host') { + label = ''; + } else { + label = $(''); + } + item.append(label); + + for (var l = 0; l < attrs[guikeysGeneral[k]].length; l++) { + // Create a input - Property value(s) + // Handle each property uniquely + item.append(attrs[guikeysGeneral[k]][l]); + } + + oList.append(item); + } + // Append to inventory form + fieldSet.append(oList); + invDiv.append(fieldSet); + + /** + * Hardware info section + */ + var hwList, hwItem; + fieldSet = $('
                '); + legend = $('Hardware'); + fieldSet.append(legend); + oList = $('
                  '); + + // Loop through each property + var label; + for (k = 0; k < guikeysHardware.length; k++) { + // Create a list item + item = $('
                1. '); + + // Create a list to hold the property value(s) + hwList = $('
                    '); + hwItem = $('
                  • '); + + /** + * Privilege section + */ + if (guikeysHardware[k] == 'priv') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Loop through each line + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + // Create a new list item for each line + hwItem = $('
                  • '); + + // Determine privilege + args = attrs[guikeysHardware[k]][l].split(' '); + if (args[0] == 'Directory:') { + label = $(''); + hwItem.append(label); + hwItem.append(args[1]); + } else if (args[0] == 'Currently:') { + label = $(''); + hwItem.append(label); + hwItem.append(args[1]); + } + + hwList.append(hwItem); + } + + item.append(hwList); + } + + /** + * Memory section + */ + else if (guikeysHardware[k] == 'memory') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Loop through each value line + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + // Create a new list item for each line + hwItem = $('
                  • '); + hwItem.append(attrs[guikeysHardware[k]][l]); + hwList.append(hwItem); + } + + item.append(hwList); + } + + /** + * Processor section + */ + else if (guikeysHardware[k] == 'proc') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold processor data + var procTable = $('
                    '); + var procBody = $(''); + var procFooter = $(''); + + // Table columns - Type, Address, ID, Base, Dedicated, and Affinity + var procTabRow = $(' Type Address ID Base Dedicated Affinity '); + procTable.append(procTabRow); + var procId, procAff; + + /** + * Remove processor + */ + var contextMenu = [{ + 'Remove' : function(menuItem, menu) { + var addr = $(this).text(); + + // Open dialog to confirm + var confirmDialog = $('

                    Are you sure you want to remove this processor?

                    '); + confirmDialog.dialog({ + title: "Confirm", + modal: true, + width: 300, + buttons: { + "Ok": function(){ + removeProcessor(node, addr); + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } + }]; + + // Loop through each processor + var n, temp; + var procType, procAddr, procLink; + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l]) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get processor type, address, ID, and affinity + n = 3; + temp = args[args.length - n]; + while (!jQuery.trim(temp)) { + n = n + 1; + temp = args[args.length - n]; + } + procType = $('' + temp + ''); + procAddr = $(''); + procLink = $('' + args[1] + ''); + + // Append context menu to link + procLink.contextMenu(contextMenu, { + theme : 'vista' + }); + + procAddr.append(procLink); + procId = $('' + args[5] + ''); + procAff = $('' + args[args.length - 1] + ''); + + // Base processor + if (args[6] == '(BASE)') { + baseProc = $('' + true + ''); + } else { + baseProc = $('' + false + ''); + } + + // Dedicated processor + if (args[args.length - 3] == 'DEDICATED') { + dedicatedProc = $('' + true + ''); + } else { + dedicatedProc = $('' + false + ''); + } + + // Create a new row for each processor + procTabRow = $(''); + procTabRow.append(procType); + procTabRow.append(procAddr); + procTabRow.append(procId); + procTabRow.append(baseProc); + procTabRow.append(dedicatedProc); + procTabRow.append(procAff); + procBody.append(procTabRow); + } + } + + procTable.append(procBody); + + /** + * Add processor + */ + var addProcLink = $('+ Add temporary processor'); + addProcLink.bind('click', function(event) { + openAddProcDialog(node); + }); + + procFooter.append(addProcLink); + procTable.append(procFooter); + item.append(procTable); + } + + /** + * Disk section + */ + else if (guikeysHardware[k] == 'disk') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold disk (DASD) data + var dasdTable = $('
                    '); + var dasdBody = $(''); + var dasdFooter = $(''); + + /** + * Remove disk + */ + contextMenu = [{ + 'Remove' : function(menuItem, menu) { + var addr = $(this).text(); + + // Open dialog to confirm + var confirmDialog = $('

                    Are you sure you want to remove this disk?

                    '); + confirmDialog.dialog({ + title: "Confirm", + modal: true, + width: 300, + buttons: { + "Ok": function(){ + removeDisk(node, addr); + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } + }]; + + // Table columns - Virtual Device, Type, VolID, Type of Access, and Size + var dasdTabRow = $(' Virtual Device # Type VolID Type of Access Size '); + dasdTable.append(dasdTabRow); + var dasdVDev, dasdType, dasdVolId, dasdAccess, dasdSize; + + // Loop through each DASD + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l]) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get DASD virtual device, type, volume ID, access, and size + dasdVDev = $(''); + dasdLink = $('' + args[1] + ''); + + // Append context menu to link + dasdLink.contextMenu(contextMenu, { + theme : 'vista' + }); + dasdVDev.append(dasdLink); + + dasdType = $('' + args[2] + ''); + dasdVolId = $('' + args[3] + ''); + dasdAccess = $('' + args[4] + ''); + dasdSize = $('' + args[args.length - 9] + ' ' + args[args.length - 8] + ''); + + // Create a new row for each DASD + dasdTabRow = $(''); + dasdTabRow.append(dasdVDev); + dasdTabRow.append(dasdType); + dasdTabRow.append(dasdVolId); + dasdTabRow.append(dasdAccess); + dasdTabRow.append(dasdSize); + dasdBody.append(dasdTabRow); + } + } + + dasdTable.append(dasdBody); + + /** + * Add disk + */ + var addDasdLink = $('+ Add disk'); + addDasdLink.bind('click', function(event) { + var hcp = attrs['hcp'][0].split('.'); + openAddDiskDialog(node, hcp[0]); + }); + dasdFooter.append(addDasdLink); + dasdTable.append(dasdFooter); + + item.append(dasdTable); + } + + /** + * zFCP section + */ + else if (guikeysHardware[k] == 'zfcp') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold NIC data + var zfcpTable = $('
                    '); + var zfcpBody = $(''); + var zfcpFooter = $(''); + + /** + * Remove zFCP + */ + contextMenu = [ { + 'Remove' : function(menuItem, menu) { + var addr = $(this).text(); + var portName = $(this).parents('tr').find('td:eq(1)').text(); + var unitNo = $(this).parents('tr').find('td:eq(2)').text(); + + // Open dialog to confirm + var confirmDialog = $('

                    Are you sure you want to remove this zFCP device?

                    '); + confirmDialog.dialog({ + title: "Confirm", + modal: true, + width: 300, + buttons: { + "Ok": function(){ + removeZfcp(node, addr, portName, unitNo); + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } + } ]; + + // Table columns - Virtual device, Adapter Type, Port Name, # of Devices, MAC Address, and LAN Name + var zfcpTabRow = $(' Virtual Device # Port Name Unit Number Size'); + zfcpTable.append(zfcpTabRow); + var zfcpVDev, zfcpPortName, zfcpLun, zfcpSize; + + // Loop through each zFCP device + if (attrs[guikeysHardware[k]]) { + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l]) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get zFCP virtual device, port name (WWPN), unit number (LUN), and size + zfcpVDev = $(''); + zfcpLink = $('' + args[1].replace('0.0.', '') + ''); + + // Append context menu to link + zfcpLink.contextMenu(contextMenu, { + theme : 'vista' + }); + zfcpVDev.append(zfcpLink); + + zfcpPortName = $('' + args[4] + ''); + zfcpLun = $('' + args[7] + ''); + zfcpSize = $('' + args[args.length - 2] + ' ' + args[args.length - 1] + ''); + + // Create a new row for each zFCP device + zfcpTabRow = $(''); + zfcpTabRow.append(zfcpVDev); + zfcpTabRow.append(zfcpPortName); + zfcpTabRow.append(zfcpLun); + zfcpTabRow.append(zfcpSize); + + zfcpBody.append(zfcpTabRow); + } + } + } + + zfcpTable.append(zfcpBody); + + /** + * Add dedicated device + */ + var dedicateDeviceLink = $('+ Add dedicated device').css('display', 'block'); + dedicateDeviceLink.bind('click', function(event) { + var hcp = attrs['hcp'][0].split('.'); + openDedicateDeviceDialog(node, hcp[0]); + }); + + /** + * Add zFCP device + */ + var addZfcpLink = $('+ Add zFCP').css('display', 'block'); + addZfcpLink.bind('click', function(event) { + var hcp = attrs['hcp'][0].split('.'); + var zvm = attrs['host'][0].toLowerCase(); + openAddZfcpDialog(node, hcp[0], zvm); + }); + + zfcpFooter.append(dedicateDeviceLink, addZfcpLink); + zfcpTable.append(zfcpFooter); + + item.append(zfcpTable); + } + + /** + * NIC section + */ + else if (guikeysHardware[k] == 'nic') { + // Create a label - Property name + label = $(''); + item.append(label); + + // Create a table to hold NIC data + var nicTable = $('
                    '); + var nicBody = $(''); + var nicFooter = $(''); + + /** + * Remove NIC + */ + contextMenu = [ { + 'Remove' : function(menuItem, menu) { + var addr = $(this).text(); + + // Open dialog to confirm + var confirmDialog = $('

                    Are you sure you want to remove this NIC?

                    '); + confirmDialog.dialog({ + title: "Confirm", + modal: true, + width: 300, + buttons: { + "Ok": function(){ + removeNic(node, addr); + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } + } ]; + + // Table columns - Virtual device, Adapter Type, Port Name, # of Devices, MAC Address, and LAN Name + var nicTabRow = $(' Virtual Device # Adapter Type Port Name # of Devices LAN Name'); + nicTable.append(nicTabRow); + var nicVDev, nicType, nicPortName, nicNumOfDevs, nicLanName; + + // Loop through each NIC (Data contained in 2 lines) + for (l = 0; l < attrs[guikeysHardware[k]].length; l++) { + if (attrs[guikeysHardware[k]][l].indexOf('Adapter') != -1) { + args = attrs[guikeysHardware[k]][l].split(' '); + + // Get NIC virtual device, type, port name, and number of devices + nicVDev = $(''); + nicLink = $('' + args[1] + ''); + + // Append context menu to link + nicLink.contextMenu(contextMenu, { + theme : 'vista' + }); + nicVDev.append(nicLink); + + nicType = $('' + args[3] + ''); + nicPortName = $('' + args[10] + ''); + nicNumOfDevs = $('' + args[args.length - 1] + ''); + + args = attrs[guikeysHardware[k]][l + 1].split(' '); + nicLanName = $('' + args[args.length - 2] + ' ' + args[args.length - 1] + ''); + + // Create a new row for each NIC + nicTabRow = $(''); + nicTabRow.append(nicVDev); + nicTabRow.append(nicType); + nicTabRow.append(nicPortName); + nicTabRow.append(nicNumOfDevs); + nicTabRow.append(nicLanName); + + nicBody.append(nicTabRow); + } + } + + nicTable.append(nicBody); + + /** + * Add NIC + */ + var addNicLink = $('+ Add NIC'); + addNicLink.bind('click', function(event) { + //var hcp = attrs['hcp'][0].split('.'); Old Code + // Pass the full zhcp hostname + openAddNicDialog(node, attrs['hcp'][0]); + }); + nicFooter.append(addNicLink); + nicTable.append(nicFooter); + + item.append(nicTable); + } + + // Ignore any fields not in key + else { + continue; + } + + oList.append(item); + } + + // Append inventory to division + fieldSet.append(oList); + invDiv.append(fieldSet); +}; + +/** + * Load hypervisor inventory + * + * @param data Data from HTTP request + */ +function loadHypervisorInventory(data) { + var args = data.msg.split(','); + + // Get tab ID + var tabId = args[0].replace('out=', ''); + // Get node + var node = args[1].replace('node=', ''); + + // Remove loader + $('#' + tabId).find('img').remove(); + + // Check for error + var error = false; + if (data.rsp.length && data.rsp[0].indexOf('Error') > -1) { + error = true; + + var warn = createWarnBar(data.rsp[0]); + $('#' + tabId).append(warn); + } + + // Get node inventory + var inv = data.rsp[0].split(node + ':'); + + // Create status bar + var statBarId = node + 'StatusBar'; + var statBar = createStatusBar(statBarId); + + // Add loader to status bar and hide it + var loader = createLoader(node + 'StatusBarLoader').hide(); + statBar.find('div').append(loader); + statBar.hide(); + + // Create array of property keys (z/VM hypervisor) + // The keys must match all possible keys that zvm.pm rinv for hypervisor could return. Can add new ones to the end. + var keys = new Array('host', 'hcp', 'arch', 'cecvendor', 'cecmodel', 'hypos', 'hypname', 'lparcputotal', 'lparcpuused', 'lparmemorytotal', 'lparmemoryused', 'lparmemoryoffline', 'hypervisornode'); + + // These two arrays show what keys and what order the data is displayed in the GUI + var guikeysGeneral = new Array('host', 'hypervisornode', 'hcp', 'arch', 'arch', 'cecvendor', 'cecmodel', 'hypos', 'hypname'); + var guikeysHardware = new Array('lparcputotal', 'lparcpuused', 'lparmemorytotal', 'lparmemoryused', 'lparmemoryoffline'); + + + // Create hash table for property names (z/VM hypervisor) + var attrNames = new Object(); + attrNames['host'] = 'z/VM Host:'; + attrNames['hcp'] = 'zHCP:'; + attrNames['arch'] = 'Architecture:'; + attrNames['cecvendor'] = 'CEC Vendor:'; + attrNames['cecmodel'] = 'CEC Model:'; + attrNames['hypos'] = 'Hypervisor OS:'; + attrNames['hypname'] = 'Hypervisor Name:'; + attrNames['lparcputotal'] = 'LPAR CPU Total:'; + attrNames['lparcpuused'] = 'LPAR CPU Used:'; + attrNames['lparmemorytotal'] = 'LPAR Memory Total:'; + attrNames['lparmemoryused'] = 'LPAR Memory Used:'; + attrNames['lparmemoryoffline'] = 'LPAR Memory Offline:'; + attrNames['hypervisornode'] = 'xCAT Hypervisor Node:'; + + // Remove loader + $('#' + tabId).find('img').remove(); + + // Create hash table for node attributes + var attrs; + if (!error) { + attrs = getAttrs(keys, attrNames, inv); + } + + // Create division to hold inventory + var invDivId = node + 'Inventory'; + var invDiv = $('
                    '); + + // Append to tab + $('#' + tabId).append(statBar); + $('#' + tabId).append(invDiv); + + // Do not continue if error + if (error) { + return; + } + + /** + * General info section + */ + var fieldSet = $('
                    '); + var legend = $('General'); + fieldSet.append(legend); + var oList = $('
                      '); + var item, label, args; + + // Loop through each property + for (var k = 0; k < guikeysGeneral.length; k++) { + // Create a list item for each property + item = $('
                    1. '); + + // Create a label - Property name. Change old "z/VM Host label" + if (guikeysGeneral[k] == 'host') { + label = ''; + } else { + label = $(''); + } + item.append(label); + + for (var l = 0; l < attrs[guikeysGeneral[k]].length; l++) { + // Create a input - Property value(s) + // Handle each property uniquely + item.append(attrs[guikeysGeneral[k]][l]); + } + + oList.append(item); + } + // Append to inventory form + fieldSet.append(oList); + invDiv.append(fieldSet); + + /** + * Hardware info section + */ + var hwList, hwItem; + fieldSet = $('
                      '); + legend = $('Hardware'); + fieldSet.append(legend); + oList = $('
                        '); + + // Loop through each property + var label; + for (k = 0; k < guikeysHardware.length; k++) { + // Create a list item for each property + item = $('
                      1. '); + + // Create a label - Property name + label = $(''); + item.append(label); + + for (var l = 0; l < attrs[guikeysHardware[k]].length; l++) { + // Create a input - Property value(s) + // Handle each property uniquely + item.append(attrs[guikeysHardware[k]][l]); + } + + oList.append(item); + } + // Append to inventory form + fieldSet.append(oList); + invDiv.append(fieldSet); + + // Append to inventory form + $('#' + tabId).append(invDiv); +}; + +/** + * Load provision page + * + * @param tabId The provision tab ID + */ +zvmPlugin.prototype.loadProvisionPage = function(tabId) { + if (typeof console == "object") { + console.log("Entering loadProvisionPage "); + } + // Get OS image names + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'osimage', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setOSImageCookies(data); + } + }); + + // Get groups + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'extnoderange', + tgt : '/.*', + args : 'subgroups', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setGroupsCookies(data); + } + }); + + // Get provision tab instance + var inst = tabId.replace('zvmProvisionTab', ''); + + // Create provision form + var provForm = $('
                        '); + + // Create status bar + var statBarId = 'zProvisionStatBar' + inst; + var statBar = createStatusBar(statBarId).hide(); + provForm.append(statBar); + + // Create loader + var loader = createLoader('zProvisionLoader' + inst).hide(); + statBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Provision a node on IBM z Systems.'); + provForm.append(infoBar); + + // Append to provision tab + $('#' + tabId).append(provForm); + + var typeFS = $('
                        '); + var typeLegend = $('Type'); + typeFS.append(typeLegend); + provForm.append(typeFS); + + // Create provision type drop down + var provType = $('
                        '); + var typeLabel = $(''); + var typeSelect = $(''); + var provNewNode = $(''); + var provExistNode = $(''); + typeSelect.append(provNewNode); + typeSelect.append(provExistNode); + provType.append(typeLabel); + provType.append(typeSelect); + typeFS.append(provType); + + /** + * Create provision new node division + */ + var provNew = createZProvisionNew(inst); + provForm.append(provNew); + + /** + * Create provision existing node division + */ + var provExisting = createZProvisionExisting(inst); + provForm.append(provExisting); + + // Toggle provision new/existing on select + typeSelect.change(function(){ + var selected = $(this).val(); + if (selected == 'new') { + provNew.toggle(); + provExisting.toggle(); + } else { + provNew.toggle(); + provExisting.toggle(); + } + }); +}; + +/** + * Load the resources + */ +zvmPlugin.prototype.loadResources = function() { + // Reset resource table + setNetworkDataTable(''); + + // Get hardware control points + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodels', + tgt : 'mgt==zvm', + args : 'zvm.hcp;hosts.hostnames', + msg : '' + }, + success : function(data) { + data = decodeRsp(data); + getZResources(data); + } + }); +}; + +/** + * Add node range + */ +zvmPlugin.prototype.addNode = function() { + // Create form to add node range + var addNodeForm = $('
                        '); + var info = createInfoBar('Add a z/VM node range'); + addNodeForm.append(info); + + // Create provision type drop down + var type = $('
                        '); + var typeLabel = $(''); + var typeSelect = $(''); + typeSelect.append(''); + typeSelect.append(''); + type.append(typeLabel); + type.append(typeSelect); + addNodeForm.append(type); + + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + addNodeForm.append('
                        '); + + // OS field only required for hosts + addNodeForm.find('input[name=os]').parent().hide(); + + // Toggle user Id on select + typeSelect.change(function(){ + var selected = $(this).val(); + if (selected == 'host') { + addNodeForm.find('input[name=userId]').parent().toggle(); + addNodeForm.find('input[name=os]').parent().toggle(); + } else { + addNodeForm.find('input[name=userId]').parent().toggle(); + addNodeForm.find('input[name=os]').parent().toggle(); + } + }); + + // Generate tooltips + addNodeForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open form as a dialog + addNodeForm.dialog({ + title: 'Add node', + modal: true, + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var type = $(this).find('select[name=type]').val(); + var nodeRange = $(this).find('input[name=node]').val(); + var ipRange = $(this).find('input[name=ip]').val(); + var hostnameRange = $(this).find('input[name=hostname]').val(); + var userIdRange = $(this).find('input[name=userId]').val(); + var os = $(this).find('input[name=os]').val(); + var group = $(this).find('input[name=groups]').val(); + var hcp = $(this).find('input[name=hcp]').val(); + + // Check required fields + if (type == 'host') { + if (!nodeRange || !os || !group || !hcp) { + var warn = createWarnBar('Please provide a value for each missing field!'); + warn.prependTo($(this)); + return; + } + } else { + if (!nodeRange || !userIdRange || !group || !hcp) { + var warn = createWarnBar('Please provide a value for each missing field!'); + warn.prependTo($(this)); + return; + } + } + + // Check node range and user ID range + // Range can be given as gpok10-gpok20, gpok[10-20], or gpok10+10 + var errMsg = ''; + var ready = true; + if (nodeRange.indexOf('-') > -1 || userIdRange.indexOf('-') > -1) { + if (nodeRange.indexOf('-') < 0 || userIdRange.indexOf('-') < 0) { + errMsg = errMsg + 'A user ID range and node range needs to be given. '; + ready = false; + } else { + var tmp = nodeRange.split('-'); + + // Get starting index + var nodeStart = parseInt(tmp[0].match(/\d+/)); + // Get ending index + var nodeEnd = parseInt(tmp[1].match(/\d+/)); + + tmp = userIdRange.split('-'); + + // Get starting index + var userIdStart = parseInt(tmp[0].match(/\d+/)); + // Get ending index + var userIdEnd = parseInt(tmp[1].match(/\d+/)); + + var ipStart = "", ipEnd = ""; + if (ipRange != "" && ipRange != null) { + tmp = ipRange.split('-'); + + // Get starting IP address + ipStart = tmp[0].substring(tmp[0].lastIndexOf(".") + 1); + // Get ending IP address + ipEnd = tmp[1].substring(tmp[1].lastIndexOf(".") + 1); + } + + var hostnameStart = "", hostnameEnd = ""; + if (hostnameRange != "" && hostnameRange != null) { + tmp = hostnameRange.split('-'); + + // Get starting hostname + hostnameStart = parseInt(tmp[0].substring(0, tmp[0].indexOf(".")).match(/\d+/)); + // Get ending hostname + hostnameEnd = parseInt(tmp[1].substring(0, tmp[1].indexOf(".")).match(/\d+/)); + } + + // If starting and ending index do not match + if (!(nodeStart == userIdStart) || !(nodeEnd == userIdEnd)) { + errMsg = errMsg + 'The node range and user ID range does not match. '; + ready = false; + } + + // If an IP address range is given and the starting and ending index do not match + if (ipRange != "" && ipRange != null && (!(nodeStart == ipStart) || !(nodeEnd == ipEnd))) { + errMsg = errMsg + 'The node range and IP address range does not match. '; + ready = false; + } + + // If a hostname range is given and the starting and ending index do not match + if (hostnameRange != "" && hostnameRange != null && (!(nodeStart == hostnameStart) || !(nodeEnd == hostnameEnd))) { + errMsg = errMsg + 'The node range and hostname range does not match. '; + ready = false; + } + } + } + + // If there are no errors + if (ready) { + $('#addZvm').append(createLoader()); + + // Change dialog buttons + $('#addZvm').dialog('option', 'buttons', { + 'Close':function() { + $('#addZvm').dialog('destroy').remove(); + } + }); + + // If a node range is given + if (nodeRange.indexOf('-') > -1 && userIdRange.indexOf('-') > -1) { + var tmp = nodeRange.split('-'); + + // Get node base name + var nodeBase = tmp[0].match(/[a-zA-Z]+/); + // Get starting index + var nodeStart = parseInt(tmp[0].match(/\d+/)); + // Get ending index + var nodeEnd = parseInt(tmp[1].match(/\d+/)); + + tmp = userIdRange.split('-'); + + // Get user ID base name + var userIdBase = tmp[0].match(/[a-zA-Z]+/); + + var ipBase = ""; + if (ipRange != "" && ipRange != null) { + tmp = ipRange.split('-'); + + // Get network base + ipBase = tmp[0].substring(0, tmp[0].lastIndexOf(".") + 1); + } + + var domain = ""; + if (hostnameRange != "" && hostnameRange != null) { + tmp = hostnameRange.split('-'); + + // Get domain name + domain = tmp[0].substring(tmp[0].indexOf(".")); + } + + // Loop through each node in the node range + for ( var i = nodeStart; i <= nodeEnd; i++) { + var node = nodeBase + i.toString(); + var userId = userIdBase + i.toString(); + var inst = i + '/' + nodeEnd; + + var args = ""; + if (type == 'host') { + args = node + ';zvm.hcp=' + hcp + + ';nodehm.mgt=zvm;nodetype.arch=s390x;hypervisor.type=zvm;groups=' + group + + ';nodetype.os=' + os; + } else { + args = node + ';zvm.hcp=' + hcp + + ';zvm.userid=' + userId + + ';nodehm.mgt=zvm' + ';nodetype.arch=s390x' + ';groups=' + group; + } + + if (ipRange != "" && ipRange != null) { + var ip = ipBase + i.toString(); + args += ';hosts.ip=' + ip; + } + + if (hostnameRange != "" && hostnameRange != null) { + var hostname = node + domain; + args += ';hosts.hostnames=' + hostname; + } + + /** + * (1) Define node + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : args, + msg : 'cmd=addnewnode;inst=' + inst + ';noderange=' + nodeRange + }, + + /** + * Return function on successful AJAX call + * + * @param data + * Data returned from HTTP request + * @return Nothing + */ + success : function (data) { + data = decodeRsp(data); + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + + // Get instance returned and node range + var inst = args[1].replace('inst=', ''); + var nodeRange = args[2].replace('noderange=', ''); + + // If the last node was added + var tmp = inst.split('/'); + if (tmp[0] == tmp[1]) { + // Update /etc/hosts + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makehosts', + tgt : '', + args : '', + msg : '' + } + }); + + // Remove loader + $('#addZvm img').remove(); + + // If there was an error, do not continue + if (rsp.length) { + $('#addZvm').prepend(createWarnBar('Failed to create node definitions')); + } else { + $('#addZvm').prepend(createInfoBar('Node definitions created for ' + nodeRange)); + } + } + } + }); + } + } else { + var args = ""; + if (type == 'host') { + args = nodeRange + ';zvm.hcp=' + hcp + + ';nodehm.mgt=zvm;nodetype.arch=s390x;hypervisor.type=zvm;groups=' + group + + ';nodetype.os=' + os; + } else { + args = nodeRange + ';zvm.hcp=' + hcp + + ';zvm.userid=' + userIdRange + + ';nodehm.mgt=zvm' + ';nodetype.arch=s390x' + ';groups=' + group; + } + + if (ipRange) + args += ';hosts.ip=' + ipRange; + + if (hostnameRange) + args += ';hosts.hostnames=' + hostnameRange; + + // Only one node to add + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : args, + msg : 'cmd=addnewnode;node=' + nodeRange + }, + + /** + * Return function on successful AJAX call + * + * @param data + * Data returned from HTTP request + * @return Nothing + */ + success : function (data) { + data = decodeRsp(data); + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + var node = args[1].replace('node=', ''); + + // Update /etc/hosts + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makehosts', + tgt : '', + args : '', + msg : '' + } + }); + + // Remove loader + $('#addZvm img').remove(); + + // If there was an error, do not continue + if (rsp.length) { + $('#addZvm').prepend(createWarnBar('Failed to create node definitions')); + } else { + $('#addZvm').prepend(createInfoBar('Node definitions created for ' + node)); + } + } + }); + } + } else { + // Show warning message + var warn = createWarnBar(errMsg); + warn.prependTo($(this)); + } + }, + "Cancel": function(){ + $(this).dialog('destroy').remove(); + } + } + }); +}; + +/** + * Migrate page + * + * @param tgtNode Targets to migrate + */ +zvmPlugin.prototype.loadMigratePage = function(tgtNode, fromhcp) { + var hosts = $.cookie('xcat_zvms').split(','); + var radio, zvmBlock, args; + var zvms = new Array(); + var hcp = new Object(); + var invalidDest = ''; + if (typeof console == "object"){ + console.log("Entering loadMigratePage. Target nodes:" + tgtNode + " from hcp:" + fromhcp); + } + + // Create a drop-down for z/VM destinations + var destSelect = $('') + for (var i in hosts) { + args = hosts[i].split(':'); // in format of host: zhcp + if (args[0].length > 0) { + hcp[args[0]] = args[1]; + // Only add target systems that are different than the nodes system + if (-1 == fromhcp.search(args[1]) ) { + zvms.push(args[0]); + destSelect.append($('')); + } else { + invalidDest += args[0] + ' '; + } + } + } + + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'migrateTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'migrateTab' + inst; + } + + // Open new tab + // Create remote script form + var migrateForm = $('
                        '); + + // Create status bar + var barId = 'migrateStatusBar' + inst; + var statBar = createStatusBar(barId); + statBar.hide(); + migrateForm.append(statBar); + + // Create loader + var loader = createLoader('migrateLoader' + inst); + statBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('The three actions you can perform from this panel are:
                        Move -- Initiate a VMRELOCATE of the virtual machine.
                        Test - Determine if the specified virtual machine is eligible to be relocated.
                        Cancel -- Stop the relocation of the specified virtual machine.'); + migrateForm.append(infoBar); + + // If any destinations were removed, add information message + if (invalidDest.length) { + infoBar.append("
                        Ineligible destinations have been removed from the destination list because at least one or more of the nodes selected are already associated with the destinations: " + invalidDest) + } + + // Virtual machine label + var vmFS = $('
                        Virtual Machine
                        '); + migrateForm.append(vmFS); + + var vmAttr = $('
                        '); + vmFS.append($('
                        ')); + vmFS.append(vmAttr); + + // Target node or group + var tgt = $('
                        '); + vmAttr.append(tgt); + + // Destination + var dest = $('
                        '); + var destInput = $(''); + destInput.autocomplete({ + source: zvms + }); + + // Create a drop-down if there are known z/VMs + if (zvms.length) { + dest.append(destSelect); + } else { + dest.append(destInput); + } + vmAttr.append(dest); + + // Action Parameter + var actionparam = $('
                        '); + vmAttr.append(actionparam); + + // Parameters label + var optionalFS = $('
                        Optional
                        ').css('margin-top', '20px'); + migrateForm.append(optionalFS); + + var optAttr = $('
                        '); + optionalFS.append($('
                        ')); + optionalFS.append(optAttr); + + // Immediate Parameter + var immediateparam = $('
                        '); + optAttr.append(immediateparam); + immediateparam.change(function() { + if ($('#' + newTabId + ' select[name=immediate]').val() == 'yes') { + $('#' + newTabId + ' input[name=maxQuiesce]').val('0'); + } else { + $('#' + newTabId + ' input[name=maxQuiesce]').val('10'); + } + }); + + // Max total + var maxTotalParam = $('
                        '); + optAttr.append(maxTotalParam); + + // Max quiesce + var maxQuiesceParam = $('
                        '); + optAttr.append(maxQuiesceParam); + + // Force parameter + var forceParam = $('
                        ArchitectureDomainStorage
                        '); + optAttr.append(forceParam); + + // Generate tooltips + migrateForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Run + */ + var runBtn = createButton('Run'); + runBtn.click(function() { + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + + var tgt = $('#' + newTabId + ' input[name=target]'); + + // Drop-down box exists if z/VM systems are known + // Otherwise, only input box exists + var dest = $('#' + newTabId + ' select[name=dest]'); + if (!dest.length) { + dest = $('#' + newTabId + ' input[name=dest]'); + } + + var action = $('#' + newTabId + ' select[name=action]'); + var immediate = $('#' + newTabId + ' select[name=immediate]'); + var maxTotal = $('#' + newTabId + ' input[name=maxTotal]'); + var maxQuiesce = $('#' + newTabId + ' input[name=maxQuiesce]'); + var tgts = $('#' + newTabId + ' input[name=target]'); + + // Change borders color back to normal + var inputs = $('#' + newTabId + ' input').css('border', 'solid #BDBDBD 1px'); + var inputs = $('#' + newTabId + ' select').css('border', 'solid #BDBDBD 1px'); + + // Check if required arguments are given + var message = ""; + if (!isInteger(maxTotal.val())) { + message += "Max total time must be an integer. "; + maxTotal.css('border', 'solid #FF0000 1px'); + } if (!isInteger(maxQuiesce.val())) { + message += "Max quiesce time must be an integer. "; + maxQuiesce.css('border', 'solid #FF0000 1px'); + } if (!tgt.val()) { + message += "Target must be specified. "; + tgt.css('border', 'solid #FF0000 1px'); + } if (!dest.val()) { + message += "Destination must be specified. "; + dest.css('border', 'solid #FF0000 1px'); + } if (!action.val()) { + message += "Action must be specified. "; + action.css('border', 'solid #FF0000 1px'); + } + + // Show warning message + if (message) { + var warn = createWarnBar(message); + warn.prependTo($(this).parent().parent()); + return; + } + + var args = "destination=" + dest.val() + ";action=" + action.val() + ";immediate=" + immediate.val() + ";"; + + // Append max total argument. Specified <= 0 to accomodate negative values. + if (maxTotal.val() <= 0) { + args = args + "max_total=NOLIMIT;"; + } else { + args = args + "max_total=" + maxTotal.val() + ";"; + } + + // Append max quiesce argument. Specified <= 0 to accomodate negative values. + if (maxQuiesce.val() <= 0) { + args = args + "max_quiesce=NOLIMIT;"; + } else { + args = args + "max_quiesce=" + maxQuiesce.val() + ";"; + } + + // Append force argument + if ($("input[name=force]:checked").length > 0) { + args = args + "'force=" + $("input[name=force]:checked").each(function() { + args += $(this).val() + ' '; + }); + args += "';"; + } + + var statBarId = 'migrateStatusBar' + inst; + $('#' + statBarId).show(); + + // Disable all fields + $('#' + newTabId + ' input').attr('disabled', 'true'); + $('#' + newTabId + ' select').attr('disabled', 'true'); + + // Disable buttons + $('#' + newTabId + ' button').attr('disabled', 'true'); + + // Run migrate + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rmigrate', + tgt : tgts.val(), + args : args, + msg : 'out=migrateStatusBar' + inst + ';cmd=rmigrate;tgt=' + tgts.val() + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + }); + migrateForm.append(runBtn); + + // Append to discover tab + tab.add(newTabId, 'Migrate', migrateForm, true); + + // Select new tab + tab.select(newTabId); +}; + +/** + * Load event log configuration page + * + * @param node Source node to clone + */ +zvmPlugin.prototype.loadLogPage = function(node) { + // Get nodes tab + var tab = getNodesTab(); + var newTabId = node + 'LogsTab'; + + // If there is no existing clone tab + if (!$('#' + newTabId).length) { + // Get table headers + var tableId = $('#' + node).parents('table').attr('id'); + var headers = $('#' + tableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); + var cols = new Array(); + for ( var i = 0; i < headers.length; i++) { + var col = headers.eq(i).text(); + cols.push(col); + } + + // Get hardware control point column + var hcpCol = $.inArray('hcp', cols); + + // Get hardware control point + var nodeRow = $('#' + node).parent().parent(); + var datatable = $('#' + getNodesTableId()).dataTable(); + var rowPos = datatable.fnGetPosition(nodeRow.get(0)); + var aData = datatable.fnGetData(rowPos); + var hcp = aData[hcpCol]; + + // Create status bar and hide it + var statBarId = node + 'CloneStatusBar'; + var statBar = createStatusBar(statBarId).hide(); + + // Create info bar + var infoBar = createInfoBar('Retrieve, clear, or set options for event logs.'); + + // Create clone form + var logForm = $('
                        '); + logForm.append(statBar); + logForm.append(infoBar); + + // Create VM fieldset + var vmFS = $('
                        '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + logForm.append(vmFS); + + var vmAttr = $('
                        '); + vmFS.append($('
                        ')); + vmFS.append(vmAttr); + + // Create logs fieldset + var logFS = $('
                        '); + var logLegend = $('Logs'); + logFS.append(logLegend); + logForm.append(logFS); + + var logAttr = $('
                        '); + logFS.append($('
                        ')); + logFS.append(logAttr); + + vmAttr.append('
                        '); + logAttr.append('
                        '); + + var optsLabel = $(''); + var optsList = $('
                          '); + logAttr.append(optsLabel); + logAttr.append(optsList); + + // Create retrieve log checkbox + var retrieveChkBox = $('
                        • '); + optsList.append(retrieveChkBox); + retrieveChkBox.append('Retrieve log'); + + // Create log destination input + var tgtLog = $('
                        • '); + tgtLog.hide(); + optsList.append(tgtLog); + + // Create set log checkbox + var setChkBox = $('
                        • '); + optsList.append(setChkBox); + setChkBox.append('Set options'); + + // Create log options input + var logOpt = $('
                        • '); + logOpt.hide(); + optsList.append(logOpt); + + // Create clear log checkbox + var clearChkBox = $('
                        • '); + optsList.append(clearChkBox); + clearChkBox.append('Clear log'); + + retrieveChkBox.bind('click', function(event) { + tgtLog.toggle(); + }); + + setChkBox.find('input').bind('click', function(event) { + logOpt.toggle(); + }); + + // Generate tooltips + logForm.find('div input[title]').tooltip({ + position : "center right", + offset : [ -2, 10 ], + effect : "fade", + opacity : 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Run node + */ + var runBtn = createButton('Run'); + runBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + + var ready = true; + var errMsg = ''; + + // Verify required inputs are provided + var inputs = $('#' + newTabId + ' input'); + for ( var i = 0; i < inputs.length; i++) { + if (!inputs.eq(i).val() + && inputs.eq(i).attr('name') != 'tgtLog' + && inputs.eq(i).attr('name') != 'logOpt') { + inputs.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + inputs.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + // Write error message + if (!ready) { + errMsg = errMsg + 'Please provide a value for each missing field.
                          '; + } + + var tgts = $('#' + newTabId + ' input[name=tgtNode]').val(); + var srcLog = $('#' + newTabId + ' input[name=srcLog]').val(); + + var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); + var optStr = '-s;' + srcLog + ';'; + var opt; + for ( var i = 0; i < chkBoxes.length; i++) { + opt = chkBoxes.eq(i).attr('name'); + optStr += '-' + opt; + + // If it is the retrieve log + if (opt == 't') { + // Append log destination + optStr += ';' + $('#' + newTabId + ' input[name=tgtLog]').val(); + } + + // If it is set options + if (opt == 'o') { + // Append options + optStr += ';' + $('#' + newTabId + ' textarea[name=logOpt]').val(); + } + + // Append ; to end of string + if (i < (chkBoxes.length - 1)) { + optStr += ';'; + } + } + + // If a value is given for every input + if (ready) { + // Do not disable all inputs + //var inputs = $('#' + newTabId + ' input'); + //inputs.attr('disabled', 'disabled'); + + /** + * (1) Retrieve, clear, or set options for event logs + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'reventlog', + tgt : tgts, + args : optStr, + msg : 'out=' + statBarId + ';cmd=reventlog;tgt=' + tgts + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + + // Create loader + $('#' + statBarId).find('div').append(createLoader()); + $('#' + statBarId).show(); + + // Do not disable run button + //$(this).attr('disabled', 'true'); + } else { + // Show warning message + var warn = createWarnBar(errMsg); + warn.prependTo($(this).parent().parent()); + } + }); + logForm.append(runBtn); + + // Add clone tab + tab.add(newTabId, 'Logs', logForm, true); + } + + tab.select(newTabId); +}; diff --git a/xCAT-UI/js/custom/zvmUtils.js b/xCAT-UI/js/custom/zvmUtils.js index 3ff044834..8af844457 100644 --- a/xCAT-UI/js/custom/zvmUtils.js +++ b/xCAT-UI/js/custom/zvmUtils.js @@ -1,8140 +1,8345 @@ -/*"use strict"; */ -/** - * Global variables - */ -var diskDatatable; // zVM datatable containing disks -var zfcpDatatable; // zVM datatable containing zFCP devices -var networkDatatable; // zVM datatable containing networks -var builtInXCAT = 1; // 1 means xCAT shipped with zVM -var zhcpQueryCountForDisks = 0; -var zhcpQueryCountForZfcps = 0; -var zhcpQueryCountForNetworks = 0; -var selectedNetworkHash; // Network details for each network - -/** - * Get the disk datatable - * - * @return Data table object - */ -function getDiskDataTable() { - return diskDatatable; -} - -/** - * Set the disk datatable - * - * @param table Data table object - */ -function setDiskDataTable(table) { - diskDatatable = table; -} - -/** - * Get the zFCP datatable - * - * @return Data table object - */ -function getZfcpDataTable() { - return zfcpDatatable; -} - -/** - * Set the zFCP datatable - * - * @param table Data table object - */ -function setZfcpDataTable(table) { - zfcpDatatable = table; -} - -/** - * Get the network datatable - * - * @return Data table object - */ -function getNetworkDataTable() { - return networkDatatable; -} - -/** - * Set the network datatable - * - * @param table Data table object - */ -function setNetworkDataTable(table) { - networkDatatable = table; -} - -/** - * Get the selectedNetworkHash datatable - * - * @return selectedNetworkHash two dimensional hash table object - */ -function getselectedNetworkHash() { - return selectedNetworkHash; -} - -/** - * Set the selectedNetworkHash two dimensional hash table - * - * @param table selectedNetworkHash object - */ -function setselectedNetworkHash(table) { - selectedNetworkHash = table; -} - -/** - * Display hcp node pool lookup finished - */ -function displayNodeHcpFinished(count){ - var infoBar = getNodesTabInfoBar(); - if (infoBar !== null) { - if (count <= 0) { - infoBar.append(" Done."); - }else { - infoBar.append(" ."); - } - } -} - -/** - * Load HCP specific info - * - * @param data Data from HTTP request - */ -function loadHcpInfo(data) { - var args = data.msg.split(';'); - var findingPools = 0; - var findingPoolsCount = 0; - - // Get group - var group = args[0].replace('group=', ''); - // Get hardware control point - var hcp = args[1].replace('hcp=', ''); - - // Get user directory entry - var userEntry = data.rsp; - if (!userEntry.length) - return; - - // Get Nodes info bar - var nodeInfoBar = getNodesTabInfoBar(); - //nodeInfoBar.append("\nEntering loadHcpInfo Load...\n"); - - if (userEntry[0].indexOf('Failed') < 0) { - if (hcp) { - // If there is no cookie for the disk pool names - if (!$.cookie('xcat_' + hcp + 'diskpools') || $.cookie('xcat_' + hcp + 'diskpools') === null) { - if (nodeInfoBar !== null) { - nodeInfoBar.append("
                          Finding pools and networks..."); - findingPools = 1; - findingPoolsCount++; - } - // Get disk pools - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--diskpoolnames', - msg : hcp - }, - - success : setDiskPoolCookies, - complete : function() { - if (nodeInfoBar !== null) { - findingPoolsCount--; - displayNodeHcpFinished(findingPoolsCount); - } - } - }); - } - - // If there is no cookie for the zFCP pool names - if (!$.cookie('xcat_' + hcp + 'zfcppools') || $.cookie('xcat_' + hcp + 'zfcppools') === null) { - - if (nodeInfoBar !== null) { - if (findingPools = 0) { - nodeInfoBar.append("
                          Finding pools and networks..."); - } - findingPools = 1; - findingPoolsCount++; - } - // Get fcp pools - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--zfcppoolnames', - msg : hcp - }, - - success : setZfcpPoolCookies, - complete : function() { - if (nodeInfoBar !== null) { - findingPoolsCount--; - displayNodeHcpFinished(findingPoolsCount); - } - } - }); - } - - // If there is no cookie for the network names - if (!$.cookie('xcat_' + hcp + 'networks') || $.cookie('xcat_' + hcp + 'networks') === null) { - - if (nodeInfoBar !== null) { - if (findingPools = 0) { - nodeInfoBar.append("
                          Finding pools and networks..."); - } - findingPools = 1; - findingPoolsCount++; - } - // Get network names - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--getnetworknames', - msg : hcp - }, - - success : setNetworkCookies, - complete : function() { - if (nodeInfoBar !== null) { - findingPoolsCount--; - displayNodeHcpFinished(findingPoolsCount); - } - } - }); - } - } // End of if (hcp) - } else { - // Create warning dialog - var warning = createWarnBar('z/VM SMAPI is not responding to ' + hcp + '. SMAPI may need to be reset. '+userEntry[0]); - var warnDialog = $('
                          ').append(warning); - - // Open dialog - warnDialog.dialog({ - title:'Warning', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - $(this).dialog("close"); - } - } - }); - } -} - -/** - * Load user entry of a given node - * - * @param data Data from HTTP request - */ -function loadUserEntry(data) { - var args = data.msg.split(';'); - - // Get tab ID - var ueDivId = args[0].replace('out=', ''); - // Get node - var node = args[1].replace('node=', ''); - // Get user directory entry - var userEntry = data.rsp[0].split(node + ':'); - - // Remove loader - $('#' + node + 'TabLoader').remove(); - - var toggleLinkId = node + 'ToggleLink'; - $('#' + toggleLinkId).click(function() { - // Get text within this link - var lnkText = $(this).text(); - - // Toggle user entry division - $('#' + node + 'UserEntry').toggle(); - // Toggle inventory division - $('#' + node + 'Inventory').toggle(); - - // Change text - if (lnkText == 'Show directory entry') { - $(this).text('Show inventory'); - } else { - $(this).text('Show directory entry'); - } - }); - - // Put user entry into a list - var fieldSet = $('
                          '); - var legend = $('Directory Entry'); - fieldSet.append(legend); - - var txtArea = $(''); - for ( var i = 1; i < userEntry.length; i++) { - userEntry[i] = jQuery.trim(userEntry[i]); - txtArea.append(userEntry[i]); - - if (i < userEntry.length) { - txtArea.append('\n'); - } - } - txtArea.attr('readonly', 'readonly'); - fieldSet.append(txtArea); - - /** - * Edit user entry - */ - txtArea.bind('dblclick', function(event) { - txtArea.attr('readonly', ''); - txtArea.css( { - 'border-width' : '1px' - }); - - saveBtn.show(); - cancelBtn.show(); - saveBtn.css('display', 'inline-table'); - cancelBtn.css('display', 'inline-table'); - }); - - /** - * Save - */ - var saveBtn = createButton('Save').hide(); - saveBtn.bind('click', function(event) { - // Show loader - $('#' + node + 'StatusBarLoader').show(); - $('#' + node + 'StatusBar').show(); - - // Replace user entry - var newUserEntry = jQuery.trim(txtArea.val()) + '\n'; - - // Replace user entry - $.ajax( { - url : 'lib/zCmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--replacevs', - att : newUserEntry, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process and save it in a cookie - incrementNodeProcess(node); - - txtArea.attr('readonly', 'readonly'); - txtArea.css( { - 'border-width' : '0px' - }); - - // Disable save button - $(this).hide(); - cancelBtn.hide(); - }); - - /** - * Cancel - */ - var cancelBtn = createButton('Cancel').hide(); - cancelBtn.bind('click', function(event) { - txtArea.attr('readonly', 'readonly'); - txtArea.css( { - 'border-width' : '0px' - }); - - cancelBtn.hide(); - saveBtn.hide(); - }); - - // Create info bar - var infoBar = createInfoBar('Double click on the directory entry to edit it.'); - - // Append user entry into division - $('#' + ueDivId).append(infoBar); - $('#' + ueDivId).append(fieldSet); - $('#' + ueDivId).append(saveBtn); - $('#' + ueDivId).append(cancelBtn); -} - -/** - * Increment number of processes running against a node - * - * @param node Node to increment running processes - */ -function incrementNodeProcess(node) { - // Get current processes - var procs = $.cookie('xcat_' + node + 'processes'); - if (procs) { - // One more process - procs = parseInt(procs) + 1; - $.cookie('xcat_' + node + 'processes', procs); - } else { - $.cookie('xcat_' + node + 'processes', 1); - } -} - -/** - * Update provision new node status - * - * @param data Data returned from HTTP request - */ -function updateZProvisionNewStatus(data) { - // Parse ajax response - var rsp = data.rsp; - var args = data.msg.split(';'); - var lastCmd = args[0].replace('cmd=', ''); - var out2Id = args[1].replace('out=', ''); - if (typeof console == "object"){ - console.log("Entering updateZProvisionNewStatus. Last command:<"+lastCmd+"> All args:<"+args+">"); - } - // IDs for status bar, tab, and loader - var statBarId = 'zProvisionStatBar' + out2Id; - var tabId = 'zvmProvisionTab' + out2Id; - var loaderId = 'zProvisionLoader' + out2Id; - - var node = $('#' + tabId + ' input[name=nodeName]').val(); - - /** - * (2) Create user entry - */ - if (lastCmd == 'nodeadd') { - if (rsp.length) { - $('#' + loaderId).hide(); - $('#' + statBarId).find('div').append('
                          (Error) Failed to create node definition
                          '); - } else { - $('#' + statBarId).find('div').append('
                          Node definition created for ' + node + '
                          '); - - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // Create user entry - var userEntry = $('#' + tabId + ' textarea').val(); - $.ajax( { - url : 'lib/zCmd.php', - dataType : 'json', - data : { - cmd : 'mkvm', - tgt : node, - args : '', - att : userEntry, - msg : 'cmd=mkvm;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - /** - * (3) Update /etc/hosts - */ - else if (lastCmd == 'mkvm') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // If there was an error, quit - if (containErrors(prg.html())) { - $('#' + loaderId).hide(); - } else { - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'makehosts', - tgt : '', - args : '', - msg : 'cmd=makehosts;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - /** - * If sourceforge xcat: (4) Update DNS - */ - else if ((lastCmd == 'makehosts') && (builtInXCAT == 0)) { - // If there was an error, quit - if (rsp.length) { - $('#' + loaderId).hide(); - $('#' + statBarId).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); - } else { - $('#' + statBarId).find('div').append('
                          /etc/hosts updated
                          '); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'makedns', - tgt : '', - args : '', - msg : 'cmd=makedns;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - /** - * If built in zVM xcat and last command was makehosts or - * If sourceforge xCAT and lastCmd was makedns - * (5) Add disk - * - */ - else if (((lastCmd == 'makehosts') && (builtInXCAT == 1)) || - ((lastCmd == 'makedns') && (builtInXCAT == 0))) { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // If there was an error, quit - if (rsp.length) { - $('#' + loaderId).hide(); - if (builtInXCAT == 1) { - $('#' + statBarId).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); - } else { - $('#' + statBarId).find('div').append('
                          (Error) Failed to makedns
                          '); - } - } else { - if (builtInXCAT == 1) { - $('#' + statBarId).find('div').append('
                          /etc/hosts updated
                          '); - } else { - $('#' + statBarId).find('div').append('
                          makedns updated
                          '); - } - - // Set cookie for number of disks - var diskRows = $('#' + tabId + ' table:eq(0):visible tbody tr'); - $.cookie('xcat_disks2add' + out2Id, diskRows.length, {path: '/xcat', secure:true }); - if (diskRows.length > 0) { - for (var i = 0; i < diskRows.length; i++) { - var diskArgs = diskRows.eq(i).find('td'); - var type = diskArgs.eq(1).find('select').val(); - var address = diskArgs.eq(2).find('input').val(); - var size = diskArgs.eq(3).find('input').val(); - var mode = diskArgs.eq(4).find('select').val(); - var pool = diskArgs.eq(5).find('select').val(); - var password = diskArgs.eq(6).find('input').val(); - - // Create ajax arguments - var args = ''; - if (type == '3390') { - args = '--add' + type + ';' + pool + ';' + address - + ';' + size + ';' + mode + ';' + password + ';' - + password + ';' + password; - } else if (type == '9336') { - args = '--add' + type + ';' + pool + ';' + address + ';' - + size + ';' + mode + ';' + password + ';' - + password + ';' + password; - } - - // Attach disk to node - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : args, - msg : 'cmd=chvm-disk;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - // Set cookie for number of zFCP devices - var zfcpRows = $('#' + tabId + ' table:eq(1):visible tbody tr'); - $.cookie('xcat_zfcp2add' + out2Id, zfcpRows.length, {path: '/xcat', secure:true }); - if (zfcpRows.length > 0) { - for ( var i = 0; i < zfcpRows.length; i++) { - var diskArgs = zfcpRows.eq(i).find('td'); - var address = diskArgs.eq(1).find('input').val(); - var size = diskArgs.eq(2).find('input').val(); - var pool = diskArgs.eq(3).find('select').val(); - var tag = diskArgs.eq(4).find('input').val(); - var portName = diskArgs.eq(5).find('input').val(); - var unitNo = diskArgs.eq(6).find('input').val(); - - // This is either true or false - var loaddev = diskArgs.eq(7).find('input').attr('checked'); - if (loaddev) { - loaddev = "1"; - } else { - loaddev = "0"; - } - - // Create ajax arguments - var args = '--addzfcp;' + pool + ';' + address + ';' + loaddev + ';' + size; - if (tag && tag != "null") { - args += ';' + tag; - } else { - args += ';'; - } if (portName && tag != "null") { - args += ';' + portName; - } else { - args += ';'; - } if (unitNo && tag != "null") { - args += ';' + unitNo; - } else { - args += ';'; - } - - // Attach zFCP device to node - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : args, - msg : 'cmd=chvm-zfcp;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - // Done if no disks to add - if (diskRows.length < 1 && zfcpRows.length < 1) { - $('#' + statBarId).find('div').append('
                          No disks found to provison, finished.
                          '); - $('#' + loaderId).hide(); - } - } - } - - /** - * (6) Set operating system for given node - */ - else if (lastCmd == 'chvm-disk' || lastCmd == 'chvm-zfcp') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // If there was an error, quit - if (containErrors(prg.html())) { - $('#' + loaderId).hide(); - } else { - // Set cookie for number of disks - // One less disk to add - var disks2add = $.cookie('xcat_disks2add' + out2Id); - if (lastCmd == 'chvm-disk') { - if (disks2add > 0) { - disks2add--; - $.cookie('xcat_disks2add' + out2Id, disks2add, {path: '/xcat', secure:true }); - } - } - - var zfcp2add = $.cookie('xcat_zfcp2add' + out2Id); - if (lastCmd == 'chvm-zfcp') { - if (zfcp2add > 0) { - zfcp2add--; - $.cookie('xcat_zfcp2add' + out2Id, zfcp2add, {path: '/xcat', secure:true }); - } - } - - // Only set operating system if there are no more disks to add - if (zfcp2add < 1 && disks2add < 1) { - // If an operating system image is given - var osImage = $('#' + tabId + ' select[name=os]:visible').val(); - if (osImage) { - // Get operating system, architecture, provision method, and profile - var tmp = osImage.split('-'); - var os = tmp[0]; - var arch = tmp[1]; - var profile = tmp[3]; - - // If the last disk is added - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeadd', - tgt : '', - args : node + ';noderes.netboot=zvm;nodetype.os=' - + os + ';nodetype.arch=' + arch - + ';nodetype.profile=' + profile, - msg : 'cmd=noderes;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } else { - $('#' + loaderId).hide(); - } - } - } - } - - /** - * (7) If sourceforge xCAT Update DHCP - */ - else if ((lastCmd == 'noderes') && (builtInXCAT == 0)) { - // If there was an error, do not continue - if (rsp.length) { - $('#' + loaderId).hide(); - $('#' + statBarId).find('div').append('
                          (Error) Failed to set operating system
                          '); - } else { - $('#' + statBarId).find('div').append('
                          Operating system for ' + node + ' set
                          '); - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'makedhcp', - tgt : '', - args : '-a', - msg : 'cmd=makedhcp;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - /** - * (8) Prepare node for boot - */ - else if (((lastCmd == 'noderes') && (builtInXCAT == 1)) || - ((lastCmd == 'makedhcp') && (builtInXCAT == 0))) { - // If there was an error, do not continue - if (rsp.length) { - $('#' + loaderId).hide(); - if (builtInXCAT == 1) { - $('#' + statBarId).find('div').append('
                          (Error) Failed to set operating system
                          '); - } else { - $('#' + statBarId).find('div').append('
                          (Error) Failed to make dhcp
                          '); - } - } else { - if (builtInXCAT == 1) { - $('#' + statBarId).find('div').append('
                          Operating system for ' + node + ' set
                          '); - } else { - $('#' + statBarId).find('div').append('
                          DHCP for ' + node + ' set
                          '); - } - - // Prepare node for boot - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeset', - tgt : node, - args : 'install', - msg : 'cmd=nodeset;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - /** - * (9) Boot node to network - */ - else if (lastCmd == 'nodeset') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // If there was an error - // Do not continue - if (containErrors(prg.html())) { - $('#' + loaderId).hide(); - } else { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rnetboot', - tgt : node, - args : 'ipl=000C', - msg : 'cmd=rnetboot;out=' + out2Id - }, - - success : updateZProvisionNewStatus - }); - } - } - - /** - * (10) Done - */ - else if (lastCmd == 'rnetboot') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - if (prg.html().indexOf('Error') < 0) { - $('#' + statBarId).find('div').append('
                          Open a VNC viewer to see the installation progress.  It might take a couple of minutes before you can connect.
                          '); - } - - // Hide loader - $('#' + loaderId).hide(); - } -} - -/** - * Update the provision existing node status - * - * @param data Data returned from HTTP request - */ -function updateZProvisionExistingStatus(data) { - // Get ajax response - var rsp = data.rsp; - var args = data.msg.split(';'); - - // Get command invoked - var cmd = args[0].replace('cmd=', ''); - // Get provision tab instance - var inst = args[1].replace('out=', ''); - if (typeof console == "object"){ - console.log("Entering updateZProvisionExistingStatus. Last command:<"+cmd+"> All args:<"+args+">"); - } - - // Get provision tab and status bar ID - var statBarId = 'zProvisionStatBar' + inst; - var tabId = 'zvmProvisionTab' + inst; - - /** - * (2) Prepare node for boot - */ - if (cmd == 'nodeadd') { - // Get operating system - var bootMethod = $('#' + tabId + ' select[name=bootMethod]').val(); - - // Get nodes that were checked - var dTableId = 'zNodesDatatable' + inst; - var tgts = getNodesChecked(dTableId); - - // Prepare node for boot - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeset', - tgt : tgts, - args : bootMethod, - msg : 'cmd=nodeset;out=' + inst - }, - - success : updateZProvisionExistingStatus - }); - } - - /** - * (3) Boot node from network - */ - else if (cmd == 'nodeset') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // If there was an error, do not continue - if (containErrors(prg.html())) { - var loaderId = 'zProvisionLoader' + inst; - $('#' + loaderId).remove(); - return; - } - - // Get nodes that were checked - var dTableId = 'zNodesDatatable' + inst; - var tgts = getNodesChecked(dTableId); - - // Boot node from network - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rnetboot', - tgt : tgts, - args : 'ipl=000C', - msg : 'cmd=rnetboot;out=' + inst - }, - - success : updateZProvisionExistingStatus - }); - } - - /** - * (4) Done - */ - else if (cmd == 'rnetboot') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - if (prg.html().indexOf('Error') < 0) { - $('#' + statBarId).find('div').append('
                          Open a VNC viewer to see the installation progress.  It might take a couple of minutes before you can connect.
                          '); - } - - var loaderId = 'zProvisionLoader' + inst; - $('#' + loaderId).remove(); - } -} - -/** - * Update zVM node status - * - * @param data Data returned from HTTP request - */ -function updateZNodeStatus(data) { - var node = data.msg; - var rsp = data.rsp; - - // Get cookie for number processes performed against this node - var actions = $.cookie('xcat_' + node + 'processes'); - // One less process - actions = actions - 1; - $.cookie('xcat_' + node + 'processes', actions, {path: '/xcat', secure:true }); - - if (actions < 1) { - // Hide loader when there are no more processes - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).hide(); - } - - var statBarId = node + 'StatusBar'; - - // Write ajax response to status bar - var prg = writeRsp(rsp, node + ': '); - $('#' + statBarId).find('div').append(prg); -} - -/** - * Update clone status - * - * @param data Data returned from HTTP request - */ -function updateZCloneStatus(data) { - // Get ajax response - var rsp = data.rsp; - var args = data.msg.split(';'); - var cmd = args[0].replace('cmd=', ''); - - // Get provision instance - var inst = args[1].replace('inst=', ''); - // Get output division ID - var out2Id = args[2].replace('out=', ''); - - /** - * (2) Update /etc/hosts - */ - if (cmd == 'nodeadd') { - var node = args[3].replace('node=', ''); - - // If there was an error, do not continue - if (rsp.length) { - $('#' + out2Id).find('img').hide(); - $('#' + out2Id).find('div').append('
                          (Error) Failed to create node definition
                          '); - } else { - $('#' + out2Id).find('div').append('
                          Node definition created for ' + node + '
                          '); - - // If last node definition was created - var tmp = inst.split('/'); - if (tmp[0] == tmp[1]) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'makehosts', - tgt : '', - args : '', - msg : 'cmd=makehosts;inst=' + inst + ';out=' + out2Id - }, - - success : updateZCloneStatus - }); - } - } - } - - /** - * (3a) Update DNS if source forge xCAT then do makedns - */ - else if ((cmd == 'makehosts') && (builtInXCAT == 0)) { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + out2Id).find('div').append(prg); - - // If there was an error, do not continue - if (rsp.length) { - $('#' + out2Id).find('img').hide(); - $('#' + out2Id).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); - } else { - $('#' + out2Id).find('div').append('
                          /etc/hosts updated
                          '); - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'makedns', - tgt : '', - args : '', - msg : 'cmd=makedns;inst=' + inst + ';out=' + out2Id - }, - - success : updateZCloneStatus - }); - } - } - - /** - * (3b) Update DNS for built in xCAT and clone - * Just clone for sourceforge xCAT - */ - else if (((cmd == 'makehosts') && (builtInXCAT == 1)) || - ((cmd == 'makedns') && (builtInXCAT == 0))) { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + out2Id).find('div').append(prg); - - // If there was an error, do not continue - if (rsp.length) { - $('#' + out2Id).find('img').hide(); - if (builtInXCAT == 1) { - $('#' + out2Id).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); - } else { - $('#' + out2Id).find('div').append('
                          (Error) Failed to makedns
                          '); - } - } - // Get clone tab - var tabId = out2Id.replace('CloneStatusBar', 'CloneTab'); - - // If a node range is given - var tgtNodeRange = $('#' + tabId + ' input[name=tgtNode]').val(); - var tgtNodes = ''; - if (tgtNodeRange.indexOf('-') > -1) { - var tmp = tgtNodeRange.split('-'); - - // Get node base name - var nodeBase = tmp[0].match(/[a-zA-Z]+/); - // Get the starting index - var nodeStart = parseInt(tmp[0].match(/\d+/)); - // Get the ending index - var nodeEnd = parseInt(tmp[1].match(/\d+/)); - for ( var i = nodeStart; i <= nodeEnd; i++) { - // Do not append comma for last node - if (i == nodeEnd) { - tgtNodes += nodeBase + i.toString(); - } else { - tgtNodes += nodeBase + i.toString() + ','; - } - } - } else { - tgtNodes = tgtNodeRange; - } - - // Get other inputs - var srcNode = $('#' + tabId + ' input[name=srcNode]').val(); - hcp = $('#' + tabId + ' input[name=newHcp]').val(); - var group = $('#' + tabId + ' input[name=newGroup]').val(); - var diskPool = $('#' + tabId + ' input[name=diskPool]').val(); - var diskPw = $('#' + tabId + ' input[name=diskPw]').val(); - if (!diskPw) { - diskPw = ''; - } - - // Clone - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'mkvm', - tgt : tgtNodes, - args : srcNode + ';pool=' + diskPool + ';pw=' + diskPw, - msg : 'cmd=mkvm;inst=' + inst + ';out=' + out2Id - }, - error: function(jqXHR, textStatus) { - $('#' + out2Id).find('div').append('
                          (Error) Failed in clone call with ' + textStatus + '
                          '); - }, - success : updateZCloneStatus - }); - } - - /** - * (5) Done - */ - else if (cmd == 'mkvm') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + out2Id).find('div').append(prg); - - // Hide loader - $('#' + out2Id).find('img').hide(); - } -} - -/** - * Get zVM resources - * - * @param data Data from HTTP request - */ -function getZResources(data) { - var tabId = 'zvmResourceTab'; - var info = createInfoBar('Manage storage and networks'); - $('#' + tabId).append(info); - - // Do not continue if there is no output - if (data.rsp.length) { - if (typeof console == "object"){ - console.log("Entering getZResources."); - } - // Push hardware control points into an array - var node, hcp; - var hcpHash = new Object(); - var hostnameHash = new Object(); - for (var i in data.rsp) { - node = data.rsp[i][0]; - hcp = data.rsp[i][1]; - // data will be coming in like "xcat xcat.endicott.ibm.com hosts.hostnames" - // or xcat zhcp.endicott.ibm.com zvm.hcp" - if (data.rsp[i][2]== "zvm.hcp") { - hcpHash[hcp] = 1; - } else { - if (hcp.length) { - hostnameHash[hcp] = node; - } - } - } - - // Create an array for hardware control points - var hcps = new Array(); - for (var key in hcpHash) { - // Get the short host name - //hcp = key.split('.')[0]; //old code - hcp = hostnameHash[key]; - if (typeof console == "object"){ - console.log("getZResources lookup for hostname "+key+" found nodename <"+hcp+">"); - } - if (jQuery.inArray(hcp, hcps) == -1) { - hcps.push(hcp); - } - } - - // Set hardware control point cookie - $.cookie('xcat_hcp', hcps, {path: '/xcat', secure:true }); - - // Delete loader - $('#' + tabId).find('img[src="images/loader.gif"]').remove(); - - // Create accordion panel for disk - var resourcesAccordion = $('
                          '); - var diskSection = $('
                          '); - var diskLnk = $('

                          Disks

                          ').click(function () { - // Do not load panel again if it is already loaded - if ($('#zvmDiskResource').children().length) { - return; - } - else - $('#zvmDiskResource').append(createLoader('')); - - // Resize accordion - $('#zvmResourceAccordion').accordion('resize'); - - // Create a array for hardware control points - var hcps = new Array(); - if ($.cookie('xcat_hcp').indexOf(',') > -1) { - hcps = $.cookie('xcat_hcp').split(','); - } else { - hcps.push($.cookie('xcat_hcp')); - } - - // Query the disk pools for each hcp - var panelId = 'zvmDiskResource'; - var info = $('#' + panelId).find('.ui-state-highlight'); - if (!info.length) { - info = createInfoBar("Querying "+hcps.length+" zhcp(s) for disk pools."); - $('#' + panelId).append(info); - } - zhcpQueryCountForDisks = hcps.length; - - for (var i in hcps) { - var itemcount = +i + 1; - info.append("
                          Querying disk pools from: "+hcps[i]+" ("+itemcount+" of "+hcps.length+")"); - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcps[i], - args : '--diskpoolnames', - msg : hcps[i] - }, - - success : getDiskPool - }); - zhcpQueryCountForDisks--; - } - }); - - // Create accordion panel for zFCP devices - var zfcpSection = $('
                          '); - var zfcpLnk = $('

                          zFCP

                          ').click(function () { - // Do not load panel again if it is already loaded - if ($('#zfcpResource').children().length) - return; - else - $('#zfcpResource').append(createLoader('')); - - // Resize accordion - $('#zvmResourceAccordion').accordion('resize'); - - // Create a array for hardware control points - var hcps = new Array(); - if ($.cookie('xcat_hcp').indexOf(',') > -1) { - hcps = $.cookie('xcat_hcp').split(','); - } else { - hcps.push($.cookie('xcat_hcp')); - - } - - // Query the fcp pools for each hcp - var panelId = 'zfcpResource'; - var info = $('#' + panelId).find('.ui-state-highlight'); - if (!info.length) { - info = createInfoBar("Querying "+hcps.length+" zhcp(s) for fcp pools."); - $('#' + panelId).append(info); - } - zhcpQueryCountForZfcps = hcps.length; - for (var i in hcps) { - // Gather fcp pools from hardware control points - var itemcount = +i + 1; - info.append("
                          Querying fcp pools from: "+hcps[i]+" ("+itemcount+" of "+hcps.length+")"); - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcps[i], - args : '--zfcppoolnames', - msg : hcps[i] - }, - - success : getZfcpPool - }); - zhcpQueryCountForZfcps--; - } - }); - - // Create accordion panel for network - var networkSection = $('
                          '); - var networkLnk = $('

                          Networks

                          ').click(function () { - // Do not load panel again if it is already loaded - if ($('#zvmNetworkResource').children().length) { - return; - } else { - $('#zvmNetworkResource').append(createLoader('')); - } - - // Resize accordion - $('#zvmResourceAccordion').accordion('resize'); - - // Create a array for hardware control points - var hcps = new Array(); - if ($.cookie('xcat_hcp').indexOf(',') > -1) { - hcps = $.cookie('xcat_hcp').split(','); - } else { - hcps.push($.cookie('xcat_hcp')); - - } - // Query the networks for each - var panelId = 'zvmNetworkResource'; - var info = $('#' + panelId).find('.ui-state-highlight'); - if (!info.length) { - info = createInfoBar("Querying "+hcps.length+" zhcp(s) for networks."); - $('#' + panelId).append(info); - } - zhcpQueryCountForNetworks = hcps.length; - for (var i in hcps) { - var itemcount = +i + 1; - info.append("
                          Querying networks from: "+hcps[i]+" ("+itemcount+" of "+hcps.length+")"); - $('#zvmResourceAccordion').accordion('resize'); - // Gather networks from hardware control points - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcps[i], - args : '--getnetworknames', - msg : hcps[i] - }, - - success : getNetwork - }); - zhcpQueryCountForNetworks--; - } - }); - - resourcesAccordion.append(diskLnk, diskSection, zfcpLnk, zfcpSection, networkLnk, networkSection); - - // Append accordion to tab - $('#' + tabId).append(resourcesAccordion); - resourcesAccordion.accordion(); - networkLnk.trigger('click'); - } -} - -/** - * Get node attributes from HTTP request data - * - * @param propNames Hash table of property names - * @param keys Property keys - * @param data Data from HTTP request - * @return Hash table of property values - */ -function getAttrs(keys, propNames, data) { - // Create hash table for property values - var attrs = new Object(); - - // Go through inventory and separate each property out - var curKey = null; // Current property key - var addLine; // Add a line to the current property? - for ( var i = 1; i < data.length; i++) { - addLine = true; - - // Loop through property keys - // Does this line contains one of the properties? - for ( var j = 0; j < keys.length; j++) { - // Find property name - if (data[i].indexOf(propNames[keys[j]]) > -1) { - attrs[keys[j]] = new Array(); - - // Get rid of property name in the line - data[i] = data[i].replace(propNames[keys[j]], ''); - // Trim the line - data[i] = jQuery.trim(data[i]); - - // Do not insert empty line - if (data[i].length > 0) { - attrs[keys[j]].push(data[i]); - } - - curKey = keys[j]; - addLine = false; // This line belongs to a property - } - } - - // Line does not contain a property - // Must belong to previous property - if (addLine && data[i].length > 1) { - data[i] = jQuery.trim(data[i]); - attrs[curKey].push(data[i]); - } - } - - return attrs; -} - -/** - * Create add processor dialog - * - * @param node Node to add processor to - */ -function openAddProcDialog(node) { - // Create form to add processor - var addProcForm = $('
                          '); - // Create info bar - var info = createInfoBar('Add a temporary processor to this virtual server.'); - addProcForm.append(info); - addProcForm.append('
                          '); - addProcForm.append('
                          '); - - // Create drop down for processor type - var procType = $('
                          '); - procType.append(''); - var typeSelect = $(''); - typeSelect.append('' - + '' - + '' - + '' - ); - procType.append(typeSelect); - addProcForm.append(procType); - - // Generate tooltips - addProcForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add processor - addProcForm.dialog({ - title:'Add processor', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var node = $(this).find('input[name=procNode]').val(); - var address = $(this).find('input[name=procAddress]').val(); - var type = $(this).find('select[name=procType]').val(); - - // If inputs are not complete, show warning message - if (!node || !address || !type) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - // Add processor - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--addprocessoractive;' + address + ';' + type, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - var statusId = node + 'StatusBar'; - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).show(); - $('#' + statusId).show(); - - // Close dialog - $(this).dialog( "close" ); - } - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create add disk dialog - * - * @param node Node to add disk to - * @param hcp Hardware control point of node - */ -function openAddDiskDialog(node, hcp) { - // Get list of disk pools - var cookie = $.cookie('xcat_' + hcp + 'diskpools'); - var pools = new Array(); - if (cookie) { - pools = cookie.split(','); - } - - // Create form to add disk - var addDiskForm = $('
                          '); - // Create info bar - var info = createInfoBar('Add a ECKD|3390 or FBA|9336 disk to this virtual server.'); - addDiskForm.append(info); - addDiskForm.append('
                          '); - addDiskForm.append('
                          '); - addDiskForm.append('
                          '); - addDiskForm.append('
                          '); - - // Create drop down for disk pool - var diskPool = $('
                          '); - diskPool.append(''); - var poolSelect = $(''); - for ( var i = 0; i < pools.length; i++) { - if( !pools[i] || 0 === pools[i].length) continue; - poolSelect.append(''); - } - diskPool.append(poolSelect); - addDiskForm.append(diskPool); - - // Create drop down for disk mode - var diskMode = $('
                          '); - diskMode.append(''); - var modeSelect = $(''); - modeSelect.append('' - + '' - + '' - + '' - + '' - + '' - + '' - ); - diskMode.append(modeSelect); - addDiskForm.append(diskMode); - - addDiskForm.append('
                          '); - - // Generate tooltips - addDiskForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add disk - addDiskForm.dialog({ - title:'Add disk', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var node = $(this).find('input[name=diskNode]').val(); - var type = $(this).find('select[name=diskType]').val(); - var address = $(this).find('input[name=diskAddress]').val(); - var size = $(this).find('input[name=diskSize]').val(); - var pool = $(this).find('select[name=diskPool]').val(); - var mode = $(this).find('select[name=diskMode]').val(); - var password = $(this).find('input[name=diskPassword]').val(); - - // If inputs are not complete, show warning message - if (!node || !type || !address || !size || !pool || !mode) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - // Add disk - if (type == '3390') { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--add3390;' + pool + ';' + address + ';' + size - + ';' + mode + ';' + password + ';' + password + ';' + password, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - var statusId = node + 'StatusBar'; - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).show(); - $('#' + statusId).show(); - } else if (type == '9336') { - // Default block size for FBA volumes = 512 - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--add9336;' + pool + ';' + address + ';' + size - + ';' + mode + ';' + password + ';' + password + ';' + password, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - var statusId = node + 'StatusBar'; - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).show(); - $('#' + statusId).show(); - } - - // Close dialog - $(this).dialog( "close" ); - } // End of else - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create add zFCP device dialog - * - * @param node Node to add disk to - * @param hcp Hardware control point of node - * @param zvm The z/VM system of node - */ -function openAddZfcpDialog(node, hcp, zvm) { - // Get list of disk pools - var cookie = $.cookie('xcat_' + hcp + 'zfcppools'); - var pools = new Array(); - if (cookie) { - pools = cookie.split(','); - } - - // Create form to add disk - var addZfcpForm = $('
                          '); - // Create info bar - var info = createInfoBar('Add a SCSI|FCP disk to this virtual server.'); - addZfcpForm.append(info); - addZfcpForm.append('
                          '); - addZfcpForm.append('
                          '); - addZfcpForm.append('
                          '); - addZfcpForm.append('
                          '); - - // Create drop down for disk pool - var diskPool = $('
                          '); - diskPool.append(''); - var poolSelect = $(''); - for ( var i = 0; i < pools.length; i++) { - if( !pools[i] || 0 === pools[i].length) continue; - poolSelect.append(''); - } - diskPool.append(poolSelect); - addZfcpForm.append(diskPool); - - // Tag to identify where device will be used - addZfcpForm.append('
                          '); - - // Create advanced link to set advanced zFCP properties - var advancedLnk = $(''); - addZfcpForm.append(advancedLnk); - var advanced = $('
                          ').hide(); - addZfcpForm.append(advanced); - - var portName = $('
                          '); - var unitNo = $('
                          '); - advanced.append(portName, unitNo); - - // Toggle port name and unit number when clicking on advanced link - advancedLnk.click(function() { - advanced.toggle(); - }); - - // Generate tooltips - addZfcpForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add disk - addZfcpForm.dialog({ - title:'Add zFCP device', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var node = $(this).find('input[name=diskNode]').val(); - var address = $(this).find('input[name=diskAddress]').val(); - var loaddev = $(this).find('input[name=diskLoaddev]'); - var size = $(this).find('input[name=diskSize]').val(); - var pool = $(this).find('select[name=diskPool]').val(); - var tag = $(this).find('input[name=diskTag]').val(); - var portName = $(this).find('input[name=diskPortName]').val(); - var unitNo = $(this).find('input[name=diskUnitNo]').val(); - - // If inputs are not complete, show warning message - if (!node || !address || !size || !pool) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - if (loaddev.attr('checked')) { - loaddev = 1; - } else { - loaddev = 0; - } - - var args = '--addzfcp||' + pool + '||' + address + '||' + loaddev + '||' + size; - - if (tag && tag != "null") { - args += '||' + tag; - } else { - args += '|| ""'; - } - - if ((portName && portName != "null") && (unitNo && unitNo != "null")) { - args += '||' + portName + '||' + unitNo; - } - - // Add zFCP device - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : args, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - var statusId = node + 'StatusBar'; - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).show(); - $('#' + statusId).show(); - - // Close dialog - $(this).dialog( "close" ); - } - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create dedicate device dialog - * - * @param node Node to dedicate device to - * @param hcp Hardware control point of node - */ -function openDedicateDeviceDialog(node, hcp) { - // Create form to add disk - var dedicateForm = $('
                          '); - // Create info bar - var info = createInfoBar('Add a dedicated device to the configuration'); - dedicateForm.append(info); - - dedicateForm.append('
                          '); - dedicateForm.append('
                          '); - dedicateForm.append('
                          '); - dedicateForm.append('
                          '); - - // Generate tooltips - dedicateForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add dedicated device - dedicateForm.dialog({ - title:'Add dedicated device', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var node = $(this).find('input[name=diskNode]').val(); - var vAddress = $(this).find('input[name=virtualAddress]').val(); - var rAddress = $(this).find('input[name=realAddress]').val() - var mode = $(this).find('select[name=mode]').val(); - - // If inputs are not complete, show warning message - if (!node || !vAddress || !rAddress || !mode) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - var args = '--dedicatedevice;' + vAddress + ';' + rAddress + ';' + mode; - - // Add zFCP device - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : args, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - var statusId = node + 'StatusBar'; - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).show(); - $('#' + statusId).show(); - - // Close dialog - $(this).dialog( "close" ); - } - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create add ECKD to system dialog - * - * @param hcp Hardware control point of node - */ -function openAddEckd2SystemDialog(hcp) { - var dialogId = 'zvmAddEckd2System'; - - // Create form to add disk - var addE2SForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Create info bar - var info = createInfoBar('Dynamically add an ECKD disk to a running z/VM system.'); - addE2SForm.append(info); - - addE2SForm.append(system); - addE2SForm.append('
                          '); - - // Generate tooltips - addE2SForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add disk - addE2SForm.dialog({ - title:'Add ECKD to system', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 420, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var system = $(this).find('select[name=system]').val(); - var devnum = $(this).find('input[name=devNum]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=devNum]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : "--addeckd;" + devnum, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create add Volume to system dialog - * - * @param hcp Hardware control point of node - */ -function openAddVolume2SystemDialog(hcp) { - var dialogId = 'zvmAddVolume2System'; - - // Create form to add volume - var addV2SForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Create info bar - var info = createInfoBar('Permanently add a volume to the z/VM system configuration.'); - addV2SForm.append(info); - - addV2SForm.append(system); - addV2SForm.append('
                          '); - addV2SForm.append('
                          '); - - // Generate tooltips - addV2SForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add volume - addV2SForm.dialog({ - title:'Add volume to system configuration', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 480, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var system = $(this).find('select[name=system]').val(); - var devnum = $(this).find('input[name=devNum]').val(); - var volser = $(this).find('input[name=volser]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=devNum]', 'input[name=volser]' ); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : "--addvolume;" + devnum + ";" + volser, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create remove Volume to system dialog - * - * @param hcp Hardware control point of node - */ -function openRemoveVolumeFromSystemDialog(hcp) { - var dialogId = 'zvmRemoveVolumeFromSystem'; - - // Create form to remove volume - var remVfromSForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Create info bar - var info = createInfoBar('Permanently remove a volume from the z/VM system configuration.'); - remVfromSForm.append(info); - - remVfromSForm.append(system); - remVfromSForm.append('
                          '); - remVfromSForm.append('
                          '); - - // Generate tooltips - remVfromSForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to remove volume - remVfromSForm.dialog({ - title:'Remove volume from system configuration', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 580, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var system = $(this).find('select[name=system]').val(); - var devnum = $(this).find('input[name=devNum]').val(); - var volser = $(this).find('input[name=volser]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=devNum]', 'input[name=volser]' ); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : "--removevolume;" + devnum + ";" + volser, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create add page or spool dialog - * - * @param hcp Hardware control point of node - */ -function openAddPageSpoolDialog(hcp) { - var dialogId = 'zvmAddPageSpool'; - - // Create form to add disk - var addPageSpoolForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Create info bar - var info = createInfoBar('Add a page or spool volume to be used by zVM.'); - addPageSpoolForm.append(info); - - var diskFS = $('
                          Disk
                          '); - addPageSpoolForm.append(diskFS); - var diskAttr = $('
                          '); - diskFS.append($('
                          ')); - diskFS.append(diskAttr); - - diskAttr.append(system); - diskAttr.append('
                          '); - diskAttr.append('
                          '); - diskAttr.append('
                          '); - - // Generate tooltips - addPageSpoolForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add disk - addPageSpoolForm.dialog({ - title:'Add page or spool', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var system = $(this).find('select[name=system]').val(); - var volAddr = $(this).find('input[name=volAddr]').val(); - var volLabel = $(this).find('input[name=volLabel]').val(); - var volUse = $(this).find('select[name=volUse]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=volAddr]', 'input[name=volLabel]', 'select[name=volUse]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - var pageSpoolArgs = volAddr + ";" + volLabel + ";" + volUse; - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : system, - args : '--addpagespool;' + pageSpoolArgs, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to share disk - * - * @param disks2share Disks selected in table - */ -function openShareDiskDialog(disks2share) { - // Create form to share disk - var dialogId = 'zvmShareDisk'; - var shareDiskForm = $('
                          '); - - var args = disks2share.split(';'); - var tgtHcp = args[0]; - var tgtVol = args[1]; - - if (!tgtVol || tgtVol == "undefined") - tgtVol = ""; - - // Create info bar - var info = createInfoBar('Indicate a full-pack minidisk is to be shared by the users of many real and virtual systems.'); - shareDiskForm.append(info); - - // Set region input based on those selected on table (if any) - var node = $('
                          '); - var volAddr = $('
                          '); - var shareEnable = $('
                          '); - shareDiskForm.append(node, volAddr, shareEnable); - - // Generate tooltips - shareDiskForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to delete disk - shareDiskForm.dialog({ - title:'Share disk', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var node = $(this).find('input[name=node]').val(); - var volAddr = $(this).find('input[name=volAddr]').val(); - var shareEnable = $(this).find('select[name=shareEnable]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('input[name=node]', 'input[name=volAddr]', 'select[name=shareEnable]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Remove disk from pool - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : "--sharevolume;" + volAddr + ";" + shareEnable, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Create add SCSI 2 system dialog - * - * @param hcp Hardware control point of node - */ -function openAddScsi2SystemDialog(hcp) { - var dialogId = 'zvmAddScsi2System'; - - // Create form to add disk - var addS2SForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - // Create info bar - var info = createInfoBar('Dynamically add an SCSI disk to a running z/VM system as an EDEV.'); - addS2SForm.append(info); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - var devNo = $('
                          '); - var devPathLabel = $(''); - var devPathCount = 1; - //var pathDiv = $('
                          '); - - var devPathDiv = $('
                          '); - var devPathTable = $('
                          '); - var devPathHeader = $(' FCP Device WWPN LUN'); - // Adjust header width - devPathHeader.find('th').css({ - 'width' : '120px' - }); - devPathHeader.find('th').eq(0).css({ - 'width' : '20px' - }); - var devPathBody = $(''); - var devPathFooter = $(''); - - // Create a row - var devPathRow = $(''); - - // Add blank column (remove button replacement) - devPathRow.append(''); - - // Create FCP device number input - var fcpDevNum = $(''); - devPathRow.append(fcpDevNum); - - // Create FCP WWPN input - var fcpWwpn = $(''); - devPathRow.append(fcpWwpn); - - if ($.cookie('xcat_zvms')) { - zvms = $.cookie('xcat_zvms').split(','); - var zvm; - for (var i in zvms) { - if( !zvms[i] || 0 === zvms[i].length) continue; - var args = zvms[i].split(':'); - var zvm = args[0].toLowerCase(); - var iHcp = args[1]; - } - } - - // Create FCP LUN input - var fcpLun = $(''); - devPathRow.append(fcpLun); - - devPathBody.append(devPathRow); - - var addDevPathLink = $('+ Add path'); - addDevPathLink.bind('click', function(event){ - devPathCount = devPathCount + 1; - // Create a row - var devPathRow = $(''); - - // Add remove button - var removeBtn = $('').css({ - "float": "left", - "cursor": "pointer" - }); - var col = $('').append(removeBtn); - removeBtn.bind('click', function(event) { - $(this).parent().parent().remove(); - }); - devPathRow.append(col); - - // Create FCP device number input - var fcpDevNum = $(''); - devPathRow.append(fcpDevNum); - - // Create FCP WWPN input - var fcpWwpn = $(''); - devPathRow.append(fcpWwpn); - - // Create FCP LUN input - var fcpLun = $(''); - devPathRow.append(fcpLun); - - devPathBody.append(devPathRow); - - // Generate tooltips - addS2SForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - }); - devPathFooter.append(addDevPathLink); - devPathTable.append(devPathHeader); - devPathTable.append(devPathBody); - devPathTable.append(devPathFooter); - devPathDiv.append(devPathLabel); - devPathDiv.append(devPathTable); - - var option = $('
                          '); - var persist = $('
                          '); - addS2SForm.append(system, devNo, devPathDiv, option, persist); - - // Generate tooltips - addS2SForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - addS2SForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - - // Open dialog to add disk - addS2SForm.dialog({ - title:'Add SCSI to running system', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 675, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var system = $(this).find('select[name=system]').val(); - var devNo = $(this).find('input[name=devNo]').val(); - var pathArray = ""; - jQuery('.devPath').each(function(index) { - pathArray += $(this).find('input[name=fcpDevNum]').val() + ','; - pathArray += $(this).find('input[name=fcpWwpn]').val() + ','; - pathArray += $(this).find('input[name=fcpLun]').val() + ';'; - }); - var option = $(this).find('select[name=option]').val(); - var persist = $(this).find('select[name=persist]').val(); - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=fcpDevNum]', 'select[name=option]', 'select[name=persist]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - // Show warning message - if (!ready || !pathArray) { - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : "--addscsi||" + devNo + "||" + pathArray + "||" + option + "||" + persist, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Delete a real SCSI disk - * - * @param hcp Hardware control point of node - */ -function openRemoveScsiDialog(hcp) { - var dialogId = 'zvmRemoveScsiDialog'; - // Create form to add disk - var removeScsiForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Create info bar - var info = createInfoBar('Delete a real SCSI disk'); - removeScsiForm.append(info, system); - removeScsiForm.append('
                          '); - removeScsiForm.append('
                          '); - addNicForm.append('
                          '); - - // Create drop down for NIC types - var nicType = $('
                          '); - nicType.append(''); - var nicTypeSelect = $(''); - nicTypeSelect.append('' - + '' - + '' - ); - nicType.append(nicTypeSelect); - addNicForm.append(nicType); - - // Create drop down for network types - var networkType = $('
                          '); - networkType.append(''); - var networkTypeSelect = $(''); - networkTypeSelect.append('' - + '' - + '' - ); - networkType.append(networkTypeSelect); - addNicForm.append(networkType); - var hashtable = getselectedNetworkHash(); - if (!hashtable) { - hashtable = [[]]; - setselectedNetworkHash(hashtable); - - if (typeof console == "object") { - console.log("openAddNicDialog. creating new hash[[]] table." ); - } - } - - // Create drop down for network names - var gLansQdioSelect = $(''); - var gLansHipersSelect = $(''); - var vswitchSelect = $(''); - for ( var i = 0; i < networks.length; i++) { - if( !networks[i] || 0 === networks[i].length) continue; - var network = networks[i].split(' '); - var networkOption = $(''); - if (network[0] == 'VSWITCH') { - vswitchSelect.append(networkOption); - - // Load and save specific vswitch details in global table if not there - network[2] = jQuery.trim(network[2]); // Remove new line x012 from end - if (typeof hashtable[node + '_NIC_' + network[2]] === 'undefined') { - if (typeof console == "object"){ - console.log("Calling getNetworkDetails for switch:<"+network[2]+">"); - } - ajaxrequest = 1; - getNetworkDetails(hcpNode, network[2], node + '_NIC_' + network[2], ''); - } - } else if (network[0] == 'LAN:QDIO') { - gLansQdioSelect.append(networkOption); - } else if (network[0] == 'LAN:HIPERS') { - gLansHipersSelect.append(networkOption); - } - } - - // Hide network name drop downs until the NIC type and network type is selected - // QDIO Guest LAN drop down - var guestLanQdio = $('
                          ').hide(); - guestLanQdio.append(''); - guestLanQdio.append(gLansQdioSelect); - addNicForm.append(guestLanQdio); - - // HIPERS Guest LAN drop down - var guestLanHipers = $('
                          ').hide(); - guestLanHipers.append(''); - guestLanHipers.append(gLansHipersSelect); - addNicForm.append(guestLanHipers); - - // VSWITCH drop down - var vswitch = $('
                          ').hide(); - vswitch.append(''); - vswitch.append(vswitchSelect); - - // VLAN id with Porttype - var vswitchvlan = $('
                          '); - vswitchvlan.append('
                          '); - var vswitchPorttype = $(''); - vswitchvlan.append(vswitchPorttype); - vswitchvlan.append('
                          '); - var vswitchVLANId = $(''); - vswitchvlan.append(vswitchVLANId); - - vswitch.append(vswitchvlan); - vswitchvlan.hide(); - addNicForm.append(vswitch); - - // Show network names on change - networkTypeSelect.change(function(){ - // Remove any warning messages - $(this).parent().parent().find('.ui-state-error').remove(); - var networkType = $(this).val(); - - if (typeof console == "object"){ - console.log("Entering networkTypeSelect.change"); - } - // Get NIC type and network type - var nicType = $(this).parent().parent().find('select[name=nicType]').val(); - var networkType = $(this).val(); - - // Hide network name drop downs - var guestLanQdio = $(this).parent().parent().find('select[name=nicLanQdioName]').parent(); - var guestLanHipers = $(this).parent().parent().find('select[name=nicLanHipersName]').parent(); - var vswitch = $(this).parent().parent().find('select[name=nicVSwitchName]').parent(); - var mynode = $(this).parent().parent().find('input[name=nicNode]').val(); - var showvlan = $(this).parent().parent().find('select[name=vswitchVLANporttype]').parent(); - var hashtable = getselectedNetworkHash(); - guestLanQdio.hide(); - guestLanHipers.hide(); - vswitch.hide(); - - // Show correct network name - if (networkType == 'Guest LAN' && nicType == 'QDIO') { - guestLanQdio.show(); - } else if (networkType == 'Guest LAN' && nicType == 'HiperSockets') { - guestLanHipers.show(); - } else if (networkType == 'Virtual Switch') { - if (nicType == 'QDIO') { - vswitch.show(); - // Show vlan information only if vlan aware - var switchname = $(this).parent().parent().find('select[name=nicVSwitchName]').val(); - var tokens = switchname.split(' '); - var switchkeyid = mynode + '_NIC_' + jQuery.trim(tokens[1]); - if (typeof console == "object"){ - console.log("Checking vswitch index:"+switchkeyid); - } - - // Is this a vlanaware switch, if so show the special fields - if (hashtable[switchkeyid]["vlan_awareness"] == "AWARE") { - showvlan.find('input[name=vswitchvlanid]').val(hashtable[switchkeyid]["vlan_id"]); - showvlan.find('select[name=vswitchVLANporttype]').val(hashtable[switchkeyid]["port_type"]); - showvlan.show(); - } else { - showvlan.hide(); - showvlan.find('input[name=vswitchvlanid]').val('default'); - showvlan.find('select[name=vswitchVLANporttype]').val('default'); - } - } else { - // No such thing as HIPERS VSWITCH - var warn = createWarnBar('The selected choices are not valid.'); - warn.prependTo($(this).parent().parent()); - } - } - }); - - // - // Show network names on change - // - nicTypeSelect.change(function(){ - // Remove any warning messages - $(this).parent().parent().find('.ui-state-error').remove(); - - if (typeof console == "object"){ - console.log("Entering nicTypeSelect.change"); - } - - // Get NIC type and network type - var nicType = $(this).val(); - var networkType = $(this).parent().parent().find('select[name=nicNetworkType]').val(); - var mynode = $(this).parent().parent().find('input[name=nicNode]').val(); - - // Hide network name drop downs - var guestLanQdio = $(this).parent().parent().find('select[name=nicLanQdioName]').parent(); - var guestLanHipers = $(this).parent().parent().find('select[name=nicLanHipersName]').parent(); - var vswitch = $(this).parent().parent().find('select[name=nicVSwitchName]').parent(); - var showvlan = $(this).parent().parent().find('select[name=vswitchVLANporttype]').parent(); - var hashtable = getselectedNetworkHash(); - guestLanQdio.hide(); - guestLanHipers.hide(); - vswitch.hide(); - - // Show correct network name - if (networkType == 'Guest LAN' && nicType == 'QDIO') { - guestLanQdio.show(); - } else if (networkType == 'Guest LAN' && nicType == 'HiperSockets') { - guestLanHipers.show(); - } else if (networkType == 'Virtual Switch') { - if (nicType == 'QDIO') { - vswitch.show(); - var switchname = $(this).parent().parent().find('select[name=nicVSwitchName]').val(); - var tokens = switchname.split(' '); - var switchkeyid = mynode + '_NIC_' + jQuery.trim(tokens[1]); - - if (typeof console == "object"){ - console.log("Entering nictypeselect.change. switchkey:<"+switchkeyid); - } - - // Is this a vlanaware switch, if so show the special fields - if (hashtable[switchkeyid]["vlan_awareness"] == "AWARE") { - showvlan.find('input[name=vswitchvlanid]').val(hashtable[switchkeyid]["vlan_id"]); - showvlan.find('select[name=vswitchVLANporttype]').val(hashtable[switchkeyid]["port_type"]); - showvlan.show(); - } else { - showvlan.hide(); - showvlan.find('input[name=vswitchvlanid]').val('default'); - showvlan.find('select[name=vswitchVLANporttype]').val('default'); - } - - } else { - // No such thing as HIPERS VSWITCH - var warn = createWarnBar('The selected choices are not valid.'); - warn.prependTo($(this).parent().parent()); - } - } - }); - - // - // Determine if vlanid fields need to be shown based on vswitch - // - vswitchSelect.change(function(){ - // Remove any warning messages - $(this).parent().parent().find('.ui-state-error').remove(); - - // Get vlan id division - var showvlan = $(this).parent().parent().find('select[name=vswitchVLANporttype]').parent(); - - // Get selected switch name and break it into tokens - var switchname = $(this).val(); - var tokens = switchname.split(' '); - - // Get the node we are doing this for and index for hash table - var mynode = $(this).parent().parent().find('input[name=nicNode]').val(); - - var tokens = switchname.split(' '); - var switchkeyid = mynode + '_NIC_' + jQuery.trim(tokens[1]); - var hashtable = getselectedNetworkHash(); - - if (typeof console == "object"){ - console.log("Entering vswitchselect.change. switchkey:<"+switchkeyid+">"); - } - // Is this a vlanaware switch, if so show the special fields - if (hashtable[switchkeyid]["vlan_awareness"] == "AWARE") { - $(this).find('').val(hashtable[switchkeyid]["vlan_id"]); - showvlan.find('input[name=vswitchvlanid]').val(hashtable[switchkeyid]["vlan_id"]); - showvlan.find('select[name=vswitchVLANporttype]').val(hashtable[switchkeyid]["port_type"]); - showvlan.show(); - } else { - showvlan.hide(); - showvlan.find('input[name=vswitchvlanid]').val('default'); - showvlan.find('select[name=vswitchVLANporttype]').val('default'); - } - }); - - - // Generate tooltips - addNicForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - - // Open dialog to add NIC - addNicForm.dialog({ - title:'Add NIC', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var ready = true; - var errMsg = ''; - - // Get inputs - var node = $(this).find('input[name=nicNode]').val(); - var nicType = $(this).find('select[name=nicType]').val(); - var networkType = $(this).find('select[name=nicNetworkType]').val(); - var address = $(this).find('input[name=nicAddress]').val(); - - // If inputs are not complete, show warning message - if (!node || !nicType || !networkType || !address) { - errMsg = 'Please provide a value for each missing field.
                          '; - ready = false; - } - - // If a HIPERS VSWITCH is selected, show warning message - if (nicType == 'HiperSockets' && networkType == 'Virtual Switch') { - errMsg += 'The selected choices are not valid.'; - ready = false; - } - - // If there are errors - if (!ready) { - // Show warning message - var warn = createWarnBar(errMsg); - warn.prependTo($(this)); - } else { - // Add guest LAN - if (networkType == 'Guest LAN') { - var temp; - if (nicType == 'QDIO') { - temp = $(this).find('select[name=nicLanQdioName]').val().split(' '); - } else { - temp = $(this).find('select[name=nicLanHipersName]').val().split(' '); - } - - var lanOwner = temp[0]; - var lanName = temp[1]; - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--addnic;' + address + ';' + nicType + ';3', - msg : 'node=' + node + ';addr=' + address + ';lan=' - + lanName + ';owner=' + lanOwner - }, - success : connect2GuestLan - }); - } - - // Add virtual switch - else if (networkType == 'Virtual Switch' && nicType == 'QDIO') { - var temp = $(this).find('select[name=nicVSwitchName]').val().split(' '); - var vswitchName = jQuery.trim(temp[1]); - var switchkeyid = node + '_NIC_' + vswitchName; - var hashtable = getselectedNetworkHash(); - var awareornot = hashtable[switchkeyid]["vlan_awareness"]; - var porttype = $(this).find('select[name=vswitchVLANporttype]').val(); - var lanid = $(this).find('input[name=vswitchvlanid]').val(); - - // Pass additional lanid data in msg for grant use by connect2VSwitch - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--addnic;' + address + ';' + nicType + ';3', - msg : 'node=' + node + ';addr=' + address + ';vsw=' - + vswitchName + ';vlanaware=' + awareornot + ';porttype=' - + porttype + ';lanid=' + lanid - }, - - success : connect2VSwitch - }); - } - - // Increment node process - incrementNodeProcess(node); - - // Show loader - $('#' + node + 'StatusBarLoader').show(); - $('#' + node + 'StatusBar').show(); - - // Close dialog - $(this).dialog( "close" ); - } // End of else - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); - // Make sure ajax is done before putting up dialog - $(document).ajaxStop(function() { - //Remove loading vswitch gif status bar - statBar.hide(); - }); - if (ajaxrequest == 0) { - //Remove loading vswitch gif status bar - statBar.hide(); - } - -} - -/** - * Create add vSwitch/VLAN dialog - * - * @param hcp Hardware control point of node - */ -function openAddVswitchVlanDialog(hcp) { - var dialogId = 'zvmAddVswitchVlan'; - - // Create form to add disk - var addVswitchForm = $('
                          '); - - // Create info bar - var info = createInfoBar('Create a virtual switch or virtual network LAN.'); - - var netFS = $('
                          '); - var netLegend = $('Network'); - netFS.append(netLegend); - - var typeFS = $('
                          ').hide(); - var typeLegend = $('Network'); - typeFS.append(typeLegend); - addVswitchForm.append(info, netFS, typeFS); - - var netAttr = $('
                          '); - netFS.append($('
                          ')); - netFS.append(netAttr); - - var networkTypeDiv = $('
                          '); - var networkType = $('
                          '); - networkTypeDiv.append(networkType) - netAttr.append(networkTypeDiv); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - netAttr.append(system); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - var typeAttr = $('
                          '); - typeFS.append($('
                          ')); - typeFS.append(typeAttr); - - // Create vSwitch parameters - var vswitchOptions = $('
                          ').hide(); - vswitchOptions.append($('
                          ')); - vswitchOptions.append($('
                          ')); - vswitchOptions.append($('
                          ')); - - // Create an advanced link to configure optional network settings - var advancedLnk = $(''); - vswitchOptions.append(advancedLnk); - var advanced = $('
                          ').hide(); - vswitchOptions.append(advanced); - - // Show IP address and hostname inputs on-click - advancedLnk.click(function() { - advanced.toggle(); - }); - - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - advanced.append($('
                          ')); - - // Create VLAN parameters - var vlanOptions = $('
                          ').hide(); - vlanOptions.append($('
                          ')); - vlanOptions.append($('
                          ')); - vlanOptions.append($('
                          ')); - vlanOptions.append($('
                          ')); - - typeAttr.append(vswitchOptions, vlanOptions); - - networkType.change(function() { - typeFS.show(); - if ($(this).val() == "vswitch") { - typeFS.find("legend").text("vSwitch"); - vswitchOptions.show(); - vlanOptions.hide(); - } else if ($(this).val() == "vlan") { - typeFS.find("legend").text("VLAN"); - vswitchOptions.hide(); - vlanOptions.show(); - } else { - typeFS.find("legend").text(""); - vswitchOptions.hide(); - vlanOptions.hide(); - typeFS.hide(); - } - }); - - // Generate tooltips - addVswitchForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add vSwitch or VLAN - addVswitchForm.dialog({ - title:'Add vSwitch or VLAN', - modal: true, - close: function() { - $(this).remove(); - }, - width: 750, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var networkType = $(this).find('select[name=networkType]').val(); - if (networkType == "vswitch") { - var networkArgs = "--addvswitch;"; - var system = $(this).find('select[name=system]').val(); - var switchName = $(this).find('input[name=switchName]').val(); - var deviceAddress = $(this).find('input[name=deviceAddress]').val(); - var portName = switchName; - var controllerName = $(this).find('input[name=controllerName]').val(); - var connection = $(this).find('select[name=connection]').val(); - var queueMemoryLimit = $(this).find('input[name=queueMemoryLimit]').val(); - var routingValue = $(this).find('select[name=routingValue]').val(); - var transportType = $(this).find('select[name=transportType]').val(); - var vlanId = $(this).find('input[name=vlanId]').val(); - var portType = $(this).find('select[name=vswitchVLANporttype]').val(); - var updateSysConfig = $(this).find('select[name=updateSysConfig]').val(); - var gvrp = $(this).find('select[name=gvrp]').val(); - var nativeVlanId = $(this).find('input[name=nativeVlanId]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=switchName]', 'input[name=deviceAddress]', 'input[name=controllerName]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - // Show warning message - if (!ready) { - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - if (switchName) - networkArgs += switchName + ";"; - if (deviceAddress) - networkArgs += deviceAddress + ";"; - if (portName) - networkArgs += portName + ";"; - if (controllerName) - networkArgs += controllerName + ";"; - - // Optional parameters - if (connection) - networkArgs += connection + ";"; - if (queueMemoryLimit) - networkArgs += queueMemoryLimit + ";"; - if (routingValue) - networkArgs += routingValue + ";"; - if (transportType) - networkArgs += transportType + ";"; - if (vlanId) - networkArgs += vlanId + ";"; - if (portType) - networkArgs += portType + ";"; - if (updateSysConfig) - networkArgs += updateSysConfig + ";"; - if (gvrp) - networkArgs += gvrp + ";"; - if (nativeVlanId) - networkArgs += nativeVlanId + ";"; - networkArgs = networkArgs.substring(0, networkArgs.length - 1); - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : networkArgs, - msg : dialogId - }, - - success : updateResourceDialog - }); - } else if (networkType == "vlan") { - var networkArgs = "--addvlan;"; - var system = $(this).find('select[name=system]').val(); - var vlanName = $(this).find('input[name=vlanName]').val(); - var vlanOwner = $(this).find('input[name=vlanOwner]').val(); - var vlanType = $(this).find('select[name=vlanType]').val(); - var vlanTransport = $(this).find('select[name=vlanTransport]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=vlanName]', 'input[name=vlanOwner]', 'select[name=vlanType]', 'select[name=vlanTransport]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - // Show warning message - if (!ready) { - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Ethernet Hipersockets are not supported - if (vlanTransport == "2") { - var warn = createWarnBar('Ethernet Hipersockets are not supported'); - warn.prependTo($(this)); - return; - } - - networkArgs += vlanName + ";"; - networkArgs += vlanOwner + ";"; - networkArgs += vlanType + ";"; - networkArgs += vlanTransport; - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : networkArgs, - msg : dialogId - }, - - success : updateResourceDialog - }); - } // End of else if - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to delete network - * - * @param node type name for removing network - */ -function openRemoveVswitchVlanDialog(networkList) { - var names = ''; - for (var i in networkList) { - var networkArgs = networkList[i].split(';'); - networkArgs[2] = jQuery.trim(networkArgs[2]); - names += networkArgs[2] + ', '; - } - names = names.substring(0, names.length - 2); // Delete last two characters - - var confirmDialog = $('

                          Are you sure you want to remove ' + names + '?

                          '); - confirmDialog.dialog({ - title: "Confirm", - modal: true, - width: 400, - buttons: { - "Ok": function() { - for (var i in networkList) { - var networkArgs = networkList[i].split(';'); - var node = networkArgs[0]; - var type = networkArgs[1]; - var name = jQuery.trim(networkArgs[2]); - var owner = networkArgs[3]; - - if (type.indexOf("VSWITCH") != -1) { - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : node, - args : '--removevswitch;' + name, - msg : '' - }, - - success: function(data) { - var infoMsg; - - // Create info message - if (jQuery.isArray(data.rsp)) { - infoMsg = ''; - for (var i in data.rsp) { - infoMsg += data.rsp[i] + '
                          '; - } - } else { - infoMsg = data.rsp; - } - - openDialog("info", infoMsg); - } - }); - } else if (type.indexOf("LAN") != -1) { - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : node, - args : '--removevlan;' + name + ';' + owner, - msg : '' - }, - - success: function(data) { - var infoMsg; - - // Create info message - if (jQuery.isArray(data.rsp)) { - infoMsg = ''; - for (var i in data.rsp) { - infoMsg += data.rsp[i] + '
                          '; - } - } else { - infoMsg = data.rsp; - } - - openDialog("info", infoMsg); - } - }); - } - } - $(this).dialog("close"); - }, - "Cancel": function() { - $(this).dialog("close"); - } - } - }); -} - -/** - * Remove processor - * - * @param node Node where processor is attached - * @param address Virtual address of processor - */ -function removeProcessor(node, address) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--removeprocessor;' + address, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - $('#' + node + 'StatusBarLoader').show(); - $('#' + node + 'StatusBar').show(); -} - -/** - * Remove disk - * - * @param node Node where disk is attached - * @param address Virtual address of disk - */ -function removeDisk(node, address) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--removedisk;' + address, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - $('#' + node + 'StatusBarLoader').show(); - $('#' + node + 'StatusBar').show(); -} - -/** - * Remove zFCP device - * - * @param node Node where disk is attached - * @param address Virtual address of zFCP device - * @param wwpn World wide port name of zFCP device - * @param lun Logical unit number of zFCP device - */ -function removeZfcp(node, address, wwpn, lun) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--removezfcp||' + address + '||' + wwpn + '||' + lun, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - $('#' + node + 'StatusBarLoader').show(); - $('#' + node + 'StatusBar').show(); -} - -/** - * Remove NIC - * - * @param node Node where NIC is attached - * @param address Virtual address of NIC - */ -function removeNic(node, nic) { - var args = nic.split('.'); - var address = args[0]; - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--removenic;' + address, - msg : node - }, - - success : updateZNodeStatus - }); - - // Increment node process - incrementNodeProcess(node); - - // Show loader - $('#' + node + 'StatusBarLoader').show(); - $('#' + node + 'StatusBar').show(); -} - -/** - * Set a cookie for the network names of a given node - * - * @param data Data from HTTP request - */ -function setNetworkCookies(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - var node = data.msg; - var networks = data.rsp[0].split(node + ': '); - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_' + node + 'networks', networks, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Get contents of each disk pool - * - * @param data HTTP request data - */ -function getDiskPool(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Invalid") == -1 && data.rsp[0].indexOf("Error") == -1) { - var hcp = data.msg; - var pools = data.rsp[0].split(hcp + ': '); - - // Get contents of each disk pool - for (var i in pools) { - if (pools[i]) { - pools[i] = jQuery.trim(pools[i]); - - // Get used space - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--diskpool;' + pools[i] + ';used', - msg : 'hcp=' + hcp + ';pool=' + pools[i] + ';stat=used' - }, - - success : loadDiskPoolTable - }); - - // Get free space - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--diskpool;' + pools[i] + ';free', - msg : 'hcp=' + hcp + ';pool=' + pools[i] + ';stat=free' - }, - - success : loadDiskPoolTable - }); - } // End of if - } // End of for - } else { - // Display any errors in info bar - if (data.rsp.length) { - var panelId = 'zvmDiskResource'; - var info = $('#' + panelId).find('.ui-state-highlight'); - // If there is no info bar, create info bar - if (!info.length) { - info = createInfoBar("Error: "+data.rsp[0]); - $('#' + panelId).append(info); - } else { - info.append("
                          Error: "+data.rsp[0]); - } - } - // Load empty table - loadDiskPoolTable(""); // Must pass something - } -} - -/** - * Get contents of each zFCP pool - * - * @param data HTTP request data - */ -function getZfcpPool(data) { - if (typeof console == "object"){ - console.log("Entering getZfcpPool."); - } - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Invalid") == -1 && data.rsp[0].indexOf("Error") == -1) { - var hcp = data.msg; - var pools = data.rsp[0].split(hcp + ': '); - zhcpQueryCountForZfcps = 0; - // Get contents of each disk pool - for (var i in pools) { - pools[i] = jQuery.trim(pools[i]); - if (pools[i]) { - - // Query used and free space - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--zfcppool;' + pools[i] + ';all', - msg : 'hcp=' + hcp + ';pool=' + pools[i] - }, - success : loadZfcpPoolTable - }); - } // End of if - } // End of for - } else { - // Display any errors in info bar - if (data.rsp.length) { - var panelId = 'zfcpResource'; - var info = $('#' + panelId).find('.ui-state-highlight'); - // If there is no info bar, create info bar - if (!info.length) { - info = createInfoBar("Error: "+data.rsp[0]); - $('#' + panelId).append(info); - } else { - info.append("
                          Error: "+data.rsp[0]); - } - } - // Load empty table - loadZfcpPoolTable(""); // Must pass something - } -} - -/** - * Get details of each network - * - * @param data HTTP request data - */ -function getNetwork(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Invalid") == -1 && data.rsp[0].indexOf("Error") == -1) { - var hcp = data.msg; - var networks = data.rsp[0].split(hcp + ': '); - if (typeof console == "object"){ - console.log("Entering getNetwork data:<"+networks+">"); - } - - // Loop through each network - for ( var i = 1; i < networks.length; i++) { - if( !networks[i] || 0 === networks[i].length) continue; - var args = networks[i].split(' '); - var type = args[0]; - var name = args[2]; - name = name.replace(/\n/g,''); - - // Get network details - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcp, - args : '--getnetwork;' + name + ';' + type, - msg : 'hcp=' + hcp + ';type=' + type + ';network=' + name - }, - - success : loadNetworkTable - }); - } // End of for - } // End of if - else { - if (data.rsp.length) { - var panelId = 'zvmNetworkResource'; - var info = $('#' + panelId).find('.ui-state-highlight'); - // If there is no info bar, create info bar - if (!info.length) { - info = createInfoBar("Error: "+data.rsp[0]); - $('#' + panelId).append(info); - } else { - info.append("
                          Error: "+data.rsp[0]); - } - } - // Normally load empty table, but not for networks - } -} - -/** - * Load disk pool contents into a table - * - * @param data HTTP request data - */ -function loadDiskPoolTable(data) { - // Remove loader if all hcps queried - var panelId = 'zvmDiskResource'; - if (!zhcpQueryCountForDisks) { - $('#' + panelId).find('img[src="images/loader.gif"]').remove(); - } - - var hcp2zvm = new Object(); - var args, hcp, pool, stat, tmp; - if (data && typeof data.rsp != "undefined") { - // Do not continue if the call failed - if (!data.rsp.length && data.rsp[0].indexOf("Failed") > 0 && data.rsp[0].indexOf("Error") > 0) { - return; - } - - // Obtain mapping for zHCP to zVM system - hcp2zvm = getHcpZvmHash(); - - args = data.msg.split(';'); - hcp = args[0].replace('hcp=', ''); - pool = args[1].replace('pool=', ''); - stat = jQuery.trim(args[2].replace('stat=', '')); - tmp = data.rsp[0].split(hcp + ': '); - } else { - // Provide empty values so the table will be generated - hcp = ''; - pool = ''; - stat = ''; - tmp = new Array(); - } - - // Resource tab ID - var info = $('#' + panelId).find('.ui-state-highlight'); - // If there is no info bar - if (!info.length) { - // Create info bar - info = createInfoBar('Below are disks that are defined in the EXTENT CONTROL file.'); - $('#' + panelId).append(info); - } - - // Get datatable - var tableId = 'zDiskDataTable'; - var dTable = getDiskDataTable(); - if (!dTable) { - // Create a datatable - var table = new DataTable(tableId); - // Resource headers: volume ID, device type, start address, and size - table.init( [ '', 'z/VM', 'Pool', 'Status', 'Volume', 'Device type', 'Starting address', 'Size' ]); - - // Append datatable to panel - $('#' + panelId).append(table.object()); - - // Turn into datatable - dTable = $('#' + tableId).dataTable({ - 'iDisplayLength': 50, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - setDiskDataTable(dTable); - } - - // Skip index 0 and 1 because it contains nothing - for (var i = 2; i < tmp.length; i++) { - tmp[i] = jQuery.trim(tmp[i]); - var diskAttrs = tmp[i].split(' '); - var key = hcp2zvm[hcp] + "-" + pool + "-" + diskAttrs[0]; - var type = diskAttrs[1]; - - // Calculate disk size - var size; - if (type.indexOf('3390') != -1) { - size = convertCylinders2Gb(parseInt(diskAttrs[3])); - } else if (type.indexOf('9336') != -1) { - size = convertBlocks2Gb(parseInt(diskAttrs[3])) - } else { - size = 0; - } - dTable.fnAddData( [ '', hcp2zvm[hcp], pool, stat, diskAttrs[0], type, diskAttrs[2], diskAttrs[3] + " (" + size + "G)" ]); - } - - // Create actions menu - if (!$('#zvmDiskResourceActions').length) { - // Empty filter area - $('#' + tableId + '_length').empty(); - - // Add disk to pool - var addLnk = $('Add'); - addLnk.bind('click', function(event){ - openAddDisk2PoolDialog(); - }); - - // Delete disk from pool - var removeLnk = $('Remove'); - removeLnk.bind('click', function(event){ - var disks = getNodesChecked(tableId); - openRemoveDiskFromPoolDialog(disks); - }); - - // Refresh table - var refreshLnk = $('Refresh'); - refreshLnk.bind('click', function(event){ - $('#zvmDiskResource').empty().append(createLoader('')); - setDiskDataTable(''); - - // Create a array for hardware control points - var hcps = new Array(); - if ($.cookie('xcat_hcp').indexOf(',') > -1) - hcps = $.cookie('xcat_hcp').split(','); - else - hcps.push($.cookie('xcat_hcp')); - - zhcpQueryCountForDisks = hcps.length; - // Query the disk pools for each - for (var i in hcps) { - if( !hcps[i] || 0 === hcps[i].length) continue; - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcps[i], - args : '--diskpoolnames', - msg : hcps[i] - }, - - success : getDiskPool - }); - zhcpQueryCountForDisks--; - } - }); - - // Add ECKD to system - var addEckdLnk = $('Add ECKD'); - addEckdLnk.bind('click', function(event){ - openAddEckd2SystemDialog(hcp); - }); - - // Add Page or Spool - var addPageSpoolLnk = $('Add page/spool') - addPageSpoolLnk.bind('click', function(event){ - openAddPageSpoolDialog(hcp); - }); - - // Add EDEV to system - var addEdevLnk = $('Add EDEV'); - addEdevLnk.bind('click', function(event){ - openAddScsi2SystemDialog(hcp); - }); - - // Remove EDEV - var removeEdevLnk = $('Remove EDEV'); - removeEdevLnk.bind('click', function(event){ - openRemoveScsiDialog(hcp); - }); - - // Indicate disk is to be shared with various users - var shareLnk = $('Share disk'); - shareLnk.bind('click', function(event){ - var disks = getNodesChecked(tableId); - openShareDiskDialog(disks); - }); - - // Add Volume to system - var addVolumeLnk = $('Add volume to system'); - addVolumeLnk.bind('click', function(event){ - openAddVolume2SystemDialog(hcp); - }); - - // Remove Volume from system - var removeVolumeLnk = $('Remove volume from system'); - removeVolumeLnk.bind('click', function(event){ - openRemoveVolumeFromSystemDialog(hcp); - }); - - // Advanced menu - var advancedLnk = 'Advanced'; - var advancedMenu = createMenu([addEckdLnk, addPageSpoolLnk, addEdevLnk, removeEdevLnk, addVolumeLnk, removeVolumeLnk, shareLnk]); - - // Create action bar - var actionBar = $('
                          ').css("width", "450px"); - - // Create an action menu - var actionsMenu = createMenu([refreshLnk, addLnk, removeLnk, [advancedLnk, advancedMenu]]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + tableId + '_length').prepend(menuDiv); - $('#' + tableId + '_length').css({ - 'padding': '0px', - 'width': '500px' - }); - $('#' + tableId + '_filter').css('padding', '10px'); - menuDiv.append(actionBar); - } - - // Resize accordion - $('#zvmResourceAccordion').accordion('resize'); -} - -/** - * Load zFCP pool contents into a table - * - * @param data HTTP request data - */ -function loadZfcpPoolTable(data) { - if (typeof console == "object"){ - console.log("Entering loadZfcpPoolTable."); - } - // Delete loader if last one - var panelId = 'zfcpResource'; - if (zhcpQueryCountForZfcps <= 0) { - $('#' + panelId).find('img[src="images/loader.gif"]').remove(); - } - - var hcp2zvm = new Object(); - var args, hcp, pool, tmp; - - // Resource tab ID - var info = $('#' + panelId).find('.ui-state-highlight'); - - // Is there any data passed? Process if some - if (typeof data.rsp != "undefined") { - // Do not continue if no data to add - if (!data.rsp.length) { - if (typeof console == "object"){ - console.log("data.rsp.length is 0."); - } - // If there is no info bar, create info bar - var msgError = '
                          Unexpected, no data returned on the lsvm --zfcppool call.'; - if (!info.length) { - info = createInfoBar(msgError); - $('#' + panelId).append(info); - } else { - info.append(msgError); - } - return; - } - if (data.rsp[0].indexOf("Failed") > 0 || data.rsp[0].indexOf("Error") > 0) { - if (typeof console == "object"){ - console.log("Failed on lsvm call for --zfcppool"); - } - var msgError = '
                          Error: Error on call to check zfcp pools: '+ data.rsp[0]; - // If there is no info bar, create info bar - if (!info.length) { - info = createInfoBar(msgError); - $('#' + panelId).append(info); - } else { - info.append(msgError); - } - return; - } - - // Obtain mapping for zHCP to zVM system - hcp2zvm = getHcpZvmHash(); - - args = data.msg.split(';'); - hcp = args[0].replace('hcp=', ''); - pool = args[1].replace('pool=', ''); - tmp = data.rsp[0].split(hcp + ': '); - } else { - // Provide empty values so the table will be generated - if (typeof console == "object"){ - console.log("Creating empty zfcp pool table."); - } - hcp = ''; - pool = '' - tmp = new Array(); - } - - // If there is no info bar, create info bar - if (!info.length) { - info = createInfoBar('Below are devices that are defined internally in the zFCP pools.'); - $('#' + panelId).append(info); - } - - // Get datatable - var tableId = 'zFcpDataTable'; - var dTable = getZfcpDataTable(); - if (!dTable) { - // Create a datatable - var table = new DataTable(tableId); - // Resource headers: status, WWPN, LUN, size, owner, channel, tag - table.init( [ '', 'z/VM', 'Pool', 'Status', 'Port name', 'Unit number', 'Size', 'Range', 'Owner', 'Channel', 'Tag' ]); - - // Append datatable to panel - $('#' + panelId).append(table.object()); - - // Turn into datatable - dTable = $('#' + tableId).dataTable({ - 'iDisplayLength': 50, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - setZfcpDataTable(dTable); - } - if ((typeof data.rsp != "undefined") && (data.rsp.length > 0)) { - // Skip index 0 and 1 because it contains nothing - var key = ""; - for (var i = 2; i < tmp.length; i++) { - tmp[i] = jQuery.trim(tmp[i]); - var diskAttrs = tmp[i].split(','); - diskAttrs[0] = diskAttrs[0].toLowerCase(); - // Key contains row data to be returned when the checkbox is selected - var key = hcp2zvm[hcp] + '-' + pool + '-' + diskAttrs[2] + '-' + diskAttrs[1]; - dTable.fnAddData( [ '', hcp2zvm[hcp], pool, diskAttrs[0], diskAttrs[1], diskAttrs[2], diskAttrs[3], diskAttrs[4], diskAttrs[5], diskAttrs[6], diskAttrs[7] ]); - } - } - // Create actions menu - if (!$('#zFcpResourceActions').length) { - // Empty filter area - $('#' + tableId + '_length').empty(); - - // Add disk to pool - var addLnk = $('Add'); - addLnk.bind('click', function(event){ - openAddZfcp2PoolDialog(); - }); - - // Delete disk from pool - var removeLnk = $('Remove'); - removeLnk.bind('click', function(event){ - if (typeof console == "object"){ - console.log("Remove button clicked for tableId:"+tableId); - } - var disks = getNodesChecked(tableId); - openRemoveZfcpFromPoolDialog(disks); - }); - - // Refresh table - var refreshLnk = $('Refresh'); - refreshLnk.bind('click', function(event){ - $('#zfcpResource').empty().append(createLoader('')); - setZfcpDataTable(''); - - // Create a array for hardware control points - var hcps = new Array(); - if ($.cookie('xcat_hcp').indexOf(',') > -1) - hcps = $.cookie('xcat_hcp').split(','); - else - hcps.push($.cookie('xcat_hcp')); - - // Query the disk pools for each - zhcpQueryCountForZfcps = hcps.length; - for (var i in hcps) { - if( !hcps[i] || 0 === hcps[i].length) continue; - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcps[i], - args : '--zfcppoolnames', - msg : hcps[i] - }, - - success : getZfcpPool - }); - zhcpQueryCountForZfcps--; - } - }); - // Create action bar - var actionBar = $('
                          ').css("width", "450px"); - - // Create an action menu - var actionsMenu = createMenu([addLnk, removeLnk, refreshLnk]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + tableId + '_length').prepend(menuDiv); - $('#' + tableId + '_length').css({ - 'padding': '0px', - 'width': '500px' - }); - $('#' + tableId + '_filter').css('padding', '10px'); - menuDiv.append(actionBar); - } - - // Resize accordion - $('#zvmResourceAccordion').accordion('resize'); -} - -/** - * Open dialog to remove disk from pool - * - * @param disks2remove Disks selected in table - */ -function openRemoveDiskFromPoolDialog(disks2remove) { - // Create form to delete disk from pool - var dialogId = 'zvmDeleteDiskFromPool'; - var deleteDiskForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var disks = new Array(); - if (disks2remove.indexOf(',') > -1) - disks = disks2remove.split(','); - else - disks.push(disks2remove); - - // Pick the last zHCP and pool it finds - var args, tgtHcp = "", tgtPool = "", tgtVol = ""; - for (var i in disks) { - if( !disks[i] || 0 === disks[i].length) continue; - args = disks[i].split('-'); - tgtHcp = args[0]; - tgtPool = args[1]; - tgtVol += args[2] + ','; - } - - // Strip out last comma - tgtVol = tgtVol.slice(0, -1); - - // Create info bar - var info = createInfoBar('Remove a disk from a disk pool defined in the EXTENT CONTROL.'); - deleteDiskForm.append(info); - var action = $('
                          '); - var actionSelect = $(''); - action.append(actionSelect); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - // Set region input based on those selected on table (if any) - var region = $('
                          '); - var group = $('
                          '); - deleteDiskForm.append(action, system, region, group); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - systemSelect.val(tgtHcp); - - actionSelect.change(function() { - if ($(this).val() == '1' || $(this).val() == '3') { - region.show(); - group.hide(); - } else if ($(this).val() == '2') { - region.show(); - group.show(); - } else if ($(this).val() == '7') { - region.val('FOOBAR'); - region.hide(); - group.show(); - } - }); - - // Generate tooltips - deleteDiskForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to delete disk - deleteDiskForm.dialog({ - title:'Delete disk from pool', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var action = $(this).find('select[name=action]').val(); - var system = $(this).find('select[name=system]').val(); - var region = $(this).find('input[name=region]').val(); - var group = $(this).find('input[name=group]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'select[name=action]', 'input[name=region]', 'input[name=group]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - var args; - if (action == '2' || action == '7') - args = region + ';' + group; - else - args = region; - - // Remove disk from pool - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : '--removediskfrompool;' + action + ';' + args, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to add disk to pool - */ -function openAddDisk2PoolDialog() { - // Create form to add disk to pool - var dialogId = 'zvmAddDisk2Pool'; - var addDiskForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - // Create info bar - var info = createInfoBar('Add a disk to a disk pool defined in the EXTENT CONTROL. The disk has to already be attached to SYSTEM.'); - addDiskForm.append(info); - var action = $('
                          '); - var actionSelect = $(''); - action.append(actionSelect); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - var volume = $('
                          '); - var group = $('
                          '); - addDiskForm.append(action, system, volume, group); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Generate tooltips - addDiskForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add disk - addDiskForm.dialog({ - title:'Add disk to pool', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get inputs - var action = $(this).find('select[name=action]').val(); - var system = $(this).find('select[name=system]').val(); - var volume = $(this).find('input[name=volume]').val(); - var group = $(this).find('input[name=group]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'select[name=action]', 'input[name=volume]', 'input[name=group]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - var args; - if (action == '4') - args = volume + ';' + volume + ';' + group; - else - args = volume + ';' + group; - - // Add disk to pool - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : '--adddisk2pool;' + action + ';' + args, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to remove zFCP from pool - * - * @param devices2remove Comman separated devices selected in table - */ -function openRemoveZfcpFromPoolDialog(devices2remove) { - // Create form to delete device from pool - var dialogId = 'zvmDeleteZfcpFromPool'; - var deleteDiskForm = $('
                          '); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - // Verify disks are in the same zFCP pool - var devices = devices2remove.split(','); - if (typeof console == "object"){ - console.log("Entering openRemoveZfcpFromPoolDialog. Device to remove:<"+devices2remove+">"); - } - var tmp, tgtPool, tgtHcp; - var tgtPort = ""; - var tgtUnitNo = ""; - for (var i in devices) { - if( !devices[i] || 0 === devices[i].length) continue; - tmp = devices[i].split('-'); - - if (tgtPool && tmp[1] != tgtPool) { - openDialog("warn", "Please select devices in the same zFCP"); - return; - } else { - tgtPool = tmp[1]; - } - - tgtHcp = tmp[0]; // Assume it is just one zHCP. Otherwise, this cannot be done on multiple zHCPs. - tgtUnitNo += tmp[2] + ","; - tgtPort = tmp[3]; - } - - // Strip out last comma - tgtUnitNo = tgtUnitNo.slice(0, -1); - - // Create info bar - var info = createInfoBar('Remove a zFCP device that is defined in a zFCP pool.'); - deleteDiskForm.append(info); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - var pool = $('
                          '); - var unitNo = $('
                          '); - var portName = $('
                          '); - deleteDiskForm.append(system, pool, unitNo, portName); - - // Append options for hardware control points - //systemSelect.append($('')); - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - systemSelect.val(tgtHcp); - - // Generate tooltips - deleteDiskForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to delete device - deleteDiskForm.dialog({ - title:'Delete device from pool', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var system = $(this).find('select[name=system]').val(); - var pool = $(this).find('input[name=zfcpPool]').val(); - var unitNo = $(this).find('input[name=zfcpUnitNo]').val(); - var portName = $(this).find('input[name=zfcpPortName]').val(); - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=zfcpPool]', 'input[name=zfcpUnitNo]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - var args = '--removezfcpfrompool;' + pool + ';' + unitNo; - if (portName) { - args += ';' + portName; - } - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : system, - args : args, - msg : dialogId - }, - - success : function(data) { - updateResourceDialog(data); - } - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to add zFCP to pool - */ -function openAddZfcp2PoolDialog() { - // Create form to add disk to pool - var dialogId = 'zvmAddDisk2Pool'; - var addDiskForm = $('
                          '); - var info = createInfoBar('Add a device to a zFCP pool defined in xCAT.'); - addDiskForm.append(info); - - // Obtain mapping for zHCP to zVM system - var hcp2zvm = new Object(); - hcp2zvm = getHcpZvmHash(); - - var system = $('
                          '); - var systemSelect = $(''); - system.append(systemSelect); - - var pool = $('
                          '); - var status = $('
                          '); - var portName = $('
                          '); - var unitNo = $('
                          '); - var size = $('
                          '); - var range = $('
                          '); - var owner = $('
                          '); - addDiskForm.append(system, pool, status, portName, unitNo, size, range, owner); - - // Create a array for hardware control points - //systemSelect.append($('')); - // Append options for hardware control points - for (var hcp in hcp2zvm) { - systemSelect.append($('')); - } - - // Generate tooltips - addDiskForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add disk - addDiskForm.dialog({ - title:'Add device to pool', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - // Delete any warning messages - $(this).find('.ui-state-error').remove(); - - var tgtSystem = $(this).find('select[name=system]').val(); - var tgtPool = $(this).find('input[name=zfcpPool]').val(); - var tgtStatus = $(this).find('select[name=zfcpStatus]').val(); - var tgtPortName = $(this).find('input[name=zfcpPortName]').val(); - var tgtUnitNo = $(this).find('input[name=zfcpUnitNo]').val(); - var tgtSize = $(this).find('input[name=zfcpSize]').val(); - var tgtRange = $(this).find('input[name=zfcpRange]').val(); - - // Device owner is optional - var tgtOwner = ""; - if ($(this).find('input[name=zfcpOwner]').val()) { - tgtOwner = $(this).find('input[name=zfcpOwner]').val(); - } - - // If inputs are not complete, show warning message - var ready = true; - var args = new Array('select[name=system]', 'input[name=zfcpPool]', 'select[name=zfcpStatus]', 'input[name=zfcpPortName]', 'input[name=zfcpUnitNo]'); - for (var i in args) { - if (!$(this).find(args[i]).val()) { - $(this).find(args[i]).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); - } - } - - if (!ready) { - // Show warning message - var warn = createWarnBar('Please provide a value for each required field.'); - warn.prependTo($(this)); - return; - } - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // zFCP range and owner are optional - var args = '--addzfcp2pool||' + tgtPool + '||' + tgtStatus + '||"' + tgtPortName + '"||' + tgtUnitNo + '||' + tgtSize; - if (tgtRange) { - args += '||' + tgtRange; - } if (tgtOwner) { - args += '||' + tgtOwner; - } - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chhypervisor', - tgt : tgtSystem, - args : args, - msg : dialogId - }, - - success : updateResourceDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Update resource dialog - * - * @param data HTTP request data - */ -function updateResourceDialog(data) { - var dialogId = data.msg; - var infoMsg; - - // Create info message - if (jQuery.isArray(data.rsp)) { - infoMsg = ''; - for (var i in data.rsp) { - infoMsg += data.rsp[i] + '
                          '; - } - } else { - infoMsg = data.rsp; - } - - // Create info bar with close button - var infoBar = $('
                          ').css('margin', '5px 0px'); - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px' - }); - - // Create close button to close info bar - var close = $('').css({ - 'display': 'inline-block', - 'float': 'right' - }).click(function() { - $(this).parent().remove(); - }); - - var msg = $('
                          ' + infoMsg + '
                          ').css({ - 'display': 'inline-block', - 'width': '90%' - }); - - infoBar.append(icon, msg, close); - infoBar.prependTo($('#' + dialogId)); -} - -/** - * Select all checkboxes in the datatable - * - * @param event Event on element - * @param obj Object triggering event - */ -function selectAllDisk(event, obj) { - // This will ascend from - var tableObj = obj.parents('.datatable'); - var status = obj.attr('checked'); - tableObj.find(' :checkbox').attr('checked', status); - - // Handle datatable scroll - tableObj = obj.parents('.dataTables_scroll'); - if (tableObj.length) { - tableObj.find(' :checkbox').attr('checked', status); - } - - event.stopPropagation(); -} - -/** - * Load network details into a table - * - * @param data HTTP request data - */ -function loadNetworkTable(data) { - // Remove loader if last one - var panelId = 'zvmNetworkResource'; - if (!zhcpQueryCountForNetworks) { - $('#' + panelId).find('img[src="images/loader.gif"]').remove(); - } - - // Get zVM host names - if (!$.cookie('xcat_zvms')) { - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - async: false, - data : { - cmd : 'webportal', - tgt : '', - args : 'lszvm', - msg : '' - }, - - success : function(data) { - setzVMCookies(data); - } - }); - } - - var zvms = $.cookie('xcat_zvms').split(','); - var hcp2zvm = new Object(); - var args, zvm, iHcp, tmp; - for (var i in zvms) { - if( !zvms[i] || 0 === zvms[i].length) continue; - args = zvms[i].split(':'); - zvm = args[0].toLowerCase(); - - if (args[1].indexOf('.') != -1) { - tmp = args[1].split('.'); - iHcp = tmp[0]; - } else { - iHcp = args[1]; - } - - hcp2zvm[iHcp] = zvm; - } - - var args = data.msg.split(';'); - var hcp = args[0].replace('hcp=', ''); - var type = args[1].replace('type=', ''); - var name = jQuery.trim(args[2].replace('network=', '')); - tmp = data.rsp[0].split(hcp + ': '); - - // Resource tab ID - var info = $('#' + panelId).find('.ui-state-highlight'); - // If there is no info bar - if (!info.length) { - // Create info bar - info = createInfoBar('Below are LANs/VSWITCHes available to use.'); - $('#' + panelId).append(info); - } - - // Get datatable - var dTable = getNetworkDataTable(); - if (!dTable) { - // Create table - var tableId = 'zNetworkDataTable'; - var table = new DataTable(tableId); - table.init( [ '', 'z/VM', 'Type', 'Name', 'Layer', 'Owner', 'Controller', 'Details' ]); - - // Append datatable to tab - $('#' + panelId).append(table.object()); - - // Turn into datatable - dTable = $('#' + tableId).dataTable({ - 'iDisplayLength': 50, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - setNetworkDataTable(dTable); - - // Set the column width - var cols = table.object().find('thead tr th'); - cols.eq(0).css('width', '20px'); // HCP column - cols.eq(1).css('width', '20px'); // Type column - cols.eq(2).css('width', '20px'); // Name column - cols.eq(3).css({'width': '600px'}); // Details column - } - - // Skip index 0 because it contains nothing - var details = '
                          ';
                          -    for ( var i = 1; i < tmp.length; i++) {
                          -        details += tmp[i];
                          -    }
                          -    details += '
                          '; - - // Determine the OSI layer - var layer = "3"; - if (details.indexOf("ETHERNET") != -1) { - layer = "2"; - } - - // Find the vSwitch/VLAN owner - var regex = /(LAN|VSWITCH) (.*?)(?:\s|$)/g; - var owner = ""; - var match = ""; - if (type == "VSWITCH") { - owner = "SYSTEM"; - } else { - owner = regex.exec(details)[2]; - } - - // Find the vSwitch controller - regex = /(?:^|\s)Controller: (.*?)(?:\s|$)/g; - var controllers = ""; - match = ""; - while (match = regex.exec(details)) { - controllers += match[1] + ","; - } - controllers = controllers.substring(0, controllers.length - 1); // Delete last two characters - - dTable.fnAddData(['', '
                          ' + hcp2zvm[hcp] + '
                          ', '
                          ' + type + '
                          ', '
                          ' + name + '
                          ', '
                          ' + layer + '
                          ', '
                          ' + owner + '
                          ', '
                          ' + controllers + '
                          ', details]); - - // Create actions menu - if (!$('#networkResourceActions').length) { - // Empty filter area - $('#' + tableId + '_length').empty(); - - // Add Vswitch/Vlan - var addLnk = $('Add'); - addLnk.bind('click', function(event){ - openAddVswitchVlanDialog(); - }); - - // Remove Vswitch/Vlan - var removeLnk = $('Remove'); - removeLnk.bind('click', function(event){ - var networkList = getNodesChecked(tableId).split(','); - if (networkList) { - openRemoveVswitchVlanDialog(networkList); - } - }); - - // Refresh table - var refreshLnk = $('Refresh'); - refreshLnk.bind('click', function(event){ - $('#zvmNetworkResource').empty().append(createLoader('')); - setNetworkDataTable(''); - - // Create a array for hardware control points - var hcps = new Array(); - if ($.cookie('xcat_hcp').indexOf(',') > -1) - hcps = $.cookie('xcat_hcp').split(','); - else - hcps.push($.cookie('xcat_hcp')); - - // Query networks - zhcpQueryCountForNetworks = hcps.length; - for (var i in hcps) { - if( !hcps[i] || 0 === hcps[i].length) continue; - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : hcps[i], - args : '--getnetworknames', - msg : hcps[i] - }, - - success : getNetwork - }); - zhcpQueryCountForNetworks--; - } - }); - - // Create action bar - var actionBar = $('
                          ').css("width", "450px"); - - // Create an action menu - var actionsMenu = createMenu([addLnk, removeLnk, refreshLnk]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + tableId + '_length').prepend(menuDiv); - $('#' + tableId + '_length').css({ - 'padding': '0px', - 'width': '500px' - }); - $('#' + tableId + '_filter').css('padding', '10px'); - menuDiv.append(actionBar); - } - - // Resize accordion - $('#zvmResourceAccordion').accordion('resize'); -} - -/** - * Connect a NIC to a Guest LAN - * - * @param data Data from HTTP request - */ -function connect2GuestLan(data) { - var rsp = data.rsp; - var args = data.msg.split(';'); - var node = args[0].replace('node=', ''); - var address = args[1].replace('addr=', ''); - var lanName = args[2].replace('lan=', ''); - var lanOwner = args[3].replace('owner=', ''); - - // Write ajax response to status bar - var prg = writeRsp(rsp, node + ': '); - $('#' + node + 'StatusBar').find('div').append(prg); - - // Continue if no errors found - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - // Connect NIC to Guest LAN - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--connectnic2guestlan;' + address + ';' + lanName + ';' - + lanOwner, - msg : node - }, - - success : updateZNodeStatus - }); - } else { - // Hide loader when error - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).hide(); - } -} - -/** - * Connect a NIC to a VSwitch - * - * @param data Data from HTTP request - */ -function connect2VSwitch(data) { - var rsp = data.rsp; - var args = data.msg.split(';'); - var node = args[0].replace('node=', ''); - var address = args[1].replace('addr=', ''); - var vswitchName = args[2].replace('vsw=', ''); - var vswitchAware = args[3].replace('vlanaware=', ''); - var vswitchPortType = args[4].replace('porttype=', ''); - var vswitchLanId = args[5].replace('lanid=', ''); - - // Set variables to empty string if notaware or they contain "default" - if (vswitchAware.toLowerCase() == 'notaware' ) { - vswitchPortType = ''; - vswitchLanId = ''; - } else { - if (vswitchPortType.toLowerCase() == 'default' ) { - vswitchPortType = ''; - } - if (vswitchLanId.toLowerCase() == 'default' ) { - vswitchLanId = ''; - } - } - - // Write ajax response to status bar - var prg = writeRsp(rsp, node + ': '); - $('#' + node + 'StatusBar').find('div').append(prg); - - // Continue if no errors found - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - // Connect NIC to VSwitch - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chvm', - tgt : node, - args : '--connectnic2vswitch;' + address + ';' + vswitchName + ';' - + vswitchPortType + ';' + vswitchLanId, - msg : node - }, - - success : updateZNodeStatus - }); - } else { - // Hide loader when error - var statusBarLoaderId = node + 'StatusBarLoader'; - $('#' + statusBarLoaderId).hide(); - } -} - -/** - * Create provision existing node division - * - * @param inst Provision tab instance - * @return Provision existing node division - */ -function createZProvisionExisting(inst) { - // Create provision existing and hide it - var provExisting = $('
                          ').hide(); - - var vmFS = $('
                          '); - var vmLegend = $('Virtual Machine'); - vmFS.append(vmLegend); - provExisting.append(vmFS); - - var vmAttr = $('
                          '); - vmFS.append($('
                          ')); - vmFS.append(vmAttr); - - var osFS = $('
                          '); - var osLegend = $('Operating System'); - osFS.append(osLegend); - provExisting.append(osFS); - - var osAttr = $('
                          '); - osFS.append($('
                          ')); - osFS.append(osAttr); - - // Create group input - var group = $('
                          '); - var groupLabel = $(''); - group.append(groupLabel); - - // Turn on auto complete for group - var groupNames = $.cookie('xcat_groups'); - if (groupNames) { - // Split group names into an array - var tmp = groupNames.split(','); - - // Create drop down for groups - var groupSelect = $(''); - groupSelect.append(''); - for (var i in tmp) { - if( !tmp[i] || 0 === tmp[i].length) continue; - // Add group into drop down - var opt = $(''); - groupSelect.append(opt); - } - group.append(groupSelect); - - // Create node datatable - groupSelect.change(function(){ - // Get group selected - var thisGroup = $(this).val(); - // If a valid group is selected - if (thisGroup) { - createNodesDatatable(thisGroup, 'zNodesDatatableDIV' + inst); - } - }); - } else { - // If no groups are cookied - var groupInput = $(''); - group.append(groupInput); - } - vmAttr.append(group); - - // Create node input - var node = $('
                          '); - var nodeLabel = $(''); - var nodeDatatable = $('

                          Select a group to view its nodes

                          '); - node.append(nodeLabel); - node.append(nodeDatatable); - vmAttr.append(node); - - // Create operating system image input - var os = $('
                          '); - var osLabel = $(''); - var osSelect = $(''); - osSelect.append($('')); - - var imageNames = $.cookie('xcat_imagenames').split(','); - if (imageNames) { - imageNames.sort(); - for (var i in imageNames) { - if( !imageNames[i] || 0 === imageNames[i].length) continue; - osSelect.append($('')); - } - } - os.append(osLabel); - os.append(osSelect); - osAttr.append(os); - - // Create boot method drop down - var bootMethod = $('
                          '); - var methoddLabel = $(''); - var methodSelect = $(''); - methodSelect.append('' - + '' - + '' - + '' - + '' - ); - bootMethod.append(methoddLabel); - bootMethod.append(methodSelect); - osAttr.append(bootMethod); - - // Generate tooltips - provExisting.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - - /** - * Provision existing - */ - var provisionBtn = createButton('Provision'); - provisionBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parent().parent().find('.ui-state-error').remove(); - - var ready = true; - var errMsg = ''; - - // Get provision tab ID - var thisTabId = $(this).parent().parent().parent().attr('id'); - // Get provision tab instance - var inst = thisTabId.replace('zvmProvisionTab', ''); - - // Get nodes that were checked - var dTableId = 'zNodesDatatable' + inst; - var tgts = getNodesChecked(dTableId); - if (!tgts) { - errMsg += 'You need to select a node.
                          '; - ready = false; - } - - // Check operating system image - var os = $('#' + thisTabId + ' select[name=os]:visible'); - if (!os.val()) { - errMsg += 'You need to select a operating system image.'; - os.css('border', 'solid #FF0000 1px'); - ready = false; - } else { - os.css('border', 'solid #BDBDBD 1px'); - } - - // If all inputs are valid, ready to provision - if (ready) { - // Disable provision button - $(this).attr('disabled', 'true'); - - // Show loader - $('#zProvisionStatBar' + inst).show(); - $('#zProvisionLoader' + inst).show(); - - // Disable all inputs - var inputs = $('#' + thisTabId + ' input:visible'); - inputs.attr('disabled', 'disabled'); - - // Disable all selects - var selects = $('#' + thisTabId + ' select'); - selects.attr('disabled', 'disabled'); - - // Get operating system image - var osImage = $('#' + thisTabId + ' select[name=os]:visible').val(); - var tmp = osImage.split('-'); - var os = tmp[0]; - var arch = tmp[1]; - var profile = tmp[3]; - - /** - * (1) Set operating system - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeadd', - tgt : '', - args : tgts + ';noderes.netboot=zvm;nodetype.os=' + os + ';nodetype.arch=' + arch + ';nodetype.profile=' + profile, - msg : 'cmd=nodeadd;out=' + inst - }, - - success : updateZProvisionExistingStatus - }); - } else { - // Show warning message - var warn = createWarnBar(errMsg); - warn.prependTo($(this).parent().parent()); - } - }); - provExisting.append(provisionBtn); - - return provExisting; -} - -/** - * Create provision new node division - * - * @param inst Provision tab instance - * @return Provision new node division - */ -function createZProvisionNew(inst) { - if (typeof console == "object"){ - console.log("Entering createZProvisionNew. Inst value:"+inst); - } - // Create provision new node division - var provNew = $('
                          '); - - // Create VM fieldset - var vmFS = $('
                          '); - var vmLegend = $('Virtual Machine'); - vmFS.append(vmLegend); - provNew.append(vmFS); - - var vmAttr = $('
                          '); - vmFS.append($('
                          ')); - vmFS.append(vmAttr); - - // Create OS fieldset - var osFS = $('
                          '); - var osLegend = $('Operating System'); - osFS.append(osLegend); - provNew.append(osFS); - - // Create hardware fieldset - var hwFS = $('
                          '); - var hwLegend = $('Hardware'); - hwFS.append(hwLegend); - provNew.append(hwFS); - - var hwAttr = $('
                          '); - hwFS.append($('
                          ')); - hwFS.append(hwAttr); - - // Create tabs for basic and advanced hardware configuration - var hwTab = new Tab('hwConfig' + inst); - hwTab.init(); - hwAttr.append(hwTab.object()); - - var osAttr = $('
                          '); - osFS.append($('
                          ')); - osFS.append(osAttr); - - // Create group input - var group = $('
                          '); - var groupLabel = $(''); - var groupInput = $(''); - // Get groups on-focus - groupInput.one('focus', function(){ - var groupNames = $.cookie('xcat_groups'); - if (groupNames) { - // Turn on auto complete - $(this).autocomplete({ - source: groupNames.split(',') - }); - } - }); - group.append(groupLabel); - group.append(groupInput); - vmAttr.append(group); - - // Create node input - var nodeName = $('
                          '); - var nodeLabel = $(''); - var nodeInput = $(''); - nodeName.append(nodeLabel); - nodeName.append(nodeInput); - vmAttr.append(nodeName); - - // Create user ID input - var userId = $('
                          '); - vmAttr.append(userId); - - // Create hardware control point input - var hcpDiv = $('
                          '); - var hcpNodeLabel = $(''); - var hcpNodeInput = $(''); - var hcpHiddenInput = $(''); - hcpNodeInput.blur(function() { - - if (typeof console == "object") { - console.log("Display loading bar "); - } - // Show the status bar with a message and loading gif - $('#'+'zProvisionStatBar'+inst).find('div').append("Loading zhcp information..."); - $('#'+'zProvisionStatBar'+inst).find('div').append(""); - $('#'+'zProvisionStatBar'+inst).show(); - - // list of calls after the zhcp is verified. Used to determine when in progress gif is to be removed. - var ajaxCalls = {"diskpoolnames":1, "zfcppoolnames":1, "userprofilenames":1}; - var zhcpToCheck = $(this).val(); - var zhcpField = $(this); - var provisionStatusBar = $('#'+'zProvisionStatBar'+inst); - - // Make sure border is set back to black - zhcpField.css('border', 'solid #BDBDBD 1px'); - - if ($(this).val()) { - // Check if this is a valid node by making network names call. - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : zhcpToCheck, - args : '--getnetworknames', - msg : zhcpToCheck - }, - - success: function(data) { - if (data.rsp.length && (data.rsp[0].indexOf("Failed") > -1 || data.rsp[0].indexOf("Invalid") > -1 || data.rsp[0].indexOf("Error") > -1) ) { - // Remove the progress gif, since bailing out - removeProvisionLoadingGif(provisionStatusBar); - - // Create warning dialog - var warning = createWarnBar('Failure getting network data for hardware control point ' + zhcpToCheck + '
                          The hcp field must be a xCAT node name.'); - var warnDialog = $('
                          ').append(warning); - - // highlight the hcp field - zhcpField.css('border', 'solid #FF0000 1px'); - - // Open warning dialog - warnDialog.dialog({ - title:'Warning', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - $(this).dialog("close"); - } - } - }); - - } else { - // Node is good, now set some cookies from network, then check/set other cookies - setNetworkCookies(data); - - // Get the HCP name from the hcp node name - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsdef', - tgt : '', - args : zhcpToCheck, - msg : 'zhcpFullName' - }, - - success: function(data) { - if (data.rsp.length && (data.rsp[0].indexOf("Failed") > -1 || data.rsp[0].indexOf("Invalid") > -1) ) { - // Remove the progress gif, since bailing out - removeProvisionLoadingGif(provisionStatusBar); - - // Create warning dialog - var warning = createWarnBar('Failure getting hcp data from hardware control point ' + zhcpToCheck + '
                          The hcp field must be a valid xCAT node name.'); - var warnDialog = $('
                          ').append(warning); - - // highlight the hcp field - zhcpField.css('border', 'solid #FF0000 1px'); - - // Open warning dialog - warnDialog.dialog({ - title:'Warning', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - $(this).dialog("close"); - } - } - }); - } else { - // Now set the hidden hcp field with the full name - // Clear hash table containing definable node attributes - nodeAttrs = new Array(); - - // Get definable attributes - // Data returned - var rsp = data.rsp; - // Group name - var group = data.msg; - // Hash of node attributes - var attrs = new Object(); - - // Go through each attribute - var node, args; - for (var i in rsp) { - // Get node name, skip processing - if (rsp[i].indexOf('Object name:') > -1) { - i++; - } - - // Get key and value - args = rsp[i].split('=', 2); - var key = jQuery.trim(args[0]); - var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1, rsp[i].length)); - - // If this is zhcp key then save full name in hidden field - if (key == "hcp") { - hcpHiddenInput.val(val); - } - - } - - } - } - }); - - if (typeof console == "object"){ - console.log("Looking for cookies from <" + zhcpToCheck + ">"); - } - - if (!$.cookie('xcat_' + zhcpToCheck + 'diskpools')) { - // Get disk pools - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : zhcpToCheck, - args : '--diskpoolnames', - msg : zhcpToCheck - }, - - success : setDiskPoolCookies, - complete : function() { - checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "diskpoolnames"); - } - }); - } else { - checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "diskpoolnames"); - } - - if (!$.cookie('xcat_' + zhcpToCheck + 'zfcppools')) { - // Get zFCP pools - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : zhcpToCheck, - args : '--zfcppoolnames', - msg : zhcpToCheck - }, - - success : setZfcpPoolCookies, - complete : function() { - checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "zfcppoolnames"); - } - }); - } else { - checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "zfcppoolnames"); - } - - if (!$.cookie('xcat_' + zhcpToCheck + 'userprofiles')) { - // Get zFCP pools - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - async: false, - data : { - cmd : 'lsvm', - tgt : zhcpToCheck, - args : '--userprofilenames', - msg : zhcpToCheck - }, - - success : setUserProfilesCookies, - complete : function() { - checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "userprofilenames"); - } - }); - } else { - checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "userprofilenames"); - } - - // Reset user profile and network drop down box - var thisTabId = zhcpField.parents('.tab').attr('id'); - var thisUserProfile = $('#' + thisTabId + ' select[name=userProfile]'); - thisUserProfile.children().remove(); - - var definedUserProfiles = $.cookie('xcat_' + zhcpToCheck + 'userprofiles').split(','); - for (var i in definedUserProfiles) { - if( !definedUserProfiles[i] || 0 === definedUserProfiles[i].length) continue; - thisUserProfile.append(''); - } - - var thisNetwork = $('#' + thisTabId + ' select[name=network]'); - thisNetwork.children().remove(); - thisNetwork.append(''); // No profile option - var definedNetworks = $.cookie('xcat_' + zhcpToCheck + 'networks').split(','); - for (var i in definedNetworks) { - if( !definedNetworks[i] || 0 === definedNetworks[i].length) continue; - if (!jQuery.trim(definedNetworks[i])) - continue; - - var directoryEntry, interfaceName; - - // Generate directory entry statement for vSwitch, hipersocket, and guest LAN - if (definedNetworks[i].indexOf('VSWITCH ') != -1) { - interfaceName = jQuery.trim(definedNetworks[i].replace('VSWITCH ', '')); - directoryEntry = "TYPE QDIO LAN " + interfaceName; - } else if (definedNetworks[i].indexOf('LAN:HIPERS ') != -1) { - interfaceName = jQuery.trim(definedNetworks[i].replace('LAN:HIPERS ', '')); - directoryEntry = "TYPE HIPERSOCKETS LAN " + interfaceName; - } else { - interfaceName = jQuery.trim(definedNetworks[i].replace('LAN:QDIO ', '')); - directoryEntry = "TYPE QDIO LAN " + interfaceName; - } - - thisNetwork.append(''); - } - - // Update user entry on change - thisNetwork.change(function() { - updateUserEntry(thisTabId); - }); - - thisUserProfile.change(function() { - updateUserEntry(thisTabId); - }); - } - } - }); - } - }); - hcpDiv.append(hcpNodeLabel); - hcpDiv.append(hcpNodeInput); - hcpDiv.append(hcpHiddenInput); - vmAttr.append(hcpDiv); - - // Create an advanced link to set IP address and hostname - var advancedLnk = $(''); - vmAttr.append(advancedLnk); - var advanced = $('
                          ').hide(); - vmAttr.append(advanced); - - var ip = $('
                          '); - advanced.append(ip); - var hostname = $('
                          '); - advanced.append(hostname); - - // Show IP address and hostname inputs on-click - advancedLnk.click(function() { - advanced.toggle(); - }); - - // Create operating system image input - var os = $('
                          '); - var osLabel = $(''); - var osSelect = $(''); - osSelect.append($('')); - - var imageNames = $.cookie('xcat_imagenames').split(','); - if (imageNames) { - imageNames.sort(); - for (var i in imageNames) { - if( !imageNames[i] || 0 === imageNames[i].length) continue; - osSelect.append($('')); - } - } - os.append(osLabel); - os.append(osSelect); - osAttr.append(os); - - // Create user entry input - var defaultChkbox = $('').click(function() { - // Remove any warning messages - $(this).parents('.form').find('.ui-state-error').remove(); - - // Get tab Id - var thisTabId = $(this).parents('.ui-tabs-panel').parents('.ui-tabs-panel').attr('id'); - - // Get objects for HCP, user ID, and OS - var userId = $('#' + thisTabId + ' input[name=userId]'); - var os = $('#' + thisTabId + ' select[name=os]'); - - // Get default user entry when clicked - if ($(this).attr('checked')) { - if (!os.val() || !userId.val()) { - // Show warning message - var warn = createWarnBar('Please specify the operating system and user ID before checking this box'); - warn.prependTo($(this).parents('.form')); - - // Highlight empty fields - jQuery.each([os, userId], function() { - if (!$(this).val()) { - $(this).css('border', 'solid #FF0000 1px'); - } - }); - } else { - // Un-highlight empty fields - jQuery.each([os, userId], function() { - $(this).css('border', 'solid #BDBDBD 1px'); - }); - - // Get profile name - var tmp = os.val().split('-'); - var profile = tmp[3]; - - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'getdefaultuserentry;' + profile, - msg : thisTabId - }, - - success:function(data) { - // Populate user entry - var tabId = data.msg; - var entry = new String(data.rsp); - var userId = $('#' + tabId + ' input[name=userId]').val(); - entry = entry.replace(new RegExp('LXUSR', 'g'), userId); - $('#' + tabId + ' textarea:visible').val(entry); - } - }); - } - } else { - $('#' + thisTabId + ' textarea:visible').val(''); - - // Un-highlight empty fields - jQuery.each([os, userId], function() { - $(this).css('border', 'solid #BDBDBD 1px'); - }); - } - }); - var userEntry = $('
                          '); - userEntry.append($('').append(defaultChkbox, 'Use default')); - - // Add division on basic tab for specifying: memory, # of CPUs, privilege, user profile, and network. - var basicConfig = $('
                          '); - var userProfile = $('
                          '); - var cpuSelect = $('').change(function() { - updateUserEntry('zvmProvisionTab' + inst); - }); - var cpuCount = $('
                          ').append(cpuSelect); - var memorySlider = $('
                          '); - var memorySize = $(''); - var memory = $('
                          ').append(memorySlider, memorySize); - var acceptableMemorySize = ['512M', '1024M', '2G', '3G', '4G', '5G', '6G', '7G', '8G']; - memorySlider.slider({ - value: 0, - min: 0, - max: 8, - step: 1, - slide: function(event, ui) { - $('#basicConfig' + inst + ' input[name=memory]').val(acceptableMemorySize[ui.value]); - - // Update user entry on change - updateUserEntry('zvmProvisionTab' + inst); - } - }); - - // Initialize storage size - memorySize.val(acceptableMemorySize[0]); - - var privilege = $('
                          ' + - '
                          ' + - ' A - Primary system operator
                          ' + - ' B - System resource operator
                          ' + - ' C - System programmer
                          ' + - ' D - Spooling operator
                          ' + - ' E - System analyst
                          ' + - ' F - IBM service representative
                          ' + - ' G - General user
                          ' + - '
                          ' + - '
                          '); - privilege.find('input').change(function() { - updateUserEntry('zvmProvisionTab' + inst); - }); - - var network = $('
                          '); - - var vswitchvlan = $('

                          ' + - '
                          ' + - '
                          '); - vswitchvlan.find('input').change(function() { - updateUserEntry('zvmProvisionTab' + inst); - }); - vswitchvlan.find('select').change(function() { - updateUserEntry('zvmProvisionTab' + inst); - }); - - vswitchvlan.hide(); - basicConfig.append(userProfile, cpuCount, memory, privilege, network, vswitchvlan); - hwTab.add('basicConfig' + inst, 'Basic', basicConfig, false); - - // Add division on advanced tab for specifying user directory entry - hwTab.add('advancedConfig' + inst, 'Advanced', userEntry, false); - - // Create disk table - var diskDiv = $('
                          '); - var diskLabel = $(''); - var diskTable = $('
                          '); - var diskHeader = $(' Type Address Size Mode Pool Password IPLNone
                          '); - // Adjust header width - diskHeader.find('th').css( { - 'width' : '80px' - }); - diskHeader.find('th').eq(0).css( { - 'width' : '20px' - }); - var diskBody = $(''); - var diskFooter = $(''); - - /** - * Add disks - */ - var addDiskLink = $('Add disk'); - addDiskLink.bind('click', function(event) { - // Get list of disk pools - var thisTabId = $(this).parents('.tab').attr('id'); - var thisHcp = $('#' + thisTabId + ' input[name=hcp]').val(); - var definedPools = null; - if (thisHcp) { - // Get node without domain name - var temp = thisHcp.split('.'); - definedPools = $.cookie('xcat_' + temp[0] + 'diskpools').split(','); - } else { - var warning = createWarnBar('You must fill in a hardware control point before adding a disk.'); - var warnDialog = $('
                          ').append(warning); - - // Open dialog - warnDialog.dialog({ - title:'Warning', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - $(this).dialog("close"); - } - } - }); - return false; - } - - // Create a row - var diskRow = $(''); - - // Add remove button - var removeBtn = $(''); - var col = $('').append(removeBtn); - removeBtn.bind('click', function(event) { - diskRow.remove(); - }); - diskRow.append(col); - - // Create disk type drop down - var diskType = $(''); - var diskTypeSelect = $(''); - diskTypeSelect.append('' - + '' - ); - diskType.append(diskTypeSelect); - diskRow.append(diskType); - - // Create disk address input - var diskAddr = $(''); - diskRow.append(diskAddr); - - // Create disk size input - var diskSize = $(''); - diskRow.append(diskSize); - - // Create disk mode input - var diskMode = $(''); - var diskModeSelect = $(''); - diskModeSelect.append('' - + '' - + '' - + '' - + '' - + '' - + '' - ); - diskMode.append(diskModeSelect); - diskRow.append(diskMode); - - // Create disk pool drop down - var diskPool = $(''); - var diskPoolSelect = $(''); - for (var i in definedPools) { - diskPoolSelect.append(''); - } - diskPool.append(diskPoolSelect); - diskRow.append(diskPool); - - // Create disk password input - var diskPw = $(''); - diskRow.append(diskPw); - - // Create IPL checkbox - //var diskIpl = $(''); - var diskIpl = $(''); - diskRow.append(diskIpl); - diskIpl.find('input').change(function() { - updateUserEntry(thisTabId); - }); - - diskBody.append(diskRow); - - // Generate tooltips - diskBody.find('td input[title],select[title]').tooltip({ - position: "top right", - offset: [-4, 4], - effect: "fade", - opacity: 0.7, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - }); - - // Create disk table - diskFooter.append(addDiskLink); - diskTable.append(diskHeader); - diskTable.append(diskBody); - diskTable.append(diskFooter); - - diskDiv.append(diskLabel); - diskDiv.append(diskTable); - hwAttr.append(diskDiv); - - // Create zFCP table - var zfcpDiv = $('
                          '); - var zfcpLabel = $(''); - var zfcpTable = $('
                          '); - var zfcpHeader = $(' Address Size Pool Tag Port Name Unit # LOADDEV'); - // Adjust header width - zfcpHeader.find('th').css({ - 'width' : '80px' - }); - zfcpHeader.find('th').eq(0).css({ - 'width' : '20px' - }); - var zfcpBody = $(''); - var zfcpFooter = $(''); - - /** - * Add zFCP devices - */ - var addZfcpLink = $('Add zFCP'); - addZfcpLink.bind('click', function(event) { - // Get list of disk pools - var thisTabId = $(this).parents('.tab').attr('id'); - var thisHcp = $('#' + thisTabId + ' input[name=hcp]').val(); - var definedPools = null; - if (thisHcp) { - // Get node without domain name - var temp = thisHcp.split('.'); - definedPools = $.cookie('xcat_' + temp[0] + 'zfcppools').split(','); - } else { - var warning = createWarnBar('You must fill in a hardware control point before adding a zFCP.'); - var warnDialog = $('
                          ').append(warning); - - // Open dialog - warnDialog.dialog({ - title:'Warning', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function() { - $(this).dialog("close"); - } - } - }); - - } - - // Create a row - var zfcpRow = $(''); - - // Add remove button - var removeBtn = $(''); - var col = $('').append(removeBtn); - removeBtn.bind('click', function(event) { - zfcpRow.remove(); - }); - zfcpRow.append(col); - - // Create disk address input - var zfcpAddr = $(''); - zfcpRow.append(zfcpAddr); - - // Create disk size input - var zfcpSize = $(''); - zfcpRow.append(zfcpSize); - - // Create zFCP pool drop down - var zfcpPool = $(''); - var zfcpPoolSelect = $(''); - for (var i in definedPools) { - zfcpPoolSelect.append(''); - } - zfcpPool.append(zfcpPoolSelect); - zfcpRow.append(zfcpPool); - - // Create disk tag - var zfcpTag = $(''); - zfcpRow.append(zfcpTag); - - // Create device port name - var zfcpPortName = $(''); - zfcpRow.append(zfcpPortName); - - // Create device unit number - var zfcpUnitNo = $(''); - zfcpRow.append(zfcpUnitNo); - - // Create LOADDEV radio button - var zfcpLoaddev = $(''); - zfcpRow.append(zfcpLoaddev); - - zfcpBody.append(zfcpRow); - - // Generate tooltips - zfcpBody.find('td input[title],select[title]').tooltip({ - position: "top right", - offset: [-4, 4], - effect: "fade", - opacity: 0.7, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - }); - - zfcpFooter.append(addZfcpLink); - zfcpTable.append(zfcpHeader); - zfcpTable.append(zfcpBody); - zfcpTable.append(zfcpFooter); - - zfcpDiv.append(zfcpLabel); - zfcpDiv.append(zfcpTable); - hwAttr.append(zfcpDiv); - - // Generate tooltips - provNew.find('div input[title],select[title],textarea[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - - // Disable IPL column if advanced tab is selected - hwTab.object().tabs({ - select: function(event, ui) { - // Get provision tab instance - var thisTabId = $(this).parents('.ui-tabs-panel').attr('id'); - var inst = thisTabId.replace('zvmProvisionTab', ''); - - // Disable and de-select IPL device - if (ui.index == 1) { - $('#' + thisTabId + ' table:eq(0):visible tbody tr td:nth-child(8) input').attr('disabled','disabled'); - } else { - $('#' + thisTabId + ' table:eq(0):visible tbody tr td:nth-child(8) input').removeAttr('disabled'); - } - - $('#' + thisTabId + ' table:eq(0):visible tbody tr td:nth-child(8) input').removeAttr('checked'); - } - }); - - /** - * Provision new - */ - var provisionBtn = createButton('Provision'); - provisionBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parent().parent().find('.ui-state-error').remove(); - - var ready = true; - var errMsg = ''; - - // Get tab ID - var thisTabId = $(this).parents('.ui-tabs-panel').attr('id'); - // Get provision tab instance - var inst = thisTabId.replace('zvmProvisionTab', ''); - - // Get the selected hardware configuration tab - // Basic tab index = 0 & advanced tab index = 1 - var hwTabIndex = $("#hwConfig" + inst).tabs('option', 'selected'); - - // Check node name, userId, hardware control point, and group - // Check disks and zFCP devices - var inputs = $('#' + thisTabId + ' input:visible'); - for (var i = 0; i < inputs.length; i++) { - // Do not check some inputs - if (inputs.eq(i).attr('name') == 'memory') { - // There should always be a value for memory - // Do not change the border - continue; - } else if (!inputs.eq(i).val() - && inputs.eq(i).attr('type') != 'password' - && inputs.eq(i).attr('name') != 'zfcpTag' - && inputs.eq(i).attr('name') != 'zfcpPortName' - && inputs.eq(i).attr('name') != 'zfcpUnitNo') { - inputs.eq(i).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - inputs.eq(i).css('border', 'solid #BDBDBD 1px'); - } - } - - var selects = $('#' + thisTabId + ' select:visible'); - for (var i = 0; i < selects.length; i++) { - if (!selects.eq(i).val() && selects.eq(i).attr('name') != 'os' && selects.eq(i).attr('name') != 'userProfile' && selects.eq(i).attr('name') != 'network') { - selects.eq(i).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - selects.eq(i).css('border', 'solid #BDBDBD 1px'); - } - } - - if (hwTabIndex == 1) { - // Check user entry - var thisUserEntry = $('#' + thisTabId + ' textarea:visible'); - thisUserEntry.val(thisUserEntry.val().toUpperCase()); - if (!thisUserEntry.val()) { - thisUserEntry.css('border', 'solid #FF0000 1px'); - ready = false; - } else { - thisUserEntry.css('border', 'solid #BDBDBD 1px'); - } - - // Check if user entry contains user ID - var thisUserId = $('#' + thisTabId + ' input[name=userId]:visible'); - var pos = thisUserEntry.val().indexOf('USER ' + thisUserId.val().toUpperCase()); - if (pos < 0) { - - pos = thisUserEntry.val().indexOf('IDENTITY ' + thisUserId.val().toUpperCase()); - if (pos < 0) { - errMsg = errMsg + 'The directory entry does not contain the correct user/identity ID.
                          '; - ready = false; - } - } - } - var hostnameCheck = $('#' + thisTabId + ' input[name=hostname]').val(); - if (hostnameCheck.length > 70) { - errMsg = errMsg + 'The host name cannot be longer than 70 characters.
                          '; - $('#' + thisTabId + ' input[name=hostname]').css('border', 'solid #FF0000 1px'); - ready = false; - } - - // Show error message for missing inputs - if (!ready) { - errMsg = errMsg + 'Please provide a value for each missing field.
                          '; - } - - // If no operating system is specified, create only user entry - os = $('#' + thisTabId + ' select[name=os]:visible'); - - // Check number of disks - var diskRows = $('#' + thisTabId + ' table tr'); - // If an OS is given, disks are needed - if (os.val() && (diskRows.length < 1)) { - errMsg = errMsg + 'You need to add at some disks.
                          '; - ready = false; - } - - // If this is basic mode, check for a disk with IPL radio button and zFCP with LOADDEV button - // Cannot have both. (In advanced mode they create the directory entries.) - if (hwTabIndex == 0) { - // Find a device to be IPLed? - var ECKD_FBA_diskRows = $('#' + thisTabId + ' table:eq(0):visible tbody tr'); - var iplSet = 0; - for (var i = 0; i < ECKD_FBA_diskRows.length; i++) { - var diskArgs = ECKD_FBA_diskRows.eq(i).find('td'); - if (diskArgs.eq(7).find('input').attr("checked") === true) { - iplSet = 1; - break; - } - } - - // Check if zFCP loaddev checked - var zfcpRows = $('#' + thisTabId + ' table:eq(1):visible tbody tr'); - if (zfcpRows.length > 0) { - for ( var i = 0; i < zfcpRows.length; i++) { - var diskArgs = zfcpRows.eq(i).find('td'); - // This is either true or false - var loaddev = diskArgs.eq(7).find('input').attr('checked'); - if (loaddev && iplSet) { - errMsg = errMsg + 'You cannot have both disk IPL and zFCP LOADDEV, can only IPL one device.
                          '; - ready = false; - } - } - } - } - - // If inputs are valid, ready to provision - if (ready) { - // Generate user directory entry if basic tab is selected - if (hwTabIndex == 0) { - updateUserEntry(thisTabId); - } - - if (!os.val()) { - // If no OS is given, create a virtual server - var msg = ''; - if (diskRows.length > 0) { - msg = 'Do you want to create a virtual server without an operating system?'; - } else { - // If no disks are given, create a virtual server (no disk) - msg = 'Do you want to create a virtual server without an operating system or disks?'; - } - - // Open dialog to confirm - var confirmDialog = $('

                          ' + msg + '

                          '); - confirmDialog.dialog({ - title:'Confirm', - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Ok": function(){ - // Disable provision button - provisionBtn.attr('disabled', 'true'); - - // Show loader - $('#zProvisionStatBar' + inst).show(); - $('#zProvisionLoader' + inst).show(); - - // Disable add disk button - addDiskLink.attr('disabled', 'true'); - - // Disable close button on disk table - $('#' + thisTabId + ' table span').unbind('click'); - - // Disable all inputs - var inputs = $('#' + thisTabId + ' input'); - inputs.attr('disabled', 'disabled'); - - // Disable all selects - var selects = $('#' + thisTabId + ' select'); - selects.attr('disabled', 'disabled'); - - // Add a new line at the end of the user entry - var textarea = $('#' + thisTabId + ' textarea'); - var tmp = jQuery.trim(textarea.val()); - textarea.val(tmp + '\n'); - textarea.attr('readonly', 'readonly'); - textarea.css( { - 'background-color' : '#F2F2F2' - }); - - // Get node name - var node = $('#' + thisTabId + ' input[name=nodeName]').val(); - // Get userId - var userId = $('#' + thisTabId + ' input[name=userId]').val(); - // Get hardware control point - var hcp = $('#' + thisTabId + ' input[name=hcp]').val(); - // Get group - var group = $('#' + thisTabId + ' input[name=group]').val(); - // Get IP address and hostname - var ip = $('#' + thisTabId + ' input[name=ip]').val(); - var hostname = $('#' + thisTabId + ' input[name=hostname]').val(); - - // Generate arguments to sent - var args = node + ';zvm.hcp=' + hcp - + ';zvm.userid=' + userId - + ';nodehm.mgt=zvm' - + ';groups=' + group; - if (ip) - args += ';hosts.ip=' + ip; - - if (hostname) - args += ';hosts.hostnames=' + hostname; - - /** - * (1) Define node - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeadd', - tgt : '', - args : args, - msg : 'cmd=nodeadd;out=' + inst - }, - - success : updateZProvisionNewStatus - }); - - $(this).dialog("close"); - }, - "Cancel": function() { - $(this).dialog("close"); - } - } - }); - } else { - /** - * Create a virtual server and install OS - */ - - // Disable provision button - $(this).attr('disabled', 'true'); - - // Show loader - $('#zProvisionStatBar' + inst).show(); - $('#zProvisionLoader' + inst).show(); - - // Disable add disk button - addDiskLink.attr('disabled', 'true'); - - // Disable close button on disk table - $('#' + thisTabId + ' table span').unbind('click'); - - // Disable all inputs - var inputs = $('#' + thisTabId + ' input'); - inputs.attr('disabled', 'disabled'); - inputs.css( { - 'background-color' : '#F2F2F2' - }); - - // Disable all selects - var selects = $('#' + thisTabId + ' select'); - selects.attr('disabled', 'disabled'); - selects.css( { - 'background-color' : '#F2F2F2' - }); - - // Add a new line at the end of the user entry - var textarea = $('#' + thisTabId + ' textarea'); - var tmp = jQuery.trim(textarea.val()); - textarea.val(tmp + '\n'); - textarea.attr('readonly', 'readonly'); - textarea.css( { - 'background-color' : '#F2F2F2' - }); - - // Get node name - var node = $('#' + thisTabId + ' input[name=nodeName]').val(); - // Get userId - var userId = $('#' + thisTabId + ' input[name=userId]').val(); - // Get hardware control point - var hcp = $('#' + thisTabId + ' input[name=hcp]').val(); - // Get group - var group = $('#' + thisTabId + ' input[name=group]').val(); - // Get IP address and hostname - var ip = $('#' + thisTabId + ' input[name=ip]').val(); - var hostname = $('#' + thisTabId + ' input[name=hostname]').val(); - - // Generate arguments to sent - var args = node + ';zvm.hcp=' + hcp - + ';zvm.userid=' + userId - + ';nodehm.mgt=zvm' - + ';groups=' + group; - if (ip) - args += ';hosts.ip=' + ip; - - if (hostname) - args += ';hosts.hostnames=' + hostname; - - /** - * (1) Define node - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeadd', - tgt : '', - args : args, - msg : 'cmd=nodeadd;out=' + inst - }, - - success : updateZProvisionNewStatus - }); - } - } else { - // Show warning message - var warn = createWarnBar(errMsg); - warn.prependTo($(this).parent().parent()); - } - }); - provNew.append(provisionBtn); - - return provNew; -} -/** - * Remove zprovision loading gif for zhcp and message - * - * @param division holding the gif and message - */ -function removeProvisionLoadingGif(provisionStatBar) { - - // Only remove the status bar message and gif we added, then hide the status bar - var items = provisionStatBar.find('div').children(); - for (var i = 0; i< items.length; i++) { - var nname = items[i].nodeName; - var myid = items[i].id; - if (nname == "B" && myid == "loadzhcp") { - items[i].remove() - } else if (nname == "IMG" && myid == "loadingpic") { - items[i].remove(); - } - } - provisionStatBar.hide(); -} - -/** - * Set hash entry to 0 and check if all are 0. If so call - * removeProvisionLoadingGif - * - * @param division holding the gif and message, and hash, and - * key - */ -function checkProvisionCallsDone(provisionStatBar, table, finishedKey) { - - table[finishedKey] = 0; - - for (var key in table) { - if (table[key] == 1) { - return; // More to do - } - } - - removeProvisionLoadingGif(provisionStatBar); -} - -/** - * Load zVMs into column (service page) - * - * @param col Table column where OS images will be placed - */ -function loadzVMs(col) { - // Get group names and description and append to group column - if (!$.cookie('xcat_zvms')) { - var infoBar = createInfoBar('No selectable z/VM available'); - col.append(infoBar); - return; - } - - var zNames = $.cookie('xcat_zvms').split(','); - - var radio, zBlock, args, zvm, hcp; - for (var i in zNames) { - if( !zNames[i] || 0 === zNames[i].length) continue; - args = zNames[i].split(':'); - zvm = args[0]; - hcp = args[1]; - - // Create block for each group - zBlock = $('
                          ').css({ - 'border': '1px solid', - 'max-width': '200px', - 'margin': '5px auto', - 'padding': '5px', - 'display': 'block', - 'vertical-align': 'middle', - 'cursor': 'pointer', - 'white-space': 'normal' - }).click(function(){ - $(this).children('input:radio').attr('checked', 'checked'); - $(this).parents('td').find('div').attr('class', 'ui-state-default'); - $(this).attr('class', 'ui-state-active'); - }); - radio = $('').css('display', 'none'); - zBlock.append(radio, $('' + zvm + ' managed by ' + hcp + '')); - zBlock.children('span').css({ - 'display': 'block', - 'margin': '5px', - 'text-align': 'left' - }); - col.append(zBlock); - } -} - -/** - * Load groups into column - * - * @param col Table column where OS images will be placed - */ -function loadSrvGroups(col) { - // Get group names and description and append to group column - if (!$.cookie('xcat_srv_groups')) { - var infoBar = createInfoBar('No selectable group available'); - col.append(infoBar); - return; - } - - var groupNames = $.cookie('xcat_srv_groups').split(','); - - var groupBlock, radio, args, name, ip, hostname, desc; - for (var i in groupNames) { - if( !groupNames[i] || 0 === groupNames[i].length) continue; - args = groupNames[i].split(':'); - name = args[0]; - ip = args[1]; - hostname = args[2]; - desc = args[3]; - - // Create block for each group - groupBlock = $('
                          ').css({ - 'border': '1px solid', - 'max-width': '200px', - 'margin': '5px auto', - 'padding': '5px', - 'display': 'block', - 'vertical-align': 'middle', - 'cursor': 'pointer', - 'white-space': 'normal' - }).click(function(){ - $(this).children('input:radio').attr('checked', 'checked'); - $(this).parents('td').find('div').attr('class', 'ui-state-default'); - $(this).attr('class', 'ui-state-active'); - }); - radio = $('').css('display', 'none'); - groupBlock.append(radio, $('' + name + ': ' + desc + '')); - groupBlock.children('span').css({ - 'display': 'block', - 'margin': '5px', - 'text-align': 'left' - }); - col.append(groupBlock); - } -} - -/** - * Load OS images into column - * - * @param col Table column where OS images will be placed - */ -function loadOSImages(col) { - // Get group names and description and append to group column - if (!$.cookie('xcat_srv_imagenames')) { - var infoBar = createInfoBar('No selectable image available'); - col.append(infoBar); - return; - } - - var imgNames = $.cookie('xcat_srv_imagenames').split(','); - - var imgBlock, radio, args, name, desc; - for (var i in imgNames) { - if( !imgNames[i] || 0 === imgNames[i].length) continue; - args = imgNames[i].split(':'); - name = args[0]; - desc = args[1]; - - // Create block for each image - imgBlock = $('
                          ').css({ - 'border': '1px solid', - 'max-width': '200px', - 'margin': '5px auto', - 'padding': '5px', - 'display': 'block', - 'vertical-align': 'middle', - 'cursor': 'pointer', - 'white-space': 'normal' - }).click(function(){ - $(this).children('input:radio').attr('checked', 'checked'); - $(this).parents('td').find('div').attr('class', 'ui-state-default'); - $(this).attr('class', 'ui-state-active'); - - $('#select-table tbody tr:eq(0) td:eq(3) input[name="master"]').attr('checked', ''); - $('#select-table tbody tr:eq(0) td:eq(3) input[name="master"]').parents('td').find('div').attr('class', 'ui-state-default'); - }); - radio = $('').css('display', 'none'); - imgBlock.append(radio, $('' + name + ': ' + desc + '')); - imgBlock.children('span').css({ - 'display': 'block', - 'margin': '5px', - 'text-align': 'left' - }); - col.append(imgBlock); - } -} - -/** - * Load golden images into column - * - * @param col Table column where master copies will be placed - */ -function loadGoldenImages(col) { - // Get group names and description and append to group column - if (!$.cookie('xcat_srv_goldenimages')) { - var infoBar = createInfoBar('No selectable master copies available'); - col.append(infoBar); - return; - } - - var imgNames = $.cookie('xcat_srv_goldenimages').split(','); - - var imgBlock, radio, args, name, desc; - for (var i in imgNames) { - if( !imgNames[i] || 0 === imgNames[i].length) continue; - args = imgNames[i].split(':'); - name = args[0]; - desc = args[1]; - - // Create block for each image - imgBlock = $('
                          ').css({ - 'border': '1px solid', - 'max-width': '200px', - 'margin': '5px auto', - 'padding': '5px', - 'display': 'block', - 'vertical-align': 'middle', - 'cursor': 'pointer', - 'white-space': 'normal' - }).click(function(){ - $(this).children('input:radio').attr('checked', 'checked'); - $(this).parents('td').find('div').attr('class', 'ui-state-default'); - $(this).attr('class', 'ui-state-active'); - - // Un-select zVM and image - $('#select-table tbody tr:eq(0) td:eq(2) input[name="image"]').attr('checked', ''); - $('#select-table tbody tr:eq(0) td:eq(2) input[name="image"]').parents('td').find('div').attr('class', 'ui-state-default'); - - $('#select-table tbody tr:eq(0) td:eq(0) input[name="hcp"]').attr('checked', ''); - $('#select-table tbody tr:eq(0) td:eq(0) input[name="hcp"]').parents('td').find('div').attr('class', 'ui-state-default'); - }); - radio = $('').css('display', 'none'); - imgBlock.append(radio, $('' + name + ': ' + desc + '')); - imgBlock.children('span').css({ - 'display': 'block', - 'margin': '5px', - 'text-align': 'left' - }); - col.append(imgBlock); - } -} - -/** - * Set a cookie for zVM host names (service page) - * - * @param data Data from HTTP request - */ -function setzVMCookies(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - var zvms = new Array(); - var hosts = data.rsp[0].split("\n"); - for ( var i = 0; i < hosts.length; i++) { - if (hosts[i] != null && hosts[i] != "") { - zvms.push(hosts[i]); - if (typeof console == "object"){ - console.log("Setting a zVM cookie:<"+hosts[i]+">"); - } - } - } - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_zvms', zvms, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Set a cookie for master copies (service page) - * - * @param data Data from HTTP request - */ -function setGoldenImagesCookies(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - var copies = new Array(); - var tmp = data.rsp[0].split(","); - for ( var i = 0; i < tmp.length; i++) { - if (tmp[i] != null && tmp[i] != "") { - copies.push(tmp[i]); - } - } - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_srv_goldenimages', copies, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Set a cookie for disk pool names of a given node - * - * @param data Data from HTTP request - */ -function setDiskPoolCookies(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - var node = data.msg; - var pools = data.rsp[0].split(node + ": "); - var pools2 = []; - for (var j in pools) { - if (pools[j] != "") { - pools2.push(jQuery.trim(pools[j])); - } - } - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_' + node + 'diskpools', pools2, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Set a cookie for zFCP pool names of a given node - * - * @param data Data from HTTP request - */ -function setZfcpPoolCookies(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - var node = data.msg; - var pools = data.rsp[0].split(node + ': '); - var pools2 = []; - for (var j in pools) { - if (pools[j] != "") { - pools2.push(jQuery.trim(pools[j])); - } - } - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_' + node + 'zfcppools', pools2, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Set a cookie for zHCP host names - * - * @param zhcps List of zHCPs known - */ -function setzHcpCookies(zhcps) { - if (zhcps.length) { - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_zhcps', zhcps, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Set a cookie for z/VM user profile names of a given node - * - * @param data Data from HTTP request - */ -function setUserProfilesCookies(data) { - if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { - var node = data.msg; - var profiles = data.rsp[0].split(node + ': '); - var profiles2 = []; - for (var j in profiles) { - if (profiles[j] != "") { - profiles2.push(jQuery.trim(profiles[j])); - } - } - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); - $.cookie('xcat_' + node + 'userprofiles', profiles2, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Create virtual machine (service page) - * - * @param tabId Tab ID - * @param group Group - * @param hcp Hardware control point - * @param img OS image - */ -function createzVM(tabId, group, hcp, img, owner) { - // Submit request to create VM - // webportal provzlinux [group] [hcp] [image] [owner] - var iframe = createIFrame('lib/srv_cmd.php?cmd=webportal&tgt=&args=provzlinux;' + group + ';' + hcp + ';' + img + ';' + owner + '&msg=&opts=flush'); - iframe.prependTo($('#' + tabId)); -} - -/** - * Query the profiles that exists - * - * @param panelId Panel ID - */ -function queryProfiles(panelId) { - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'osimage', - msg : panelId - }, - - success : function(data) { - var panelId = data.msg; - setOSImageCookies(data); - configProfilePanel(panelId); - } - }); -} - -/** - * Panel to configure directory entries and disks for a profile - * - * @param panelId Panel ID - */ -function configProfilePanel(panelId) { - // Wipe panel clean - $('#' + panelId).empty(); - - // Add info bar - $('#' + panelId).append(createInfoBar('Create, edit, and delete profiles for the self-service portal. It is important to note the default z/VM user ID for any profile should be LXUSR.')); - - // Create table - var tableId = 'zvmProfileTable'; - var table = new DataTable(tableId); - table.init(['', 'Profile', 'Disk pool', 'Disk size', 'Directory entry']); - - // Insert profiles into table - var profiles = $.cookie('xcat_profiles').split(','); - profiles.push('default'); // Add default profile - for (var i in profiles) { - if (profiles[i]) { - // Columns are: profile, selectable, description, disk pool, disk size, and directory entry - var cols = new Array(profiles[i], '', '', ''); - - // Add remove button where id = user name - cols.unshift(''); - - // Add row - table.add(cols); - } - } - - // Append datatable to tab - $('#' + panelId).append(table.object()); - - // Turn into datatable - $('#' + tableId).dataTable({ - 'iDisplayLength': 50, - 'bLengthChange': false, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - - // Create action bar - var actionBar = $('
                          ').css("width", "450px"); - - // Create a profile - var createLnk = $('Create'); - createLnk.click(function() { - profileDialog(); - }); - - // Edit a profile - var editLnk = $('Edit'); - editLnk.click(function() { - var profiles = $('#' + tableId + ' input[type=checkbox]:checked'); - for (var i in profiles) { - var profile = profiles.eq(i).attr('name'); - if (profile) { - // Column order is: profile, selectable, disk pool, disk size, and directory entry - var cols = profiles.eq(i).parents('tr').find('td'); - var pool = cols.eq(2).text(); - var size = cols.eq(3).text(); - var entry = cols.eq(4).html().replace(new RegExp('
                          ', 'g'), '\n'); - - editProfileDialog(profile, pool, size, entry); - } - } - }); - - // Delete a profile - var deleteLnk = $('Delete'); - deleteLnk.click(function() { - var profiles = getNodesChecked(tableId); - if (profiles) { - deleteProfileDialog(profiles); - } - }); - - // Refresh profiles table - var refreshLnk = $('Refresh'); - refreshLnk.click(function() { - queryProfiles(panelId); - }); - - // Create an action menu - var actionsMenu = createMenu([refreshLnk, createLnk, editLnk, deleteLnk]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + tableId + '_wrapper').prepend(menuDiv); - menuDiv.append(actionBar); - $('#' + tableId + '_filter').appendTo(menuDiv); - - // Resize accordion - $('#' + tableId).parents('.ui-accordion').accordion('resize'); - - // Query directory entries and disk pool/size for each profile - for (var i in profiles) { - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'getdefaultuserentry;' + profiles[i], - msg : 'out=' + panelId + ';profile=' + profiles[i] - }, - - success: insertDirectoryEntry - }); - - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'getzdiskinfo;' + profiles[i], - msg : 'out=' + panelId + ';profile=' + profiles[i] - }, - - success: insertDiskInfo - }); - } -} - -/** - * Insert the directory entry into the profile table - * - * @param data Data from HTTP request - */ -function insertDirectoryEntry(data) { - var tableId = 'zvmProfileTable'; - var args = data.msg.split(';'); - - var profile = args[1].replace('profile=', ''); - - // Do not continue if there is nothing - if (!data.rsp.length) - return; - - var entry = data.rsp[0].replace(new RegExp('\n', 'g'), '
                          '); - - // Get the row containing the profile - var rowPos = findRow(profile, '#' + tableId, 1); - if (rowPos < 0) - return; - - // Update the directory entry column - var dTable = $('#' + tableId).dataTable(); - dTable.fnUpdate(entry, rowPos, 4, false); - - // Adjust table styling - $('#' + tableId + ' td:nth-child(5)').css({ - 'text-align': 'left' - }); - adjustColumnSize(tableId); -} - -/** - * Insert the disk info into the profile table - * - * @param data Data from HTTP request - */ -function insertDiskInfo(data) { - var tableId = 'zvmProfileTable'; - var args = data.msg.split(';'); - - var profile = args[1].replace('profile=', ''); - - // Do not continue if there is nothing - if (!data.rsp.length) - return; - - // Get the row containing the profile - var rowPos = findRow(profile, '#' + tableId, 1); - if (rowPos < 0) - return; - - // Update the disk info columns - var dTable = $('#' + tableId).dataTable(); - - var tmp = ""; - var pool = ""; - var eckdSize = 0; - var info = data.rsp[0].split('\n'); - for (var i in info) { - if (info[i].indexOf('diskpool') > -1) { - tmp = info[i].split('='); - pool = jQuery.trim(tmp[1]); - - dTable.fnUpdate(pool, rowPos, 2, false); - } if (info[i].indexOf('eckd_size') > -1) { - tmp = info[i].split('='); - eckdSize = jQuery.trim(tmp[1]); - - dTable.fnUpdate(eckdSize, rowPos, 3, false); - } - } - - // Adjust table styling - adjustColumnSize(tableId); -} - -/** - * Open profile dialog - */ -function profileDialog() { - // Create form to add profile - var dialogId = 'zvmCreateProfile'; - var profileForm = $('
                          '); - - // Create info bar - var info = createInfoBar('Configure the default settings for a profile'); - profileForm.append(info); - - // Insert profiles into select - var profileSelect = $(''); - var profiles = $.cookie('xcat_profiles').split(','); - profiles.push('default'); // Add default profile - for (var i in profiles) { - if (profiles[i]) { - profileSelect.append($('')); - } - } - - profileForm.append($('
                          ').append(profileSelect)); - profileForm.append('
                          '); - profileForm.append('
                          '); - profileForm.append('
                          '); + for ( var i = 1; i < userEntry.length; i++) { + userEntry[i] = jQuery.trim(userEntry[i]); + txtArea.append(userEntry[i]); + + if (i < userEntry.length) { + txtArea.append('\n'); + } + } + txtArea.attr('readonly', 'readonly'); + fieldSet.append(txtArea); + + /** + * Edit user entry + */ + txtArea.bind('dblclick', function(event) { + txtArea.attr('readonly', ''); + txtArea.css( { + 'border-width' : '1px' + }); + + saveBtn.show(); + cancelBtn.show(); + saveBtn.css('display', 'inline-table'); + cancelBtn.css('display', 'inline-table'); + }); + + /** + * Save + */ + var saveBtn = createButton('Save').hide(); + saveBtn.bind('click', function(event) { + // Show loader + $('#' + node + 'StatusBarLoader').show(); + $('#' + node + 'StatusBar').show(); + + // Replace user entry + var newUserEntry = jQuery.trim(txtArea.val()) + '\n'; + + // Replace user entry + $.ajax( { + url : 'lib/zCmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--replacevs', + att : newUserEntry, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process and save it in a cookie + incrementNodeProcess(node); + + txtArea.attr('readonly', 'readonly'); + txtArea.css( { + 'border-width' : '0px' + }); + + // Disable save button + $(this).hide(); + cancelBtn.hide(); + }); + + /** + * Cancel + */ + var cancelBtn = createButton('Cancel').hide(); + cancelBtn.bind('click', function(event) { + txtArea.attr('readonly', 'readonly'); + txtArea.css( { + 'border-width' : '0px' + }); + + cancelBtn.hide(); + saveBtn.hide(); + }); + + // Create info bar + var infoBar = createInfoBar('Double click on the directory entry to edit it.'); + + // Append user entry into division + $('#' + ueDivId).append(infoBar); + $('#' + ueDivId).append(fieldSet); + $('#' + ueDivId).append(saveBtn); + $('#' + ueDivId).append(cancelBtn); +} + +/** + * Increment number of processes running against a node + * + * @param node Node to increment running processes + */ +function incrementNodeProcess(node) { + // Get current processes + var procs = $.cookie('xcat_' + node + 'processes'); + if (procs) { + // One more process + procs = parseInt(procs) + 1; + $.cookie('xcat_' + node + 'processes', procs); + } else { + $.cookie('xcat_' + node + 'processes', 1); + } +} + +/** + * Update provision new node status + * + * @param data Data returned from HTTP request + */ +function updateZProvisionNewStatus(data) { + // Parse ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + var lastCmd = args[0].replace('cmd=', ''); + var out2Id = args[1].replace('out=', ''); + if (typeof console == "object"){ + console.log("Entering updateZProvisionNewStatus. Last command:<"+lastCmd+"> All args:<"+args+">"); + } + // IDs for status bar, tab, and loader + var statBarId = 'zProvisionStatBar' + out2Id; + var tabId = 'zvmProvisionTab' + out2Id; + var loaderId = 'zProvisionLoader' + out2Id; + + var node = $('#' + tabId + ' input[name=nodeName]').val(); + + /** + * (2) Create user entry + */ + if (lastCmd == 'nodeadd') { + if (rsp.length) { + $('#' + loaderId).hide(); + $('#' + statBarId).find('div').append('
                          (Error) Failed to create node definition
                          '); + } else { + $('#' + statBarId).find('div').append('
                          Node definition created for ' + node + '
                          '); + + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // Create user entry + var userEntry = $('#' + tabId + ' textarea').val(); + $.ajax( { + url : 'lib/zCmd.php', + dataType : 'json', + data : { + cmd : 'mkvm', + tgt : node, + args : '', + att : userEntry, + msg : 'cmd=mkvm;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + /** + * (3) Update /etc/hosts + */ + else if (lastCmd == 'mkvm') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // If there was an error, quit + if (containErrors(prg.html())) { + $('#' + loaderId).hide(); + } else { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makehosts', + tgt : '', + args : '', + msg : 'cmd=makehosts;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + /** + * If sourceforge xcat: (4) Update DNS + */ + else if ((lastCmd == 'makehosts') && (builtInXCAT == 0)) { + // If there was an error, quit + if (rsp.length) { + $('#' + loaderId).hide(); + $('#' + statBarId).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); + } else { + $('#' + statBarId).find('div').append('
                          /etc/hosts updated
                          '); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makedns', + tgt : '', + args : '', + msg : 'cmd=makedns;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + /** + * If built in zVM xcat and last command was makehosts or + * If sourceforge xCAT and lastCmd was makedns + * (5) Add disk + * + */ + else if (((lastCmd == 'makehosts') && (builtInXCAT == 1)) || + ((lastCmd == 'makedns') && (builtInXCAT == 0))) { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // If there was an error, quit + if (rsp.length) { + $('#' + loaderId).hide(); + if (builtInXCAT == 1) { + $('#' + statBarId).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); + } else { + $('#' + statBarId).find('div').append('
                          (Error) Failed to makedns
                          '); + } + } else { + if (builtInXCAT == 1) { + $('#' + statBarId).find('div').append('
                          /etc/hosts updated
                          '); + } else { + $('#' + statBarId).find('div').append('
                          makedns updated
                          '); + } + + // Set cookie for number of disks + var diskRows = $('#' + tabId + ' table:eq(0):visible tbody tr'); + $.cookie('xcat_disks2add' + out2Id, diskRows.length, {path: '/xcat', secure:true }); + if (diskRows.length > 0) { + for (var i = 0; i < diskRows.length; i++) { + var diskArgs = diskRows.eq(i).find('td'); + var type = diskArgs.eq(1).find('select').val(); + var address = diskArgs.eq(2).find('input').val(); + var size = diskArgs.eq(3).find('input').val(); + var mode = diskArgs.eq(4).find('select').val(); + var pool = diskArgs.eq(5).find('select').val(); + var password = diskArgs.eq(6).find('input').val(); + + // Create ajax arguments + var args = ''; + if (type == '3390') { + args = '--add' + type + ';' + pool + ';' + address + + ';' + size + ';' + mode + ';' + password + ';' + + password + ';' + password; + } else if (type == '9336') { + args = '--add' + type + ';' + pool + ';' + address + ';' + + size + ';' + mode + ';' + password + ';' + + password + ';' + password; + } + + // Attach disk to node + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : args, + msg : 'cmd=chvm-disk;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + // Set cookie for number of zFCP devices + var zfcpRows = $('#' + tabId + ' table:eq(1):visible tbody tr'); + $.cookie('xcat_zfcp2add' + out2Id, zfcpRows.length, {path: '/xcat', secure:true }); + if (zfcpRows.length > 0) { + for ( var i = 0; i < zfcpRows.length; i++) { + var diskArgs = zfcpRows.eq(i).find('td'); + var address = diskArgs.eq(1).find('input').val(); + var size = diskArgs.eq(2).find('input').val(); + var pool = diskArgs.eq(3).find('select').val(); + var tag = diskArgs.eq(4).find('input').val(); + var portName = diskArgs.eq(5).find('input').val(); + var unitNo = diskArgs.eq(6).find('input').val(); + + // This is either true or false + var loaddev = diskArgs.eq(7).find('input').attr('checked'); + if (loaddev) { + loaddev = "1"; + } else { + loaddev = "0"; + } + + // Create ajax arguments + var args = '--addzfcp;' + pool + ';' + address + ';' + loaddev + ';' + size; + if (tag && tag != "null") { + args += ';' + tag; + } else { + args += ';'; + } + if (portName && tag != "null") { + args += ';' + portName; + } else { + args += ';'; + } if (unitNo && tag != "null") { + args += ';' + unitNo; + } else { + args += ';'; + } + + // Attach zFCP device to node + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : args, + msg : 'cmd=chvm-zfcp;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + // Done if no disks to add + if (diskRows.length < 1 && zfcpRows.length < 1) { + $('#' + statBarId).find('div').append('
                          No disks found to provison, finished.
                          '); + $('#' + loaderId).hide(); + } + } + } + + /** + * (6) Set operating system for given node + */ + else if (lastCmd == 'chvm-disk' || lastCmd == 'chvm-zfcp') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // If there was an error, quit + if (containErrors(prg.html())) { + $('#' + loaderId).hide(); + } else { + // Set cookie for number of disks + // One less disk to add + var disks2add = $.cookie('xcat_disks2add' + out2Id); + if (lastCmd == 'chvm-disk') { + if (disks2add > 0) { + disks2add--; + $.cookie('xcat_disks2add' + out2Id, disks2add, {path: '/xcat', secure:true }); + } + } + + var zfcp2add = $.cookie('xcat_zfcp2add' + out2Id); + if (lastCmd == 'chvm-zfcp') { + if (zfcp2add > 0) { + zfcp2add--; + $.cookie('xcat_zfcp2add' + out2Id, zfcp2add, {path: '/xcat', secure:true }); + } + } + + // Only set operating system if there are no more disks to add + if (zfcp2add < 1 && disks2add < 1) { + // If an operating system image is given + var osImage = $('#' + tabId + ' select[name=os]:visible').val(); + if (osImage) { + // Get operating system, architecture, provision method, and profile + var tmp = osImage.split('-'); + var os = tmp[0]; + var arch = tmp[1]; + var profile = tmp[3]; + + // If the last disk is added + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : node + ';noderes.netboot=zvm;nodetype.os=' + + os + ';nodetype.arch=' + arch + + ';nodetype.profile=' + profile, + msg : 'cmd=noderes;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } else { + $('#' + loaderId).hide(); + } + } + } + } + + /** + * (7) If sourceforge xCAT Update DHCP + */ + else if ((lastCmd == 'noderes') && (builtInXCAT == 0)) { + // If there was an error, do not continue + if (rsp.length) { + $('#' + loaderId).hide(); + $('#' + statBarId).find('div').append('
                          (Error) Failed to set operating system
                          '); + } else { + $('#' + statBarId).find('div').append('
                          Operating system for ' + node + ' set
                          '); + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makedhcp', + tgt : '', + args : '-a', + msg : 'cmd=makedhcp;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + /** + * (8) Prepare node for boot + */ + else if (((lastCmd == 'noderes') && (builtInXCAT == 1)) || + ((lastCmd == 'makedhcp') && (builtInXCAT == 0))) { + // If there was an error, do not continue + if (rsp.length) { + $('#' + loaderId).hide(); + if (builtInXCAT == 1) { + $('#' + statBarId).find('div').append('
                          (Error) Failed to set operating system
                          '); + } else { + $('#' + statBarId).find('div').append('
                          (Error) Failed to make dhcp
                          '); + } + } else { + if (builtInXCAT == 1) { + $('#' + statBarId).find('div').append('
                          Operating system for ' + node + ' set
                          '); + } else { + $('#' + statBarId).find('div').append('
                          DHCP for ' + node + ' set
                          '); + } + + // Prepare node for boot + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeset', + tgt : node, + args : 'install', + msg : 'cmd=nodeset;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + /** + * (9) Boot node to network + */ + else if (lastCmd == 'nodeset') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // If there was an error + // Do not continue + if (containErrors(prg.html())) { + $('#' + loaderId).hide(); + } else { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rnetboot', + tgt : node, + args : 'ipl=000C', + msg : 'cmd=rnetboot;out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } + + /** + * (10) Done + */ + else if (lastCmd == 'rnetboot') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + if (prg.html().indexOf('Error') < 0) { + $('#' + statBarId).find('div').append('
                          Open a VNC viewer to see the installation progress.  It might take a couple of minutes before you can connect.
                          '); + } + + // Hide loader + $('#' + loaderId).hide(); + } +} + +/** + * Update the provision existing node status + * + * @param data Data returned from HTTP request + */ +function updateZProvisionExistingStatus(data) { + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + + // Get command invoked + var cmd = args[0].replace('cmd=', ''); + // Get provision tab instance + var inst = args[1].replace('out=', ''); + if (typeof console == "object"){ + console.log("Entering updateZProvisionExistingStatus. Last command:<"+cmd+"> All args:<"+args+">"); + } + + // Get provision tab and status bar ID + var statBarId = 'zProvisionStatBar' + inst; + var tabId = 'zvmProvisionTab' + inst; + + /** + * (2) Prepare node for boot + */ + if (cmd == 'nodeadd') { + // Get operating system + var bootMethod = $('#' + tabId + ' select[name=bootMethod]').val(); + + // Get nodes that were checked + var dTableId = 'zNodesDatatable' + inst; + var tgts = getNodesChecked(dTableId); + + // Prepare node for boot + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeset', + tgt : tgts, + args : bootMethod, + msg : 'cmd=nodeset;out=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionExistingStatus(data); + } + }); + } + + /** + * (3) Boot node from network + */ + else if (cmd == 'nodeset') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // If there was an error, do not continue + if (containErrors(prg.html())) { + var loaderId = 'zProvisionLoader' + inst; + $('#' + loaderId).remove(); + return; + } + + // Get nodes that were checked + var dTableId = 'zNodesDatatable' + inst; + var tgts = getNodesChecked(dTableId); + + // Boot node from network + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rnetboot', + tgt : tgts, + args : 'ipl=000C', + msg : 'cmd=rnetboot;out=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionExistingStatus(data); + } + }); + } + + /** + * (4) Done + */ + else if (cmd == 'rnetboot') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + if (prg.html().indexOf('Error') < 0) { + $('#' + statBarId).find('div').append('
                          Open a VNC viewer to see the installation progress.  It might take a couple of minutes before you can connect.
                          '); + } + + var loaderId = 'zProvisionLoader' + inst; + $('#' + loaderId).remove(); + } +} + +/** + * Update zVM node status + * + * @param data Data returned from HTTP request + */ +function updateZNodeStatus(data) { + var node = data.msg; + var rsp = data.rsp; + + // Get cookie for number processes performed against this node + var actions = $.cookie('xcat_' + node + 'processes'); + // One less process + actions = actions - 1; + $.cookie('xcat_' + node + 'processes', actions, {path: '/xcat', secure:true }); + + if (actions < 1) { + // Hide loader when there are no more processes + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).hide(); + } + + var statBarId = node + 'StatusBar'; + + // Write ajax response to status bar + var prg = writeRsp(rsp, node + ': '); + $('#' + statBarId).find('div').append(prg); +} + +/** + * Update clone status + * + * @param data Data returned from HTTP request + */ +function updateZCloneStatus(data) { + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + var cmd = args[0].replace('cmd=', ''); + + // Get provision instance + var inst = args[1].replace('inst=', ''); + // Get output division ID + var out2Id = args[2].replace('out=', ''); + + /** + * (2) Update /etc/hosts + */ + if (cmd == 'nodeadd') { + var node = args[3].replace('node=', ''); + + // If there was an error, do not continue + if (rsp.length) { + $('#' + out2Id).find('img').hide(); + $('#' + out2Id).find('div').append('
                          (Error) Failed to create node definition
                          '); + } else { + $('#' + out2Id).find('div').append('
                          Node definition created for ' + node + '
                          '); + + // If last node definition was created + var tmp = inst.split('/'); + if (tmp[0] == tmp[1]) { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makehosts', + tgt : '', + args : '', + msg : 'cmd=makehosts;inst=' + inst + ';out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZCloneStatus(data); + } + }); + } + } + } + + /** + * (3a) Update DNS if source forge xCAT then do makedns + */ + else if ((cmd == 'makehosts') && (builtInXCAT == 0)) { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + out2Id).find('div').append(prg); + + // If there was an error, do not continue + if (rsp.length) { + $('#' + out2Id).find('img').hide(); + $('#' + out2Id).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); + } else { + $('#' + out2Id).find('div').append('
                          /etc/hosts updated
                          '); + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makedns', + tgt : '', + args : '', + msg : 'cmd=makedns;inst=' + inst + ';out=' + out2Id + }, + + success : function(data) { + data = decodeRsp(data); + updateZCloneStatus(data); + } + }); + } + } + + /** + * (3b) Update DNS for built in xCAT and clone + * Just clone for sourceforge xCAT + */ + else if (((cmd == 'makehosts') && (builtInXCAT == 1)) || + ((cmd == 'makedns') && (builtInXCAT == 0))) { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + out2Id).find('div').append(prg); + + // If there was an error, do not continue + if (rsp.length) { + $('#' + out2Id).find('img').hide(); + if (builtInXCAT == 1) { + $('#' + out2Id).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); + } else { + $('#' + out2Id).find('div').append('
                          (Error) Failed to makedns
                          '); + } + } + // Get clone tab + var tabId = out2Id.replace('CloneStatusBar', 'CloneTab'); + + // If a node range is given + var tgtNodeRange = $('#' + tabId + ' input[name=tgtNode]').val(); + var tgtNodes = ''; + if (tgtNodeRange.indexOf('-') > -1) { + var tmp = tgtNodeRange.split('-'); + + // Get node base name + var nodeBase = tmp[0].match(/[a-zA-Z]+/); + // Get the starting index + var nodeStart = parseInt(tmp[0].match(/\d+/)); + // Get the ending index + var nodeEnd = parseInt(tmp[1].match(/\d+/)); + for ( var i = nodeStart; i <= nodeEnd; i++) { + // Do not append comma for last node + if (i == nodeEnd) { + tgtNodes += nodeBase + i.toString(); + } else { + tgtNodes += nodeBase + i.toString() + ','; + } + } + } else { + tgtNodes = tgtNodeRange; + } + + // Get other inputs + var srcNode = $('#' + tabId + ' input[name=srcNode]').val(); + hcp = $('#' + tabId + ' input[name=newHcp]').val(); + var group = $('#' + tabId + ' input[name=newGroup]').val(); + var diskPool = $('#' + tabId + ' input[name=diskPool]').val(); + var diskPw = $('#' + tabId + ' input[name=diskPw]').val(); + if (!diskPw) { + diskPw = ''; + } + + // Clone + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'mkvm', + tgt : tgtNodes, + args : srcNode + ';pool=' + diskPool + ';pw=' + diskPw, + msg : 'cmd=mkvm;inst=' + inst + ';out=' + out2Id + }, + error: function(jqXHR, textStatus) { + $('#' + out2Id).find('div').append('
                          (Error) Failed in clone call with ' + textStatus + '
                          '); + }, + success : function(data) { + data = decodeRsp(data); + updateZCloneStatus(data); + } + }); + } + + /** + * (5) Done + */ + else if (cmd == 'mkvm') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + out2Id).find('div').append(prg); + + // Hide loader + $('#' + out2Id).find('img').hide(); + } +} + +/** + * Get zVM resources + * + * @param data Data from HTTP request + */ +function getZResources(data) { + var tabId = 'zvmResourceTab'; + var info = createInfoBar('Manage storage and networks'); + $('#' + tabId).append(info); + + // Do not continue if there is no output + if (data.rsp.length) { + if (typeof console == "object"){ + console.log("Entering getZResources."); + } + // Push hardware control points into an array + var node, hcp; + var hcpHash = new Object(); + var hostnameHash = new Object(); + for (var i in data.rsp) { + node = data.rsp[i][0]; + hcp = data.rsp[i][1]; + // data will be coming in like "xcat xcat.endicott.ibm.com hosts.hostnames" + // or xcat zhcp.endicott.ibm.com zvm.hcp" + if (data.rsp[i][2]== "zvm.hcp") { + hcpHash[hcp] = 1; + } else { + if (hcp.length) { + hostnameHash[hcp] = node; + } + } + } + + // Create an array for hardware control points + var hcps = new Array(); + for (var key in hcpHash) { + // Get the short host name + //hcp = key.split('.')[0]; //old code + hcp = hostnameHash[key]; + if (typeof console == "object"){ + console.log("getZResources lookup for hostname "+key+" found nodename <"+hcp+">"); + } + if (jQuery.inArray(hcp, hcps) == -1) { + hcps.push(hcp); + } + } + + // Set hardware control point cookie + $.cookie('xcat_hcp', hcps, {path: '/xcat', secure:true }); + + // Delete loader + $('#' + tabId).find('img[src="images/loader.gif"]').remove(); + + // Create accordion panel for disk + var resourcesAccordion = $('
                          '); + var diskSection = $('
                          '); + var diskLnk = $('

                          Disks

                          ').click(function () { + // Do not load panel again if it is already loaded + if ($('#zvmDiskResource').children().length) { + return; + } + else + $('#zvmDiskResource').append(createLoader('')); + + // Resize accordion + $('#zvmResourceAccordion').accordion('resize'); + + // Create a array for hardware control points + var hcps = new Array(); + if ($.cookie('xcat_hcp').indexOf(',') > -1) { + hcps = $.cookie('xcat_hcp').split(','); + } else { + hcps.push($.cookie('xcat_hcp')); + } + + // Query the disk pools for each hcp + var panelId = 'zvmDiskResource'; + var info = $('#' + panelId).find('.ui-state-highlight'); + if (!info.length) { + info = createInfoBar("Querying "+hcps.length+" zhcp(s) for disk pools."); + $('#' + panelId).append(info); + } + zhcpQueryCountForDisks = hcps.length; + + for (var i in hcps) { + var itemcount = +i + 1; + info.append("
                          Querying disk pools from: "+hcps[i]+" ("+itemcount+" of "+hcps.length+")"); + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcps[i], + args : '--diskpoolnames', + msg : hcps[i] + }, + + success : function(data) { + data = decodeRsp(data); + getDiskPool(data); + } + }); + zhcpQueryCountForDisks--; + } + }); + + // Create accordion panel for zFCP devices + var zfcpSection = $('
                          '); + var zfcpLnk = $('

                          zFCP

                          ').click(function () { + // Do not load panel again if it is already loaded + if ($('#zfcpResource').children().length) + return; + else + $('#zfcpResource').append(createLoader('')); + + // Resize accordion + $('#zvmResourceAccordion').accordion('resize'); + + // Create a array for hardware control points + var hcps = new Array(); + if ($.cookie('xcat_hcp').indexOf(',') > -1) { + hcps = $.cookie('xcat_hcp').split(','); + } else { + hcps.push($.cookie('xcat_hcp')); + + } + + // Query the fcp pools for each hcp + var panelId = 'zfcpResource'; + var info = $('#' + panelId).find('.ui-state-highlight'); + if (!info.length) { + info = createInfoBar("Querying "+hcps.length+" zhcp(s) for fcp pools."); + $('#' + panelId).append(info); + } + zhcpQueryCountForZfcps = hcps.length; + for (var i in hcps) { + // Gather fcp pools from hardware control points + var itemcount = +i + 1; + info.append("
                          Querying fcp pools from: "+hcps[i]+" ("+itemcount+" of "+hcps.length+")"); + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcps[i], + args : '--zfcppoolnames', + msg : hcps[i] + }, + + success : function(data) { + data = decodeRsp(data); + getZfcpPool(data); + } + }); + zhcpQueryCountForZfcps--; + } + }); + + // Create accordion panel for network + var networkSection = $('
                          '); + var networkLnk = $('

                          Networks

                          ').click(function () { + // Do not load panel again if it is already loaded + if ($('#zvmNetworkResource').children().length) { + return; + } else { + $('#zvmNetworkResource').append(createLoader('')); + } + + // Resize accordion + $('#zvmResourceAccordion').accordion('resize'); + + // Create a array for hardware control points + var hcps = new Array(); + if ($.cookie('xcat_hcp').indexOf(',') > -1) { + hcps = $.cookie('xcat_hcp').split(','); + } else { + hcps.push($.cookie('xcat_hcp')); + + } + // Query the networks for each + var panelId = 'zvmNetworkResource'; + var info = $('#' + panelId).find('.ui-state-highlight'); + if (!info.length) { + info = createInfoBar("Querying "+hcps.length+" zhcp(s) for networks."); + $('#' + panelId).append(info); + } + zhcpQueryCountForNetworks = hcps.length; + for (var i in hcps) { + var itemcount = +i + 1; + info.append("
                          Querying networks from: "+hcps[i]+" ("+itemcount+" of "+hcps.length+")"); + $('#zvmResourceAccordion').accordion('resize'); + // Gather networks from hardware control points + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcps[i], + args : '--getnetworknames', + msg : hcps[i] + }, + + success : function(data) { + data = decodeRsp(data); + getNetwork(data); + } + }); + zhcpQueryCountForNetworks--; + } + }); + + resourcesAccordion.append(diskLnk, diskSection, zfcpLnk, zfcpSection, networkLnk, networkSection); + + // Append accordion to tab + $('#' + tabId).append(resourcesAccordion); + resourcesAccordion.accordion(); + networkLnk.trigger('click'); + } +} + +/** + * Get node attributes from HTTP request data + * + * @param propNames Hash table of property names + * @param keys Property keys + * @param data Data from HTTP request + * @return Hash table of property values + */ +function getAttrs(keys, propNames, data) { + // Create hash table for property values + var attrs = new Object(); + + // Go through inventory and separate each property out + var curKey = null; // Current property key + var addLine; // Add a line to the current property? + for ( var i = 1; i < data.length; i++) { + addLine = true; + + // Loop through property keys + // Does this line contains one of the properties? + for ( var j = 0; j < keys.length; j++) { + // Find property name + if (data[i].indexOf(propNames[keys[j]]) > -1) { + attrs[keys[j]] = new Array(); + + // Get rid of property name in the line + data[i] = data[i].replace(propNames[keys[j]], ''); + // Trim the line + data[i] = jQuery.trim(data[i]); + + // Do not insert empty line + if (data[i].length > 0) { + attrs[keys[j]].push(data[i]); + } + + curKey = keys[j]; + addLine = false; // This line belongs to a property + } + } + + // Line does not contain a property + // Must belong to previous property + if (addLine && data[i].length > 1) { + data[i] = jQuery.trim(data[i]); + attrs[curKey].push(data[i]); + } + } + + return attrs; +} + +/** + * Create add processor dialog + * + * @param node Node to add processor to + */ +function openAddProcDialog(node) { + // Create form to add processor + var addProcForm = $('
                          '); + // Create info bar + var info = createInfoBar('Add a temporary processor to this virtual server.'); + addProcForm.append(info); + addProcForm.append('
                          '); + addProcForm.append('
                          '); + + // Create drop down for processor type + var procType = $('
                          '); + procType.append(''); + var typeSelect = $(''); + typeSelect.append('' + + '' + + '' + + '' + ); + procType.append(typeSelect); + addProcForm.append(procType); + + // Generate tooltips + addProcForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add processor + addProcForm.dialog({ + title:'Add processor', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var node = $(this).find('input[name=procNode]').val(); + var address = $(this).find('input[name=procAddress]').val(); + var type = $(this).find('select[name=procType]').val(); + + // If inputs are not complete, show warning message + if (!node || !address || !type) { + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this)); + } else { + // Add processor + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--addprocessoractive;' + address + ';' + type, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + var statusId = node + 'StatusBar'; + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).show(); + $('#' + statusId).show(); + + // Close dialog + $(this).dialog( "close" ); + } + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create add disk dialog + * + * @param node Node to add disk to + * @param hcp Hardware control point of node + */ +function openAddDiskDialog(node, hcp) { + // Get list of disk pools + var cookie = $.cookie('xcat_' + hcp + 'diskpools'); + var pools = new Array(); + if (cookie) { + pools = cookie.split(','); + } + + // Create form to add disk + var addDiskForm = $('
                          '); + // Create info bar + var info = createInfoBar('Add a ECKD|3390 or FBA|9336 disk to this virtual server.'); + addDiskForm.append(info); + addDiskForm.append('
                          '); + addDiskForm.append('
                          '); + addDiskForm.append('
                          '); + addDiskForm.append('
                          '); + + // Create drop down for disk pool + var diskPool = $('
                          '); + diskPool.append(''); + var poolSelect = $(''); + for ( var i = 0; i < pools.length; i++) { + if( !pools[i] || 0 === pools[i].length) continue; + poolSelect.append(''); + } + diskPool.append(poolSelect); + addDiskForm.append(diskPool); + + // Create drop down for disk mode + var diskMode = $('
                          '); + diskMode.append(''); + var modeSelect = $(''); + modeSelect.append('' + + '' + + '' + + '' + + '' + + '' + + '' + ); + diskMode.append(modeSelect); + addDiskForm.append(diskMode); + + addDiskForm.append('
                          '); + + // Generate tooltips + addDiskForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add disk + addDiskForm.dialog({ + title:'Add disk', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var node = $(this).find('input[name=diskNode]').val(); + var type = $(this).find('select[name=diskType]').val(); + var address = $(this).find('input[name=diskAddress]').val(); + var size = $(this).find('input[name=diskSize]').val(); + var pool = $(this).find('select[name=diskPool]').val(); + var mode = $(this).find('select[name=diskMode]').val(); + var password = $(this).find('input[name=diskPassword]').val(); + + // If inputs are not complete, show warning message + if (!node || !type || !address || !size || !pool || !mode) { + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this)); + } else { + // Add disk + if (type == '3390') { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--add3390;' + pool + ';' + address + ';' + size + + ';' + mode + ';' + password + ';' + password + ';' + password, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + var statusId = node + 'StatusBar'; + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).show(); + $('#' + statusId).show(); + } else if (type == '9336') { + // Default block size for FBA volumes = 512 + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--add9336;' + pool + ';' + address + ';' + size + + ';' + mode + ';' + password + ';' + password + ';' + password, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + var statusId = node + 'StatusBar'; + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).show(); + $('#' + statusId).show(); + } + + // Close dialog + $(this).dialog( "close" ); + } // End of else + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create add zFCP device dialog + * + * @param node Node to add disk to + * @param hcp Hardware control point of node + * @param zvm The z/VM system of node + */ +function openAddZfcpDialog(node, hcp, zvm) { + // Get list of disk pools + var cookie = $.cookie('xcat_' + hcp + 'zfcppools'); + var pools = new Array(); + if (cookie) { + pools = cookie.split(','); + } + + // Create form to add disk + var addZfcpForm = $('
                          '); + // Create info bar + var info = createInfoBar('Add a SCSI|FCP disk to this virtual server.'); + addZfcpForm.append(info); + addZfcpForm.append('
                          '); + addZfcpForm.append('
                          '); + addZfcpForm.append('
                          '); + addZfcpForm.append('
                          '); + + // Create drop down for disk pool + var diskPool = $('
                          '); + diskPool.append(''); + var poolSelect = $(''); + for ( var i = 0; i < pools.length; i++) { + if( !pools[i] || 0 === pools[i].length) continue; + poolSelect.append(''); + } + diskPool.append(poolSelect); + addZfcpForm.append(diskPool); + + // Tag to identify where device will be used + addZfcpForm.append('
                          '); + + // Create advanced link to set advanced zFCP properties + var advancedLnk = $(''); + addZfcpForm.append(advancedLnk); + var advanced = $('
                          ').hide(); + addZfcpForm.append(advanced); + + var portName = $('
                          '); + var unitNo = $('
                          '); + advanced.append(portName, unitNo); + + // Toggle port name and unit number when clicking on advanced link + advancedLnk.click(function() { + advanced.toggle(); + }); + + // Generate tooltips + addZfcpForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add disk + addZfcpForm.dialog({ + title:'Add zFCP device', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var node = $(this).find('input[name=diskNode]').val(); + var address = $(this).find('input[name=diskAddress]').val(); + var loaddev = $(this).find('input[name=diskLoaddev]'); + var size = $(this).find('input[name=diskSize]').val(); + var pool = $(this).find('select[name=diskPool]').val(); + var tag = $(this).find('input[name=diskTag]').val(); + var portName = $(this).find('input[name=diskPortName]').val(); + var unitNo = $(this).find('input[name=diskUnitNo]').val(); + + // If inputs are not complete, show warning message + if (!node || !address || !size || !pool) { + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this)); + } else { + if (loaddev.attr('checked')) { + loaddev = 1; + } else { + loaddev = 0; + } + + var args = '--addzfcp||' + pool + '||' + address + '||' + loaddev + '||' + size; + + if (tag && tag != "null") { + args += '||' + tag; + } else { + args += '|| ""'; + } + + if ((portName && portName != "null") && (unitNo && unitNo != "null")) { + args += '||' + portName + '||' + unitNo; + } + + // Add zFCP device + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : args, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + var statusId = node + 'StatusBar'; + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).show(); + $('#' + statusId).show(); + + // Close dialog + $(this).dialog( "close" ); + } + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create dedicate device dialog + * + * @param node Node to dedicate device to + * @param hcp Hardware control point of node + */ +function openDedicateDeviceDialog(node, hcp) { + // Create form to add disk + var dedicateForm = $('
                          '); + // Create info bar + var info = createInfoBar('Add a dedicated device to the configuration'); + dedicateForm.append(info); + + dedicateForm.append('
                          '); + dedicateForm.append('
                          '); + dedicateForm.append('
                          '); + dedicateForm.append('
                          '); + + // Generate tooltips + dedicateForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add dedicated device + dedicateForm.dialog({ + title:'Add dedicated device', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var node = $(this).find('input[name=diskNode]').val(); + var vAddress = $(this).find('input[name=virtualAddress]').val(); + var rAddress = $(this).find('input[name=realAddress]').val() + var mode = $(this).find('select[name=mode]').val(); + + // If inputs are not complete, show warning message + if (!node || !vAddress || !rAddress || !mode) { + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this)); + } else { + var args = '--dedicatedevice;' + vAddress + ';' + rAddress + ';' + mode; + + // Add zFCP device + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : args, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + var statusId = node + 'StatusBar'; + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).show(); + $('#' + statusId).show(); + + // Close dialog + $(this).dialog( "close" ); + } + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create add ECKD to system dialog + * + * @param hcp Hardware control point of node + */ +function openAddEckd2SystemDialog(hcp) { + var dialogId = 'zvmAddEckd2System'; + + // Create form to add disk + var addE2SForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Create info bar + var info = createInfoBar('Dynamically add an ECKD disk to a running z/VM system.'); + addE2SForm.append(info); + + addE2SForm.append(system); + addE2SForm.append('
                          '); + + // Generate tooltips + addE2SForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add disk + addE2SForm.dialog({ + title:'Add ECKD to system', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 420, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var system = $(this).find('select[name=system]').val(); + var devnum = $(this).find('input[name=devNum]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=devNum]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : "--addeckd;" + devnum, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create add Volume to system dialog + * + * @param hcp Hardware control point of node + */ +function openAddVolume2SystemDialog(hcp) { + var dialogId = 'zvmAddVolume2System'; + + // Create form to add volume + var addV2SForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Create info bar + var info = createInfoBar('Permanently add a volume to the z/VM system configuration.'); + addV2SForm.append(info); + + addV2SForm.append(system); + addV2SForm.append('
                          '); + addV2SForm.append('
                          '); + + // Generate tooltips + addV2SForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add volume + addV2SForm.dialog({ + title:'Add volume to system configuration', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 480, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var system = $(this).find('select[name=system]').val(); + var devnum = $(this).find('input[name=devNum]').val(); + var volser = $(this).find('input[name=volser]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=devNum]', 'input[name=volser]' ); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : "--addvolume;" + devnum + ";" + volser, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create remove Volume to system dialog + * + * @param hcp Hardware control point of node + */ +function openRemoveVolumeFromSystemDialog(hcp) { + var dialogId = 'zvmRemoveVolumeFromSystem'; + + // Create form to remove volume + var remVfromSForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Create info bar + var info = createInfoBar('Permanently remove a volume from the z/VM system configuration.'); + remVfromSForm.append(info); + + remVfromSForm.append(system); + remVfromSForm.append('
                          '); + remVfromSForm.append('
                          '); + + // Generate tooltips + remVfromSForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to remove volume + remVfromSForm.dialog({ + title:'Remove volume from system configuration', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 580, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var system = $(this).find('select[name=system]').val(); + var devnum = $(this).find('input[name=devNum]').val(); + var volser = $(this).find('input[name=volser]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=devNum]', 'input[name=volser]' ); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : "--removevolume;" + devnum + ";" + volser, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create add page or spool dialog + * + * @param hcp Hardware control point of node + */ +function openAddPageSpoolDialog(hcp) { + var dialogId = 'zvmAddPageSpool'; + + // Create form to add disk + var addPageSpoolForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Create info bar + var info = createInfoBar('Add a page or spool volume to be used by zVM.'); + addPageSpoolForm.append(info); + + var diskFS = $('
                          Disk
                          '); + addPageSpoolForm.append(diskFS); + var diskAttr = $('
                          '); + diskFS.append($('
                          ')); + diskFS.append(diskAttr); + + diskAttr.append(system); + diskAttr.append('
                          '); + diskAttr.append('
                          '); + diskAttr.append('
                          '); + + // Generate tooltips + addPageSpoolForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add disk + addPageSpoolForm.dialog({ + title:'Add page or spool', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var system = $(this).find('select[name=system]').val(); + var volAddr = $(this).find('input[name=volAddr]').val(); + var volLabel = $(this).find('input[name=volLabel]').val(); + var volUse = $(this).find('select[name=volUse]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=volAddr]', 'input[name=volLabel]', 'select[name=volUse]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + var pageSpoolArgs = volAddr + ";" + volLabel + ";" + volUse; + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : system, + args : '--addpagespool;' + pageSpoolArgs, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Open dialog to share disk + * + * @param disks2share Disks selected in table + */ +function openShareDiskDialog(disks2share) { + // Create form to share disk + var dialogId = 'zvmShareDisk'; + var shareDiskForm = $('
                          '); + + var args = disks2share.split(';'); + var tgtHcp = args[0]; + var tgtVol = args[1]; + + if (!tgtVol || tgtVol == "undefined") + tgtVol = ""; + + // Create info bar + var info = createInfoBar('Indicate a full-pack minidisk is to be shared by the users of many real and virtual systems.'); + shareDiskForm.append(info); + + // Set region input based on those selected on table (if any) + var node = $('
                          '); + var volAddr = $('
                          '); + var shareEnable = $('
                          '); + shareDiskForm.append(node, volAddr, shareEnable); + + // Generate tooltips + shareDiskForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to delete disk + shareDiskForm.dialog({ + title:'Share disk', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var node = $(this).find('input[name=node]').val(); + var volAddr = $(this).find('input[name=volAddr]').val(); + var shareEnable = $(this).find('select[name=shareEnable]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('input[name=node]', 'input[name=volAddr]', 'select[name=shareEnable]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + // Remove disk from pool + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : "--sharevolume;" + volAddr + ";" + shareEnable, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Create add SCSI 2 system dialog + * + * @param hcp Hardware control point of node + */ +function openAddScsi2SystemDialog(hcp) { + var dialogId = 'zvmAddScsi2System'; + + // Create form to add disk + var addS2SForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + // Create info bar + var info = createInfoBar('Dynamically add an SCSI disk to a running z/VM system as an EDEV.'); + addS2SForm.append(info); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + var devNo = $('
                          '); + var devPathLabel = $(''); + var devPathCount = 1; + //var pathDiv = $('
                          '); + + var devPathDiv = $('
                          '); + var devPathTable = $('
                          '); + var devPathHeader = $(' FCP Device WWPN LUN'); + // Adjust header width + devPathHeader.find('th').css({ + 'width' : '120px' + }); + devPathHeader.find('th').eq(0).css({ + 'width' : '20px' + }); + var devPathBody = $(''); + var devPathFooter = $(''); + + // Create a row + var devPathRow = $(''); + + // Add blank column (remove button replacement) + devPathRow.append(''); + + // Create FCP device number input + var fcpDevNum = $(''); + devPathRow.append(fcpDevNum); + + // Create FCP WWPN input + var fcpWwpn = $(''); + devPathRow.append(fcpWwpn); + + if ($.cookie('xcat_zvms')) { + zvms = $.cookie('xcat_zvms').split(','); + var zvm; + for (var i in zvms) { + if( !zvms[i] || 0 === zvms[i].length) continue; + var args = zvms[i].split(':'); + var zvm = args[0].toLowerCase(); + var iHcp = args[1]; + } + } + + // Create FCP LUN input + var fcpLun = $(''); + devPathRow.append(fcpLun); + + devPathBody.append(devPathRow); + + var addDevPathLink = $('+ Add path'); + addDevPathLink.bind('click', function(event){ + devPathCount = devPathCount + 1; + // Create a row + var devPathRow = $(''); + + // Add remove button + var removeBtn = $('').css({ + "float": "left", + "cursor": "pointer" + }); + var col = $('').append(removeBtn); + removeBtn.bind('click', function(event) { + $(this).parent().parent().remove(); + }); + devPathRow.append(col); + + // Create FCP device number input + var fcpDevNum = $(''); + devPathRow.append(fcpDevNum); + + // Create FCP WWPN input + var fcpWwpn = $(''); + devPathRow.append(fcpWwpn); + + // Create FCP LUN input + var fcpLun = $(''); + devPathRow.append(fcpLun); + + devPathBody.append(devPathRow); + + // Generate tooltips + addS2SForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + }); + devPathFooter.append(addDevPathLink); + devPathTable.append(devPathHeader); + devPathTable.append(devPathBody); + devPathTable.append(devPathFooter); + devPathDiv.append(devPathLabel); + devPathDiv.append(devPathTable); + + var option = $('
                          '); + var persist = $('
                          '); + addS2SForm.append(system, devNo, devPathDiv, option, persist); + + // Generate tooltips + addS2SForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + addS2SForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + + // Open dialog to add disk + addS2SForm.dialog({ + title:'Add SCSI to running system', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 675, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var system = $(this).find('select[name=system]').val(); + var devNo = $(this).find('input[name=devNo]').val(); + var pathArray = ""; + jQuery('.devPath').each(function(index) { + pathArray += $(this).find('input[name=fcpDevNum]').val() + ','; + pathArray += $(this).find('input[name=fcpWwpn]').val() + ','; + pathArray += $(this).find('input[name=fcpLun]').val() + ';'; + }); + var option = $(this).find('select[name=option]').val(); + var persist = $(this).find('select[name=persist]').val(); + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=fcpDevNum]', 'select[name=option]', 'select[name=persist]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + // Show warning message + if (!ready || !pathArray) { + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : "--addscsi||" + devNo + "||" + pathArray + "||" + option + "||" + persist, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Delete a real SCSI disk + * + * @param hcp Hardware control point of node + */ +function openRemoveScsiDialog(hcp) { + var dialogId = 'zvmRemoveScsiDialog'; + // Create form to add disk + var removeScsiForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Create info bar + var info = createInfoBar('Delete a real SCSI disk'); + removeScsiForm.append(info, system); + removeScsiForm.append('
                          '); + removeScsiForm.append('
                          '); + addNicForm.append('
                          '); + + // Create drop down for NIC types + var nicType = $('
                          '); + nicType.append(''); + var nicTypeSelect = $(''); + nicTypeSelect.append('' + + '' + + '' + ); + nicType.append(nicTypeSelect); + addNicForm.append(nicType); + + // Create drop down for network types + var networkType = $('
                          '); + networkType.append(''); + var networkTypeSelect = $(''); + networkTypeSelect.append('' + + '' + + '' + ); + networkType.append(networkTypeSelect); + addNicForm.append(networkType); + var hashtable = getselectedNetworkHash(); + if (!hashtable) { + hashtable = [[]]; + setselectedNetworkHash(hashtable); + + if (typeof console == "object") { + console.log("openAddNicDialog. creating new hash[[]] table." ); + } + } + + // Create drop down for network names + var gLansQdioSelect = $(''); + var gLansHipersSelect = $(''); + var vswitchSelect = $(''); + for ( var i = 0; i < networks.length; i++) { + if( !networks[i] || 0 === networks[i].length) continue; + var network = networks[i].split(' '); + var networkOption = $(''); + if (network[0] == 'VSWITCH') { + vswitchSelect.append(networkOption); + + // Load and save specific vswitch details in global table if not there + network[2] = jQuery.trim(network[2]); // Remove new line x012 from end + if (typeof hashtable[node + '_NIC_' + network[2]] === 'undefined') { + if (typeof console == "object"){ + console.log("Calling getNetworkDetails for switch:<"+network[2]+">"); + } + ajaxrequest = 1; + getNetworkDetails(hcpNode, network[2], node + '_NIC_' + network[2], ''); + } + } else if (network[0] == 'LAN:QDIO') { + gLansQdioSelect.append(networkOption); + } else if (network[0] == 'LAN:HIPERS') { + gLansHipersSelect.append(networkOption); + } + } + + // Hide network name drop downs until the NIC type and network type is selected + // QDIO Guest LAN drop down + var guestLanQdio = $('
                          ').hide(); + guestLanQdio.append(''); + guestLanQdio.append(gLansQdioSelect); + addNicForm.append(guestLanQdio); + + // HIPERS Guest LAN drop down + var guestLanHipers = $('
                          ').hide(); + guestLanHipers.append(''); + guestLanHipers.append(gLansHipersSelect); + addNicForm.append(guestLanHipers); + + // VSWITCH drop down + var vswitch = $('
                          ').hide(); + vswitch.append(''); + vswitch.append(vswitchSelect); + + // VLAN id with Porttype + var vswitchvlan = $('
                          '); + vswitchvlan.append('
                          '); + var vswitchPorttype = $(''); + vswitchvlan.append(vswitchPorttype); + vswitchvlan.append('
                          '); + var vswitchVLANId = $(''); + vswitchvlan.append(vswitchVLANId); + + vswitch.append(vswitchvlan); + vswitchvlan.hide(); + addNicForm.append(vswitch); + + // Show network names on change + networkTypeSelect.change(function(){ + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + var networkType = $(this).val(); + + if (typeof console == "object"){ + console.log("Entering networkTypeSelect.change"); + } + // Get NIC type and network type + var nicType = $(this).parent().parent().find('select[name=nicType]').val(); + var networkType = $(this).val(); + + // Hide network name drop downs + var guestLanQdio = $(this).parent().parent().find('select[name=nicLanQdioName]').parent(); + var guestLanHipers = $(this).parent().parent().find('select[name=nicLanHipersName]').parent(); + var vswitch = $(this).parent().parent().find('select[name=nicVSwitchName]').parent(); + var mynode = $(this).parent().parent().find('input[name=nicNode]').val(); + var showvlan = $(this).parent().parent().find('select[name=vswitchVLANporttype]').parent(); + var hashtable = getselectedNetworkHash(); + guestLanQdio.hide(); + guestLanHipers.hide(); + vswitch.hide(); + + // Show correct network name + if (networkType == 'Guest LAN' && nicType == 'QDIO') { + guestLanQdio.show(); + } else if (networkType == 'Guest LAN' && nicType == 'HiperSockets') { + guestLanHipers.show(); + } else if (networkType == 'Virtual Switch') { + if (nicType == 'QDIO') { + vswitch.show(); + // Show vlan information only if vlan aware + var switchname = $(this).parent().parent().find('select[name=nicVSwitchName]').val(); + var tokens = switchname.split(' '); + var switchkeyid = mynode + '_NIC_' + jQuery.trim(tokens[1]); + if (typeof console == "object"){ + console.log("Checking vswitch index:"+switchkeyid); + } + + // Is this a vlanaware switch, if so show the special fields + if (hashtable[switchkeyid]["vlan_awareness"] == "AWARE") { + showvlan.find('input[name=vswitchvlanid]').val(hashtable[switchkeyid]["vlan_id"]); + showvlan.find('select[name=vswitchVLANporttype]').val(hashtable[switchkeyid]["port_type"]); + showvlan.show(); + } else { + showvlan.hide(); + showvlan.find('input[name=vswitchvlanid]').val('default'); + showvlan.find('select[name=vswitchVLANporttype]').val('default'); + } + } else { + // No such thing as HIPERS VSWITCH + var warn = createWarnBar('The selected choices are not valid.'); + warn.prependTo($(this).parent().parent()); + } + } + }); + + // + // Show network names on change + // + nicTypeSelect.change(function(){ + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + + if (typeof console == "object"){ + console.log("Entering nicTypeSelect.change"); + } + + // Get NIC type and network type + var nicType = $(this).val(); + var networkType = $(this).parent().parent().find('select[name=nicNetworkType]').val(); + var mynode = $(this).parent().parent().find('input[name=nicNode]').val(); + + // Hide network name drop downs + var guestLanQdio = $(this).parent().parent().find('select[name=nicLanQdioName]').parent(); + var guestLanHipers = $(this).parent().parent().find('select[name=nicLanHipersName]').parent(); + var vswitch = $(this).parent().parent().find('select[name=nicVSwitchName]').parent(); + var showvlan = $(this).parent().parent().find('select[name=vswitchVLANporttype]').parent(); + var hashtable = getselectedNetworkHash(); + guestLanQdio.hide(); + guestLanHipers.hide(); + vswitch.hide(); + + // Show correct network name + if (networkType == 'Guest LAN' && nicType == 'QDIO') { + guestLanQdio.show(); + } else if (networkType == 'Guest LAN' && nicType == 'HiperSockets') { + guestLanHipers.show(); + } else if (networkType == 'Virtual Switch') { + if (nicType == 'QDIO') { + vswitch.show(); + var switchname = $(this).parent().parent().find('select[name=nicVSwitchName]').val(); + var tokens = switchname.split(' '); + var switchkeyid = mynode + '_NIC_' + jQuery.trim(tokens[1]); + + if (typeof console == "object"){ + console.log("Entering nictypeselect.change. switchkey:<"+switchkeyid); + } + + // Is this a vlanaware switch, if so show the special fields + if (hashtable[switchkeyid]["vlan_awareness"] == "AWARE") { + showvlan.find('input[name=vswitchvlanid]').val(hashtable[switchkeyid]["vlan_id"]); + showvlan.find('select[name=vswitchVLANporttype]').val(hashtable[switchkeyid]["port_type"]); + showvlan.show(); + } else { + showvlan.hide(); + showvlan.find('input[name=vswitchvlanid]').val('default'); + showvlan.find('select[name=vswitchVLANporttype]').val('default'); + } + + } else { + // No such thing as HIPERS VSWITCH + var warn = createWarnBar('The selected choices are not valid.'); + warn.prependTo($(this).parent().parent()); + } + } + }); + + // + // Determine if vlanid fields need to be shown based on vswitch + // + vswitchSelect.change(function(){ + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + + // Get vlan id division + var showvlan = $(this).parent().parent().find('select[name=vswitchVLANporttype]').parent(); + + // Get selected switch name and break it into tokens + var switchname = $(this).val(); + var tokens = switchname.split(' '); + + // Get the node we are doing this for and index for hash table + var mynode = $(this).parent().parent().find('input[name=nicNode]').val(); + + var tokens = switchname.split(' '); + var switchkeyid = mynode + '_NIC_' + jQuery.trim(tokens[1]); + var hashtable = getselectedNetworkHash(); + + if (typeof console == "object"){ + console.log("Entering vswitchselect.change. switchkey:<"+switchkeyid+">"); + } + // Is this a vlanaware switch, if so show the special fields + if (hashtable[switchkeyid]["vlan_awareness"] == "AWARE") { + $(this).find('').val(hashtable[switchkeyid]["vlan_id"]); + showvlan.find('input[name=vswitchvlanid]').val(hashtable[switchkeyid]["vlan_id"]); + showvlan.find('select[name=vswitchVLANporttype]').val(hashtable[switchkeyid]["port_type"]); + showvlan.show(); + } else { + showvlan.hide(); + showvlan.find('input[name=vswitchvlanid]').val('default'); + showvlan.find('select[name=vswitchVLANporttype]').val('default'); + } + }); + + + // Generate tooltips + addNicForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + + // Open dialog to add NIC + addNicForm.dialog({ + title:'Add NIC', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var ready = true; + var errMsg = ''; + + // Get inputs + var node = $(this).find('input[name=nicNode]').val(); + var nicType = $(this).find('select[name=nicType]').val(); + var networkType = $(this).find('select[name=nicNetworkType]').val(); + var address = $(this).find('input[name=nicAddress]').val(); + + // If inputs are not complete, show warning message + if (!node || !nicType || !networkType || !address) { + errMsg = 'Please provide a value for each missing field.
                          '; + ready = false; + } + + // If a HIPERS VSWITCH is selected, show warning message + if (nicType == 'HiperSockets' && networkType == 'Virtual Switch') { + errMsg += 'The selected choices are not valid.'; + ready = false; + } + + // If there are errors + if (!ready) { + // Show warning message + var warn = createWarnBar(errMsg); + warn.prependTo($(this)); + } else { + // Add guest LAN + if (networkType == 'Guest LAN') { + var temp; + if (nicType == 'QDIO') { + temp = $(this).find('select[name=nicLanQdioName]').val().split(' '); + } else { + temp = $(this).find('select[name=nicLanHipersName]').val().split(' '); + } + + var lanOwner = temp[0]; + var lanName = temp[1]; + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--addnic;' + address + ';' + nicType + ';3', + msg : 'node=' + node + ';addr=' + address + ';lan=' + + lanName + ';owner=' + lanOwner + }, + success : function(data) { + data = decodeRsp(data); + connect2GuestLan(data); + } + }); + } + + // Add virtual switch + else if (networkType == 'Virtual Switch' && nicType == 'QDIO') { + var temp = $(this).find('select[name=nicVSwitchName]').val().split(' '); + var vswitchName = jQuery.trim(temp[1]); + var switchkeyid = node + '_NIC_' + vswitchName; + var hashtable = getselectedNetworkHash(); + var awareornot = hashtable[switchkeyid]["vlan_awareness"]; + var porttype = $(this).find('select[name=vswitchVLANporttype]').val(); + var lanid = $(this).find('input[name=vswitchvlanid]').val(); + + // Pass additional lanid data in msg for grant use by connect2VSwitch + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--addnic;' + address + ';' + nicType + ';3', + msg : 'node=' + node + ';addr=' + address + ';vsw=' + + vswitchName + ';vlanaware=' + awareornot + ';porttype=' + + porttype + ';lanid=' + lanid + }, + + success : function(data) { + data = decodeRsp(data); + connect2VSwitch(data); + } + }); + } + + // Increment node process + incrementNodeProcess(node); + + // Show loader + $('#' + node + 'StatusBarLoader').show(); + $('#' + node + 'StatusBar').show(); + + // Close dialog + $(this).dialog( "close" ); + } // End of else + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); + // Make sure ajax is done before putting up dialog + $(document).ajaxStop(function() { + //Remove loading vswitch gif status bar + statBar.hide(); + }); + if (ajaxrequest == 0) { + //Remove loading vswitch gif status bar + statBar.hide(); + } + +} + +/** + * Create add vSwitch/VLAN dialog + * + * @param hcp Hardware control point of node + */ +function openAddVswitchVlanDialog(hcp) { + var dialogId = 'zvmAddVswitchVlan'; + + // Create form to add disk + var addVswitchForm = $('
                          '); + + // Create info bar + var info = createInfoBar('Create a virtual switch or virtual network LAN.'); + + var netFS = $('
                          '); + var netLegend = $('Network'); + netFS.append(netLegend); + + var typeFS = $('
                          ').hide(); + var typeLegend = $('Network'); + typeFS.append(typeLegend); + addVswitchForm.append(info, netFS, typeFS); + + var netAttr = $('
                          '); + netFS.append($('
                          ')); + netFS.append(netAttr); + + var networkTypeDiv = $('
                          '); + var networkType = $('
                          '); + networkTypeDiv.append(networkType) + netAttr.append(networkTypeDiv); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + netAttr.append(system); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + var typeAttr = $('
                          '); + typeFS.append($('
                          ')); + typeFS.append(typeAttr); + + // Create vSwitch parameters + var vswitchOptions = $('
                          ').hide(); + vswitchOptions.append($('
                          ')); + vswitchOptions.append($('
                          ')); + vswitchOptions.append($('
                          ')); + + // Create an advanced link to configure optional network settings + var advancedLnk = $(''); + vswitchOptions.append(advancedLnk); + var advanced = $('
                          ').hide(); + vswitchOptions.append(advanced); + + // Show IP address and hostname inputs on-click + advancedLnk.click(function() { + advanced.toggle(); + }); + + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + advanced.append($('
                          ')); + + // Create VLAN parameters + var vlanOptions = $('
                          ').hide(); + vlanOptions.append($('
                          ')); + vlanOptions.append($('
                          ')); + vlanOptions.append($('
                          ')); + vlanOptions.append($('
                          ')); + + typeAttr.append(vswitchOptions, vlanOptions); + + networkType.change(function() { + typeFS.show(); + if ($(this).val() == "vswitch") { + typeFS.find("legend").text("vSwitch"); + vswitchOptions.show(); + vlanOptions.hide(); + } else if ($(this).val() == "vlan") { + typeFS.find("legend").text("VLAN"); + vswitchOptions.hide(); + vlanOptions.show(); + } else { + typeFS.find("legend").text(""); + vswitchOptions.hide(); + vlanOptions.hide(); + typeFS.hide(); + } + }); + + // Generate tooltips + addVswitchForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add vSwitch or VLAN + addVswitchForm.dialog({ + title:'Add vSwitch or VLAN', + modal: true, + close: function() { + $(this).remove(); + }, + width: 750, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var networkType = $(this).find('select[name=networkType]').val(); + if (networkType == "vswitch") { + var networkArgs = "--addvswitch;"; + var system = $(this).find('select[name=system]').val(); + var switchName = $(this).find('input[name=switchName]').val(); + var deviceAddress = $(this).find('input[name=deviceAddress]').val(); + var portName = switchName; + var controllerName = $(this).find('input[name=controllerName]').val(); + var connection = $(this).find('select[name=connection]').val(); + var queueMemoryLimit = $(this).find('input[name=queueMemoryLimit]').val(); + var routingValue = $(this).find('select[name=routingValue]').val(); + var transportType = $(this).find('select[name=transportType]').val(); + var vlanId = $(this).find('input[name=vlanId]').val(); + var portType = $(this).find('select[name=vswitchVLANporttype]').val(); + var updateSysConfig = $(this).find('select[name=updateSysConfig]').val(); + var gvrp = $(this).find('select[name=gvrp]').val(); + var nativeVlanId = $(this).find('input[name=nativeVlanId]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=switchName]', 'input[name=deviceAddress]', 'input[name=controllerName]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + // Show warning message + if (!ready) { + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + if (switchName) + networkArgs += switchName + ";"; + if (deviceAddress) + networkArgs += deviceAddress + ";"; + if (portName) + networkArgs += portName + ";"; + if (controllerName) + networkArgs += controllerName + ";"; + + // Optional parameters + if (connection) + networkArgs += connection + ";"; + if (queueMemoryLimit) + networkArgs += queueMemoryLimit + ";"; + if (routingValue) + networkArgs += routingValue + ";"; + if (transportType) + networkArgs += transportType + ";"; + if (vlanId) + networkArgs += vlanId + ";"; + if (portType) + networkArgs += portType + ";"; + if (updateSysConfig) + networkArgs += updateSysConfig + ";"; + if (gvrp) + networkArgs += gvrp + ";"; + if (nativeVlanId) + networkArgs += nativeVlanId + ";"; + networkArgs = networkArgs.substring(0, networkArgs.length - 1); + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : networkArgs, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + } else if (networkType == "vlan") { + var networkArgs = "--addvlan;"; + var system = $(this).find('select[name=system]').val(); + var vlanName = $(this).find('input[name=vlanName]').val(); + var vlanOwner = $(this).find('input[name=vlanOwner]').val(); + var vlanType = $(this).find('select[name=vlanType]').val(); + var vlanTransport = $(this).find('select[name=vlanTransport]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=vlanName]', 'input[name=vlanOwner]', 'select[name=vlanType]', 'select[name=vlanTransport]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + // Show warning message + if (!ready) { + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Ethernet Hipersockets are not supported + if (vlanTransport == "2") { + var warn = createWarnBar('Ethernet Hipersockets are not supported'); + warn.prependTo($(this)); + return; + } + + networkArgs += vlanName + ";"; + networkArgs += vlanOwner + ";"; + networkArgs += vlanType + ";"; + networkArgs += vlanTransport; + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : networkArgs, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + } // End of else if + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Open dialog to delete network + * + * @param node type name for removing network + */ +function openRemoveVswitchVlanDialog(networkList) { + var names = ''; + for (var i in networkList) { + var networkArgs = networkList[i].split(';'); + networkArgs[2] = jQuery.trim(networkArgs[2]); + names += networkArgs[2] + ', '; + } + names = names.substring(0, names.length - 2); // Delete last two characters + + var confirmDialog = $('

                          Are you sure you want to remove ' + names + '?

                          '); + confirmDialog.dialog({ + title: "Confirm", + modal: true, + width: 400, + buttons: { + "Ok": function() { + for (var i in networkList) { + var networkArgs = networkList[i].split(';'); + var node = networkArgs[0]; + var type = networkArgs[1]; + var name = jQuery.trim(networkArgs[2]); + var owner = networkArgs[3]; + + if (type.indexOf("VSWITCH") != -1) { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : node, + args : '--removevswitch;' + name, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + var infoMsg; + + // Create info message + if (jQuery.isArray(data.rsp)) { + infoMsg = ''; + for (var i in data.rsp) { + infoMsg += data.rsp[i] + '
                          '; + } + } else { + infoMsg = data.rsp; + } + + openDialog("info", infoMsg); + } + }); + } else if (type.indexOf("LAN") != -1) { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : node, + args : '--removevlan;' + name + ';' + owner, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + var infoMsg; + + // Create info message + if (jQuery.isArray(data.rsp)) { + infoMsg = ''; + for (var i in data.rsp) { + infoMsg += data.rsp[i] + '
                          '; + } + } else { + infoMsg = data.rsp; + } + + openDialog("info", infoMsg); + } + }); + } + } + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); +} + +/** + * Remove processor + * + * @param node Node where processor is attached + * @param address Virtual address of processor + */ +function removeProcessor(node, address) { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--removeprocessor;' + address, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + $('#' + node + 'StatusBarLoader').show(); + $('#' + node + 'StatusBar').show(); +} + +/** + * Remove disk + * + * @param node Node where disk is attached + * @param address Virtual address of disk + */ +function removeDisk(node, address) { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--removedisk;' + address, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + $('#' + node + 'StatusBarLoader').show(); + $('#' + node + 'StatusBar').show(); +} + +/** + * Remove zFCP device + * + * @param node Node where disk is attached + * @param address Virtual address of zFCP device + * @param wwpn World wide port name of zFCP device + * @param lun Logical unit number of zFCP device + */ +function removeZfcp(node, address, wwpn, lun) { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--removezfcp||' + address + '||' + wwpn + '||' + lun, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + $('#' + node + 'StatusBarLoader').show(); + $('#' + node + 'StatusBar').show(); +} + +/** + * Remove NIC + * + * @param node Node where NIC is attached + * @param address Virtual address of NIC + */ +function removeNic(node, nic) { + var args = nic.split('.'); + var address = args[0]; + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--removenic;' + address, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + + // Increment node process + incrementNodeProcess(node); + + // Show loader + $('#' + node + 'StatusBarLoader').show(); + $('#' + node + 'StatusBar').show(); +} + +/** + * Set a cookie for the network names of a given node + * + * @param data Data from HTTP request + */ +function setNetworkCookies(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var node = data.msg; + var networks = data.rsp[0].split(node + ': '); + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_' + node + 'networks', networks, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Get contents of each disk pool + * + * @param data HTTP request data + */ +function getDiskPool(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Invalid") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var hcp = data.msg; + var pools = data.rsp[0].split(hcp + ': '); + + // Get contents of each disk pool + for (var i in pools) { + if (pools[i]) { + pools[i] = jQuery.trim(pools[i]); + + // Get used space + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcp, + args : '--diskpool;' + pools[i] + ';used', + msg : 'hcp=' + hcp + ';pool=' + pools[i] + ';stat=used' + }, + + success : function(data) { + data = decodeRsp(data); + loadDiskPoolTable(data); + } + }); + + // Get free space + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcp, + args : '--diskpool;' + pools[i] + ';free', + msg : 'hcp=' + hcp + ';pool=' + pools[i] + ';stat=free' + }, + + success : function(data) { + data = decodeRsp(data); + loadDiskPoolTable(data); + } + }); + } // End of if + } // End of for + } else { + // Display any errors in info bar + if (data.rsp.length) { + var panelId = 'zvmDiskResource'; + var info = $('#' + panelId).find('.ui-state-highlight'); + // If there is no info bar, create info bar + if (!info.length) { + info = createInfoBar("Error: "+data.rsp[0]); + $('#' + panelId).append(info); + } else { + info.append("
                          Error: "+data.rsp[0]); + } + } + // Load empty table + loadDiskPoolTable(""); // Must pass something + } +} + +/** + * Get contents of each zFCP pool + * + * @param data HTTP request data + */ +function getZfcpPool(data) { + if (typeof console == "object"){ + console.log("Entering getZfcpPool."); + } + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Invalid") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var hcp = data.msg; + var pools = data.rsp[0].split(hcp + ': '); + zhcpQueryCountForZfcps = 0; + // Get contents of each disk pool + for (var i in pools) { + pools[i] = jQuery.trim(pools[i]); + if (pools[i]) { + + // Query used and free space + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcp, + args : '--zfcppool;' + pools[i] + ';all', + msg : 'hcp=' + hcp + ';pool=' + pools[i] + }, + success : function(data) { + data = decodeRsp(data); + loadZfcpPoolTable(data); + } + }); + } // End of if + } // End of for + } else { + // Display any errors in info bar + if (data.rsp.length) { + var panelId = 'zfcpResource'; + var info = $('#' + panelId).find('.ui-state-highlight'); + // If there is no info bar, create info bar + if (!info.length) { + info = createInfoBar("Error: "+data.rsp[0]); + $('#' + panelId).append(info); + } else { + info.append("
                          Error: "+data.rsp[0]); + } + } + // Load empty table + loadZfcpPoolTable(""); // Must pass something + } +} + +/** + * Get details of each network + * + * @param data HTTP request data + */ +function getNetwork(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Invalid") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var hcp = data.msg; + var networks = data.rsp[0].split(hcp + ': '); + if (typeof console == "object"){ + console.log("Entering getNetwork data:<"+networks+">"); + } + + // Loop through each network + for ( var i = 1; i < networks.length; i++) { + if( !networks[i] || 0 === networks[i].length) continue; + var args = networks[i].split(' '); + var type = args[0]; + var name = args[2]; + name = name.replace(/\n/g,''); + + // Get network details + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcp, + args : '--getnetwork;' + name + ';' + type, + msg : 'hcp=' + hcp + ';type=' + type + ';network=' + name + }, + + success : function(data) { + data = decodeRsp(data); + loadNetworkTable(data); + } + }); + } // End of for + } // End of if + else { + if (data.rsp.length) { + var panelId = 'zvmNetworkResource'; + var info = $('#' + panelId).find('.ui-state-highlight'); + // If there is no info bar, create info bar + if (!info.length) { + info = createInfoBar("Error: "+data.rsp[0]); + $('#' + panelId).append(info); + } else { + info.append("
                          Error: "+data.rsp[0]); + } + } + // Normally load empty table, but not for networks + } +} + +/** + * Load disk pool contents into a table + * + * @param data HTTP request data + */ +function loadDiskPoolTable(data) { + // Remove loader if all hcps queried + var panelId = 'zvmDiskResource'; + if (!zhcpQueryCountForDisks) { + $('#' + panelId).find('img[src="images/loader.gif"]').remove(); + } + + var hcp2zvm = new Object(); + var args, hcp, pool, stat, tmp; + if (data && typeof data.rsp != "undefined") { + // Do not continue if the call failed + if (!data.rsp.length && data.rsp[0].indexOf("Failed") > 0 && data.rsp[0].indexOf("Error") > 0) { + return; + } + + // Obtain mapping for zHCP to zVM system + hcp2zvm = getHcpZvmHash(); + + args = data.msg.split(';'); + hcp = args[0].replace('hcp=', ''); + pool = args[1].replace('pool=', ''); + stat = jQuery.trim(args[2].replace('stat=', '')); + tmp = data.rsp[0].split(hcp + ': '); + } else { + // Provide empty values so the table will be generated + hcp = ''; + pool = ''; + stat = ''; + tmp = new Array(); + } + + // Resource tab ID + var info = $('#' + panelId).find('.ui-state-highlight'); + // If there is no info bar + if (!info.length) { + // Create info bar + info = createInfoBar('Below are disks that are defined in the EXTENT CONTROL file.'); + $('#' + panelId).append(info); + } + + // Get datatable + var tableId = 'zDiskDataTable'; + var dTable = getDiskDataTable(); + if (!dTable) { + // Create a datatable + var table = new DataTable(tableId); + // Resource headers: volume ID, device type, start address, and size + table.init( [ '', 'z/VM', 'Pool', 'Status', 'Volume', 'Device type', 'Starting address', 'Size' ]); + + // Append datatable to panel + $('#' + panelId).append(table.object()); + + // Turn into datatable + dTable = $('#' + tableId).dataTable({ + 'iDisplayLength': 50, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + setDiskDataTable(dTable); + } + + // Skip index 0 and 1 because it contains nothing + for (var i = 2; i < tmp.length; i++) { + tmp[i] = jQuery.trim(tmp[i]); + var diskAttrs = tmp[i].split(' '); + var key = hcp2zvm[hcp] + "-" + pool + "-" + diskAttrs[0]; + var type = diskAttrs[1]; + + // Calculate disk size + var size; + if (type.indexOf('3390') != -1) { + size = convertCylinders2Gb(parseInt(diskAttrs[3])); + } else if (type.indexOf('9336') != -1) { + size = convertBlocks2Gb(parseInt(diskAttrs[3])) + } else { + size = 0; + } + dTable.fnAddData( [ '', hcp2zvm[hcp], pool, stat, diskAttrs[0], type, diskAttrs[2], diskAttrs[3] + " (" + size + "G)" ]); + } + + // Create actions menu + if (!$('#zvmDiskResourceActions').length) { + // Empty filter area + $('#' + tableId + '_length').empty(); + + // Add disk to pool + var addLnk = $('Add'); + addLnk.bind('click', function(event){ + openAddDisk2PoolDialog(); + }); + + // Delete disk from pool + var removeLnk = $('Remove'); + removeLnk.bind('click', function(event){ + var disks = getNodesChecked(tableId); + openRemoveDiskFromPoolDialog(disks); + }); + + // Refresh table + var refreshLnk = $('Refresh'); + refreshLnk.bind('click', function(event){ + $('#zvmDiskResource').empty().append(createLoader('')); + setDiskDataTable(''); + + // Create a array for hardware control points + var hcps = new Array(); + if ($.cookie('xcat_hcp').indexOf(',') > -1) + hcps = $.cookie('xcat_hcp').split(','); + else + hcps.push($.cookie('xcat_hcp')); + + zhcpQueryCountForDisks = hcps.length; + // Query the disk pools for each + for (var i in hcps) { + if( !hcps[i] || 0 === hcps[i].length) continue; + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcps[i], + args : '--diskpoolnames', + msg : hcps[i] + }, + + success : function(data) { + data = decodeRsp(data); + getDiskPool(data); + } + }); + zhcpQueryCountForDisks--; + } + }); + + // Add ECKD to system + var addEckdLnk = $('Add ECKD'); + addEckdLnk.bind('click', function(event){ + openAddEckd2SystemDialog(hcp); + }); + + // Add Page or Spool + var addPageSpoolLnk = $('Add page/spool') + addPageSpoolLnk.bind('click', function(event){ + openAddPageSpoolDialog(hcp); + }); + + // Add EDEV to system + var addEdevLnk = $('Add EDEV'); + addEdevLnk.bind('click', function(event){ + openAddScsi2SystemDialog(hcp); + }); + + // Remove EDEV + var removeEdevLnk = $('Remove EDEV'); + removeEdevLnk.bind('click', function(event){ + openRemoveScsiDialog(hcp); + }); + + // Indicate disk is to be shared with various users + var shareLnk = $('Share disk'); + shareLnk.bind('click', function(event){ + var disks = getNodesChecked(tableId); + openShareDiskDialog(disks); + }); + + // Add Volume to system + var addVolumeLnk = $('Add volume to system'); + addVolumeLnk.bind('click', function(event){ + openAddVolume2SystemDialog(hcp); + }); + + // Remove Volume from system + var removeVolumeLnk = $('Remove volume from system'); + removeVolumeLnk.bind('click', function(event){ + openRemoveVolumeFromSystemDialog(hcp); + }); + + // Advanced menu + var advancedLnk = 'Advanced'; + var advancedMenu = createMenu([addEckdLnk, addPageSpoolLnk, addEdevLnk, removeEdevLnk, addVolumeLnk, removeVolumeLnk, shareLnk]); + + // Create action bar + var actionBar = $('
                          ').css("width", "450px"); + + // Create an action menu + var actionsMenu = createMenu([refreshLnk, addLnk, removeLnk, [advancedLnk, advancedMenu]]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + tableId + '_length').prepend(menuDiv); + $('#' + tableId + '_length').css({ + 'padding': '0px', + 'width': '500px' + }); + $('#' + tableId + '_filter').css('padding', '10px'); + menuDiv.append(actionBar); + } + + // Resize accordion + $('#zvmResourceAccordion').accordion('resize'); +} + +/** + * Load zFCP pool contents into a table + * + * @param data HTTP request data + */ +function loadZfcpPoolTable(data) { + if (typeof console == "object"){ + console.log("Entering loadZfcpPoolTable."); + } + // Delete loader if last one + var panelId = 'zfcpResource'; + if (zhcpQueryCountForZfcps <= 0) { + $('#' + panelId).find('img[src="images/loader.gif"]').remove(); + } + + var hcp2zvm = new Object(); + var args, hcp, pool, tmp; + + // Resource tab ID + var info = $('#' + panelId).find('.ui-state-highlight'); + + // Is there any data passed? Process if some + if (typeof data.rsp != "undefined") { + // Do not continue if no data to add + if (!data.rsp.length) { + if (typeof console == "object"){ + console.log("data.rsp.length is 0."); + } + // If there is no info bar, create info bar + var msgError = '
                          Unexpected, no data returned on the lsvm --zfcppool call.'; + if (!info.length) { + info = createInfoBar(msgError); + $('#' + panelId).append(info); + } else { + info.append(msgError); + } + return; + } + if (data.rsp[0].indexOf("Failed") > 0 || data.rsp[0].indexOf("Error") > 0) { + if (typeof console == "object"){ + console.log("Failed on lsvm call for --zfcppool"); + } + var msgError = '
                          Error: Error on call to check zfcp pools: '+ data.rsp[0]; + // If there is no info bar, create info bar + if (!info.length) { + info = createInfoBar(msgError); + $('#' + panelId).append(info); + } else { + info.append(msgError); + } + return; + } + + // Obtain mapping for zHCP to zVM system + hcp2zvm = getHcpZvmHash(); + + args = data.msg.split(';'); + hcp = args[0].replace('hcp=', ''); + pool = args[1].replace('pool=', ''); + tmp = data.rsp[0].split(hcp + ': '); + } else { + // Provide empty values so the table will be generated + if (typeof console == "object"){ + console.log("Creating empty zfcp pool table."); + } + hcp = ''; + pool = '' + tmp = new Array(); + } + + // If there is no info bar, create info bar + if (!info.length) { + info = createInfoBar('Below are devices that are defined internally in the zFCP pools.'); + $('#' + panelId).append(info); + } + + // Get datatable + var tableId = 'zFcpDataTable'; + var dTable = getZfcpDataTable(); + if (!dTable) { + // Create a datatable + var table = new DataTable(tableId); + // Resource headers: status, WWPN, LUN, size, owner, channel, tag + table.init( [ '', 'z/VM', 'Pool', 'Status', 'Port name', 'Unit number', 'Size', 'Range', 'Owner', 'Channel', 'Tag' ]); + + // Append datatable to panel + $('#' + panelId).append(table.object()); + + // Turn into datatable + dTable = $('#' + tableId).dataTable({ + 'iDisplayLength': 50, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + setZfcpDataTable(dTable); + } + if ((typeof data.rsp != "undefined") && (data.rsp.length > 0)) { + // Skip index 0 and 1 because it contains nothing + var key = ""; + for (var i = 2; i < tmp.length; i++) { + tmp[i] = jQuery.trim(tmp[i]); + var diskAttrs = tmp[i].split(','); + diskAttrs[0] = diskAttrs[0].toLowerCase(); + // Key contains row data to be returned when the checkbox is selected + var key = hcp2zvm[hcp] + '-' + pool + '-' + diskAttrs[2] + '-' + diskAttrs[1]; + dTable.fnAddData( [ '', hcp2zvm[hcp], pool, diskAttrs[0], diskAttrs[1], diskAttrs[2], diskAttrs[3], diskAttrs[4], diskAttrs[5], diskAttrs[6], diskAttrs[7] ]); + } + } + // Create actions menu + if (!$('#zFcpResourceActions').length) { + // Empty filter area + $('#' + tableId + '_length').empty(); + + // Add disk to pool + var addLnk = $('Add'); + addLnk.bind('click', function(event){ + openAddZfcp2PoolDialog(); + }); + + // Delete disk from pool + var removeLnk = $('Remove'); + removeLnk.bind('click', function(event){ + if (typeof console == "object"){ + console.log("Remove button clicked for tableId:"+tableId); + } + var disks = getNodesChecked(tableId); + openRemoveZfcpFromPoolDialog(disks); + }); + + // Refresh table + var refreshLnk = $('Refresh'); + refreshLnk.bind('click', function(event){ + $('#zfcpResource').empty().append(createLoader('')); + setZfcpDataTable(''); + + // Create a array for hardware control points + var hcps = new Array(); + if ($.cookie('xcat_hcp').indexOf(',') > -1) + hcps = $.cookie('xcat_hcp').split(','); + else + hcps.push($.cookie('xcat_hcp')); + + // Query the disk pools for each + zhcpQueryCountForZfcps = hcps.length; + for (var i in hcps) { + if( !hcps[i] || 0 === hcps[i].length) continue; + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcps[i], + args : '--zfcppoolnames', + msg : hcps[i] + }, + + success : function(data) { + data = decodeRsp(data); + getZfcpPool(data); + } + }); + zhcpQueryCountForZfcps--; + } + }); + // Create action bar + var actionBar = $('
                          ').css("width", "450px"); + + // Create an action menu + var actionsMenu = createMenu([addLnk, removeLnk, refreshLnk]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + tableId + '_length').prepend(menuDiv); + $('#' + tableId + '_length').css({ + 'padding': '0px', + 'width': '500px' + }); + $('#' + tableId + '_filter').css('padding', '10px'); + menuDiv.append(actionBar); + } + + // Resize accordion + $('#zvmResourceAccordion').accordion('resize'); +} + +/** + * Open dialog to remove disk from pool + * + * @param disks2remove Disks selected in table + */ +function openRemoveDiskFromPoolDialog(disks2remove) { + // Create form to delete disk from pool + var dialogId = 'zvmDeleteDiskFromPool'; + var deleteDiskForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var disks = new Array(); + if (disks2remove.indexOf(',') > -1) + disks = disks2remove.split(','); + else + disks.push(disks2remove); + + // Pick the last zHCP and pool it finds + var args, tgtHcp = "", tgtPool = "", tgtVol = ""; + for (var i in disks) { + if( !disks[i] || 0 === disks[i].length) continue; + args = disks[i].split('-'); + tgtHcp = args[0]; + tgtPool = args[1]; + tgtVol += args[2] + ','; + } + + // Strip out last comma + tgtVol = tgtVol.slice(0, -1); + + // Create info bar + var info = createInfoBar('Remove a disk from a disk pool defined in the EXTENT CONTROL.'); + deleteDiskForm.append(info); + var action = $('
                          '); + var actionSelect = $(''); + action.append(actionSelect); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + // Set region input based on those selected on table (if any) + var region = $('
                          '); + var group = $('
                          '); + deleteDiskForm.append(action, system, region, group); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + systemSelect.val(tgtHcp); + + actionSelect.change(function() { + if ($(this).val() == '1' || $(this).val() == '3') { + region.show(); + group.hide(); + } else if ($(this).val() == '2') { + region.show(); + group.show(); + } else if ($(this).val() == '7') { + region.val('FOOBAR'); + region.hide(); + group.show(); + } + }); + + // Generate tooltips + deleteDiskForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to delete disk + deleteDiskForm.dialog({ + title:'Delete disk from pool', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var action = $(this).find('select[name=action]').val(); + var system = $(this).find('select[name=system]').val(); + var region = $(this).find('input[name=region]').val(); + var group = $(this).find('input[name=group]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'select[name=action]', 'input[name=region]', 'input[name=group]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + var args; + if (action == '2' || action == '7') + args = region + ';' + group; + else + args = region; + + // Remove disk from pool + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : '--removediskfrompool;' + action + ';' + args, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Open dialog to add disk to pool + */ +function openAddDisk2PoolDialog() { + // Create form to add disk to pool + var dialogId = 'zvmAddDisk2Pool'; + var addDiskForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + // Create info bar + var info = createInfoBar('Add a disk to a disk pool defined in the EXTENT CONTROL. The disk has to already be attached to SYSTEM.'); + addDiskForm.append(info); + var action = $('
                          '); + var actionSelect = $(''); + action.append(actionSelect); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + var volume = $('
                          '); + var group = $('
                          '); + addDiskForm.append(action, system, volume, group); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Generate tooltips + addDiskForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add disk + addDiskForm.dialog({ + title:'Add disk to pool', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get inputs + var action = $(this).find('select[name=action]').val(); + var system = $(this).find('select[name=system]').val(); + var volume = $(this).find('input[name=volume]').val(); + var group = $(this).find('input[name=group]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'select[name=action]', 'input[name=volume]', 'input[name=group]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + var args; + if (action == '4') + args = volume + ';' + volume + ';' + group; + else + args = volume + ';' + group; + + // Add disk to pool + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : '--adddisk2pool;' + action + ';' + args, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Open dialog to remove zFCP from pool + * + * @param devices2remove Comman separated devices selected in table + */ +function openRemoveZfcpFromPoolDialog(devices2remove) { + // Create form to delete device from pool + var dialogId = 'zvmDeleteZfcpFromPool'; + var deleteDiskForm = $('
                          '); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + // Verify disks are in the same zFCP pool + var devices = devices2remove.split(','); + if (typeof console == "object"){ + console.log("Entering openRemoveZfcpFromPoolDialog. Device to remove:<"+devices2remove+">"); + } + var tmp, tgtPool, tgtHcp; + var tgtPort = ""; + var tgtUnitNo = ""; + for (var i in devices) { + if( !devices[i] || 0 === devices[i].length) continue; + tmp = devices[i].split('-'); + + if (tgtPool && tmp[1] != tgtPool) { + openDialog("warn", "Please select devices in the same zFCP"); + return; + } else { + tgtPool = tmp[1]; + } + + tgtHcp = tmp[0]; // Assume it is just one zHCP. Otherwise, this cannot be done on multiple zHCPs. + tgtUnitNo += tmp[2] + ","; + tgtPort = tmp[3]; + } + + // Strip out last comma + tgtUnitNo = tgtUnitNo.slice(0, -1); + + // Create info bar + var info = createInfoBar('Remove a zFCP device that is defined in a zFCP pool.'); + deleteDiskForm.append(info); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + var pool = $('
                          '); + var unitNo = $('
                          '); + var portName = $('
                          '); + deleteDiskForm.append(system, pool, unitNo, portName); + + // Append options for hardware control points + //systemSelect.append($('')); + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + systemSelect.val(tgtHcp); + + // Generate tooltips + deleteDiskForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to delete device + deleteDiskForm.dialog({ + title:'Delete device from pool', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var system = $(this).find('select[name=system]').val(); + var pool = $(this).find('input[name=zfcpPool]').val(); + var unitNo = $(this).find('input[name=zfcpUnitNo]').val(); + var portName = $(this).find('input[name=zfcpPortName]').val(); + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=zfcpPool]', 'input[name=zfcpUnitNo]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + var args = '--removezfcpfrompool;' + pool + ';' + unitNo; + if (portName) { + args += ';' + portName; + } + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : system, + args : args, + msg : dialogId + }, + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Open dialog to add zFCP to pool + */ +function openAddZfcp2PoolDialog() { + // Create form to add disk to pool + var dialogId = 'zvmAddDisk2Pool'; + var addDiskForm = $('
                          '); + var info = createInfoBar('Add a device to a zFCP pool defined in xCAT.'); + addDiskForm.append(info); + + // Obtain mapping for zHCP to zVM system + var hcp2zvm = new Object(); + hcp2zvm = getHcpZvmHash(); + + var system = $('
                          '); + var systemSelect = $(''); + system.append(systemSelect); + + var pool = $('
                          '); + var status = $('
                          '); + var portName = $('
                          '); + var unitNo = $('
                          '); + var size = $('
                          '); + var range = $('
                          '); + var owner = $('
                          '); + addDiskForm.append(system, pool, status, portName, unitNo, size, range, owner); + + // Create a array for hardware control points + //systemSelect.append($('')); + // Append options for hardware control points + for (var hcp in hcp2zvm) { + systemSelect.append($('')); + } + + // Generate tooltips + addDiskForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add disk + addDiskForm.dialog({ + title:'Add device to pool', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + // Delete any warning messages + $(this).find('.ui-state-error').remove(); + + var tgtSystem = $(this).find('select[name=system]').val(); + var tgtPool = $(this).find('input[name=zfcpPool]').val(); + var tgtStatus = $(this).find('select[name=zfcpStatus]').val(); + var tgtPortName = $(this).find('input[name=zfcpPortName]').val(); + var tgtUnitNo = $(this).find('input[name=zfcpUnitNo]').val(); + var tgtSize = $(this).find('input[name=zfcpSize]').val(); + var tgtRange = $(this).find('input[name=zfcpRange]').val(); + + // Device owner is optional + var tgtOwner = ""; + if ($(this).find('input[name=zfcpOwner]').val()) { + tgtOwner = $(this).find('input[name=zfcpOwner]').val(); + } + + // If inputs are not complete, show warning message + var ready = true; + var args = new Array('select[name=system]', 'input[name=zfcpPool]', 'select[name=zfcpStatus]', 'input[name=zfcpPortName]', 'input[name=zfcpUnitNo]'); + for (var i in args) { + if (!$(this).find(args[i]).val()) { + $(this).find(args[i]).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + $(this).find(args[i]).css('border', 'solid #BDBDBD 1px'); + } + } + + if (!ready) { + // Show warning message + var warn = createWarnBar('Please provide a value for each required field.'); + warn.prependTo($(this)); + return; + } + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + // zFCP range and owner are optional + var args = '--addzfcp2pool||' + tgtPool + '||' + tgtStatus + '||"' + tgtPortName + '"||' + tgtUnitNo + '||' + tgtSize; + if (tgtRange) { + args += '||' + tgtRange; + } if (tgtOwner) { + args += '||' + tgtOwner; + } + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chhypervisor', + tgt : tgtSystem, + args : args, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateResourceDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Update resource dialog + * + * @param data HTTP request data + */ +function updateResourceDialog(data) { + var dialogId = data.msg; + var infoMsg; + + // Create info message + if (jQuery.isArray(data.rsp)) { + infoMsg = ''; + for (var i in data.rsp) { + infoMsg += data.rsp[i] + '
                          '; + } + } else { + infoMsg = data.rsp; + } + + // Create info bar with close button + var infoBar = $('
                          ').css('margin', '5px 0px'); + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px' + }); + + // Create close button to close info bar + var close = $('').css({ + 'display': 'inline-block', + 'float': 'right' + }).click(function() { + $(this).parent().remove(); + }); + + var msg = $('
                          ' + infoMsg + '
                          ').css({ + 'display': 'inline-block', + 'width': '90%' + }); + + infoBar.append(icon, msg, close); + infoBar.prependTo($('#' + dialogId)); +} + +/** + * Select all checkboxes in the datatable + * + * @param event Event on element + * @param obj Object triggering event + */ +function selectAllDisk(event, obj) { + // This will ascend from + var tableObj = obj.parents('.datatable'); + var status = obj.attr('checked'); + tableObj.find(' :checkbox').attr('checked', status); + + // Handle datatable scroll + tableObj = obj.parents('.dataTables_scroll'); + if (tableObj.length) { + tableObj.find(' :checkbox').attr('checked', status); + } + + event.stopPropagation(); +} + +/** + * Load network details into a table + * + * @param data HTTP request data + */ +function loadNetworkTable(data) { + // Remove loader if last one + var panelId = 'zvmNetworkResource'; + if (!zhcpQueryCountForNetworks) { + $('#' + panelId).find('img[src="images/loader.gif"]').remove(); + } + + // Get zVM host names + if (!$.cookie('xcat_zvms')) { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + async: false, + data : { + cmd : 'webportal', + tgt : '', + args : 'lszvm', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setzVMCookies(data); + } + }); + } + + var zvms = $.cookie('xcat_zvms').split(','); + var hcp2zvm = new Object(); + var args, zvm, iHcp, tmp; + for (var i in zvms) { + if( !zvms[i] || 0 === zvms[i].length) continue; + args = zvms[i].split(':'); + zvm = args[0].toLowerCase(); + + if (args[1].indexOf('.') != -1) { + tmp = args[1].split('.'); + iHcp = tmp[0]; + } else { + iHcp = args[1]; + } + + hcp2zvm[iHcp] = zvm; + } + + var args = data.msg.split(';'); + var hcp = args[0].replace('hcp=', ''); + var type = args[1].replace('type=', ''); + var name = jQuery.trim(args[2].replace('network=', '')); + tmp = data.rsp[0].split(hcp + ': '); + + // Resource tab ID + var info = $('#' + panelId).find('.ui-state-highlight'); + // If there is no info bar + if (!info.length) { + // Create info bar + info = createInfoBar('Below are LANs/VSWITCHes available to use.'); + $('#' + panelId).append(info); + } + + // Get datatable + var dTable = getNetworkDataTable(); + if (!dTable) { + // Create table + var tableId = 'zNetworkDataTable'; + var table = new DataTable(tableId); + table.init( [ '', 'z/VM', 'Type', 'Name', 'Layer', 'Owner', 'Controller', 'Details' ]); + + // Append datatable to tab + $('#' + panelId).append(table.object()); + + // Turn into datatable + dTable = $('#' + tableId).dataTable({ + 'iDisplayLength': 50, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + setNetworkDataTable(dTable); + + // Set the column width + var cols = table.object().find('thead tr th'); + cols.eq(0).css('width', '20px'); // HCP column + cols.eq(1).css('width', '20px'); // Type column + cols.eq(2).css('width', '20px'); // Name column + cols.eq(3).css({'width': '600px'}); // Details column + } + + // Skip index 0 because it contains nothing + var details = '
                          ';
                          +    for ( var i = 1; i < tmp.length; i++) {
                          +        details += tmp[i];
                          +    }
                          +    details += '
                          '; + + // Determine the OSI layer + var layer = "3"; + if (details.indexOf("ETHERNET") != -1) { + layer = "2"; + } + + // Find the vSwitch/VLAN owner + var regex = /(LAN|VSWITCH) (.*?)(?:\s|$)/g; + var owner = ""; + var match = ""; + if (type == "VSWITCH") { + owner = "SYSTEM"; + } else { + owner = regex.exec(details)[2]; + } + + // Find the vSwitch controller + regex = /(?:^|\s)Controller: (.*?)(?:\s|$)/g; + var controllers = ""; + match = ""; + while (match = regex.exec(details)) { + controllers += match[1] + ","; + } + controllers = controllers.substring(0, controllers.length - 1); // Delete last two characters + + dTable.fnAddData(['', '
                          ' + hcp2zvm[hcp] + '
                          ', '
                          ' + type + '
                          ', '
                          ' + name + '
                          ', '
                          ' + layer + '
                          ', '
                          ' + owner + '
                          ', '
                          ' + controllers + '
                          ', details]); + + // Create actions menu + if (!$('#networkResourceActions').length) { + // Empty filter area + $('#' + tableId + '_length').empty(); + + // Add Vswitch/Vlan + var addLnk = $('Add'); + addLnk.bind('click', function(event){ + openAddVswitchVlanDialog(); + }); + + // Remove Vswitch/Vlan + var removeLnk = $('Remove'); + removeLnk.bind('click', function(event){ + var networkList = getNodesChecked(tableId).split(','); + if (networkList) { + openRemoveVswitchVlanDialog(networkList); + } + }); + + // Refresh table + var refreshLnk = $('Refresh'); + refreshLnk.bind('click', function(event){ + $('#zvmNetworkResource').empty().append(createLoader('')); + setNetworkDataTable(''); + + // Create a array for hardware control points + var hcps = new Array(); + if ($.cookie('xcat_hcp').indexOf(',') > -1) + hcps = $.cookie('xcat_hcp').split(','); + else + hcps.push($.cookie('xcat_hcp')); + + // Query networks + zhcpQueryCountForNetworks = hcps.length; + for (var i in hcps) { + if( !hcps[i] || 0 === hcps[i].length) continue; + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : hcps[i], + args : '--getnetworknames', + msg : hcps[i] + }, + + success : function(data) { + data = decodeRsp(data); + getNetwork(data); + } + }); + zhcpQueryCountForNetworks--; + } + }); + + // Create action bar + var actionBar = $('
                          ').css("width", "450px"); + + // Create an action menu + var actionsMenu = createMenu([addLnk, removeLnk, refreshLnk]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + tableId + '_length').prepend(menuDiv); + $('#' + tableId + '_length').css({ + 'padding': '0px', + 'width': '500px' + }); + $('#' + tableId + '_filter').css('padding', '10px'); + menuDiv.append(actionBar); + } + + // Resize accordion + $('#zvmResourceAccordion').accordion('resize'); +} + +/** + * Connect a NIC to a Guest LAN + * + * @param data Data from HTTP request + */ +function connect2GuestLan(data) { + var rsp = data.rsp; + var args = data.msg.split(';'); + var node = args[0].replace('node=', ''); + var address = args[1].replace('addr=', ''); + var lanName = args[2].replace('lan=', ''); + var lanOwner = args[3].replace('owner=', ''); + + // Write ajax response to status bar + var prg = writeRsp(rsp, node + ': '); + $('#' + node + 'StatusBar').find('div').append(prg); + + // Continue if no errors found + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + // Connect NIC to Guest LAN + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--connectnic2guestlan;' + address + ';' + lanName + ';' + + lanOwner, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + } else { + // Hide loader when error + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).hide(); + } +} + +/** + * Connect a NIC to a VSwitch + * + * @param data Data from HTTP request + */ +function connect2VSwitch(data) { + var rsp = data.rsp; + var args = data.msg.split(';'); + var node = args[0].replace('node=', ''); + var address = args[1].replace('addr=', ''); + var vswitchName = args[2].replace('vsw=', ''); + var vswitchAware = args[3].replace('vlanaware=', ''); + var vswitchPortType = args[4].replace('porttype=', ''); + var vswitchLanId = args[5].replace('lanid=', ''); + + // Set variables to empty string if notaware or they contain "default" + if (vswitchAware.toLowerCase() == 'notaware' ) { + vswitchPortType = ''; + vswitchLanId = ''; + } else { + if (vswitchPortType.toLowerCase() == 'default' ) { + vswitchPortType = ''; + } + if (vswitchLanId.toLowerCase() == 'default' ) { + vswitchLanId = ''; + } + } + + // Write ajax response to status bar + var prg = writeRsp(rsp, node + ': '); + $('#' + node + 'StatusBar').find('div').append(prg); + + // Continue if no errors found + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + // Connect NIC to VSwitch + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chvm', + tgt : node, + args : '--connectnic2vswitch;' + address + ';' + vswitchName + ';' + + vswitchPortType + ';' + vswitchLanId, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + updateZNodeStatus(data); + } + }); + } else { + // Hide loader when error + var statusBarLoaderId = node + 'StatusBarLoader'; + $('#' + statusBarLoaderId).hide(); + } +} + +/** + * Create provision existing node division + * + * @param inst Provision tab instance + * @return Provision existing node division + */ +function createZProvisionExisting(inst) { + // Create provision existing and hide it + var provExisting = $('
                          ').hide(); + + var vmFS = $('
                          '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + provExisting.append(vmFS); + + var vmAttr = $('
                          '); + vmFS.append($('
                          ')); + vmFS.append(vmAttr); + + var osFS = $('
                          '); + var osLegend = $('Operating System'); + osFS.append(osLegend); + provExisting.append(osFS); + + var osAttr = $('
                          '); + osFS.append($('
                          ')); + osFS.append(osAttr); + + // Create group input + var group = $('
                          '); + var groupLabel = $(''); + group.append(groupLabel); + + // Turn on auto complete for group + var groupNames = $.cookie('xcat_groups'); + if (groupNames) { + // Split group names into an array + var tmp = groupNames.split(','); + + // Create drop down for groups + var groupSelect = $(''); + groupSelect.append(''); + for (var i in tmp) { + if( !tmp[i] || 0 === tmp[i].length) continue; + // Add group into drop down + var opt = $(''); + groupSelect.append(opt); + } + group.append(groupSelect); + + // Create node datatable + groupSelect.change(function(){ + // Get group selected + var thisGroup = $(this).val(); + // If a valid group is selected + if (thisGroup) { + createNodesDatatable(thisGroup, 'zNodesDatatableDIV' + inst); + } + }); + } else { + // If no groups are cookied + var groupInput = $(''); + group.append(groupInput); + } + vmAttr.append(group); + + // Create node input + var node = $('
                          '); + var nodeLabel = $(''); + var nodeDatatable = $('

                          Select a group to view its nodes

                          '); + node.append(nodeLabel); + node.append(nodeDatatable); + vmAttr.append(node); + + // Create operating system image input + var os = $('
                          '); + var osLabel = $(''); + var osSelect = $(''); + osSelect.append($('')); + + var imageNames = $.cookie('xcat_imagenames').split(','); + if (imageNames) { + imageNames.sort(); + for (var i in imageNames) { + if( !imageNames[i] || 0 === imageNames[i].length) continue; + osSelect.append($('')); + } + } + os.append(osLabel); + os.append(osSelect); + osAttr.append(os); + + // Create boot method drop down + var bootMethod = $('
                          '); + var methoddLabel = $(''); + var methodSelect = $(''); + methodSelect.append('' + + '' + + '' + + '' + + '' + ); + bootMethod.append(methoddLabel); + bootMethod.append(methodSelect); + osAttr.append(bootMethod); + + // Generate tooltips + provExisting.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + + /** + * Provision existing + */ + var provisionBtn = createButton('Provision'); + provisionBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + + var ready = true; + var errMsg = ''; + + // Get provision tab ID + var thisTabId = $(this).parent().parent().parent().attr('id'); + // Get provision tab instance + var inst = thisTabId.replace('zvmProvisionTab', ''); + + // Get nodes that were checked + var dTableId = 'zNodesDatatable' + inst; + var tgts = getNodesChecked(dTableId); + if (!tgts) { + errMsg += 'You need to select a node.
                          '; + ready = false; + } + + // Check operating system image + var os = $('#' + thisTabId + ' select[name=os]:visible'); + if (!os.val()) { + errMsg += 'You need to select a operating system image.'; + os.css('border', 'solid #FF0000 1px'); + ready = false; + } else { + os.css('border', 'solid #BDBDBD 1px'); + } + + // If all inputs are valid, ready to provision + if (ready) { + // Disable provision button + $(this).attr('disabled', 'true'); + + // Show loader + $('#zProvisionStatBar' + inst).show(); + $('#zProvisionLoader' + inst).show(); + + // Disable all inputs + var inputs = $('#' + thisTabId + ' input:visible'); + inputs.attr('disabled', 'disabled'); + + // Disable all selects + var selects = $('#' + thisTabId + ' select'); + selects.attr('disabled', 'disabled'); + + // Get operating system image + var osImage = $('#' + thisTabId + ' select[name=os]:visible').val(); + var tmp = osImage.split('-'); + var os = tmp[0]; + var arch = tmp[1]; + var profile = tmp[3]; + + /** + * (1) Set operating system + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : tgts + ';noderes.netboot=zvm;nodetype.os=' + os + ';nodetype.arch=' + arch + ';nodetype.profile=' + profile, + msg : 'cmd=nodeadd;out=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionExistingStatus(data); + } + }); + } else { + // Show warning message + var warn = createWarnBar(errMsg); + warn.prependTo($(this).parent().parent()); + } + }); + provExisting.append(provisionBtn); + + return provExisting; +} + +/** + * Create provision new node division + * + * @param inst Provision tab instance + * @return Provision new node division + */ +function createZProvisionNew(inst) { + if (typeof console == "object"){ + console.log("Entering createZProvisionNew. Inst value:"+inst); + } + // Create provision new node division + var provNew = $('
                          '); + + // Create VM fieldset + var vmFS = $('
                          '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + provNew.append(vmFS); + + var vmAttr = $('
                          '); + vmFS.append($('
                          ')); + vmFS.append(vmAttr); + + // Create OS fieldset + var osFS = $('
                          '); + var osLegend = $('Operating System'); + osFS.append(osLegend); + provNew.append(osFS); + + // Create hardware fieldset + var hwFS = $('
                          '); + var hwLegend = $('Hardware'); + hwFS.append(hwLegend); + provNew.append(hwFS); + + var hwAttr = $('
                          '); + hwFS.append($('
                          ')); + hwFS.append(hwAttr); + + // Create tabs for basic and advanced hardware configuration + var hwTab = new Tab('hwConfig' + inst); + hwTab.init(); + hwAttr.append(hwTab.object()); + + var osAttr = $('
                          '); + osFS.append($('
                          ')); + osFS.append(osAttr); + + // Create group input + var group = $('
                          '); + var groupLabel = $(''); + var groupInput = $(''); + // Get groups on-focus + groupInput.one('focus', function(){ + var groupNames = $.cookie('xcat_groups'); + if (groupNames) { + // Turn on auto complete + $(this).autocomplete({ + source: groupNames.split(',') + }); + } + }); + group.append(groupLabel); + group.append(groupInput); + vmAttr.append(group); + + // Create node input + var nodeName = $('
                          '); + var nodeLabel = $(''); + var nodeInput = $(''); + nodeName.append(nodeLabel); + nodeName.append(nodeInput); + vmAttr.append(nodeName); + + // Create user ID input + var userId = $('
                          '); + vmAttr.append(userId); + + // Create hardware control point input + var hcpDiv = $('
                          '); + var hcpNodeLabel = $(''); + var hcpNodeInput = $(''); + var hcpHiddenInput = $(''); + hcpNodeInput.blur(function() { + + if (typeof console == "object") { + console.log("Display loading bar "); + } + // Show the status bar with a message and loading gif + $('#'+'zProvisionStatBar'+inst).find('div').append("Loading zhcp information..."); + $('#'+'zProvisionStatBar'+inst).find('div').append(""); + $('#'+'zProvisionStatBar'+inst).show(); + + // list of calls after the zhcp is verified. Used to determine when in progress gif is to be removed. + var ajaxCalls = {"diskpoolnames":1, "zfcppoolnames":1, "userprofilenames":1}; + var zhcpToCheck = $(this).val(); + var zhcpField = $(this); + var provisionStatusBar = $('#'+'zProvisionStatBar'+inst); + + // Make sure border is set back to black + zhcpField.css('border', 'solid #BDBDBD 1px'); + + if ($(this).val()) { + // Check if this is a valid node by making network names call. + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : zhcpToCheck, + args : '--getnetworknames', + msg : zhcpToCheck + }, + + success : function(data) { + data = decodeRsp(data); + if (data.rsp.length && (data.rsp[0].indexOf("Failed") > -1 || data.rsp[0].indexOf("Invalid") > -1 || data.rsp[0].indexOf("Error") > -1 ) ) { + // Remove the progress gif, since bailing out + removeProvisionLoadingGif(provisionStatusBar); + + // Create warning dialog + var warning = createWarnBar('Failure getting network data for hardware control point ' + zhcpToCheck + '
                          The hcp field must be a xCAT node name.'); + var warnDialog = $('
                          ').append(warning); + + // highlight the hcp field + zhcpField.css('border', 'solid #FF0000 1px'); + + // Open warning dialog + warnDialog.dialog({ + title:'Warning', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function() { + $(this).dialog("close"); + } + } + }); + + } else { + // Node is good, now set some cookies from network, then check/set other cookies + setNetworkCookies(data); + + // Get the HCP name from the hcp node name + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : zhcpToCheck, + msg : 'zhcpFullName' + }, + + success : function(data) { + data = decodeRsp(data); + if (data.rsp.length && (data.rsp[0].indexOf("Failed") > -1 || data.rsp[0].indexOf("Invalid") > -1) ) { + // Remove the progress gif, since bailing out + removeProvisionLoadingGif(provisionStatusBar); + + // Create warning dialog + var warning = createWarnBar('Failure getting hcp data from hardware control point ' + zhcpToCheck + '
                          The hcp field must be a valid xCAT node name.'); + var warnDialog = $('
                          ').append(warning); + + // highlight the hcp field + zhcpField.css('border', 'solid #FF0000 1px'); + + // Open warning dialog + warnDialog.dialog({ + title:'Warning', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function() { + $(this).dialog("close"); + } + } + }); + } else { + // Now set the hidden hcp field with the full name + // Clear hash table containing definable node attributes + nodeAttrs = new Array(); + + // Get definable attributes + // Data returned + var rsp = data.rsp; + // Group name + var group = data.msg; + // Hash of node attributes + var attrs = new Object(); + + // Go through each attribute + var node, args; + for (var i in rsp) { + // Get node name, skip processing + if (rsp[i].indexOf('Object name:') > -1) { + i++; + } + + // Get key and value + args = rsp[i].split('=', 2); + var key = jQuery.trim(args[0]); + var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1, rsp[i].length)); + + // If this is zhcp key then save full name in hidden field + if (key == "hcp") { + hcpHiddenInput.val(val); + } + + } + + } + } + }); + + if (typeof console == "object"){ + console.log("Looking for cookies from <" + zhcpToCheck + ">"); + } + + if (!$.cookie('xcat_' + zhcpToCheck + 'diskpools')) { + // Get disk pools + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : zhcpToCheck, + args : '--diskpoolnames', + msg : zhcpToCheck + }, + + success : function(data) { + data = decodeRsp(data); + setDiskPoolCookies(data); + }, + complete : function() { + checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "diskpoolnames"); + } + }); + } else { + checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "diskpoolnames"); + } + + if (!$.cookie('xcat_' + zhcpToCheck + 'zfcppools')) { + // Get zFCP pools + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : zhcpToCheck, + args : '--zfcppoolnames', + msg : zhcpToCheck + }, + + success : function(data) { + data = decodeRsp(data); + setZfcpPoolCookies(data); + }, + complete : function() { + checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "zfcppoolnames"); + } + }); + } else { + checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "zfcppoolnames"); + } + + if (!$.cookie('xcat_' + zhcpToCheck + 'userprofiles')) { + // Get zFCP pools + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + async: false, + data : { + cmd : 'lsvm', + tgt : zhcpToCheck, + args : '--userprofilenames', + msg : zhcpToCheck + }, + + success : function(data) { + data = decodeRsp(data); + setUserProfilesCookies(data); + }, + complete : function() { + checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "userprofilenames"); + } + }); + } else { + checkProvisionCallsDone(provisionStatusBar, ajaxCalls, "userprofilenames"); + } + + // Reset user profile and network drop down box + var thisTabId = zhcpField.parents('.tab').attr('id'); + var thisUserProfile = $('#' + thisTabId + ' select[name=userProfile]'); + thisUserProfile.children().remove(); + + var definedUserProfiles = $.cookie('xcat_' + zhcpToCheck + 'userprofiles').split(','); + for (var i in definedUserProfiles) { + if( !definedUserProfiles[i] || 0 === definedUserProfiles[i].length) continue; + thisUserProfile.append(''); + } + + var thisNetwork = $('#' + thisTabId + ' select[name=network]'); + thisNetwork.children().remove(); + thisNetwork.append(''); // No profile option + var definedNetworks = $.cookie('xcat_' + zhcpToCheck + 'networks').split(','); + for (var i in definedNetworks) { + if( !definedNetworks[i] || 0 === definedNetworks[i].length) continue; + if (!jQuery.trim(definedNetworks[i])) + continue; + + var directoryEntry, interfaceName; + + // Generate directory entry statement for vSwitch, hipersocket, and guest LAN + if (definedNetworks[i].indexOf('VSWITCH ') != -1) { + interfaceName = jQuery.trim(definedNetworks[i].replace('VSWITCH ', '')); + directoryEntry = "TYPE QDIO LAN " + interfaceName; + } else if (definedNetworks[i].indexOf('LAN:HIPERS ') != -1) { + interfaceName = jQuery.trim(definedNetworks[i].replace('LAN:HIPERS ', '')); + directoryEntry = "TYPE HIPERSOCKETS LAN " + interfaceName; + } else { + interfaceName = jQuery.trim(definedNetworks[i].replace('LAN:QDIO ', '')); + directoryEntry = "TYPE QDIO LAN " + interfaceName; + } + + thisNetwork.append(''); + } + + // Update user entry on change + thisNetwork.change(function() { + updateUserEntry(thisTabId); + }); + + thisUserProfile.change(function() { + updateUserEntry(thisTabId); + }); + } + } + }); + } + }); + hcpDiv.append(hcpNodeLabel); + hcpDiv.append(hcpNodeInput); + hcpDiv.append(hcpHiddenInput); + vmAttr.append(hcpDiv); + + // Create an advanced link to set IP address and hostname + var advancedLnk = $(''); + vmAttr.append(advancedLnk); + var advanced = $('
                          ').hide(); + vmAttr.append(advanced); + + var ip = $('
                          '); + advanced.append(ip); + var hostname = $('
                          '); + advanced.append(hostname); + + // Show IP address and hostname inputs on-click + advancedLnk.click(function() { + advanced.toggle(); + }); + + // Create operating system image input + var os = $('
                          '); + var osLabel = $(''); + var osSelect = $(''); + osSelect.append($('')); + + var imageNames = $.cookie('xcat_imagenames').split(','); + if (imageNames) { + imageNames.sort(); + for (var i in imageNames) { + if( !imageNames[i] || 0 === imageNames[i].length) continue; + osSelect.append($('')); + } + } + os.append(osLabel); + os.append(osSelect); + osAttr.append(os); + + // Create user entry input + var defaultChkbox = $('').click(function() { + // Remove any warning messages + $(this).parents('.form').find('.ui-state-error').remove(); + + // Get tab Id + var thisTabId = $(this).parents('.ui-tabs-panel').parents('.ui-tabs-panel').attr('id'); + + // Get objects for HCP, user ID, and OS + var userId = $('#' + thisTabId + ' input[name=userId]'); + var os = $('#' + thisTabId + ' select[name=os]'); + + // Get default user entry when clicked + if ($(this).attr('checked')) { + if (!os.val() || !userId.val()) { + // Show warning message + var warn = createWarnBar('Please specify the operating system and user ID before checking this box'); + warn.prependTo($(this).parents('.form')); + + // Highlight empty fields + jQuery.each([os, userId], function() { + if (!$(this).val()) { + $(this).css('border', 'solid #FF0000 1px'); + } + }); + } else { + // Un-highlight empty fields + jQuery.each([os, userId], function() { + $(this).css('border', 'solid #BDBDBD 1px'); + }); + + // Get profile name + var tmp = os.val().split('-'); + var profile = tmp[3]; + + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'getdefaultuserentry;' + profile, + msg : thisTabId + }, + + success :function(data) { + data = decodeRsp(data); + // Populate user entry + var tabId = data.msg; + var entry = new String(data.rsp); + var userId = $('#' + tabId + ' input[name=userId]').val(); + entry = entry.replace(new RegExp('LXUSR', 'g'), userId); + $('#' + tabId + ' textarea:visible').val(entry); + } + }); + } + } else { + $('#' + thisTabId + ' textarea:visible').val(''); + + // Un-highlight empty fields + jQuery.each([os, userId], function() { + $(this).css('border', 'solid #BDBDBD 1px'); + }); + } + }); + var userEntry = $('
                          '); + userEntry.append($('').append(defaultChkbox, 'Use default')); + + // Add division on basic tab for specifying: memory, # of CPUs, privilege, user profile, and network. + var basicConfig = $('
                          '); + var userProfile = $('
                          '); + var cpuSelect = $('').change(function() { + updateUserEntry('zvmProvisionTab' + inst); + }); + var cpuCount = $('
                          ').append(cpuSelect); + var memorySlider = $('
                          '); + var memorySize = $(''); + var memory = $('
                          ').append(memorySlider, memorySize); + var acceptableMemorySize = ['512M', '1024M', '2G', '3G', '4G', '5G', '6G', '7G', '8G']; + memorySlider.slider({ + value: 0, + min: 0, + max: 8, + step: 1, + slide: function(event, ui) { + $('#basicConfig' + inst + ' input[name=memory]').val(acceptableMemorySize[ui.value]); + + // Update user entry on change + updateUserEntry('zvmProvisionTab' + inst); + } + }); + + // Initialize storage size + memorySize.val(acceptableMemorySize[0]); + + var privilege = $('
                          ' + + '
                          ' + + ' A - Primary system operator
                          ' + + ' B - System resource operator
                          ' + + ' C - System programmer
                          ' + + ' D - Spooling operator
                          ' + + ' E - System analyst
                          ' + + ' F - IBM service representative
                          ' + + ' G - General user
                          ' + + '
                          ' + + '
                          '); + privilege.find('input').change(function() { + updateUserEntry('zvmProvisionTab' + inst); + }); + + var network = $('
                          '); + + var vswitchvlan = $('

                          ' + + '
                          ' + + '
                          '); + vswitchvlan.find('input').change(function() { + updateUserEntry('zvmProvisionTab' + inst); + }); + vswitchvlan.find('select').change(function() { + updateUserEntry('zvmProvisionTab' + inst); + }); + + vswitchvlan.hide(); + basicConfig.append(userProfile, cpuCount, memory, privilege, network, vswitchvlan); + hwTab.add('basicConfig' + inst, 'Basic', basicConfig, false); + + // Add division on advanced tab for specifying user directory entry + hwTab.add('advancedConfig' + inst, 'Advanced', userEntry, false); + + // Create disk table + var diskDiv = $('
                          '); + var diskLabel = $(''); + var diskTable = $('
                          '); + var diskHeader = $(' Type Address Size Mode Pool Password IPLNone
                          '); + // Adjust header width + diskHeader.find('th').css( { + 'width' : '80px' + }); + diskHeader.find('th').eq(0).css( { + 'width' : '20px' + }); + var diskBody = $(''); + var diskFooter = $(''); + + /** + * Add disks + */ + var addDiskLink = $('Add disk'); + addDiskLink.bind('click', function(event) { + // Get list of disk pools + var thisTabId = $(this).parents('.tab').attr('id'); + var thisHcp = $('#' + thisTabId + ' input[name=hcp]').val(); + var definedPools = null; + if (thisHcp) { + // Get node without domain name + var temp = thisHcp.split('.'); + definedPools = $.cookie('xcat_' + temp[0] + 'diskpools').split(','); + } else { + var warning = createWarnBar('You must fill in a hardware control point before adding a disk.'); + var warnDialog = $('
                          ').append(warning); + + // Open dialog + warnDialog.dialog({ + title:'Warning', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function() { + $(this).dialog("close"); + } + } + }); + return false; + } + + // Create a row + var diskRow = $(''); + + // Add remove button + var removeBtn = $(''); + var col = $('').append(removeBtn); + removeBtn.bind('click', function(event) { + diskRow.remove(); + }); + diskRow.append(col); + + // Create disk type drop down + var diskType = $(''); + var diskTypeSelect = $(''); + diskTypeSelect.append('' + + '' + ); + diskType.append(diskTypeSelect); + diskRow.append(diskType); + + // Create disk address input + var diskAddr = $(''); + diskRow.append(diskAddr); + + // Create disk size input + var diskSize = $(''); + diskRow.append(diskSize); + + // Create disk mode input + var diskMode = $(''); + var diskModeSelect = $(''); + diskModeSelect.append('' + + '' + + '' + + '' + + '' + + '' + + '' + ); + diskMode.append(diskModeSelect); + diskRow.append(diskMode); + + // Create disk pool drop down + var diskPool = $(''); + var diskPoolSelect = $(''); + for (var i in definedPools) { + diskPoolSelect.append(''); + } + diskPool.append(diskPoolSelect); + diskRow.append(diskPool); + + // Create disk password input + var diskPw = $(''); + diskRow.append(diskPw); + + // Create IPL checkbox + //var diskIpl = $(''); + var diskIpl = $(''); + diskRow.append(diskIpl); + diskIpl.find('input').change(function() { + updateUserEntry(thisTabId); + }); + + diskBody.append(diskRow); + + // Generate tooltips + diskBody.find('td input[title],select[title]').tooltip({ + position: "top right", + offset: [-4, 4], + effect: "fade", + opacity: 0.7, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + }); + + // Create disk table + diskFooter.append(addDiskLink); + diskTable.append(diskHeader); + diskTable.append(diskBody); + diskTable.append(diskFooter); + + diskDiv.append(diskLabel); + diskDiv.append(diskTable); + hwAttr.append(diskDiv); + + // Create zFCP table + var zfcpDiv = $('
                          '); + var zfcpLabel = $(''); + var zfcpTable = $('
                          '); + var zfcpHeader = $(' Address Size Pool Tag Port Name Unit # LOADDEV'); + // Adjust header width + zfcpHeader.find('th').css({ + 'width' : '80px' + }); + zfcpHeader.find('th').eq(0).css({ + 'width' : '20px' + }); + var zfcpBody = $(''); + var zfcpFooter = $(''); + + /** + * Add zFCP devices + */ + var addZfcpLink = $('Add zFCP'); + addZfcpLink.bind('click', function(event) { + // Get list of disk pools + var thisTabId = $(this).parents('.tab').attr('id'); + var thisHcp = $('#' + thisTabId + ' input[name=hcp]').val(); + var definedPools = null; + if (thisHcp) { + // Get node without domain name + var temp = thisHcp.split('.'); + definedPools = $.cookie('xcat_' + temp[0] + 'zfcppools').split(','); + } else { + var warning = createWarnBar('You must fill in a hardware control point before adding a zFCP.'); + var warnDialog = $('
                          ').append(warning); + + // Open dialog + warnDialog.dialog({ + title:'Warning', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function() { + $(this).dialog("close"); + } + } + }); + + } + + // Create a row + var zfcpRow = $(''); + + // Add remove button + var removeBtn = $(''); + var col = $('').append(removeBtn); + removeBtn.bind('click', function(event) { + zfcpRow.remove(); + }); + zfcpRow.append(col); + + // Create disk address input + var zfcpAddr = $(''); + zfcpRow.append(zfcpAddr); + + // Create disk size input + var zfcpSize = $(''); + zfcpRow.append(zfcpSize); + + // Create zFCP pool drop down + var zfcpPool = $(''); + var zfcpPoolSelect = $(''); + for (var i in definedPools) { + zfcpPoolSelect.append(''); + } + zfcpPool.append(zfcpPoolSelect); + zfcpRow.append(zfcpPool); + + // Create disk tag + var zfcpTag = $(''); + zfcpRow.append(zfcpTag); + + // Create device port name + var zfcpPortName = $(''); + zfcpRow.append(zfcpPortName); + + // Create device unit number + var zfcpUnitNo = $(''); + zfcpRow.append(zfcpUnitNo); + + // Create LOADDEV radio button + var zfcpLoaddev = $(''); + zfcpRow.append(zfcpLoaddev); + + zfcpBody.append(zfcpRow); + + // Generate tooltips + zfcpBody.find('td input[title],select[title]').tooltip({ + position: "top right", + offset: [-4, 4], + effect: "fade", + opacity: 0.7, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + }); + + zfcpFooter.append(addZfcpLink); + zfcpTable.append(zfcpHeader); + zfcpTable.append(zfcpBody); + zfcpTable.append(zfcpFooter); + + zfcpDiv.append(zfcpLabel); + zfcpDiv.append(zfcpTable); + hwAttr.append(zfcpDiv); + + // Generate tooltips + provNew.find('div input[title],select[title],textarea[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + + // Disable IPL column if advanced tab is selected + hwTab.object().tabs({ + select: function(event, ui) { + // Get provision tab instance + var thisTabId = $(this).parents('.ui-tabs-panel').attr('id'); + var inst = thisTabId.replace('zvmProvisionTab', ''); + + // Disable and de-select IPL device + if (ui.index == 1) { + $('#' + thisTabId + ' table:eq(0):visible tbody tr td:nth-child(8) input').attr('disabled','disabled'); + } else { + $('#' + thisTabId + ' table:eq(0):visible tbody tr td:nth-child(8) input').removeAttr('disabled'); + } + + $('#' + thisTabId + ' table:eq(0):visible tbody tr td:nth-child(8) input').removeAttr('checked'); + } + }); + + /** + * Provision new + */ + var provisionBtn = createButton('Provision'); + provisionBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parent().parent().find('.ui-state-error').remove(); + + var ready = true; + var errMsg = ''; + + // Get tab ID + var thisTabId = $(this).parents('.ui-tabs-panel').attr('id'); + // Get provision tab instance + var inst = thisTabId.replace('zvmProvisionTab', ''); + + // Get the selected hardware configuration tab + // Basic tab index = 0 & advanced tab index = 1 + var hwTabIndex = $("#hwConfig" + inst).tabs('option', 'selected'); + + // Check node name, userId, hardware control point, and group + // Check disks and zFCP devices + var inputs = $('#' + thisTabId + ' input:visible'); + for (var i = 0; i < inputs.length; i++) { + // Do not check some inputs + if (inputs.eq(i).attr('name') == 'memory') { + // There should always be a value for memory + // Do not change the border + continue; + } else if (!inputs.eq(i).val() + && inputs.eq(i).attr('type') != 'password' + && inputs.eq(i).attr('name') != 'zfcpTag' + && inputs.eq(i).attr('name') != 'zfcpPortName' + && inputs.eq(i).attr('name') != 'zfcpUnitNo') { + inputs.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + inputs.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + var selects = $('#' + thisTabId + ' select:visible'); + for (var i = 0; i < selects.length; i++) { + if (!selects.eq(i).val() && selects.eq(i).attr('name') != 'os' && selects.eq(i).attr('name') != 'userProfile' && selects.eq(i).attr('name') != 'network') { + selects.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + selects.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + if (hwTabIndex == 1) { + // Check user entry + var thisUserEntry = $('#' + thisTabId + ' textarea:visible'); + thisUserEntry.val(thisUserEntry.val().toUpperCase()); + if (!thisUserEntry.val()) { + thisUserEntry.css('border', 'solid #FF0000 1px'); + ready = false; + } else { + thisUserEntry.css('border', 'solid #BDBDBD 1px'); + } + + // Check if user entry contains user ID + var thisUserId = $('#' + thisTabId + ' input[name=userId]:visible'); + var pos = thisUserEntry.val().indexOf('USER ' + thisUserId.val().toUpperCase()); + if (pos < 0) { + + pos = thisUserEntry.val().indexOf('IDENTITY ' + thisUserId.val().toUpperCase()); + if (pos < 0) { + errMsg = errMsg + 'The directory entry does not contain the correct user/identity ID.
                          '; + ready = false; + } + } + } + var hostnameCheck = $('#' + thisTabId + ' input[name=hostname]').val(); + if (hostnameCheck.length > 70) { + errMsg = errMsg + 'The host name cannot be longer than 70 characters.
                          '; + $('#' + thisTabId + ' input[name=hostname]').css('border', 'solid #FF0000 1px'); + ready = false; + } + + // Show error message for missing inputs + if (!ready) { + errMsg = errMsg + 'Please provide a value for each missing field.
                          '; + } + + // If no operating system is specified, create only user entry + os = $('#' + thisTabId + ' select[name=os]:visible'); + + // Check number of disks + var diskRows = $('#' + thisTabId + ' table tr'); + // If an OS is given, disks are needed + if (os.val() && (diskRows.length < 1)) { + errMsg = errMsg + 'You need to add at some disks.
                          '; + ready = false; + } + + // If this is basic mode, check for a disk with IPL radio button and zFCP with LOADDEV button + // Cannot have both. (In advanced mode they create the directory entries.) + if (hwTabIndex == 0) { + // Find a device to be IPLed? + var ECKD_FBA_diskRows = $('#' + thisTabId + ' table:eq(0):visible tbody tr'); + var iplSet = 0; + for (var i = 0; i < ECKD_FBA_diskRows.length; i++) { + var diskArgs = ECKD_FBA_diskRows.eq(i).find('td'); + if (diskArgs.eq(7).find('input').attr("checked") === true) { + iplSet = 1; + break; + } + } + + // Check if zFCP loaddev checked + var zfcpRows = $('#' + thisTabId + ' table:eq(1):visible tbody tr'); + if (zfcpRows.length > 0) { + for ( var i = 0; i < zfcpRows.length; i++) { + var diskArgs = zfcpRows.eq(i).find('td'); + // This is either true or false + var loaddev = diskArgs.eq(7).find('input').attr('checked'); + if (loaddev && iplSet) { + errMsg = errMsg + 'You cannot have both disk IPL and zFCP LOADDEV, can only IPL one device.
                          '; + ready = false; + } + } + } + } + + // If inputs are valid, ready to provision + if (ready) { + // Generate user directory entry if basic tab is selected + if (hwTabIndex == 0) { + updateUserEntry(thisTabId); + } + + if (!os.val()) { + // If no OS is given, create a virtual server + var msg = ''; + if (diskRows.length > 0) { + msg = 'Do you want to create a virtual server without an operating system?'; + } else { + // If no disks are given, create a virtual server (no disk) + msg = 'Do you want to create a virtual server without an operating system or disks?'; + } + + // Open dialog to confirm + var confirmDialog = $('

                          ' + msg + '

                          '); + confirmDialog.dialog({ + title:'Confirm', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + // Disable provision button + provisionBtn.attr('disabled', 'true'); + + // Show loader + $('#zProvisionStatBar' + inst).show(); + $('#zProvisionLoader' + inst).show(); + + // Disable add disk button + addDiskLink.attr('disabled', 'true'); + + // Disable close button on disk table + $('#' + thisTabId + ' table span').unbind('click'); + + // Disable all inputs + var inputs = $('#' + thisTabId + ' input'); + inputs.attr('disabled', 'disabled'); + + // Disable all selects + var selects = $('#' + thisTabId + ' select'); + selects.attr('disabled', 'disabled'); + + // Add a new line at the end of the user entry + var textarea = $('#' + thisTabId + ' textarea'); + var tmp = jQuery.trim(textarea.val()); + textarea.val(tmp + '\n'); + textarea.attr('readonly', 'readonly'); + textarea.css( { + 'background-color' : '#F2F2F2' + }); + + // Get node name + var node = $('#' + thisTabId + ' input[name=nodeName]').val(); + // Get userId + var userId = $('#' + thisTabId + ' input[name=userId]').val(); + // Get hardware control point + var hcp = $('#' + thisTabId + ' input[name=hcp]').val(); + // Get group + var group = $('#' + thisTabId + ' input[name=group]').val(); + // Get IP address and hostname + var ip = $('#' + thisTabId + ' input[name=ip]').val(); + var hostname = $('#' + thisTabId + ' input[name=hostname]').val(); + + // Generate arguments to sent + var args = node + ';zvm.hcp=' + hcp + + ';zvm.userid=' + userId + + ';nodehm.mgt=zvm' + + ';groups=' + group; + if (ip) + args += ';hosts.ip=' + ip; + + if (hostname) + args += ';hosts.hostnames=' + hostname; + + /** + * (1) Define node + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : args, + msg : 'cmd=nodeadd;out=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } else { + /** + * Create a virtual server and install OS + */ + + // Disable provision button + $(this).attr('disabled', 'true'); + + // Show loader + $('#zProvisionStatBar' + inst).show(); + $('#zProvisionLoader' + inst).show(); + + // Disable add disk button + addDiskLink.attr('disabled', 'true'); + + // Disable close button on disk table + $('#' + thisTabId + ' table span').unbind('click'); + + // Disable all inputs + var inputs = $('#' + thisTabId + ' input'); + inputs.attr('disabled', 'disabled'); + inputs.css( { + 'background-color' : '#F2F2F2' + }); + + // Disable all selects + var selects = $('#' + thisTabId + ' select'); + selects.attr('disabled', 'disabled'); + selects.css( { + 'background-color' : '#F2F2F2' + }); + + // Add a new line at the end of the user entry + var textarea = $('#' + thisTabId + ' textarea'); + var tmp = jQuery.trim(textarea.val()); + textarea.val(tmp + '\n'); + textarea.attr('readonly', 'readonly'); + textarea.css( { + 'background-color' : '#F2F2F2' + }); + + // Get node name + var node = $('#' + thisTabId + ' input[name=nodeName]').val(); + // Get userId + var userId = $('#' + thisTabId + ' input[name=userId]').val(); + // Get hardware control point + var hcp = $('#' + thisTabId + ' input[name=hcp]').val(); + // Get group + var group = $('#' + thisTabId + ' input[name=group]').val(); + // Get IP address and hostname + var ip = $('#' + thisTabId + ' input[name=ip]').val(); + var hostname = $('#' + thisTabId + ' input[name=hostname]').val(); + + // Generate arguments to sent + var args = node + ';zvm.hcp=' + hcp + + ';zvm.userid=' + userId + + ';nodehm.mgt=zvm' + + ';groups=' + group; + if (ip) + args += ';hosts.ip=' + ip; + + if (hostname) + args += ';hosts.hostnames=' + hostname; + + /** + * (1) Define node + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : args, + msg : 'cmd=nodeadd;out=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateZProvisionNewStatus(data); + } + }); + } + } else { + // Show warning message + var warn = createWarnBar(errMsg); + warn.prependTo($(this).parent().parent()); + } + }); + provNew.append(provisionBtn); + + return provNew; +} +/** + * Remove zprovision loading gif for zhcp and message + * + * @param division holding the gif and message + */ +function removeProvisionLoadingGif(provisionStatBar) { + + // Only remove the status bar message and gif we added, then hide the status bar + var items = provisionStatBar.find('div').children(); + for (var i = 0; i< items.length; i++) { + var nname = items[i].nodeName; + var myid = items[i].id; + if (nname == "B" && myid == "loadzhcp") { + items[i].remove() + } else if (nname == "IMG" && myid == "loadingpic") { + items[i].remove(); + } + } + provisionStatBar.hide(); +} + +/** + * Set hash entry to 0 and check if all are 0. If so call + * removeProvisionLoadingGif + * + * @param division holding the gif and message, and hash, and + * key + */ +function checkProvisionCallsDone(provisionStatBar, table, finishedKey) { + + table[finishedKey] = 0; + + for (var key in table) { + if (table[key] == 1) { + return; // More to do + } + } + + removeProvisionLoadingGif(provisionStatBar); +} + +/** + * Load zVMs into column (service page) + * + * @param col Table column where OS images will be placed + */ +function loadzVMs(col) { + // Get group names and description and append to group column + if (!$.cookie('xcat_zvms')) { + var infoBar = createInfoBar('No selectable z/VM available'); + col.append(infoBar); + return; + } + + var zNames = $.cookie('xcat_zvms').split(','); + + var radio, zBlock, args, zvm, hcp; + for (var i in zNames) { + if( !zNames[i] || 0 === zNames[i].length) continue; + args = zNames[i].split(':'); + zvm = args[0]; + hcp = args[1]; + + // Create block for each group + zBlock = $('
                          ').css({ + 'border': '1px solid', + 'max-width': '200px', + 'margin': '5px auto', + 'padding': '5px', + 'display': 'block', + 'vertical-align': 'middle', + 'cursor': 'pointer', + 'white-space': 'normal' + }).click(function(){ + $(this).children('input:radio').attr('checked', 'checked'); + $(this).parents('td').find('div').attr('class', 'ui-state-default'); + $(this).attr('class', 'ui-state-active'); + }); + radio = $('').css('display', 'none'); + zBlock.append(radio, $('' + zvm + ' managed by ' + hcp + '')); + zBlock.children('span').css({ + 'display': 'block', + 'margin': '5px', + 'text-align': 'left' + }); + col.append(zBlock); + } +} + +/** + * Load groups into column + * + * @param col Table column where OS images will be placed + */ +function loadSrvGroups(col) { + // Get group names and description and append to group column + if (!$.cookie('xcat_srv_groups')) { + var infoBar = createInfoBar('No selectable group available'); + col.append(infoBar); + return; + } + + var groupNames = $.cookie('xcat_srv_groups').split(','); + + var groupBlock, radio, args, name, ip, hostname, desc; + for (var i in groupNames) { + if( !groupNames[i] || 0 === groupNames[i].length) continue; + args = groupNames[i].split(':'); + name = args[0]; + ip = args[1]; + hostname = args[2]; + desc = args[3]; + + // Create block for each group + groupBlock = $('
                          ').css({ + 'border': '1px solid', + 'max-width': '200px', + 'margin': '5px auto', + 'padding': '5px', + 'display': 'block', + 'vertical-align': 'middle', + 'cursor': 'pointer', + 'white-space': 'normal' + }).click(function(){ + $(this).children('input:radio').attr('checked', 'checked'); + $(this).parents('td').find('div').attr('class', 'ui-state-default'); + $(this).attr('class', 'ui-state-active'); + }); + radio = $('').css('display', 'none'); + groupBlock.append(radio, $('' + name + ': ' + desc + '')); + groupBlock.children('span').css({ + 'display': 'block', + 'margin': '5px', + 'text-align': 'left' + }); + col.append(groupBlock); + } +} + +/** + * Load OS images into column + * + * @param col Table column where OS images will be placed + */ +function loadOSImages(col) { + // Get group names and description and append to group column + if (!$.cookie('xcat_srv_imagenames')) { + var infoBar = createInfoBar('No selectable image available'); + col.append(infoBar); + return; + } + + var imgNames = $.cookie('xcat_srv_imagenames').split(','); + + var imgBlock, radio, args, name, desc; + for (var i in imgNames) { + if( !imgNames[i] || 0 === imgNames[i].length) continue; + args = imgNames[i].split(':'); + name = args[0]; + desc = args[1]; + + // Create block for each image + imgBlock = $('
                          ').css({ + 'border': '1px solid', + 'max-width': '200px', + 'margin': '5px auto', + 'padding': '5px', + 'display': 'block', + 'vertical-align': 'middle', + 'cursor': 'pointer', + 'white-space': 'normal' + }).click(function(){ + $(this).children('input:radio').attr('checked', 'checked'); + $(this).parents('td').find('div').attr('class', 'ui-state-default'); + $(this).attr('class', 'ui-state-active'); + + $('#select-table tbody tr:eq(0) td:eq(3) input[name="master"]').attr('checked', ''); + $('#select-table tbody tr:eq(0) td:eq(3) input[name="master"]').parents('td').find('div').attr('class', 'ui-state-default'); + }); + radio = $('').css('display', 'none'); + imgBlock.append(radio, $('' + name + ': ' + desc + '')); + imgBlock.children('span').css({ + 'display': 'block', + 'margin': '5px', + 'text-align': 'left' + }); + col.append(imgBlock); + } +} + +/** + * Load golden images into column + * + * @param col Table column where master copies will be placed + */ +function loadGoldenImages(col) { + // Get group names and description and append to group column + if (!$.cookie('xcat_srv_goldenimages')) { + var infoBar = createInfoBar('No selectable master copies available'); + col.append(infoBar); + return; + } + + var imgNames = $.cookie('xcat_srv_goldenimages').split(','); + + var imgBlock, radio, args, name, desc; + for (var i in imgNames) { + if( !imgNames[i] || 0 === imgNames[i].length) continue; + args = imgNames[i].split(':'); + name = args[0]; + desc = args[1]; + + // Create block for each image + imgBlock = $('
                          ').css({ + 'border': '1px solid', + 'max-width': '200px', + 'margin': '5px auto', + 'padding': '5px', + 'display': 'block', + 'vertical-align': 'middle', + 'cursor': 'pointer', + 'white-space': 'normal' + }).click(function(){ + $(this).children('input:radio').attr('checked', 'checked'); + $(this).parents('td').find('div').attr('class', 'ui-state-default'); + $(this).attr('class', 'ui-state-active'); + + // Un-select zVM and image + $('#select-table tbody tr:eq(0) td:eq(2) input[name="image"]').attr('checked', ''); + $('#select-table tbody tr:eq(0) td:eq(2) input[name="image"]').parents('td').find('div').attr('class', 'ui-state-default'); + + $('#select-table tbody tr:eq(0) td:eq(0) input[name="hcp"]').attr('checked', ''); + $('#select-table tbody tr:eq(0) td:eq(0) input[name="hcp"]').parents('td').find('div').attr('class', 'ui-state-default'); + }); + radio = $('').css('display', 'none'); + imgBlock.append(radio, $('' + name + ': ' + desc + '')); + imgBlock.children('span').css({ + 'display': 'block', + 'margin': '5px', + 'text-align': 'left' + }); + col.append(imgBlock); + } +} + +/** + * Set a cookie for zVM host names (service page) + * + * @param data Data from HTTP request + */ +function setzVMCookies(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var zvms = new Array(); + var hosts = data.rsp[0].split("\n"); + for ( var i = 0; i < hosts.length; i++) { + if (hosts[i] != null && hosts[i] != "") { + zvms.push(hosts[i]); + if (typeof console == "object"){ + console.log("Setting a zVM cookie:<"+hosts[i]+">"); + } + } + } + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_zvms', zvms, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Set a cookie for master copies (service page) + * + * @param data Data from HTTP request + */ +function setGoldenImagesCookies(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1) { + var copies = new Array(); + var tmp = data.rsp[0].split(","); + for ( var i = 0; i < tmp.length; i++) { + if (tmp[i] != null && tmp[i] != "") { + copies.push(tmp[i]); + } + } + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_srv_goldenimages', copies, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Set a cookie for disk pool names of a given node + * + * @param data Data from HTTP request + */ +function setDiskPoolCookies(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var node = data.msg; + var pools = data.rsp[0].split(node + ": "); + var pools2 = []; + for (var j in pools) { + if (pools[j] != "") { + pools2.push(jQuery.trim(pools[j])); + } + } + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_' + node + 'diskpools', pools2, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Set a cookie for zFCP pool names of a given node + * + * @param data Data from HTTP request + */ +function setZfcpPoolCookies(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var node = data.msg; + var pools = data.rsp[0].split(node + ': '); + var pools2 = []; + for (var j in pools) { + if (pools[j] != "") { + pools2.push(jQuery.trim(pools[j])); + } + } + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_' + node + 'zfcppools', pools2, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Set a cookie for zHCP host names + * + * @param zhcps List of zHCPs known + */ +function setzHcpCookies(zhcps) { + if (zhcps.length) { + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_zhcps', zhcps, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Set a cookie for z/VM user profile names of a given node + * + * @param data Data from HTTP request + */ +function setUserProfilesCookies(data) { + if (data.rsp.length && data.rsp[0].indexOf("Failed") == -1 && data.rsp[0].indexOf("Error") == -1 ) { + var node = data.msg; + var profiles = data.rsp[0].split(node + ': '); + var profiles2 = []; + for (var j in profiles) { + if (profiles[j] != "") { + profiles2.push(jQuery.trim(profiles[j])); + } + } + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (60 * 60 * 1000)); + $.cookie('xcat_' + node + 'userprofiles', profiles2, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Create virtual machine (service page) + * + * @param tabId Tab ID + * @param group Group + * @param hcp Hardware control point + * @param img OS image + */ +function createzVM(tabId, group, hcp, img, owner) { + // Submit request to create VM + // webportal provzlinux [group] [hcp] [image] [owner] + var iframe = createIFrame('lib/srv_cmd.php?cmd=webportal&tgt=&args=provzlinux;' + group + ';' + hcp + ';' + img + ';' + owner + '&msg=&opts=flush'); + iframe.prependTo($('#' + tabId)); +} + +/** + * Query the profiles that exists + * + * @param panelId Panel ID + */ +function queryProfiles(panelId) { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'osimage', + msg : panelId + }, + + success : function(data) { + data = decodeRsp(data); + var panelId = data.msg; + setOSImageCookies(data); + configProfilePanel(panelId); + } + }); +} + +/** + * Panel to configure directory entries and disks for a profile + * + * @param panelId Panel ID + */ +function configProfilePanel(panelId) { + // Wipe panel clean + $('#' + panelId).empty(); + + // Add info bar + $('#' + panelId).append(createInfoBar('Create, edit, and delete profiles for the self-service portal. It is important to note the default z/VM user ID for any profile should be LXUSR.')); + + // Create table + var tableId = 'zvmProfileTable'; + var table = new DataTable(tableId); + table.init(['', 'Profile', 'Disk pool', 'Disk size', 'Directory entry']); + + // Insert profiles into table + var profiles = $.cookie('xcat_profiles').split(','); + profiles.push('default'); // Add default profile + for (var i in profiles) { + if (profiles[i]) { + // Columns are: profile, selectable, description, disk pool, disk size, and directory entry + var cols = new Array(profiles[i], '', '', ''); + + // Add remove button where id = user name + cols.unshift(''); + + // Add row + table.add(cols); + } + } + + // Append datatable to tab + $('#' + panelId).append(table.object()); + + // Turn into datatable + $('#' + tableId).dataTable({ + 'iDisplayLength': 50, + 'bLengthChange': false, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + + // Create action bar + var actionBar = $('
                          ').css("width", "450px"); + + // Create a profile + var createLnk = $('Create'); + createLnk.click(function() { + profileDialog(); + }); + + // Edit a profile + var editLnk = $('Edit'); + editLnk.click(function() { + var profiles = $('#' + tableId + ' input[type=checkbox]:checked'); + for (var i in profiles) { + var profile = profiles.eq(i).attr('name'); + if (profile) { + // Column order is: profile, selectable, disk pool, disk size, and directory entry + var cols = profiles.eq(i).parents('tr').find('td'); + var pool = cols.eq(2).text(); + var size = cols.eq(3).text(); + var entry = cols.eq(4).html().replace(new RegExp('
                          ', 'g'), '\n'); + + editProfileDialog(profile, pool, size, entry); + } + } + }); + + // Delete a profile + var deleteLnk = $('Delete'); + deleteLnk.click(function() { + var profiles = getNodesChecked(tableId); + if (profiles) { + deleteProfileDialog(profiles); + } + }); + + // Refresh profiles table + var refreshLnk = $('Refresh'); + refreshLnk.click(function() { + queryProfiles(panelId); + }); + + // Create an action menu + var actionsMenu = createMenu([refreshLnk, createLnk, editLnk, deleteLnk]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + tableId + '_wrapper').prepend(menuDiv); + menuDiv.append(actionBar); + $('#' + tableId + '_filter').appendTo(menuDiv); + + // Resize accordion + $('#' + tableId).parents('.ui-accordion').accordion('resize'); + + // Query directory entries and disk pool/size for each profile + for (var i in profiles) { + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'getdefaultuserentry;' + profiles[i], + msg : 'out=' + panelId + ';profile=' + profiles[i] + }, + + success : function(data) { + data = decodeRsp(data); + insertDirectoryEntry(data); + } + }); + + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'getzdiskinfo;' + profiles[i], + msg : 'out=' + panelId + ';profile=' + profiles[i] + }, + + success : function(data) { + data = decodeRsp(data); + insertDiskInfo(data); + } + }); + } +} + +/** + * Insert the directory entry into the profile table + * + * @param data Data from HTTP request + */ +function insertDirectoryEntry(data) { + var tableId = 'zvmProfileTable'; + var args = data.msg.split(';'); + + var profile = args[1].replace('profile=', ''); + + // Do not continue if there is nothing + if (!data.rsp.length) + return; + + var entry = data.rsp[0].replace(new RegExp('\n', 'g'), '
                          '); + + // Get the row containing the profile + var rowPos = findRow(profile, '#' + tableId, 1); + if (rowPos < 0) + return; + + // Update the directory entry column + var dTable = $('#' + tableId).dataTable(); + dTable.fnUpdate(entry, rowPos, 4, false); + + // Adjust table styling + $('#' + tableId + ' td:nth-child(5)').css({ + 'text-align': 'left' + }); + adjustColumnSize(tableId); +} + +/** + * Insert the disk info into the profile table + * + * @param data Data from HTTP request + */ +function insertDiskInfo(data) { + var tableId = 'zvmProfileTable'; + var args = data.msg.split(';'); + + var profile = args[1].replace('profile=', ''); + + // Do not continue if there is nothing + if (!data.rsp.length) + return; + + // Get the row containing the profile + var rowPos = findRow(profile, '#' + tableId, 1); + if (rowPos < 0) + return; + + // Update the disk info columns + var dTable = $('#' + tableId).dataTable(); + + var tmp = ""; + var pool = ""; + var eckdSize = 0; + var info = data.rsp[0].split('\n'); + for (var i in info) { + if (info[i].indexOf('diskpool') > -1) { + tmp = info[i].split('='); + pool = jQuery.trim(tmp[1]); + + dTable.fnUpdate(pool, rowPos, 2, false); + } if (info[i].indexOf('eckd_size') > -1) { + tmp = info[i].split('='); + eckdSize = jQuery.trim(tmp[1]); + + dTable.fnUpdate(eckdSize, rowPos, 3, false); + } + } + + // Adjust table styling + adjustColumnSize(tableId); +} + +/** + * Open profile dialog + */ +function profileDialog() { + // Create form to add profile + var dialogId = 'zvmCreateProfile'; + var profileForm = $('
                          '); + + // Create info bar + var info = createInfoBar('Configure the default settings for a profile'); + profileForm.append(info); + + // Insert profiles into select + var profileSelect = $(''); + var profiles = $.cookie('xcat_profiles').split(','); + profiles.push('default'); // Add default profile + for (var i in profiles) { + if (profiles[i]) { + profileSelect.append($('')); + } + } + + profileForm.append($('
                          ').append(profileSelect)); + profileForm.append('
                          '); + profileForm.append('
                          '); + profileForm.append('
                          ').css({ - 'font-size': '10px', - 'height': '50px', - 'width': '200px', - 'background-color': '#000', - 'color': '#fff', - 'border': '0px', - 'display': 'block' - }); - - // Create links to save and cancel changes - var lnkStyle = { - 'color': '#58ACFA', - 'font-size': '10px', - 'display': 'inline-block', - 'padding': '5px', - 'float': 'right' - }; - - var saveLnk = $('Save').css(lnkStyle).hide(); - var cancelLnk = $('Cancel').css(lnkStyle).hide(); - var infoSpan = $('Click to edit').css(lnkStyle); - - // Save changes onclick - saveLnk.bind('click', function(){ - // Get node and comment - var node = $(this).parent().parent().find('img').attr('id').replace('Tip', ''); - var comments = $(this).parent().find('textarea').val(); - - // Save comment - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chdef', - tgt : '', - args : '-t;node;-o;' + node + ';usercomment=' + comments, - msg : 'out=nodesTab;tgt=' + node - }, - - success: showChdefOutput - }); - - // Hide cancel and save links - $(this).hide(); - cancelLnk.hide(); - }); - - // Cancel changes onclick - cancelLnk.bind('click', function(){ - // Get original comment and put it back - var orignComments = $(this).parent().find('textarea').text(); - $(this).parent().find('textarea').val(orignComments); - - // Hide cancel and save links - $(this).hide(); - saveLnk.hide(); - infoSpan.show(); - }); - - // Show save link when comment is edited - txtArea.bind('click', function(){ - saveLnk.show(); - cancelLnk.show(); - infoSpan.hide(); - }); - - toolTip.append(txtArea); - toolTip.append(cancelLnk); - toolTip.append(saveLnk); - toolTip.append(infoSpan); - - return toolTip; -} - -/** - * Create a tool tip for node status - * - * @return Tool tip - */ -function createStatusToolTip() { - // Create tooltip container - var toolTip = $('
                          ').css({ - 'width': '150px', - 'font-weight': 'normal' - }); - - // Create info text - var info = $('

                          ').css({ - 'white-space': 'normal' - }); - info.append('Click here to refresh the node status. To configure the xCAT monitor, '); - - // Create link to turn on xCAT monitoring - var monitorLnk = $('click here').css({ - 'color': '#58ACFA', - 'font-size': '10px' - }); - - // Open dialog to configure xCAT monitor - monitorLnk.bind('click', function(){ - // Check if xCAT monitor is enabled - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'monls', - tgt : '', - args : 'xcatmon', - msg : '' - }, - - success : openConfXcatMon - }); - }); - - info.append(monitorLnk); - toolTip.append(info); - - return toolTip; -} - -/** - * Create a tool tip for power status - * - * @return Tool tip - */ -function createPowerToolTip() { - // Create tooltip container - var toolTip = $('
                          Click here to refresh the power status
                          ').css({ - 'width': '150px', - 'white-space': 'normal', - 'font-weight': 'normal' - }); - return toolTip; -} - -/** - * Create a tool tip for monitoring status - * - * @return Tool tip - */ -function createMonitorToolTip() { - // Create tooltip container - var toolTip = $('
                          Click here to refresh the monitoring status
                          ').css({ - 'width': '150px', - 'white-space': 'normal', - 'font-weight': 'normal' - }); - return toolTip; -} - -/** - * Open dialog to configure xCAT monitor - * - * @param data Data returned from HTTP request - */ -function openConfXcatMon(data) { - // Create info bar - var info = createInfoBar('Configure the xCAT monitor. Select to enable or disable the monitor below.'); - var dialog = $('
                          '); - dialog.append(info); - - // Create status area - var statusArea = $('
                          ').css('padding-top', '10px'); - var label = $(''); - statusArea.append(label); - - // Get xCAT monitor status - var status = data.rsp[0]; - var buttons; - // If xCAT monitor is disabled - if (status.indexOf('not-monitored') > -1) { - status = $('Disabled').css('padding', '0px 5px'); - statusArea.append(status); - - // Create enable and cancel buttons - buttons = { - "Enable": function(){ - // Enable xCAT monitor - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'monstart', - tgt : '', - args : 'xcatmon', - msg : '' - }, - - success : function(data){ - openDialog('info', data.rsp[0]); - } - }); - $(this).dialog("close"); - }, - "Cancel": function(){ - $(this).dialog("close"); - } - }; - } else { - status = $('Enabled').css('padding', '0px 5px'); - statusArea.append(status); - - // Create disable and cancel buttons - buttons = { - "Disable": function(){ - // Disable xCAT monitor - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'monstop', - tgt : '', - args : 'xcatmon', - msg : '' - }, - - success : function(data){ - openDialog('info', data.rsp[0]); - } - }); - $(this).dialog("close"); - }, - "Cancel": function(){ - $(this).dialog("close"); - } - }; - } - - dialog.append(statusArea); - - // Open dialog - dialog.dialog({ - modal: true, - width: 500, - buttons: buttons - }); -} - -/** - * Show chdef output - * - * @param data Data returned from HTTP request - */ -function showChdefOutput(data) { - // Get output - var out = data.rsp; - var args = data.msg.split(';'); - var tabID = args[0].replace('out=', ''); - var tgt = args[1].replace('tgt=', ''); - - // Find info bar on nodes tab, if any - var info = $('#' + tabID).find('.ui-state-highlight'); - if (!info.length) { - // Create info bar if one does not exist - info = createInfoBar(''); - $('#' + tabID).append(info); - } - - // Go through output and append to paragraph - var prg = $('

                          '); - for (var i in out) { - prg.append(tgt + ': ' + out[i] + '
                          '); - } - - info.append(prg); -} - -/** - * Set node attributes - * - * @param data Data returned from HTTP request - */ -function setNodeAttrs(data) { - // Clear hash table containing definable node attributes - nodeAttrs = new Array(); - - // Get definable attributes - var attrs = data.rsp[2].split(/\n/); - - // Go through each line - var attr, key, descr; - for (var i in attrs) { - attr = attrs[i]; - - // If the line is not empty - if (attr) { - // If the line has the attribute name - if (attr.indexOf(':') && attr.indexOf(' ')) { - // Get attribute name and description - key = jQuery.trim(attr.substring(0, attr.indexOf(':'))); - descr = jQuery.trim(attr.substring(attr.indexOf(':') + 1)); - - // Remove arrow brackets - descr = descr.replace(new RegExp('<|>', 'g'), ''); - - // Set hash table where key = attribute name and value = description - nodeAttrs[key] = descr; - } else { - // Remove arrow brackets - attr = attr.replace(new RegExp('<|>', 'g'), ''); - - // Append description to hash table - nodeAttrs[key] = nodeAttrs[key] + '\n' + attr; - } - } // End of if - } // End of for -} - - -/** - * Load the discover z/VM virtual systems page - * - * @param tgtNode Target node to set properties - */ -function discoverVMNodes(tgtNodes) { - var fs, legend, htmlLine; - - // Get nodes tab - var tab = getNodesTab(); - - // Generate new tab ID - var inst = 0; - var newTabId = 'discoverVMNodesTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = 'discoverVMNodesTab' + inst; - } - - // Open new tab - // Create set properties form - var discoverVMNodesForm = $('
                          '); - - // Create info bar - var infoBar = createInfoBar( 'Initiate, stop or query the status of z/VM node discovery. ' + - 'To initiate discovery, specify discovery parameters and click on the Discover button. ' + - 'To stop an on-going discovery related to a z/VM host, specify the host node name and '+ - 'click on the Stop button. ' + - 'To obtain the status of discovery for a particular host, specify the host node name and '+ - 'click on the Stop button.' - ); - discoverVMNodesForm.append(infoBar); - - // Create the status bar and hide it. - var statBarId = 'statusBar_' + newTabId; - //var statBar = $( '
                          ' ); - //discoverVMNodesForm.append( statBar ); - statBar = createStatusBar( statBarId ); - statBar.hide(); - discoverVMNodesForm.append( statBar ); - - // Create Host fieldset - fs = $('
                          '); - legend = $('z/VM Host'); - fs.append(legend); - discoverVMNodesForm.append(fs); - - // Target node or group - htmlLine = $('
                          '); - discoverVMNodesForm.append( htmlLine ); - - // Create Discovery Parameters fieldset - fs = $('
                          '); - legend = $('Discovery Parameters'); - fs.append( legend ); - discoverVMNodesForm.append( fs ); - - // Create an input for each definable attribute - var div, label, input, descr, value; - - // Define DefineTo radio buttons - div = $('
                          ').css( 'display', 'inline-block' ).css( 'vertical-align', 'top' ); - div.append( '' ); - div.append( '
                        • xCAT and OpenStack
                        • ' ); - div.append( '
                        • xCAT only
                        • ' ); - div.append( '
                        • OpenStack only (only already discovered xCAT nodes)
                        • ' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - - // Userid filter - divUserid = newTabId + "_divUserid"; - div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); - div.append( '' ); - div.append( '' ).css( 'margin-top', '5px' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - - // IP address filter - divIP = newTabId + "_divIP"; - div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); - div.append( '' ); - div.append( '' ).css( 'margin-top', '5px' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - - // Group name input - divGroup = newTabId + '_divGroup'; - div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); - div.append( '' ); - div.append( '' ).css( 'margin-top', '5px' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - - // Node Name Format input - divNodename = newTabId + '_divNodename'; - div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); - div.append( '' ); - div.append( '' ).css( 'margin-top', '5px' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - discoverVMNodesForm.find('#' + divNodename).hide(); - - // OpenStack operand input - divOpenStackOps = newTabId + '_divOpenStackOps'; - div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); - div.append( '' ); - div.append( '' ).css( 'margin-top', '5px' ); - div.append( '
                          ' ); - div.append( '' ); - div.append( '' ).css( 'margin-top', '5px' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - - // Define Verbose radio buttons - div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ).css( 'text-align', 'top' ); - div.append( '' ); - div.append( '
                        • Normal response, showing only important information
                        • ' ); - div.append( '
                        • Verbose response, normal response plus additional information (e.g. reason a system is ignored)
                        • ' ); - discoverVMNodesForm.append( div ); - discoverVMNodesForm.append( '
                          ' ); - - // Generate tooltips - discoverVMNodesForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - - // Show appropriate input fields for defineto choices. - discoverVMNodesForm.change(function(){ - var defineTo = $(this).parent().find('input[name="defineTo"]:checked').val(); - if ( defineTo == 'both' ) { - discoverVMNodesForm.find('#' + divUserid).show(); - discoverVMNodesForm.find('#' + divIP).show(); - discoverVMNodesForm.find('#' + divGroup).show(); - discoverVMNodesForm.find('#' + divOpenStackOps).show(); - discoverVMNodesForm.find('#' + divNodename).hide(); - } else if ( defineTo == 'xcatonly' ) { - discoverVMNodesForm.find('#' + divUserid).show(); - discoverVMNodesForm.find('#' + divIP).show(); - discoverVMNodesForm.find('#' + divGroup).show(); - discoverVMNodesForm.find('#' + divOpenStackOps).hide(); - discoverVMNodesForm.find('#' + divNodename).show(); - } else if ( defineTo == 'openstackonly' ) { - discoverVMNodesForm.find('#' + divUserid).hide(); - discoverVMNodesForm.find('#' + divIP).hide(); - discoverVMNodesForm.find('#' + divGroup).hide(); - discoverVMNodesForm.find('#' + divOpenStackOps).show(); - discoverVMNodesForm.find('#' + divNodename).hide(); - } - }); - - - // Discover nodes button action - var discoverBtn = createButton('Discover'); - discoverBtn.click(function() { - var argList = ''; - var filter = ''; - - var hosts = $(this).parent().find('input[name=hosts]').val(); - if ( hosts != '' ) { - argList = 'zvmhost=' + hosts; - } - - var defineTo = $(this).parent().find('input[name="defineTo"]:checked').val(); - argList = argList + '||defineto=' + defineTo; - - var verbose = $(this).parent().find('input[name="verbose"]:checked').val(); - if ( verbose == 'yes' ) { - argList = argList + '||--verbose'; - } - - var useridFilter = $(this).parent().find('input[name=useridFilter]').val(); - if (( defineTo == 'both' || defineTo == 'xcatonly' ) && ( useridFilter != '' )) { - argList = argList + '||useridfilter=' + useridFilter; - } - - var ipFilter = $(this).parent().find('input[name=ipFilter]').val(); - if (( defineTo == 'both' || defineTo == 'xcatonly' ) && ( ipFilter != '' )) { - argList = argList + '||ipfilter=' + ipFilter; - } - - var group = $(this).parent().find('input[name=group]').val(); - if (( defineTo == 'both' || defineTo == 'xcatonly' ) && ( group != '' )) { - argList = argList + '||groups=' + group; - } - - var nodeNameFmt = $(this).parent().find('input[name=nodeNameFmt]').val(); - if ( defineTo == 'xcatonly' && nodeNameFmt != '' ) { - argList = argList + '||nodenameformat=' + nodeNameFmt; - } - - var openStackProj = $(this).parent().find('input[name=openStackProj]').val(); - var openStackUser = $(this).parent().find('input[name=openStackUser]').val(); - if ((( defineTo == 'both' ) || ( defineTo == 'openstackonly' )) && - (( openStackProj != '' ) || ( openStackUser != '' ))) { - if ( openStackProj != '' ) { - osArgs = '--project ' + openStackProj; - } else { - osArgs = ''; - } - if ( openStackUser != '' ) { - if ( osArgs != '' ) { - osArgs = osArgs + ' --user ' + openStackUser; - } else { - osArgs = '--user ' + openStackUser; - } - } - argList = argList + "||openstackoperands='" + osArgs + "'"; - } - - var out = $('

                          '); - out.append( 'Starting node discovery...' ); - out.append( '
                          ' ); - out.append( 'If node discovery is a short running task then its response will follow. If, however, the time it takes to complete discovery exceeds the http request timeout of a few minutes then the discovery response will not be returned to the browser. The status and list buttons can be used to obtained status on the discovery and see what systems have been discovered.' ); - $( '#' + statBarId ).find( 'div' ).append( out ); - statBar.show(); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodediscoverstart', - tgt : '', - args : argList, - att : '', - msg : statBarId - }, - success: function(data) { - updateDiscoverStatusBar( data, 1 ); - } - }); - - }); - discoverVMNodesForm.append(discoverBtn); - - // Status button - var statusBtn = createButton('Status'); - statusBtn.click( function() { - var hosts = $(this).parent().find('input[name=hosts]').val(); - var out = $('

                          '); - out.append( 'Querying status for discovery on ' + hosts + '...' ); - out.append( '
                          ' ); - $( '#' + statBarId ).find( 'div' ).append( out ); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodediscoverstatus', - tgt : '', - args : '--zvmhost||' + hosts, - att : '', - msg : statBarId - }, - success: function(data) { - updateDiscoverStatusBar( data, 1 ); - } - }); - - }); - discoverVMNodesForm.append( statusBtn ); - - // List button - var listBtn = createButton('List'); - listBtn.click( function() { - var hosts = $(this).parent().find('input[name=hosts]').val(); - var out = $('

                          '); - out.append( 'Listing systems discovered by the latest discovery on ' + hosts + '...' ); - out.append( '
                          ' ); - $( '#' + statBarId ).find( 'div' ).append( out ); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodediscoverls', - tgt : '', - args : '-t||zvm||--zvmhost||' + hosts, - att : '', - msg : statBarId - }, - success: function(data) { - updateDiscoverStatusBar( data, 1 ); - } - }); - }); - discoverVMNodesForm.append( listBtn ); - - // Stop button - var stopBtn = createButton('Stop'); - stopBtn.click(function() { - var hosts = $(this).parent().find('input[name=hosts]').val(); - var out = $('

                          '); - out.append( 'Stopping discovery on ' + hosts + '...' ); - out.append( '
                          ' ); - $( '#' + statBarId ).find( 'div' ).append( out ); - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodediscoverstop', - tgt : '', - args : '--zvmhost||' + hosts, - att : '', - msg : statBarId - }, - success: function(data) { - updateDiscoverStatusBar( data, 1 ); - } - }); - }); - discoverVMNodesForm.append( stopBtn ); - - // Append to discover tab - tab.add(newTabId, 'Discover', discoverVMNodesForm, true); - - // Select new tab - tab.select(newTabId); -} - - -/** - * Update discovery status bar - * - * @param data Data returned from HTTP request - */ -function updateDiscoverStatusBar( data, preformatted ) { - var statBarId = data.msg; - var rsp = data.rsp; - var statBar = $( '#' + statBarId ); - - // Go through response to make it readable in the status bar. - var out = $('

                          '); - for ( var i in rsp ) { - if ( preformatted == 1 ) { - out.append( '
                          ' + rsp[i] + '
                          ' ); - } else { - out.append( rsp[i] + '
                          ' ); - } - } - - // Write response to status bar and show the bar. - $( '#' + statBarId ).find( 'div' ).append( out ); - statBar.show(); -} - -/** - * Load set node properties page - * - * @param tgtNode Target node to set properties - */ -function editNodeProps(tgtNode) { - // Get nodes tab - var tab = getNodesTab(); - - // Generate new tab ID - var inst = 0; - var newTabId = 'editPropsTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = 'editPropsTab' + inst; - } - - // Open new tab - // Create set properties form - var editPropsForm = $('
                          '); - - // Create info bar - var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); - editPropsForm.append(infoBar); - - // Create an input for each definable attribute - var div, label, input, descr, value; - // Set node attribute - origAttrs[tgtNode]['node'] = tgtNode; - for (var key in nodeAttrs) { - // If an attribute value exists - if (origAttrs[tgtNode][key]) { - // Set the value - value = origAttrs[tgtNode][key]; - } else { - value = ''; - } - - // Create label and input for attribute - div = $('
                          ').css('display', 'inline-table'); - label = $('').css('vertical-align', 'middle'); - input = $('').css('margin-top', '5px'); - - // Change border to blue onchange - input.bind('change', function(event) { - $(this).css('border-color', 'blue'); - }); - - div.append(label); - div.append(input); - editPropsForm.append(div); - } - - // Change style for last division - div.css({ - 'display': 'block', - 'margin': '0px 0px 10px 0px' - }); - - // Generate tooltips - editPropsForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - - // Save changes - var saveBtn = createButton('Save'); - saveBtn.click(function() { - // Get all inputs - var inputs = $('#' + newTabId + ' input'); - - // Go through each input - var args = ''; - var attrName, attrVal; - inputs.each(function(){ - // If the border color is blue - if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { - // Change border color back to normal - $(this).css('border-color', ''); - - // Get attribute name and value - attrName = $(this).parent().find('label').text().replace(':', ''); - attrVal = $(this).val(); - - // Build argument string - if (args) { - // Handle subsequent arguments - args += ';' + attrName + '=' + attrVal; - } else { - // Handle the 1st argument - args += attrName + '=' + attrVal; - } - } - }); - - // Send command to change node attributes - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chdef', - tgt : '', - args : '-t;node;-o;' + tgtNode + ';' + args, - msg : 'out=' + newTabId + ';tgt=' + tgtNode - }, - - success: showChdefOutput - }); - }); - editPropsForm.append(saveBtn); - - // Cancel changes - var cancelBtn = createButton('Cancel'); - cancelBtn.click(function() { - // Close the tab - tab.remove($(this).parent().parent().attr('id')); - }); - editPropsForm.append(cancelBtn); - - // Append to discover tab - tab.add(newTabId, 'Edit', editPropsForm, true); - - // Select new tab - tab.select(newTabId); -} - -/** - * Open set node attributes dialog - */ -function openSetAttrsDialog() { - // Open new tab - // Create set properties form - var setPropsForm = $('
                          '); - - // Create info bar - var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); - setPropsForm.append(infoBar); - - // Create an input for each definable attribute - var div, label, input, descr, value; - for (var key in nodeAttrs) { - value = ''; - - // Create label and input for attribute - div = $('
                          ').css('display', 'inline'); - label = $('').css('vertical-align', 'middle'); - input = $('').css('margin-top', '5px'); - - // Change border to blue onchange - input.bind('change', function(event) { - $(this).css('border-color', 'blue'); - }); - - div.append(label); - div.append(input); - setPropsForm.append(div); - } - - // Change style for last division - div.css({ - 'display': 'block', - 'margin': '0px 0px 10px 0px' - }); - - // Generate tooltips - setPropsForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Enable vertical scroll - setPropsForm.css('overflow', 'auto'); - - // Open form as a dialog - setPropsForm.dialog({ - title: 'Set attributes', - modal: true, - close: function(){ - $(this).remove(); - }, - height: 400, - width: 800, - buttons: { - "Save": function() { - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get all inputs - var inputs = $(this).find('input'); - - // Go through each input - var args = ''; - var tgtNode, attrName, attrVal; - inputs.each(function(){ - // If the border color is blue - if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { - // Change border color back to normal - $(this).css('border-color', ''); - - // Get attribute name and value - attrName = $(this).parent().find('label').text().replace(':', ''); - attrVal = $(this).val(); - - // Get node name - if (attrName == 'node') { - tgtNode = attrVal; - } else { - // Build argument string - if (args) { - // Handle subsequent arguments - args += ';' + attrName + '=' + attrVal; - } else { - // Handle the 1st argument - args += attrName + '=' + attrVal; - } - } - } - }); - - // Send command to change node attributes - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chdef', - tgt : '', - args : '-t;node;-o;' + tgtNode + ';' + args, - msg : 'node=' + tgtNode - }, - - /** - * Show results - * - * @param data - * Data returned from HTTP request - * @return Nothing - */ - success: function(data) { - // Get output - var out = data.rsp; - var node = data.msg.replace('node=', ''); - - // Go through output and append to paragraph - var msg = ''; - for (var i in out) { - if (!msg) { - msg = node + ': ' + out[i]; - } else { - msg += '
                          ' + node + ': ' + out[i]; - } - } - - openDialog('info', msg); - } - }); - - // Close dialog - $(this).dialog( "close" ); - }, - "Cancel": function(){ - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Turn on monitoring for a given node - * - * @param node Node to monitor on or off - * @param monitor Monitor state, on or off - */ -function monitorNode(node, monitor) { - // Show ganglia loader - var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); - gangliaCol.find('img').show(); - - if (monitor == 'on') { - // Append loader to warning bar - var warningBar = $('#nodesTab').find('.ui-state-error p'); - if (warningBar.length) { - warningBar.append(createLoader('')); - } - - if (node) { - // Check if ganglia RPMs are installed - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliacheck;' + node, - msg : node // Node range will be passed along in data.msg - }, - - /** - * Start ganglia on a given node range - * - * @param data Data returned from HTTP request - */ - success : function(data) { - // Get response - var out = data.rsp[0].split(/\n/); - - // Go through each line - var warn = false; - var warningMsg = ''; - for (var i in out) { - // If an RPM is not installed - if (out[i].indexOf('not installed') > -1) { - warn = true; - - if (warningMsg) { - warningMsg += '
                          ' + out[i]; - } else { - warningMsg = out[i]; - } - } - } - - // If there are warnings - if (warn) { - // Create warning bar - var warningBar = createWarnBar(warningMsg); - warningBar.css('margin-bottom', '10px'); - warningBar.prependTo($('#nodesTab')); - } else { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastart;' + data.msg + ';-r', - msg : data.msg - }, - - success : function(data) { - // Remove any warnings - $('#nodesTab').find('.ui-state-error').remove(); - - // Update datatable - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastatus;' + data.msg, - msg : '' - }, - - success : loadGangliaStatus - }); - } - }); - } // End of if (warn) - } // End of function(data) - }); - } else { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastart', - msg : '' - }, - - success : function(data) { - // Remove any warnings - $('#nodesTab').find('.ui-state-error').remove(); - } - }); - } // End of if (node) - } else { - var args; - if (node) { - args = 'gangliastop;' + node + ';-r'; - } else { - args = 'gangliastop'; - } - - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : args, - msg : '' - }, - - success : function(data) { - // Hide ganglia loader - var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); - gangliaCol.find('img').hide(); - } - }); - } -} - -/** - * Install Ganglia on a given node - * - * @param node Node to install Ganglia on - */ -function installGanglia(node) { - var iframe = createIFrame('lib/cmd.php?cmd=webrun&tgt=&args=installganglia;' + node + '&msg=' + node + '&opts=flush'); - iframe.prependTo($('#nodesTab')); - - // Turn on Ganglia for node - monitorNode(node, 'on'); -} - -/** - * After nodes are loaded, load more information based on different hardware architectures - * - * @param group Group name - */ -function advancedLoad(group){ - var tempIndex = 0; - var tableHeaders = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); - var colNameHash = new Object(); - var colName = ''; - var archCol = 0, hcpCol = 0; - - // Find out the column name and their index - for (tempIndex = 0; tempIndex < tableHeaders.size(); tempIndex++){ - var header = tableHeaders.eq(tempIndex); - // Skip headers that are links, e.g. status, power, and monitor - if (header.find('a').size() > 0){ - continue; - } - - colName = header.text(); - - if (colName) { - colNameHash[colName] = tempIndex; - } - } - - // If there is no arch column, exit because you cannot distinguish hardware type - if (!colNameHash['arch']) { - return; - } - - if (!colNameHash['hcp']) { - return; - } - archCol = colNameHash['arch']; - hcpCol = colNameHash['hcp']; - - // Get hardware control point - var rows = $('#' + nodesTableId + ' tbody tr'); - var hcps = new Object(); - var rowsNum = rows.size(); - for (var j = 0; j < rowsNum; j++) { - var val = rows.eq(j).find('td').eq(hcpCol).html(); - var archval = rows.eq(j).find('td').eq(archCol).html(); - if (-1 == archval.indexOf('390')){ - continue; - } - hcps[val] = 1; - } - - if (Object.keys(hcps).length == 0) { - openDialog('warn', "No node found with hcp column filled in and 390 arch!"); - return; - } - // Get Nodes info bar - //var nodeInfoBar = getNodesTabInfoBar(); - //nodeInfoBar.append("\nEntering Advanced Load...\n") - - var args; - var shortzHcps = new Array(); - var zhcpHash = new Object(); - for (var h in hcps) { - // Get node without domain name - args = h.split('.'); - - if (!zhcpHash[args[0]]) { - - shortzHcps.push(args[0]); - zhcpHash[args[0]] = 1; - - // If there are no disk pools or network names cookie for this hcp - if (!$.cookie('xcat_' + args[0] + 'diskpools') || !$.cookie('xcat_' + args[0] + 'networks')) { - // Check if SMAPI is online - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsvm', - tgt : args[0], - args : '', - msg : 'group=' + group + ';hcp=' + args[0] - }, - - // Load hardware control point specific info - // Get disk pools and network names - success : loadHcpInfo - }); - } - } - } // End of for - - // Save zHCPs as a cookie - setzHcpCookies(shortzHcps); - - // Retrieve z/VM hypervisors and their zHCPs - if (!$.cookie('xcat_zvms')) { - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webportal', - tgt : '', - args : 'lszvm', - msg : '' - }, - - success : function(data) { - setzVMCookies(data); - } - }); - } -} - -/** - * Jump to provision page on-click - * - * @param tgtNodes Target nodes - */ -function jump2Provision(tgtNodes){ - var nodeArray = tgtNodes.split(','); - var nodeName = ''; - var index = 0; - var archType = ''; - var errorMsg = ''; - var master = ''; - var tftpserver = ''; - var nfsserver = ''; - var diaDiv = $('
                          '); - - // Check the first node's arch type - for (index in nodeArray){ - nodeName = nodeArray[index]; - - // Skip if node does not have arch - if (!origAttrs[nodeName]['arch']){ - errorMsg = 'Nodes should have arch defined! '; - break; - } - - if (index == 0) { - archType = origAttrs[nodeName]['arch']; - } - - // Skip if nodes do not have same arch - if (archType != origAttrs[nodeName]['arch']){ - errorMsg = 'Nodes should belong to the same arch!
                          '; - break; - } - } - - // Skip if nodes do not have MAC address - for (index in nodeArray){ - if (!origAttrs[nodeName]['mac'] || !origAttrs[nodeName]['ip']){ - errorMsg += 'Nodes should have the IP and MAC addresses defined!
                          '; - break; - } - } - - if (archType.indexOf('390') != -1) { - errorMsg += 'Please use the provision page'; - } - - // Open dialog to show error message - if (errorMsg){ - diaDiv.append(createWarnBar(errorMsg)); - diaDiv.dialog({ - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - 'Close': function(){ - $(this).dialog('destroy'); - } - } - }); - - return; - } - - if (origAttrs[nodeName]['xcatmaster']) { - master = origAttrs[nodeName]['xcatmaster']; - } - - if (origAttrs[nodeName]['tftpserver']) { - tftpserver = origAttrs[nodeName]['tftpserver']; - } - - if (origAttrs[nodeName]['nfsserver']) { - nfsserver = origAttrs[nodeName]['nfsserver']; - } - - window.location.href = 'provision.php?nodes=' + tgtNodes + '&arch=' + archType + '&master=' + master + - '&tftpserver=' + tftpserver + '&nfsserver=' + nfsserver; -} \ No newline at end of file +/** + * Global variables + *Test Git update in MCP_DEV branch */ +var nodesTab; // Nodes tabs +var origAttrs = new Object(); // Original node attributes +var nodeAttrs; // Node attributes +var nodesList; // Node list +var nodesTableId = 'nodesDatatable'; // Nodes datatable ID +var nodesTabInfoBar; +var builtInXCAT = 1; // 1 means xCAT shipped with zVM + +/** + * Set node tab + * + * @param tab + * Tab object + * @return Nothing + */ +function setNodesTab(tab) { + nodesTab = tab; +} + +/** + * Get node tab + * + * @return Tab object + */ +function getNodesTab() { + return nodesTab; +} + +/** + * Set node tab info bar. Used by zvmUtils.js + * + * @param tab + * infobar object + * @return Nothing + */ +function setNodesTabInfoBar(tabinfo) { + nodesTabInfoBar = tabinfo; +} + +/** + * Get node tab info bar + * + * @return Tab info bar object + */ +function getNodesTabInfoBar() { + return nodesTabInfoBar; +} + +/** + * Get node list + * + * @return Node list + */ +function getNodesList() { + return nodesList; +} + +/** + * Get nodes table ID + * + * @return Nodes table ID + */ +function getNodesTableId() { + return nodesTableId; +} + +/** + * Load nodes page + */ +function loadNodesPage() { + // If groups are not already loaded + if (!$('#groups').length) { + // Create a groups division + var groups = $('
                          '); + var nodes = $('
                          '); + $('#content').append(groups); + $('#content').append(nodes); + + // Create loader and info bar + groups.append(createLoader()); + + // Get groups + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'extnoderange', + tgt : '/.*', + args : 'subgroups', + msg : '' + }, + + // Load groups + success : function(data){ + data = decodeRsp(data); + loadGroups(data); + + var cookieGroup = $.cookie('xcat_selectgrouponnodes'); + if (cookieGroup) { + $('#groups .groupdiv div').each(function(){ + if ($(this).text() == cookieGroup){ + $(this).trigger('click'); + return false; + } + }); + } else { + // Trigger the first group click event + $('#groups .groupdiv div').eq(0).trigger('click'); + } + } + }); + } +} + +/** + * Show cluster summary in pie charts + * + * @param groupName Group name + */ +function loadPieSummary(groupName){ + var summaryTable = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
                          '; + $('#summaryTab').append(summaryTable); + $('#summaryTab .summarypie').append(createLoader()); + + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'summary;' + groupName, + msg : '' + }, + + success:function(data) { + data = decodeRsp(data); + for (var i in data.rsp) { + drawPieSummary(i, data.rsp[i]); + } + } + }); +} + +/** + * Get nodes information and draw pie chart + * + * @param index Node index + * @param valuePair Node information key value pairing + */ +function drawPieSummary(index, valuePair){ + var position = 0; + var key = ''; + var val = ''; + var chartTitle = ''; + var dataArray = []; + var tempArray = []; + var container = $('#summaryTab .summarypie').eq(index); + + position = valuePair.indexOf('='); + chartTitle = valuePair.substr(0, position); + tempArray = valuePair.substr(position + 1).split(';'); + + for (var i in tempArray) { + position = tempArray[i].indexOf(':'); + key = tempArray[i].substr(0, position); + val = Number(tempArray[i].substr(position + 1)); + dataArray.push([key,val]); + } + + container.empty(); + + var plot = $.jqplot(container.attr('id'), [dataArray], { + title: chartTitle, + seriesDefaults: { + renderer: $.jqplot.PieRenderer, + rendererOptions: { + padding: 5, + fill: true, + shadow: true, + shadowOffset: 2, + shadowDepth: 5, + shadowAlpha: 0.07, + dataLabels : 'value', + showDataLabels: true + } + }, + legend: { + show:true, + location: 'e' + } + }); +} + +/** + * Load groups + * + * @param data Data returned from HTTP request + */ +function loadGroups(data) { + // Remove loader + $('#groups').find('img').remove(); + + // Save group in cookie + var groups = data.rsp; + setGroupsCookies(data); + + // Create a list of groups + $('#groups').append('
                          Groups
                          '); + var grouplist= $('
                          '); + // Create a link for each group + for (var i = 0; i < groups.length; i++) { + grouplist.append('
                          ' + groups[i] + '
                          '); + } + + $('#groups').append(grouplist); + + // Bind the click event + $('#groups .groupdiv div').bind('click', function(){ + var thisGroup = $(this).text(); + $('#groups .groupdiv div').removeClass('selectgroup'); + $(this).addClass('selectgroup'); + + // Save selected group into cookie + $.cookie('xcat_selectgrouponnodes', thisGroup, { expires: 7, path: '/xcat', secure:true }); + + drawNodesArea(thisGroup,'',thisGroup); + }); + + // Make a link to add nodes + $('#groups').append('
                          '); + $('#groups #adddiv').append(mkAddNodeLink()); +} + +/** + * Empty the nodes area and add three tabs for nodes result + * + * @param targetgroup The name range for nodels command + * @param cmdargs Filter arguments for nodels command + * @param message The useful information from the HTTP request + */ +function drawNodesArea(targetgroup, cmdargs, message){ + // Clear nodes division + $('#nodes').empty(); + + // Create a tab for this group + var tab = new Tab('nodesPageTabs'); + setNodesTab(tab); + tab.init(); + $('#nodes').append(tab.object()); + tab.add('summaryTab', 'Summary', '', false); + tab.add('nodesTab', 'Nodes', '', false); + if (builtInXCAT == 0) { + tab.add('graphTab', 'Graphic', '', false); + } + + // Load nodes table when tab is selected + $('#nodesPageTabs').bind('tabsselect', function(event, ui) { + // Load summary when tab is selected + if (!$('#summaryTab').children().length && ui.index == 0) { + loadPieSummary(targetgroup); + } + + // Load nodes table when tab is selected + else if (!$('#nodesTab').children().length && ui.index == 1) { + // Create loader + $('#nodesTab').append($('
                          ').append(createLoader())); + + // To improve performance, get all nodes within selected group + // Get node definitions only for first 50 nodes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodels', + tgt : targetgroup, + args : cmdargs, + msg : message + }, + + /** + * Get node definitions for first 50 nodes + * + * @param data Data returned from HTTP request + */ + success : function(data) { + data = decodeRsp(data); + var rsp = data.rsp; + var group = data.msg; + + // Save nodes in a list so it can be accessed later + nodesList = new Array(); + for (var i in rsp) { + if (rsp[i][0]) { + nodesList.push(rsp[i][0]); + } + } + + // Sort nodes list + nodesList.sort(); + + // Get first 50 nodes + var nodes = ''; + for (var i = 0; i < nodesList.length; i++) { + if (i > 49) { + break; + } + + nodes += nodesList[i] + ','; + } + + // Remove last comma + nodes = nodes.substring(0, nodes.length-1); + + // Get nodes definitions + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : nodes, + msg : targetgroup + }, + + success : function(data) { + data = decodeRsp(data); + loadNodes(data); + } + }); + + } + }); + } + + // Load graphical layout when tab is selected + else if ((builtInXCAT == 0) && (!$('#graphTab').children().length && ui.index == 2)) { + // For the graphical tab, check the graphical data first + createPhysicalLayout(nodesList); + } + + }); + + // Get last view (if any) + // This can be summary, nodes, or graphic + if ($.cookie('xcat_tabindex_history')) { + var order = $.cookie('xcat_tabindex_history').split(','); + order[0] = parseInt(order[0]); + order[1] = parseInt(order[1]); + if (order[0] == 0 || order[1] == 0) { + // For some reason, you cannot trigger a select of index 0 + loadPieSummary(targetgroup); + } else if (order[0] == 1 || order[0] == 2) { + $('#nodesPageTabs').tabs('select', order[0]); + } else if (order[1] == 1 || order[1] == 2) { + $('#nodesPageTabs').tabs('select', order[1]); + } else { + loadPieSummary(targetgroup); + } + } else { + loadPieSummary(targetgroup); + } +} + +/** + * Make a link to add nodes + * + * @returns Link to add nodes + */ +function mkAddNodeLink() { + // Create link to add nodes + var addNodeLink = $('+ Add node'); + addNodeLink.click(function() { + // Create info bar + var info = createInfoBar('Select the hardware management for the new node range'); + + // Create form to add node + var addNodeForm = $('
                          '); + addNodeForm.append(info); + if (builtInXCAT == 0) { + addNodeForm.append('
                          ' + + '' + + '
                          '); + } else { + addNodeForm.append('
                          ' + + '' + + '
                          '); + } + + // Create advanced link to set advanced node properties + var advanced = $('
                          '); + var advancedLnk = $('Advanced').css({ + 'cursor': 'pointer', + 'color': '#0000FF' + }); + advancedLnk.click(function() { + // Get node attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : '-t;node;-h', + msg : '' + }, + + /** + * Set node attributes and open dialog + * + * @param data Data returned from HTTP request + */ + success : function(data) { + data = decodeRsp(data); + // Save node attributes + setNodeAttrs(data); + // Open a dialog to set node attributes + openSetAttrsDialog(); + } + }); + + // Close dialog + addNodeForm.dialog('destroy').remove(); + }); + advanced.append(advancedLnk); + addNodeForm.append(advanced); + + // Open dialog to add node + addNodeForm.dialog({ + modal: true, + width: 400, + title:'Add node', + close: function() {$(this).remove();}, + buttons: { + 'Ok': function() { + // Get hardware management + var mgt = $(this).find('select[name=mgt]').val(); + + var plugin; + switch(mgt) { + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "blade": + plugin = new bladePlugin(); + break; + case "hmc": + plugin = new hmcPlugin(); + break; + case "ipmi": + plugin = new ipmiPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + + $(this).dialog('destroy').remove(); + plugin.addNode(); + }, + 'Cancel': function(){ + $(this).dialog('destroy').remove(); + } + } + }); + + }); + + // Generate tooltips + addNodeLink.tooltip({ + position: 'center right', + offset: [-2, 10], + effect: 'fade', + opacity: 0.7, + predelay: 800 + }); + + return addNodeLink; +} + +/** + * Load nodes belonging to a given group + * + * @param data Data returned from HTTP request + */ +function loadNodes(data) { + // Clear the tab before inserting the table + $('#nodesTab').children().remove(); + + // Data returned + var rsp = data.rsp; + // Group name + var group = data.msg; + // Hash of Node attributes + var attrs = new Object(); + // Node attributes + var headers = new Object(); + + // Variable to send command and request node status + var getNodeStatus = true; + + // Clear hash table containing node attributes + origAttrs = ''; + + var node, args; + for (var i in rsp) { + // Get node name + if (rsp[i].indexOf('Object name:') > -1) { + var temp = rsp[i].split(': '); + node = jQuery.trim(temp[1]); + + // Create a hash for the node attributes + attrs[node] = new Object(); + i++; + } + + // Get key and value + args = rsp[i].split('=', 2); + var key = jQuery.trim(args[0]); + var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1)); + + // Create a hash table + attrs[node][key] = val; + headers[key] = 1; + + // If node status is available + if (key == 'status') { + // Do not request node status + getNodeStatus = false; + } + } + + // Add nodes that are not in data returned + for (var i in nodesList) { + if (!attrs[nodesList[i]]) { + // Create attributes list and save node name + attrs[nodesList[i]] = new Object(); + attrs[nodesList[i]]['node'] = nodesList[i]; + } + } + + // Save attributes in hash table + origAttrs = attrs; + + // Sort headers + var sorted = new Array(); + for (var key in headers) { + // Do not put comments and status in twice + if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) { + sorted.push(key); + } + } + sorted.sort(); + + // Add column for check box, node, ping, power, monitor, and comments + sorted.unshift('', + 'node', + 'status', + 'power', + 'monitor', + 'comments'); + + // Create a datatable + var nodesTable = new DataTable(nodesTableId); + nodesTable.init(sorted); + + // Go through each node + for (var node in attrs) { + // Create a row + var row = new Array(); + + // Create a check box, node link, and get node status + var checkBx = ''; + var nodeLink = $('' + node + '').bind('click', loadNode); + + // If there is no status attribute for the node, do not try to access hash table + // else the code will break + var status = ''; + if (attrs[node]['status']) { + status = attrs[node]['status'].replace('sshd', 'ping'); + } + + // Push in checkbox, node, status, monitor, and power + row.push(checkBx, nodeLink, status, '', ''); + + // If the node attributes are known (i.e the group is known) + if (attrs[node]['groups']) { + // Put in comments + var comments = attrs[node]['usercomment']; + // If no comments exists, show 'No comments' and set icon image source + var iconSrc; + if (!comments) { + comments = 'No comments'; + iconSrc = 'images/nodes/ui-icon-no-comment.png'; + } else { + iconSrc = 'images/nodes/ui-icon-comment.png'; + } + + // Create comments icon + var tipID = node + 'Tip'; + var icon = $('').css({ + 'width': '18px', + 'height': '18px' + }); + + // Create tooltip + var tip = createCommentsToolTip(comments); + var col = $('').append(icon); + col.append(tip); + row.push(col); + + // Generate tooltips + icon.tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + relative: true, + delay: 500 + }); + } else { + // Do not put in comments if attributes are not known + row.push(''); + } + + // Go through each header + for (var i = 6; i < sorted.length; i++) { + // Add the node attributes to the row + var key = sorted[i]; + + // Do not put comments and status in twice + if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) { + var val = attrs[node][key]; + if (val) { + row.push(val); + } else { + row.push(''); + } + } + } + + // Add the row to the table + nodesTable.add(row); + } + + // Clear the tab before inserting the table + $('#nodesTab').children().remove(); + + // Create info bar for nodes tab + var info = createInfoBar('Double-click on a cell to edit a node\'s properties. Click outside the table to save changes. Hit the Escape key to ignore changes.'); + $('#nodesTab').append(info); + info.id = 'NodesInfoBar'; + setNodesTabInfoBar(info); + + // Create action bar + var actionBar = $('
                          ').css("width", "400px"); + + /** + * Create menu for actions to perform against a given node + */ + + // Power on + var powerOnLnk = $('Power on'); + powerOnLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + powerNode(tgtNodes, 'on'); + } + }); + + // Power off + var powerOffLnk = $('Power off'); + powerOffLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + + var msg = 'Do you want to power off: ' + tgtNodes + '?'; + // Open dialog to confirm + var confirmDialog = $('

                          ' + msg + '

                          '); + confirmDialog.dialog({ + title:'Confirm', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + powerNode(tgtNodes, 'off'); + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } + }); + + // Power softoff + var powerSoftoffLnk = $('Shutdown'); + powerSoftoffLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + var msg = 'Do you want to shutdown: ' + tgtNodes + '?'; + // Open dialog to confirm + var confirmDialog = $('

                          ' + msg + '

                          '); + confirmDialog.dialog({ + title:'Confirm', + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Ok": function(){ + powerNode(tgtNodes, 'softoff'); + $(this).dialog("close"); + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); + } + }); + if (builtInXCAT == 0) { + // Turn monitoring on + var monitorOnLnk = $('Monitor on'); + monitorOnLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + monitorNode(tgtNodes, 'on'); + } + }); + + // Turn monitoring off + var monitorOffLnk = $('Monitor off'); + monitorOffLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + monitorNode(tgtNodes, 'off'); + } + }); + } + + // Clone + var cloneLnk = $('Clone'); + cloneLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId).split(','); + for (var i in tgtNodes) { + var mgt = getNodeAttr(tgtNodes[i], 'mgt'); + + // Create an instance of the plugin + var plugin; + switch(mgt) { + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + if (mgt == "zvm") { + var nodeOS = getNodeAttr(tgtNodes[i], 'os'); + var nodeArch = getNodeAttr(tgtNodes[i], 'arch'); + plugin.loadClonePage(tgtNodes[i], nodeOS, nodeArch); + } else { + plugin.loadClonePage(tgtNodes[i]); + } + + } + }); + + // Delete + var deleteLnk = $('Delete'); + deleteLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadDeletePage(tgtNodes); + } + }); + + // Unlock Function + var unlockLnk = $('Unlock'); + unlockLnk.click(function() { + + // Get the "master" value from the site table. + var siteMaster; + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'site', + msg : 'cmd=tabdump site;' + }, + + success : function(data) { + data = decodeRsp(data); + var outId = $(data.msg); + var props = data.rsp; + if ( jQuery.isArray(data.rsp) ) { + for (var i in data.rsp) { + if ( data.rsp[i].indexOf('"master"') == 0 ) { + siteMaster = data.rsp[i].slice( 10, -3 ); + } + } + } else { + if ( data.rsp.indexOf('"master"') == 0 ) { + siteMaster = data.rsp.slice( 10, -3 ); + } + } + + // Process the nodes that were checked. + var tgtNodes = getNodesChecked(nodesTableId).split(','); + var normalNodes = ''; + var mnNode = ''; + for (var i in tgtNodes) { + var nodeIP = getNodeAttr( tgtNodes[i], 'ip' ); + if ( nodeIP == siteMaster ) { + if ( mnNode == '' ) { + // Only one xCAT node will have the same address as + // the site master IP address and this is the + // xCAT MN. + mnNode = tgtNodes[i]; + } + } else { + if ( normalNodes == '' ) { + normalNodes = tgtNodes[i]; + } else { + normalNodes = normalNodes + "," + tgtNodes[i]; + } + } + } + + if ( normalNodes != '' ) { + loadUnlockPage( normalNodes ); + } + if ( mnNode != '' ) { + loadUnlockNonNodesPage( mnNode ); + } + } + }); + }); + + // Run script + var scriptLnk = $('Run script'); + scriptLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadScriptPage(tgtNodes); + } else { + openDialog('warn', "No nodes checked!"); + } + }); + + // Migrate VM + var migrateLnk = $('Migrate'); + migrateLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId).split(','); + var mgt = "", tmp = ""; + var fromhcp = ""; + for (var i in tgtNodes) { + tmp = getNodeAttr(tgtNodes[i], 'mgt'); + fromhcp += getNodeAttr(tgtNodes[i], 'hcp') + ','; + if (!mgt) { + mgt = tmp + } else { + if (tmp != mgt) { + openDialog('warn', "You can pick only one type (mgt) of node to migrate!"); + return; + } + } + } + + // Create an instance of the plugin + var plugin; + switch(mgt) { + // Only hypervisors support migration + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + if (mgt == "zvm") { + plugin.loadMigratePage(tgtNodes, fromhcp); + } else { + plugin.loadMigratePage(tgtNodes); + } + }); + + // Update + var updateLnk = $('Update'); + updateLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadUpdatenodePage(tgtNodes); + } + }); + + // Set boot state + var setBootStateLnk = $('Set boot state'); + setBootStateLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadNodesetPage(tgtNodes); + } + }); + + // Boot to network + var boot2NetworkLnk = $('Boot to network'); + boot2NetworkLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadNetbootPage(tgtNodes); + } + }); + + // Provision node + var provisionLnk = $('Provision'); + provisionLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes){ + // Jump directly to the provision page + jump2Provision(tgtNodes); + } + }); + + // Remote console + var rcons = $('Open console'); + rcons.bind('click', function(event){ + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadRconsPage(tgtNodes); + } + }); + + // Discovery + var discoverLnk = $('Discover systems'); + discoverLnk.bind( 'click', function(event) { + var tgtNodes = getNodesChecked(nodesTableId).split(','); + if ( tgtNodes ) { + var hosts; + for (var i in tgtNodes) { + var hostType = getNodeAttr( tgtNodes[i], 'hosttype' ); + if ( hostType == 'zvm' ) { + if ( hosts ) { + hosts += "," + tgtNodes[i]; + } else { + hosts = tgtNodes[i]; + } + + } else { + openDialog('warn', tgtNodes[i] + " does not have a hosttype of 'zvm': " + hostType ); + } + } + if ( hosts ) { + discoverVMNodes( hosts ); + } + } else { + openDialog('warn', "No nodes checked!"); + } + }); + + // Edit properties + var editProps = $('Edit properties'); + editProps.bind('click', function(event){ + var tgtNodes = getNodesChecked(nodesTableId).split(','); + for (var i in tgtNodes) { + editNodeProps(tgtNodes[i]); + } + }); + + // Install Ganglia + var installMonLnk = $('Install monitoring'); + installMonLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + installGanglia(tgtNodes); + } + }); + + // Scan + var rscanLnk = $('Scan'); + rscanLnk.bind('click', function(event){ + var tgtNodes = getNodesChecked(nodesTableId); + if (tgtNodes) { + loadRscanPage(tgtNodes); + } + }); + + // Event log + var logLnk = $('Event log'); + logLnk.click(function() { + var tgtNodes = getNodesChecked(nodesTableId).split(','); + for (var i in tgtNodes) { + var mgt = getNodeAttr(tgtNodes[i], 'mgt'); + + // Create an instance of the plugin + var plugin; + switch(mgt) { + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "blade": + plugin = new bladePlugin(); + break; + case "hmc": + plugin = new hmcPlugin(); + break; + case "ipmi": + plugin = new ipmiPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + + plugin.loadLogPage(tgtNodes[i]); + } + }); + + // Actions + var actionsLnk = 'Actions'; + if (builtInXCAT == 0) { + if (group == 'hosts') { + var actsMenu = createMenu([deleteLnk]); + } else { + var actsMenu = createMenu([cloneLnk, deleteLnk, migrateLnk, monitorOnLnk, monitorOffLnk, powerOnLnk, powerOffLnk, scriptLnk, powerSoftoffLnk]); + } + } else { + if (group == 'hosts') { + var actsMenu = createMenu([deleteLnk]); + } else { + var actsMenu = createMenu([cloneLnk, deleteLnk, migrateLnk, powerOnLnk, powerOffLnk, scriptLnk, powerSoftoffLnk]); + } + } + + // Configurations + var configLnk = 'Configuration'; + if (builtInXCAT == 0) { + if (group == 'hosts') { + var configMenu = createMenu([editProps]); + } else { + var configMenu = createMenu([editProps, logLnk, installMonLnk, rscanLnk, unlockLnk, updateLnk]); + } + } else { + if (group == 'hosts') { + var configMenu = createMenu([ discoverLnk, editProps ]); + } else { + var configMenu = createMenu([editProps, logLnk, rscanLnk, unlockLnk, updateLnk]); + } + } + + // Provision + var provLnk = 'Provision'; + if (builtInXCAT == 0) { + var provMenu = createMenu([boot2NetworkLnk, rcons, setBootStateLnk, provisionLnk]); + } else { + var provMenu = createMenu([boot2NetworkLnk, setBootStateLnk]); + } + + /** + * Refresh button + */ + var refreshBtn = createButton('Refresh'); + refreshBtn.click(function() { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + var zhcpsCheck = $.cookie('xcat_zhcps').split(','); + var zhcpHash = new Object(); + for (var h in zhcpsCheck) { + if (!zhcpHash[zhcpsCheck[h]]) { + + zhcpHash[zhcpsCheck[h]] = 1; + if (typeof console == "object"){ + console.log("zhcp check for <"+zhcpsCheck[h]+">"); + } + + // Check if SMAPI is online + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : zhcpsCheck[h], + args : '', + msg : 'group=refreshgroups' + ';hcp=' + zhcpsCheck[h] + }, + + // Load hardware control point specific info + // Get disk pools and network names + success : function(data) { + data = decodeRsp(data); + loadHcpInfo(data); + } + }); + } + } + + window.location.reload(); + }); + refreshBtn.css({'width': '80px'}); + refreshBtn.css({'height': '27px'}); + //refreshBtn.css('vertical-align', 'top'); + refreshBtn.css({'align': 'top'}); + + // Create an action menu + var actionsMenu; + if (group == 'hosts') { + actionsMenu = createMenu([ [ actionsLnk, actsMenu ], [ configLnk, configMenu ] ]); + } else { + actionsMenu = createMenu([ [ actionsLnk, actsMenu ], [ configLnk, configMenu ], [ provLnk, provMenu ] ]); + } + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + actionBar.append(refreshBtn); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Insert action bar and nodes datatable + $('#nodesTab').append(nodesTable.object()); + + // Turn table into a datatable + var nodesDatatable = $('#' + nodesTableId).dataTable({ + 'iDisplayLength': 50, + 'bLengthChange': false, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + + // Filter table when enter key is pressed + $('#' + nodesTableId + '_filter input').unbind(); + $('#' + nodesTableId + '_filter input').bind('keyup', function(e){ + if (e.keyCode == 13) { + var table = $('#' + nodesTableId).dataTable(); + table.fnFilter($(this).val()); + + // If there are nodes found, get the node attributes + if (!$('#' + nodesTableId + ' .dataTables_empty').length) { + getNodeAttrs(group); + } + } + }); + + // Load node definitions when next or previous buttons are clicked + $('#' + nodesTableId + '_next, #' + nodesTableId + '_previous').click(function() { + getNodeAttrs(group); + }); + + /** + * Change how datatable behaves + */ + + // Do not sort ping, power, and comment column + var cols = $('#' + nodesTableId + ' thead tr th').click(function() { + getNodeAttrs(group); + }); + var checkboxCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(0)'); + var pingCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + var monitorCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + var commentCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(5)'); + checkboxCol.unbind('click'); + pingCol.unbind('click'); + powerCol.unbind('click'); + monitorCol.unbind('click'); + commentCol.unbind('click'); + + // Create enough space for loader to be displayed + // Center align power, ping, and comments + $('#' + nodesTableId + ' td:nth-child(3),td:nth-child(4),td:nth-child(5)').css({'text-align': 'center'}); + + // No minimum width for comments column + $('#' + nodesTableId + ' tbody tr td:nth-child(6)').css('text-align', 'center'); + + // Instead refresh the node, power, and monitor status + pingCol.find('span a').click(function() { + refreshNodeStatus(group, nodesTableId); + }); + powerCol.find('span a').click(function() { + refreshPowerStatus(group, nodesTableId); + }); + monitorCol.find('span a').click(function() { + refreshGangliaStatus(group, nodesTableId); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); // #
                          '); + update.append(createInfoBar('Updating table ')); + + update.dialog({ + title: 'Updating', + modal: true, + width: 300, + position: 'center' + }); + } +} + +/** + * Add nodes to datatable + * + * @param data Data returned from HTTP request + */ +function addNodes2Table(data) { + // Data returned + var rsp = data.rsp; + // Group name + var group = data.msg; + // Hash of node attributes + var attrs = new Object(); + // Node attributes + var headers = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr th'); + + // Variable to send command and request node status + var getNodeStatus = true; + + // Go through each attribute + var node, args; + for (var i in rsp) { + // Get node name + if (rsp[i].indexOf('Object name:') > -1) { + var temp = rsp[i].split(': '); + node = jQuery.trim(temp[1]); + + // Create a hash for node attributes + attrs[node] = new Object(); + i++; + } + + // Get key and value + args = rsp[i].split('=', 2); + var key = jQuery.trim(args[0]); + var val = jQuery.trim(rsp[i].substring(rsp[i].indexOf('=') + 1, rsp[i].length)); + + // Create a hash table + attrs[node][key] = val; + // Save attributes in original hash table + origAttrs[node][key] = val; + + // If node status is available + if (key == 'status') { + // Do not request node status + getNodeStatus = false; + } + } + + // Set the first five headers + var headersCol = new Object(); + headersCol['node'] = 1; + headersCol['status'] = 2; + headersCol['power'] = 3; + headersCol['monitor'] = 4; + headersCol['comments'] = 5; + + // Go through each header + for (var i = 6; i < headers.length; i++) { + // Get the column index + headersCol[headers.eq(i).html()] = i; + } + + // Go through each node + var datatable = $('#' + nodesTableId).dataTable(); + var rows = datatable.fnGetData(); + for (var node in attrs) { + // Get row containing node + var nodeRowPos = 0; + for (var i in rows) { + // If column contains node + if (rows[i][1].indexOf('>' + node + '<') > -1) { + nodeRowPos = i; + break; + } + } + + // Get node status + var status = ''; + if (attrs[node]['status']){ + status = attrs[node]['status'].replace('sshd', 'ping'); + } + + rows[nodeRowPos][headersCol['status']] = status; + + // Go through each header + for (var key in headersCol) { + // Do not put comments and status in twice + if (key != 'usercomment' && key != 'status' && key.indexOf('status') < 0) { + var val = attrs[node][key]; + if (val) { + rows[nodeRowPos][headersCol[key]] = val; + } + } + } + + // Update row + datatable.fnUpdate(rows[nodeRowPos], parseInt(nodeRowPos), undefined, false); + + // Insert node comments + // This is done after datatable is updated because + // you cannot insert an object using fnUpdate() + var comments = attrs[node]['usercomment']; + + // If no comments exists, show 'No comments' and + // set icon image source + var iconSrc; + if (!comments) { + comments = 'No comments'; + iconSrc = 'images/nodes/ui-icon-no-comment.png'; + } else { + iconSrc = 'images/nodes/ui-icon-comment.png'; + } + + // Create icon for node comments + var tipID = node + 'Tip'; + var commentsCol = $('#' + node).parent().parent().find('td').eq(5); + + // Create tooltip + var icon = $('').css({ + 'width': '18px', + 'height': '18px' + }); + + var tip = createCommentsToolTip(comments); + var span = $('').append(icon); + span.append(tip); + commentsCol.append(span); + + // Generate tooltips + icon.tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + relative: true, + delay: 500 + }); + } + + // Enable node link + $('.node').bind('click', loadNode); + + // Close dialog for updating table + $('.ui-dialog-content').dialog('destroy').remove(); + + /** + * Enable editable columns + */ + // Do not make 1st, 2nd, 3rd, 4th, 5th, or 6th column editable + $('#' + nodesTableId + ' td:not(td:nth-child(1),td:nth-child(2),td:nth-child(3),td:nth-child(4),td:nth-child(5),td:nth-child(6))').editable( + function(value, settings) { + // If users did not do changes, return the value directly + // jeditable save the old value in this.revert + if ($(this).attr('revert') == value){ + return value; + } + // Get column index + var colPos = this.cellIndex; + + // Get row index + var dTable = $('#' + nodesTableId).dataTable(); + var rowPos = dTable.fnGetPosition(this.parentNode); + + // Update datatable + dTable.fnUpdate(value, rowPos, colPos, false); + + // Get table headers + var headers = $('#' + nodesTableId + ' thead tr th'); + + // Get node name + var node = $(this).parent().find('td a.node').text(); + // Get attribute name + var attrName = jQuery.trim(headers.eq(colPos).text()); + // Get column value + var value = $(this).text(); + + // Build argument + var args = attrName + '=' + value; + + // Send command to change node attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : '-t;node;-o;' + node + ';' + args, + msg : 'out=nodesTab;tgt=' + node + }, + + success: function(data) { + data = decodeRsp(data); + showChdefOutput(data); + } + }); + + return value; + }, { + onblur : 'submit', // Clicking outside editable area submits changes + type : 'textarea', + placeholder: ' ', + event : 'dblclick', + height : '30px' // The height of the text area + }); + + // If request to get node status is made + if (getNodeStatus) { + // Get node status + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodestat', + tgt : group, + args : '-u', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadNodeStatus(data); + } + }); + } else { + // Hide status loader + var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + statCol.find('img').hide(); + } + + /** + * Additional ajax requests need to be made for zVM + */ + advancedLoad(group); + adjustColumnSize(nodesTableId); +} + +/** + * Load the status of Ganglia for a given group + * + * @param data Data returned from HTTP request + */ +function loadGangliaStatus(data) { + // Get datatable + var datatable = $('#' + nodesTableId).dataTable(); + var ganglia = data.rsp; + var rowNum, node, status; + + for ( var i in ganglia) { + // ganglia[0] = nodeName and ganglia[1] = state + node = jQuery.trim(ganglia[i][0]); + status = jQuery.trim(ganglia[i][1]); + + if (node) { + // Get the row containing the node + rowNum = findRow(node, '#' + nodesTableId, 1); + + // Update the power status column + datatable.fnUpdate(status, rowNum, 4); + } + } + + // Hide Ganglia loader + var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').hide(); + adjustColumnSize(nodesTableId); +} + +/** + * Refresh the status of Ganglia for each node + * + * @param group Group name + */ +function refreshGangliaStatus(group) { + // Show ganglia loader + var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').show(); + + // Get power status for nodes shown + var nodes = getNodesShown(nodesTableId); + + // Get the status of Ganglia + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastatus;' + nodes, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadGangliaStatus(data); + } + }); +} + +/** + * Load power status for each node + * + * @param data Data returned from HTTP request + */ +function loadPowerStatus(data) { + var dTable = $('#' + nodesTableId).dataTable(); + var power = data.rsp; + var rowPos, node, status, args; + + for (var i in power) { + // power[0] = nodeName and power[1] = state + args = power[i].split(':'); + node = jQuery.trim(args[0]); + status = jQuery.trim(args[1]); + + // Get the row containing the node + rowPos = findRow(node, '#' + nodesTableId, 1); + + // Update the power status column + dTable.fnUpdate(status, rowPos, 3); + } + + // Hide power loader + var powerCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + powerCol.find('img').hide(); + adjustColumnSize(nodesTableId); +} + +/** + * Refresh power status for each node + * + * @param group Group name + * @param tableId Table to update node status + */ +function refreshPowerStatus(group, tableId) { + // Show power loader + var powerCol = $('#' + tableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + powerCol.find('img').show(); + + // Get power status for nodes shown + var nodes = getNodesShown(tableId); + + // Get power status + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rpower', + tgt : nodes, + args : 'stat', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadPowerStatus(data); + } + }); +} + +/** + * Load node status for each node + * + * @param data Data returned from HTTP request + */ +function loadNodeStatus(data) { + var dTable = $('#' + nodesTableId).dataTable(); + var rsp = data.rsp; + var args, rowPos, node, status; + + // Get all nodes within datatable + for (var i in rsp) { + args = rsp[i].split(':'); + + // args[0] = node and args[1] = status + node = jQuery.trim(args[0]); + status = jQuery.trim(args[1]).replace('sshd', 'ping'); + + // Get row containing node + rowPos = findRow(node, '#' + nodesTableId, 1); + + // Update ping status column + dTable.fnUpdate(status, rowPos, 2, false); + } + + // Hide status loader + var statCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + statCol.find('img').hide(); + adjustColumnSize(nodesTableId); +} + +/** + * Refresh ping status for each node + * + * @param group Group name + * @param tableId Table to update node status + */ +function refreshNodeStatus(group, tableId) { + // Show ping loader + var pingCol = $('#' + tableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + pingCol.find('img').show(); + + // Get power status for nodes shown + var nodes = getNodesShown(tableId); + + // Get the node status + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodestat', + tgt : nodes, + args : '-u', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadNodeStatus(data); + } + }); +} + +/** + * Load inventory for given node + * + * @param e Windows event + */ +function loadNode(e) { + if (!e) { + e = window.event; + } + + // Get node that was clicked + var node = (e.target) ? e.target.id : e.srcElement.id; + var mgt = getNodeAttr(node, 'mgt'); + + // Create an instance of the plugin + var plugin; + switch(mgt) { + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "blade": + plugin = new bladePlugin(); + break; + case "hmc": + plugin = new hmcPlugin(); + break; + case "ipmi": + plugin = new ipmiPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + + // Get tab area where a new tab will be inserted + var myTab = getNodesTab(); + var inst = 0; + var newTabId = 'nodeTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'nodeTab' + inst; + } + // Reset node process + $.cookie('xcat_' + node + 'Processes', 0, { path: '/xcat', secure:true }); + + // Add new tab, only if one does not exist + var loader = createLoader(newTabId + 'TabLoader'); + loader = $('
                          ').append(loader); + myTab.add(newTabId, node, loader, true); + + // Get node inventory + var msg = 'out=' + newTabId + ',node=' + node; + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rinv', + tgt : node, + args : 'all', + msg : msg + }, + + success : function(data) { + data = decodeRsp(data); + plugin.loadInventory(data); + } + }); + + // Select new tab + myTab.select(newTabId); +} + +/** + * Unlock a node by setting the ssh keys + * + * @param tgtNodes Nodes to unlock + */ +function loadUnlockPage(tgtNodes) { + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var instance = 0; + var newTabId = 'unlockTab' + instance; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + instance = instance + 1; + newTabId = 'unlockTab' + instance; + } + + // Create status bar, hide on load + var statBarId = 'unlockStatusBar' + instance; + var statBar = createStatusBar(statBarId).hide(); + + // Create loader + var loader = createLoader(''); + statBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Give the root password for this node range to setup its SSH keys.'); + + // Create unlock form + var unlockForm = $('
                          '); + unlockForm.append(statBar, infoBar); + + // Create VM fieldset + var vmFS = $('
                          '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + unlockForm.append(vmFS); + + var vmAttr = $('
                          '); + vmFS.append($('
                          ')); + vmFS.append(vmAttr); + + vmAttr.append('
                          '); + vmAttr.append('
                          '); + + // Generate tooltips + unlockForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Ok + */ + var unlockBtn = createButton('Unlock'); + unlockBtn.css({ + 'width': '80px', + 'display': 'block' + }); + unlockBtn.click(function() { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // If a password is given + var password = $('#' + newTabId + ' input[name=password]').css('border', 'solid #BDBDBD 1px'); + if (password.val()) { + // Setup SSH keys + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'unlock;' + tgtNodes + ';' + password.val(), + msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + + // Show status bar + statBar.show(); + + // Disable all inputs and Ok button + $('#' + newTabId + ' input').attr('disabled', 'disabled'); + $(this).attr('disabled', 'true'); + } else { + // Show warning message + var warn = createWarnBar('You are missing some values!'); + warn.prependTo($(this).parents('.ui-tabs-panel')); + password.css('border', 'solid #FF0000 1px'); + } + }); + + unlockForm.append(unlockBtn); + tab.add(newTabId, 'Unlock', unlockForm, true); + tab.select(newTabId); +} + +function loadUnlockNonNodesPage( tgtNodes ) { + + // Get nodes tab + var tab = getNodesTab(); + var fs, legend; + + // Generate new tab ID + var instance = 0; + var newTabId = 'unlockNonNodesTab' + instance; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + instance = instance + 1; + newTabId = 'unlockNonNodesTab' + instance; + } + + // Create info bar and status bar + var infoBar = createInfoBar( 'Unlock systems that have not been defined to xCAT. Either:
                          ' + + '-Create a script to install the xCAT Management Node\'s public key on the target systems, or
                          ' + + '-Unlock system(s) directly using their IP address(es) ' + + '(Specify multiple systems by separating the addresses with a comma),
                          ' + + '-Show the xCAT Management Node\'s public key to use to unlock the system.' ); + + var statBarId = 'unlockNonNodesStatusBar' + instance; + var statBar = createStatusBar(statBarId).hide(); + var loader = createLoader( '' ); + statBar.find('div').append( loader ); + + // Create unlock form and put info and status bars on the form + var unlockNonNodesForm = $( '
                          ' ); + unlockNonNodesForm.append( infoBar, statBar ); + + // Create 'Create an Unlock Script' fieldset + fs = $( '
                          ' ); + legend = $( 'Create an Unlock Script' ); + fs.append( legend ); + unlockNonNodesForm.append( fs ); + + // Create Script bar + var scriptBarId = 'unlockNonNodesScriptBar' + instance; + var scriptBar = createStatusBar(scriptBarId).hide(); + unlockNonNodesForm.append( scriptBar ); + + var createBtn = createButton( 'Create Script' ); + createBtn.css({ + 'width': '200px', + 'display': 'block' + }); + + createBtn.click(function() { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // Get the SSH keys for a non-node + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'unlockshow;' + tgtNodes + ';script;', + msg : 'out=' + statBarId + ';scriptBar=' + scriptBarId +';cmd=unlock;tgt=' + tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + updateScriptBar(data); + } + }); + + // Show status bar + statBar.show(); + }); + + unlockNonNodesForm.append( createBtn ); + + // Create 'Unlock a system using the root password' Script fieldset + fs = $( '
                          ' ); + legend = $( 'Unlock a system using the root password' ); + fs.append( legend ); + unlockNonNodesForm.append( fs ); + + var vmAttr = $('
                          ' ); + fs.append($('
                          ')); + fs.append( vmAttr ); + + vmAttr.append( '
                          ' ); + vmAttr.append( '
                          ' ); + + /** + * Unlock button for non-nodes + */ + var unlockBtn = createButton('Unlock'); + unlockBtn.css({ + 'width': '80px', + 'display': 'block' + }); + + unlockBtn.click(function() { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // If an ip and password is given + var ip = $('#' + newTabId + ' input[name=ip]').css('border', 'solid #BDBDBD 1px'); + var password = $( '#' + newTabId + ' input[name=password]').css('border', 'solid #BDBDBD 1px' ); + if ( password.val() && ip.val() ) { + $('#' + statBarId).find('img').show(); + // Setup SSH keys for a non-node + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'unlockbyip;' + tgtNodes + ';' + password.val() + ";" + ip.val(), + msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + + // Show status bar + statBar.show(); + } else { + // Show warning message + var warn = createWarnBar('Both ip address and password must be specified.'); + warn.prependTo($(this).parents('.ui-tabs-panel')); + password.css('border', 'solid #FF0000 1px'); + } + }); + + unlockNonNodesForm.append(unlockBtn); + + // Create 'SSH Key' fieldset + var sshKey = ''; + fs = $( '
                          ' ); + legend = $( 'xCAT Management Node Public Key' ); + fs.append( legend ); + unlockNonNodesForm.append( fs ); + + // Create key bar, hide on load + var keyBarId = 'unlockNonNodesKeyBar' + instance; + var keyBar = createStatusBar(keyBarId).hide(); + unlockNonNodesForm.append( keyBar ); + + // Generate tooltips + unlockNonNodesForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * "Get Key" button + */ + var getBtn = createButton('Get Key'); + getBtn.css({ + 'width': '80px', + 'display': 'block' + }); + + getBtn.click(function() { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // Get the SSH keys for a non-node + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'unlockshow;' + tgtNodes + ';key;', + msg : 'out=' + statBarId + ';keyBar=' + keyBarId +';cmd=unlock;tgt=' + tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + updateKeyBar(data); + } + }); + + // Show status bar + statBar.show(); + }); + + unlockNonNodesForm.append(getBtn); + + tab.add(newTabId, 'Unlock System', unlockNonNodesForm, true); + tab.select(newTabId); +} + + +/** + * Update key and status bar of a given tab + * + * @param data Data returned from HTTP request + */ +function updateKeyBar(data) { + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + var statBarId = args[0].replace('out=', ''); + var keyBarId = args[1].replace('keyBar=', ''); + var cmd = args[2].replace('cmd=', ''); + var tgts = args[3].replace('tgt=', '').split(','); + + $('#' + statBarId).find('img').hide(); + + // Extract the key portion and status portions from the response + var keyRespStart = rsp[0].indexOf(""); + var keyRespEnd = rsp[0].indexOf("");; + var keyOnly = rsp[0].substring( keyRespStart+9, keyRespEnd ); + var statLines = rsp[0].substring( 0, keyRespStart-1 ); + + if ( keyOnly.length > 0 ) { + // Expected response was returned, show it in the key bar. + var keyLines = keyOnly.split(/\n/); + for ( var i in keyLines ) { + $('#' + keyBarId).find( 'div' ).append( keyLines[i] ); + $('#' + keyBarId).find( 'div' ).append( '
                          ' ); + } + $('#' + keyBarId).show(); + + // Other lines from the response are shown in the Status bar. + if ( statLines.length > 0 ) { + prg = statLines.split(/\n/); + prg = writeRsp( prg, '' ); + $('#' + statBarId).find( 'div' ).append( prg ); + } + } else { + // Did not find the expected response, write the ajax response to status bar. + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + } + +} + + +/** + * Update the script and status bar of a given tab + * + * @param data Data returned from HTTP request + */ +function updateScriptBar(data) { + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + var statBarId = args[0].replace('out=', ''); + var scriptBarId = args[1].replace('scriptBar=', ''); + var cmd = args[2].replace('cmd=', ''); + var tgts = args[3].replace('tgt=', '').split(','); + + $('#' + statBarId).find('img').hide(); + + // Extract the key portion and status portions from the response + var scriptRespStart = rsp[0].indexOf(""); + var scriptRespEnd = rsp[0].indexOf("");; + var scriptOnly = rsp[0].substring( scriptRespStart+12, scriptRespEnd ); + var statLines = rsp[0].substring( 0, scriptRespStart-1 ); + + if ( scriptOnly.length > 0 ) { + // Expected response was returned, show it in the script bar along with the rest of the code. + var scriptLines = scriptOnly.split(/\n/); + for ( var i in scriptLines ) { + scriptLines[i] = scriptLines[i].replace(/^\s\s\s\s\s\s\s\s+/gm, '        '); + scriptLines[i] = scriptLines[i].replace(/^\s\s\s\s\s\s+/gm, '      '); + scriptLines[i] = scriptLines[i].replace(/^\s\s\s\s+/gm, '    '); + scriptLines[i] = scriptLines[i].replace(/^\s\s+/gm, '  '); + $('#' + scriptBarId).find( 'div' ).append( scriptLines[i] + '
                          ' ); + } + $('#' + scriptBarId).show(); + + // Other lines from the response are shown in the Status bar. + if ( statLines.length > 0 ) { + prg = statLines.split(/\n/); + prg = writeRsp( prg, '' ); + $('#' + statBarId).find( 'div' ).append( prg ); + } + } else { + // Did not find the expected response, write the ajax response to status bar. + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + } + +} + + +/** + * Load script page + * + * @param tgtNodes Targets to run script against + */ +function loadScriptPage(tgtNodes) { + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'scriptTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'scriptTab' + inst; + } + + // Create remote script form + var scriptForm = $('
                          '); + + // Create status bar + var barId = 'scriptStatusBar' + inst; + var statBar = createStatusBar(barId); + statBar.hide(); + var loader = createLoader('scriptLoader' + inst); + statBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Load a script to run against this node range.'); + scriptForm.append(infoBar, statBar); + + // Create VM fieldset + var vmFS = $('
                          '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + scriptForm.append(vmFS); + + var vmAttr = $('
                          '); + vmFS.append($('
                          ')); + vmFS.append(vmAttr); + + // Create logs fieldset + var scriptFS = $('
                          '); + var scriptLegend = $('Script'); + scriptFS.append(scriptLegend); + scriptForm.append(scriptFS); + + var scriptAttr = $('
                          '); + scriptFS.append($('
                          ')); + scriptFS.append(scriptAttr); + + // Target node or group + var tgt = $('
                          '); + vmAttr.append(tgt); + + // Upload file + var upload = $('
                          '); + var label = $(''); + var file = $(''); + var subBtn = createButton('Load'); + upload.append(label, file, subBtn); + scriptAttr.append(upload); + + // Script + var script = $('
                          ').css({ + 'font-size': '10px', + 'height': '50px', + 'width': '200px', + 'background-color': '#000', + 'color': '#fff', + 'border': '0px', + 'display': 'block' + }); + + // Create links to save and cancel changes + var lnkStyle = { + 'color': '#58ACFA', + 'font-size': '10px', + 'display': 'inline-block', + 'padding': '5px', + 'float': 'right' + }; + + var saveLnk = $('Save').css(lnkStyle).hide(); + var cancelLnk = $('Cancel').css(lnkStyle).hide(); + var infoSpan = $('Click to edit').css(lnkStyle); + + // Save changes onclick + saveLnk.bind('click', function(){ + // Get node and comment + var node = $(this).parent().parent().find('img').attr('id').replace('Tip', ''); + var comments = $(this).parent().find('textarea').val(); + + // Save comment + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : '-t;node;-o;' + node + ';usercomment=' + comments, + msg : 'out=nodesTab;tgt=' + node + }, + + success: function(data) { + data = decodeRsp(data); + showChdefOutput(data); + } + }); + + // Hide cancel and save links + $(this).hide(); + cancelLnk.hide(); + }); + + // Cancel changes onclick + cancelLnk.bind('click', function(){ + // Get original comment and put it back + var orignComments = $(this).parent().find('textarea').text(); + $(this).parent().find('textarea').val(orignComments); + + // Hide cancel and save links + $(this).hide(); + saveLnk.hide(); + infoSpan.show(); + }); + + // Show save link when comment is edited + txtArea.bind('click', function(){ + saveLnk.show(); + cancelLnk.show(); + infoSpan.hide(); + }); + + toolTip.append(txtArea); + toolTip.append(cancelLnk); + toolTip.append(saveLnk); + toolTip.append(infoSpan); + + return toolTip; +} + +/** + * Create a tool tip for node status + * + * @return Tool tip + */ +function createStatusToolTip() { + // Create tooltip container + var toolTip = $('
                          ').css({ + 'width': '150px', + 'font-weight': 'normal' + }); + + // Create info text + var info = $('

                          ').css({ + 'white-space': 'normal' + }); + info.append('Click here to refresh the node status. To configure the xCAT monitor, '); + + // Create link to turn on xCAT monitoring + var monitorLnk = $('click here').css({ + 'color': '#58ACFA', + 'font-size': '10px' + }); + + // Open dialog to configure xCAT monitor + monitorLnk.bind('click', function(){ + // Check if xCAT monitor is enabled + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'monls', + tgt : '', + args : 'xcatmon', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + openConfXcatMon(data); + } + }); + }); + + info.append(monitorLnk); + toolTip.append(info); + + return toolTip; +} + +/** + * Create a tool tip for power status + * + * @return Tool tip + */ +function createPowerToolTip() { + // Create tooltip container + var toolTip = $('
                          Click here to refresh the power status
                          ').css({ + 'width': '150px', + 'white-space': 'normal', + 'font-weight': 'normal' + }); + return toolTip; +} + +/** + * Create a tool tip for monitoring status + * + * @return Tool tip + */ +function createMonitorToolTip() { + // Create tooltip container + var toolTip = $('
                          Click here to refresh the monitoring status
                          ').css({ + 'width': '150px', + 'white-space': 'normal', + 'font-weight': 'normal' + }); + return toolTip; +} + +/** + * Open dialog to configure xCAT monitor + * + * @param data Data returned from HTTP request + */ +function openConfXcatMon(data) { + // Create info bar + var info = createInfoBar('Configure the xCAT monitor. Select to enable or disable the monitor below.'); + var dialog = $('
                          '); + dialog.append(info); + + // Create status area + var statusArea = $('
                          ').css('padding-top', '10px'); + var label = $(''); + statusArea.append(label); + + // Get xCAT monitor status + var status = data.rsp[0]; + var buttons; + // If xCAT monitor is disabled + if (status.indexOf('not-monitored') > -1) { + status = $('Disabled').css('padding', '0px 5px'); + statusArea.append(status); + + // Create enable and cancel buttons + buttons = { + "Enable": function(){ + // Enable xCAT monitor + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'monstart', + tgt : '', + args : 'xcatmon', + msg : '' + }, + + success : function(data){ + data = decodeRsp(data); + openDialog('info', data.rsp[0]); + } + }); + $(this).dialog("close"); + }, + "Cancel": function(){ + $(this).dialog("close"); + } + }; + } else { + status = $('Enabled').css('padding', '0px 5px'); + statusArea.append(status); + + // Create disable and cancel buttons + buttons = { + "Disable": function(){ + // Disable xCAT monitor + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'monstop', + tgt : '', + args : 'xcatmon', + msg : '' + }, + + success : function(data){ + data = decodeRsp(data); + openDialog('info', data.rsp[0]); + } + }); + $(this).dialog("close"); + }, + "Cancel": function(){ + $(this).dialog("close"); + } + }; + } + + dialog.append(statusArea); + + // Open dialog + dialog.dialog({ + modal: true, + width: 500, + buttons: buttons + }); +} + +/** + * Show chdef output + * + * @param data Data returned from HTTP request + */ +function showChdefOutput(data) { + // Get output + var out = data.rsp; + var args = data.msg.split(';'); + var tabID = args[0].replace('out=', ''); + var tgt = args[1].replace('tgt=', ''); + + // Find info bar on nodes tab, if any + var info = $('#' + tabID).find('.ui-state-highlight'); + if (!info.length) { + // Create info bar if one does not exist + info = createInfoBar(''); + $('#' + tabID).append(info); + } + + // Go through output and append to paragraph + var prg = $('

                          '); + for (var i in out) { + prg.append(tgt + ': ' + out[i] + '
                          '); + } + + info.append(prg); +} + +/** + * Set node attributes + * + * @param data Data returned from HTTP request + */ +function setNodeAttrs(data) { + // Clear hash table containing definable node attributes + nodeAttrs = new Array(); + + // Get definable attributes + var attrs = data.rsp[2].split(/\n/); + + // Go through each line + var attr, key, descr; + for (var i in attrs) { + attr = attrs[i]; + + // If the line is not empty + if (attr) { + // If the line has the attribute name + if (attr.indexOf(':') && attr.indexOf(' ')) { + // Get attribute name and description + key = jQuery.trim(attr.substring(0, attr.indexOf(':'))); + descr = jQuery.trim(attr.substring(attr.indexOf(':') + 1)); + + // Remove arrow brackets + descr = descr.replace(new RegExp('<|>', 'g'), ''); + + // Set hash table where key = attribute name and value = description + nodeAttrs[key] = descr; + } else { + // Remove arrow brackets + attr = attr.replace(new RegExp('<|>', 'g'), ''); + + // Append description to hash table + nodeAttrs[key] = nodeAttrs[key] + '\n' + attr; + } + } // End of if + } // End of for +} + + +/** + * Load the discover z/VM virtual systems page + * + * @param tgtNode Target node to set properties + */ +function discoverVMNodes(tgtNodes) { + var fs, legend, htmlLine; + + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'discoverVMNodesTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'discoverVMNodesTab' + inst; + } + + // Open new tab + // Create set properties form + var discoverVMNodesForm = $('
                          '); + + // Create info bar + var infoBar = createInfoBar( 'Initiate, stop or query the status of z/VM node discovery. ' + + 'To initiate discovery, specify discovery parameters and click on the Discover button. ' + + 'To stop an on-going discovery related to a z/VM host, specify the host node name and '+ + 'click on the Stop button. ' + + 'To obtain the status of discovery for a particular host, specify the host node name and '+ + 'click on the Stop button.' + ); + discoverVMNodesForm.append(infoBar); + + // Create the status bar and hide it. + var statBarId = 'statusBar_' + newTabId; + //var statBar = $( '
                          ' ); + //discoverVMNodesForm.append( statBar ); + statBar = createStatusBar( statBarId ); + statBar.hide(); + discoverVMNodesForm.append( statBar ); + + // Create Host fieldset + fs = $('
                          '); + legend = $('z/VM Host'); + fs.append(legend); + discoverVMNodesForm.append(fs); + + // Target node or group + htmlLine = $('
                          '); + discoverVMNodesForm.append( htmlLine ); + + // Create Discovery Parameters fieldset + fs = $('
                          '); + legend = $('Discovery Parameters'); + fs.append( legend ); + discoverVMNodesForm.append( fs ); + + // Create an input for each definable attribute + var div, label, input, descr, value; + + // Define DefineTo radio buttons + div = $('
                          ').css( 'display', 'inline-block' ).css( 'vertical-align', 'top' ); + div.append( '' ); + div.append( '
                        • xCAT and OpenStack
                        • ' ); + div.append( '
                        • xCAT only
                        • ' ); + div.append( '
                        • OpenStack only (only already discovered xCAT nodes)
                        • ' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + + // Userid filter + divUserid = newTabId + "_divUserid"; + div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); + div.append( '' ); + div.append( '' ).css( 'margin-top', '5px' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + + // IP address filter + divIP = newTabId + "_divIP"; + div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); + div.append( '' ); + div.append( '' ).css( 'margin-top', '5px' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + + // Group name input + divGroup = newTabId + '_divGroup'; + div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); + div.append( '' ); + div.append( '' ).css( 'margin-top', '5px' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + + // Node Name Format input + divNodename = newTabId + '_divNodename'; + div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); + div.append( '' ); + div.append( '' ).css( 'margin-top', '5px' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + discoverVMNodesForm.find('#' + divNodename).hide(); + + // OpenStack operand input + divOpenStackOps = newTabId + '_divOpenStackOps'; + div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ); + div.append( '' ); + div.append( '' ).css( 'margin-top', '5px' ); + div.append( '
                          ' ); + div.append( '' ); + div.append( '' ).css( 'margin-top', '5px' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + + // Define Verbose radio buttons + div = $('
                          ').css( 'display', 'inline-table' ).css( 'vertical-align', 'top' ).css( 'text-align', 'top' ); + div.append( '' ); + div.append( '
                        • Normal response, showing only important information
                        • ' ); + div.append( '
                        • Verbose response, normal response plus additional information (e.g. reason a system is ignored)
                        • ' ); + discoverVMNodesForm.append( div ); + discoverVMNodesForm.append( '
                          ' ); + + // Generate tooltips + discoverVMNodesForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + + // Show appropriate input fields for defineto choices. + discoverVMNodesForm.change(function(){ + var defineTo = $(this).parent().find('input[name="defineTo"]:checked').val(); + if ( defineTo == 'both' ) { + discoverVMNodesForm.find('#' + divUserid).show(); + discoverVMNodesForm.find('#' + divIP).show(); + discoverVMNodesForm.find('#' + divGroup).show(); + discoverVMNodesForm.find('#' + divOpenStackOps).show(); + discoverVMNodesForm.find('#' + divNodename).hide(); + } else if ( defineTo == 'xcatonly' ) { + discoverVMNodesForm.find('#' + divUserid).show(); + discoverVMNodesForm.find('#' + divIP).show(); + discoverVMNodesForm.find('#' + divGroup).show(); + discoverVMNodesForm.find('#' + divOpenStackOps).hide(); + discoverVMNodesForm.find('#' + divNodename).show(); + } else if ( defineTo == 'openstackonly' ) { + discoverVMNodesForm.find('#' + divUserid).hide(); + discoverVMNodesForm.find('#' + divIP).hide(); + discoverVMNodesForm.find('#' + divGroup).hide(); + discoverVMNodesForm.find('#' + divOpenStackOps).show(); + discoverVMNodesForm.find('#' + divNodename).hide(); + } + }); + + + // Discover nodes button action + var discoverBtn = createButton('Discover'); + discoverBtn.click(function() { + var argList = ''; + var filter = ''; + + var hosts = $(this).parent().find('input[name=hosts]').val(); + if ( hosts != '' ) { + argList = 'zvmhost=' + hosts; + } + + var defineTo = $(this).parent().find('input[name="defineTo"]:checked').val(); + argList = argList + '||defineto=' + defineTo; + + var verbose = $(this).parent().find('input[name="verbose"]:checked').val(); + if ( verbose == 'yes' ) { + argList = argList + '||--verbose'; + } + + var useridFilter = $(this).parent().find('input[name=useridFilter]').val(); + if (( defineTo == 'both' || defineTo == 'xcatonly' ) && ( useridFilter != '' )) { + argList = argList + '||useridfilter=' + useridFilter; + } + + var ipFilter = $(this).parent().find('input[name=ipFilter]').val(); + if (( defineTo == 'both' || defineTo == 'xcatonly' ) && ( ipFilter != '' )) { + argList = argList + '||ipfilter=' + ipFilter; + } + + var group = $(this).parent().find('input[name=group]').val(); + if (( defineTo == 'both' || defineTo == 'xcatonly' ) && ( group != '' )) { + argList = argList + '||groups=' + group; + } + + var nodeNameFmt = $(this).parent().find('input[name=nodeNameFmt]').val(); + if ( defineTo == 'xcatonly' && nodeNameFmt != '' ) { + argList = argList + '||nodenameformat=' + nodeNameFmt; + } + + var openStackProj = $(this).parent().find('input[name=openStackProj]').val(); + var openStackUser = $(this).parent().find('input[name=openStackUser]').val(); + if ((( defineTo == 'both' ) || ( defineTo == 'openstackonly' )) && + (( openStackProj != '' ) || ( openStackUser != '' ))) { + if ( openStackProj != '' ) { + osArgs = '--project ' + openStackProj; + } else { + osArgs = ''; + } + if ( openStackUser != '' ) { + if ( osArgs != '' ) { + osArgs = osArgs + ' --user ' + openStackUser; + } else { + osArgs = '--user ' + openStackUser; + } + } + argList = argList + "||openstackoperands='" + osArgs + "'"; + } + + var out = $('

                          '); + out.append( 'Starting node discovery...' ); + out.append( '
                          ' ); + out.append( 'If node discovery is a short running task then its response will follow. If, however, the time it takes to complete discovery exceeds the http request timeout of a few minutes then the discovery response will not be returned to the browser. The status and list buttons can be used to obtained status on the discovery and see what systems have been discovered.' ); + $( '#' + statBarId ).find( 'div' ).append( out ); + statBar.show(); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodediscoverstart', + tgt : '', + args : argList, + att : '', + msg : statBarId + }, + success: function(data) { + data = decodeRsp(data); + updateDiscoverStatusBar( data, 1 ); + } + }); + + }); + discoverVMNodesForm.append(discoverBtn); + + // Status button + var statusBtn = createButton('Status'); + statusBtn.click( function() { + var hosts = $(this).parent().find('input[name=hosts]').val(); + var out = $('

                          '); + out.append( 'Querying status for discovery on ' + hosts + '...' ); + out.append( '
                          ' ); + $( '#' + statBarId ).find( 'div' ).append( out ); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodediscoverstatus', + tgt : '', + args : '--zvmhost||' + hosts, + att : '', + msg : statBarId + }, + success: function(data) { + data = decodeRsp(data); + updateDiscoverStatusBar( data, 1 ); + } + }); + + }); + discoverVMNodesForm.append( statusBtn ); + + // List button + var listBtn = createButton('List'); + listBtn.click( function() { + var hosts = $(this).parent().find('input[name=hosts]').val(); + var out = $('

                          '); + out.append( 'Listing systems discovered by the latest discovery on ' + hosts + '...' ); + out.append( '
                          ' ); + $( '#' + statBarId ).find( 'div' ).append( out ); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodediscoverls', + tgt : '', + args : '-t||zvm||--zvmhost||' + hosts, + att : '', + msg : statBarId + }, + success: function(data) { + data = decodeRsp(data); + updateDiscoverStatusBar( data, 1 ); + } + }); + }); + discoverVMNodesForm.append( listBtn ); + + // Stop button + var stopBtn = createButton('Stop'); + stopBtn.click(function() { + var hosts = $(this).parent().find('input[name=hosts]').val(); + var out = $('

                          '); + out.append( 'Stopping discovery on ' + hosts + '...' ); + out.append( '
                          ' ); + $( '#' + statBarId ).find( 'div' ).append( out ); + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodediscoverstop', + tgt : '', + args : '--zvmhost||' + hosts, + att : '', + msg : statBarId + }, + success: function(data) { + data = decodeRsp(data); + updateDiscoverStatusBar( data, 1 ); + } + }); + }); + discoverVMNodesForm.append( stopBtn ); + + // Append to discover tab + tab.add(newTabId, 'Discover', discoverVMNodesForm, true); + + // Select new tab + tab.select(newTabId); +} + + +/** + * Update discovery status bar + * + * @param data Data returned from HTTP request + */ +function updateDiscoverStatusBar( data, preformatted ) { + var statBarId = data.msg; + var rsp = data.rsp; + var statBar = $( '#' + statBarId ); + + // Go through response to make it readable in the status bar. + var out = $('

                          '); + for ( var i in rsp ) { + if ( preformatted == 1 ) { + out.append( '
                          ' + rsp[i] + '
                          ' ); + } else { + out.append( rsp[i] + '
                          ' ); + } + } + + // Write response to status bar and show the bar. + $( '#' + statBarId ).find( 'div' ).append( out ); + statBar.show(); +} + + +/** + * Load set node properties page + * + * @param tgtNode Target node to set properties + */ +function editNodeProps(tgtNode) { + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'editPropsTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'editPropsTab' + inst; + } + + // Open new tab + // Create set properties form + var editPropsForm = $('
                          '); + + // Create info bar + var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); + editPropsForm.append(infoBar); + + // Create an input for each definable attribute + var div, label, input, descr, value; + // Set node attribute + origAttrs[tgtNode]['node'] = tgtNode; + for (var key in nodeAttrs) { + // If an attribute value exists + if (origAttrs[tgtNode][key]) { + // Set the value + value = origAttrs[tgtNode][key]; + } else { + value = ''; + } + + // Create label and input for attribute + div = $('
                          ').css('display', 'inline-table'); + label = $('').css('vertical-align', 'middle'); + input = $('').css('margin-top', '5px'); + + // Change border to blue onchange + input.bind('change', function(event) { + $(this).css('border-color', 'blue'); + }); + + div.append(label); + div.append(input); + editPropsForm.append(div); + } + + // Change style for last division + div.css({ + 'display': 'block', + 'margin': '0px 0px 10px 0px' + }); + + // Generate tooltips + editPropsForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + + // Save changes + var saveBtn = createButton('Save'); + saveBtn.click(function() { + // Get all inputs + var inputs = $('#' + newTabId + ' input'); + + // Go through each input + var args = ''; + var attrName, attrVal; + inputs.each(function(){ + // If the border color is blue + if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { + // Change border color back to normal + $(this).css('border-color', ''); + + // Get attribute name and value + attrName = $(this).parent().find('label').text().replace(':', ''); + attrVal = $(this).val(); + + // Build argument string + if (args) { + // Handle subsequent arguments + args += ';' + attrName + '=' + attrVal; + } else { + // Handle the 1st argument + args += attrName + '=' + attrVal; + } + } + }); + + // Send command to change node attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : '-t;node;-o;' + tgtNode + ';' + args, + msg : 'out=' + newTabId + ';tgt=' + tgtNode + }, + + success: function(data) { + data = decodeRsp(data); + showChdefOutput(data); + } + }); + }); + editPropsForm.append(saveBtn); + + // Cancel changes + var cancelBtn = createButton('Cancel'); + cancelBtn.click(function() { + // Close the tab + tab.remove($(this).parent().parent().attr('id')); + }); + editPropsForm.append(cancelBtn); + + // Append to discover tab + tab.add(newTabId, 'Edit', editPropsForm, true); + + // Select new tab + tab.select(newTabId); +} + +/** + * Open set node attributes dialog + */ +function openSetAttrsDialog() { + // Open new tab + // Create set properties form + var setPropsForm = $('
                          '); + + // Create info bar + var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); + setPropsForm.append(infoBar); + + // Create an input for each definable attribute + var div, label, input, descr, value; + for (var key in nodeAttrs) { + value = ''; + + // Create label and input for attribute + div = $('
                          ').css('display', 'inline'); + label = $('').css('vertical-align', 'middle'); + input = $('').css('margin-top', '5px'); + + // Change border to blue onchange + input.bind('change', function(event) { + $(this).css('border-color', 'blue'); + }); + + div.append(label); + div.append(input); + setPropsForm.append(div); + } + + // Change style for last division + div.css({ + 'display': 'block', + 'margin': '0px 0px 10px 0px' + }); + + // Generate tooltips + setPropsForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Enable vertical scroll + setPropsForm.css('overflow', 'auto'); + + // Open form as a dialog + setPropsForm.dialog({ + title: 'Set attributes', + modal: true, + close: function(){ + $(this).remove(); + }, + height: 400, + width: 800, + buttons: { + "Save": function() { + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get all inputs + var inputs = $(this).find('input'); + + // Go through each input + var args = ''; + var tgtNode, attrName, attrVal; + inputs.each(function(){ + // If the border color is blue + if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { + // Change border color back to normal + $(this).css('border-color', ''); + + // Get attribute name and value + attrName = $(this).parent().find('label').text().replace(':', ''); + attrVal = $(this).val(); + + // Get node name + if (attrName == 'node') { + tgtNode = attrVal; + } else { + // Build argument string + if (args) { + // Handle subsequent arguments + args += ';' + attrName + '=' + attrVal; + } else { + // Handle the 1st argument + args += attrName + '=' + attrVal; + } + } + } + }); + + // Send command to change node attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : '-t;node;-o;' + tgtNode + ';' + args, + msg : 'node=' + tgtNode + }, + + /** + * Show results + * + * @param data + * Data returned from HTTP request + * @return Nothing + */ + success: function(data) { + // Get output + data = decodeRsp(data); + var out = data.rsp; + var node = data.msg.replace('node=', ''); + + // Go through output and append to paragraph + var msg = ''; + for (var i in out) { + if (!msg) { + msg = node + ': ' + out[i]; + } else { + msg += '
                          ' + node + ': ' + out[i]; + } + } + + openDialog('info', msg); + } + }); + + // Close dialog + $(this).dialog( "close" ); + }, + "Cancel": function(){ + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Turn on monitoring for a given node + * + * @param node Node to monitor on or off + * @param monitor Monitor state, on or off + */ +function monitorNode(node, monitor) { + // Show ganglia loader + var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').show(); + + if (monitor == 'on') { + // Append loader to warning bar + var warningBar = $('#nodesTab').find('.ui-state-error p'); + if (warningBar.length) { + warningBar.append(createLoader('')); + } + + if (node) { + // Check if ganglia RPMs are installed + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliacheck;' + node, + msg : node // Node range will be passed along in data.msg + }, + + /** + * Start ganglia on a given node range + * + * @param data Data returned from HTTP request + */ + success : function(data) { + // Get response + data = decodeRsp(data); + var out = data.rsp[0].split(/\n/); + + // Go through each line + var warn = false; + var warningMsg = ''; + for (var i in out) { + // If an RPM is not installed + if (out[i].indexOf('not installed') > -1) { + warn = true; + + if (warningMsg) { + warningMsg += '
                          ' + out[i]; + } else { + warningMsg = out[i]; + } + } + } + + // If there are warnings + if (warn) { + // Create warning bar + var warningBar = createWarnBar(warningMsg); + warningBar.css('margin-bottom', '10px'); + warningBar.prependTo($('#nodesTab')); + } else { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastart;' + data.msg + ';-r', + msg : data.msg + }, + + success : function(data) { + data = decodeRsp(data); + // Remove any warnings + $('#nodesTab').find('.ui-state-error').remove(); + + // Update datatable + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastatus;' + data.msg, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadGangliaStatus(data); + } + }); + } + }); + } // End of if (warn) + } // End of function(data) + }); + } else { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastart', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + // Remove any warnings + $('#nodesTab').find('.ui-state-error').remove(); + } + }); + } // End of if (node) + } else { + var args; + if (node) { + args = 'gangliastop;' + node + ';-r'; + } else { + args = 'gangliastop'; + } + + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : args, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + // Hide ganglia loader + var gangliaCol = $('#' + nodesTableId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').hide(); + } + }); + } +} + +/** + * Install Ganglia on a given node + * + * @param node Node to install Ganglia on + */ +function installGanglia(node) { + var iframe = createIFrame('lib/cmd.php?cmd=webrun&tgt=&args=installganglia;' + node + '&msg=' + node + '&opts=flush'); + iframe.prependTo($('#nodesTab')); + + // Turn on Ganglia for node + monitorNode(node, 'on'); +} + +/** + * After nodes are loaded, load more information based on different hardware architectures + * + * @param group Group name + */ +function advancedLoad(group){ + var tempIndex = 0; + var tableHeaders = $('#' + nodesTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); + var colNameHash = new Object(); + var colName = ''; + var archCol = 0, hcpCol = 0; + + // Find out the column name and their index + for (tempIndex = 0; tempIndex < tableHeaders.size(); tempIndex++){ + var header = tableHeaders.eq(tempIndex); + // Skip headers that are links, e.g. status, power, and monitor + if (header.find('a').size() > 0){ + continue; + } + + colName = header.text(); + + if (colName) { + colNameHash[colName] = tempIndex; + } + } + + // If there is no arch column, exit because you cannot distinguish hardware type + if (!colNameHash['arch']) { + return; + } + + if (!colNameHash['hcp']) { + return; + } + archCol = colNameHash['arch']; + hcpCol = colNameHash['hcp']; + + // Get hardware control point + var rows = $('#' + nodesTableId + ' tbody tr'); + var hcps = new Object(); + var rowsNum = rows.size(); + for (var j = 0; j < rowsNum; j++) { + var val = rows.eq(j).find('td').eq(hcpCol).html(); + var archval = rows.eq(j).find('td').eq(archCol).html(); + if (-1 == archval.indexOf('390')){ + continue; + } + hcps[val] = 1; + } + + if (Object.keys(hcps).length == 0) { + openDialog('warn', "No node found with hcp column filled in and 390 arch!"); + return; + } + // Get Nodes info bar + //var nodeInfoBar = getNodesTabInfoBar(); + //nodeInfoBar.append("\nEntering Advanced Load...\n") + + var args; + var shortzHcps = new Array(); + var zhcpHash = new Object(); + for (var h in hcps) { + // Get node without domain name + args = h.split('.'); + + if (!zhcpHash[args[0]]) { + + shortzHcps.push(args[0]); + zhcpHash[args[0]] = 1; + + // If there are no disk pools or network names cookie for this hcp + if (!$.cookie('xcat_' + args[0] + 'diskpools') || !$.cookie('xcat_' + args[0] + 'networks')) { + // Check if SMAPI is online + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsvm', + tgt : args[0], + args : '', + msg : 'group=' + group + ';hcp=' + args[0] + }, + + // Load hardware control point specific info + // Get disk pools and network names + success : function(data) { + data = decodeRsp(data); + loadHcpInfo(data); + } + }); + } + } + } // End of for + + // Save zHCPs as a cookie + setzHcpCookies(shortzHcps); + + // Retrieve z/VM hypervisors and their zHCPs + if (!$.cookie('xcat_zvms')) { + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webportal', + tgt : '', + args : 'lszvm', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setzVMCookies(data); + } + }); + } +} + +/** + * Jump to provision page on-click + * + * @param tgtNodes Target nodes + */ +function jump2Provision(tgtNodes){ + var nodeArray = tgtNodes.split(','); + var nodeName = ''; + var index = 0; + var archType = ''; + var errorMsg = ''; + var master = ''; + var tftpserver = ''; + var nfsserver = ''; + var diaDiv = $('
                          '); + + // Check the first node's arch type + for (index in nodeArray){ + nodeName = nodeArray[index]; + + // Skip if node does not have arch + if (!origAttrs[nodeName]['arch']){ + errorMsg = 'Nodes should have arch defined! '; + break; + } + + if (index == 0) { + archType = origAttrs[nodeName]['arch']; + } + + // Skip if nodes do not have same arch + if (archType != origAttrs[nodeName]['arch']){ + errorMsg = 'Nodes should belong to the same arch!
                          '; + break; + } + } + + // Skip if nodes do not have MAC address + for (index in nodeArray){ + if (!origAttrs[nodeName]['mac'] || !origAttrs[nodeName]['ip']){ + errorMsg += 'Nodes should have the IP and MAC addresses defined!
                          '; + break; + } + } + + if (archType.indexOf('390') != -1) { + errorMsg += 'Please use the provision page'; + } + + // Open dialog to show error message + if (errorMsg){ + diaDiv.append(createWarnBar(errorMsg)); + diaDiv.dialog({ + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + 'Close': function(){ + $(this).dialog('destroy'); + } + } + }); + + return; + } + + if (origAttrs[nodeName]['xcatmaster']) { + master = origAttrs[nodeName]['xcatmaster']; + } + + if (origAttrs[nodeName]['tftpserver']) { + tftpserver = origAttrs[nodeName]['tftpserver']; + } + + if (origAttrs[nodeName]['nfsserver']) { + nfsserver = origAttrs[nodeName]['nfsserver']; + } + + window.location.href = 'provision.php?nodes=' + tgtNodes + '&arch=' + archType + '&master=' + master + + '&tftpserver=' + tftpserver + '&nfsserver=' + nfsserver; +} diff --git a/xCAT-UI/js/nodes/nodeset.js b/xCAT-UI/js/nodes/nodeset.js index c1b2acfe8..87d84ba1e 100644 --- a/xCAT-UI/js/nodes/nodeset.js +++ b/xCAT-UI/js/nodes/nodeset.js @@ -1,296 +1,308 @@ -/** - * Load nodeset page - * - * @param tgtNodes Targets to run nodeset against - */ -function loadNodesetPage(tgtNodes) { - // Get OS images - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'osimage', - msg : '' - }, - - success : setOSImageCookies - }); - - // Get nodes tab - var tab = getNodesTab(); - - // Generate new tab ID - var inst = 0; - var tabId = 'nodesetTab' + inst; - while ($('#' + tabId).length) { - // If one already exists, generate another one - inst = inst + 1; - tabId = 'nodesetTab' + inst; - } - - // Create nodeset form - var nodesetForm = $('
                          '); - - // Create status bar - var statBarId = 'nodesetStatusBar' + inst; - var statBar = createStatusBar(statBarId).hide(); - - // Create loader - var loader = createLoader('nodesetLoader'); - statBar.find('div').append(loader); - - // Create info bar - var infoBar = createInfoBar('Set the boot state for a node range'); - nodesetForm.append(statBar, infoBar); - - // Create VM fieldset - var vmFS = $('
                          '); - var vmLegend = $('Virtual Machine'); - vmFS.append(vmLegend); - nodesetForm.append(vmFS); - - var vmAttr = $('
                          '); - vmFS.append($('
                          ')); - vmFS.append(vmAttr); - - // Create options fieldset - var imageFS = $('
                          '); - var imageLegend = $('Image'); - imageFS.append(imageLegend); - nodesetForm.append(imageFS); - - var imageAttr = $('
                          '); - imageFS.append($('
                          ')); - imageFS.append(imageAttr); - - // Create target node or group - var tgt = $('
                          '); - vmAttr.append(tgt); - - // Create boot type drop down - var type = $('
                          '); - var typeLabel = $(''); - var typeSelect = $(''); - typeSelect.append('' - + '' - + '' - ); - type.append(typeLabel); - type.append(typeSelect); - imageAttr.append(type); - - // Create operating system image input - var os = $('
                          '); - var osLabel = $(''); - var osSelect = $(''); - osSelect.append($('')); - - var imageNames = $.cookie('xcat_imagenames').split(','); - if (imageNames) { - imageNames.sort(); - for (var i in imageNames) { - osSelect.append($('')); - } - } - os.append(osLabel); - os.append(osSelect); - imageAttr.append(os); - - // Generate tooltips - nodesetForm.find('div input[title],select').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events : { - def : "mouseover,mouseout", - input : "mouseover,mouseout", - widget : "focus mouseover,blur mouseout", - tooltip : "mouseover,mouseout" - } - }); - - /** - * Ok - */ - var okBtn = createButton('Ok'); - okBtn.css({ - 'width': '80px', - 'display': 'block' - }); - okBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); - - // Check state, OS, arch, and profile - var ready = true; - var inputs = $('#' + tabId + ' input'); - for ( var i = 0; i < inputs.length; i++) { - if (!inputs.eq(i).val() && inputs.eq(i).attr('name') != 'diskPw') { - inputs.eq(i).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - inputs.eq(i).css('border', 'solid #BDBDBD 1px'); - } - } - - if (ready) { - // Get nodes - var tgts = $('#' + tabId + ' input[name=target]').val(); - // Get boot type - var type = $('#' + tabId + ' select[id=bootType]').val(); - // Get operating system image - var os = $('#' + tabId + ' select[name=os]').val(); - - // Disable all inputs, selects, and Ok button - inputs.attr('disabled', 'disabled'); - $('#' + tabId + ' select').attr('disabled', 'disabled'); - $(this).attr('disabled', 'true'); - - /** - * (1) Set the OS, arch, and profile - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeadd', - tgt : '', - args : tgts + ';noderes.netboot=' + type, - msg : 'cmd=nodeadd;inst=' + inst - }, - - success : updateNodesetStatus - }); - - // Show status bar - statBar.show(); - } else { - // Show warning message - var warn = createWarnBar('You are missing some values!'); - warn.prependTo($(this).parents('.ui-tabs-panel')); - } - }); - nodesetForm.append(okBtn); - - // Append to discover tab - tab.add(tabId, 'Nodeset', nodesetForm, true); - - // Select new tab - tab.select(tabId); -} - -/** - * Update nodeset status - * - * @param data Data returned from HTTP request - */ -function updateNodesetStatus(data) { - // Get ajax response - var rsp = data.rsp; - var args = data.msg.split(';'); - var cmd = args[0].replace('cmd=', ''); - - // Get nodeset instance - var inst = args[1].replace('inst=', ''); - // Get status bar ID - var statBarId = 'nodesetStatusBar' + inst; - // Get tab ID - var tabId = 'nodesetTab' + inst; - - // Get nodes - var tgts = $('#' + tabId + ' input[name=target]').val(); - // Get operating system image - var os = $('#' + tabId + ' select[name=os]').val(); - - /** - * (2) Update /etc/hosts - */ - if (cmd == 'nodeadd') { - if (rsp.length) { - $('#' + statBarId).find('img').hide(); - $('#' + statBarId).find('div').append('
                          (Error) Failed to create node definition
                          '); - } else { - // Create target nodes string - var tgtNodesStr = ''; - var nodes = tgts.split(','); - - // Loop through each node - for ( var i in nodes) { - // If it is the 1st and only node - if (i == 0 && i == nodes.length - 1) { - tgtNodesStr += nodes[i]; - } - // If it is the 1st node of many nodes - else if (i == 0 && i != nodes.length - 1) { - // Append a comma to the string - tgtNodesStr += nodes[i] + ', '; - } else { - // If it is the last node - if (i == nodes.length - 1) { - // Append nothing to the string - tgtNodesStr += nodes[i]; - } else { - // Append a comma to the string - tgtNodesStr += nodes[i] + ', '; - } - } - } - - $('#' + statBarId).find('div').append('
                          Node definition created for ' + tgtNodesStr + '
                          '); - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'makehosts', - tgt : '', - args : '', - msg : 'cmd=makehosts;inst=' + inst - }, - - success : updateNodesetStatus - }); - } - } - - /** - * (4) Update DNS - */ - else if (cmd == 'makehosts') { - // If no output, no errors occurred - if (rsp.length) { - $('#' + statBarId).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); - } else { - $('#' + statBarId).find('div').append('
                          /etc/hosts updated
                          '); - } - - // Go straight to prepare node for boot - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodeset', - tgt : tgts, - args : 'osimage=' + os, - msg : 'cmd=nodeset;inst=' + inst - }, - - success : updateNodesetStatus - }); - } - - /** - * (5) Boot node from network - */ - else if (cmd == 'nodeset') { - // Write ajax response to status bar - var prg = writeRsp(rsp, ''); - $('#' + statBarId).find('div').append(prg); - - // Hide loader - $('#' + statBarId).find('img').hide(); - } +/** + * Load nodeset page + * + * @param tgtNodes Targets to run nodeset against + */ +function loadNodesetPage(tgtNodes) { + // Get OS images + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'osimage', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setOSImageCookies(data); + } + }); + + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var tabId = 'nodesetTab' + inst; + while ($('#' + tabId).length) { + // If one already exists, generate another one + inst = inst + 1; + tabId = 'nodesetTab' + inst; + } + + // Create nodeset form + var nodesetForm = $('
                          '); + + // Create status bar + var statBarId = 'nodesetStatusBar' + inst; + var statBar = createStatusBar(statBarId).hide(); + + // Create loader + var loader = createLoader('nodesetLoader'); + statBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Set the boot state for a node range'); + nodesetForm.append(statBar, infoBar); + + // Create VM fieldset + var vmFS = $('
                          '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + nodesetForm.append(vmFS); + + var vmAttr = $('
                          '); + vmFS.append($('
                          ')); + vmFS.append(vmAttr); + + // Create options fieldset + var imageFS = $('
                          '); + var imageLegend = $('Image'); + imageFS.append(imageLegend); + nodesetForm.append(imageFS); + + var imageAttr = $('
                          '); + imageFS.append($('
                          ')); + imageFS.append(imageAttr); + + // Create target node or group + var tgt = $('
                          '); + vmAttr.append(tgt); + + // Create boot type drop down + var type = $('
                          '); + var typeLabel = $(''); + var typeSelect = $(''); + typeSelect.append('' + + '' + + '' + ); + type.append(typeLabel); + type.append(typeSelect); + imageAttr.append(type); + + // Create operating system image input + var os = $('
                          '); + var osLabel = $(''); + var osSelect = $(''); + osSelect.append($('')); + + var imageNames = $.cookie('xcat_imagenames').split(','); + if (imageNames) { + imageNames.sort(); + for (var i in imageNames) { + osSelect.append($('')); + } + } + os.append(osLabel); + os.append(osSelect); + imageAttr.append(os); + + // Generate tooltips + nodesetForm.find('div input[title],select').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Ok + */ + var okBtn = createButton('Ok'); + okBtn.css({ + 'width': '80px', + 'display': 'block' + }); + okBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // Check state, OS, arch, and profile + var ready = true; + var inputs = $('#' + tabId + ' input'); + for ( var i = 0; i < inputs.length; i++) { + if (!inputs.eq(i).val() && inputs.eq(i).attr('name') != 'diskPw') { + inputs.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + inputs.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + if (ready) { + // Get nodes + var tgts = $('#' + tabId + ' input[name=target]').val(); + // Get boot type + var type = $('#' + tabId + ' select[id=bootType]').val(); + // Get operating system image + var os = $('#' + tabId + ' select[name=os]').val(); + + // Disable all inputs, selects, and Ok button + inputs.attr('disabled', 'disabled'); + $('#' + tabId + ' select').attr('disabled', 'disabled'); + $(this).attr('disabled', 'true'); + + /** + * (1) Set the OS, arch, and profile + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeadd', + tgt : '', + args : tgts + ';noderes.netboot=' + type, + msg : 'cmd=nodeadd;inst=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateNodesetStatus(data); + } + }); + + // Show status bar + statBar.show(); + } else { + // Show warning message + var warn = createWarnBar('You are missing some values!'); + warn.prependTo($(this).parents('.ui-tabs-panel')); + } + }); + nodesetForm.append(okBtn); + + // Append to discover tab + tab.add(tabId, 'Nodeset', nodesetForm, true); + + // Select new tab + tab.select(tabId); +} + +/** + * Update nodeset status + * + * @param data Data returned from HTTP request + */ +function updateNodesetStatus(data) { + // Get ajax response + var rsp = data.rsp; + var args = data.msg.split(';'); + var cmd = args[0].replace('cmd=', ''); + + // Get nodeset instance + var inst = args[1].replace('inst=', ''); + // Get status bar ID + var statBarId = 'nodesetStatusBar' + inst; + // Get tab ID + var tabId = 'nodesetTab' + inst; + + // Get nodes + var tgts = $('#' + tabId + ' input[name=target]').val(); + // Get operating system image + var os = $('#' + tabId + ' select[name=os]').val(); + + /** + * (2) Update /etc/hosts + */ + if (cmd == 'nodeadd') { + if (rsp.length) { + $('#' + statBarId).find('img').hide(); + $('#' + statBarId).find('div').append('
                          (Error) Failed to create node definition
                          '); + } else { + // Create target nodes string + var tgtNodesStr = ''; + var nodes = tgts.split(','); + + // Loop through each node + for ( var i in nodes) { + // If it is the 1st and only node + if (i == 0 && i == nodes.length - 1) { + tgtNodesStr += nodes[i]; + } + // If it is the 1st node of many nodes + else if (i == 0 && i != nodes.length - 1) { + // Append a comma to the string + tgtNodesStr += nodes[i] + ', '; + } else { + // If it is the last node + if (i == nodes.length - 1) { + // Append nothing to the string + tgtNodesStr += nodes[i]; + } else { + // Append a comma to the string + tgtNodesStr += nodes[i] + ', '; + } + } + } + + $('#' + statBarId).find('div').append('
                          Node definition created for ' + tgtNodesStr + '
                          '); + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'makehosts', + tgt : '', + args : '', + msg : 'cmd=makehosts;inst=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateNodesetStatus(data); + } + }); + } + } + + /** + * (4) Update DNS + */ + else if (cmd == 'makehosts') { + // If no output, no errors occurred + if (rsp.length) { + $('#' + statBarId).find('div').append('
                          (Error) Failed to update /etc/hosts
                          '); + } else { + $('#' + statBarId).find('div').append('
                          /etc/hosts updated
                          '); + } + + // Go straight to prepare node for boot + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodeset', + tgt : tgts, + args : 'osimage=' + os, + msg : 'cmd=nodeset;inst=' + inst + }, + + success : function(data) { + data = decodeRsp(data); + updateNodesetStatus(data); + } + }); + } + + /** + * (5) Boot node from network + */ + else if (cmd == 'nodeset') { + // Write ajax response to status bar + var prg = writeRsp(rsp, ''); + $('#' + statBarId).find('div').append(prg); + + // Hide loader + $('#' + statBarId).find('img').hide(); + } } \ No newline at end of file diff --git a/xCAT-UI/js/nodes/physical.js b/xCAT-UI/js/nodes/physical.js index e8df2d4c8..24b010c8c 100644 --- a/xCAT-UI/js/nodes/physical.js +++ b/xCAT-UI/js/nodes/physical.js @@ -1,933 +1,935 @@ -var bpaList; -var fspList; -var lparList; -var bladeList; -var rackList; -var unknownList; -var graphicalNodeList; -var selectNode; - -/** - * Get all nodes useful attributes from remote server - * - * @param dataTypeIndex The index in the array which contains attributes we need. - * @param attrNullNode The target node list for this attribute - */ -function initGraphicalData() { - $('#graphTab').append(createLoader()); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'graph', - msg : '' - }, - success : function(data) { - if (!data.rsp[0]) { - return; - } - extractGraphicalData(data.rsp[0]); - getNodesAndDraw(); - } - }); -} - -/** - * Extract all nodes userful data into a hash, which will be used for creating graphical - * - * @param data The response from xCAT command 'nodels all nodetype.nodetype ppc.parent ...' - * @return nodes list for next time query - */ -function extractGraphicalData(data) { - var nodes = data.split(';'); - var attrs; - var nodeName; - - // Extract useful info into tempList - for (var i = 0; i < nodes.length; i++) { - attrs = nodes[i].split(':'); - nodeName = attrs[0]; - if (undefined == graphicalNodeList[nodeName]) { - graphicalNodeList[nodeName] = new Object(); - } - - graphicalNodeList[nodeName]['type'] = attrs[1].toLowerCase(); - switch (attrs[1].toLowerCase()) { - case 'cec': - case 'frame': - case 'lpar': - case 'lpar,osi': - case 'osi,lpar': - graphicalNodeList[nodeName]['parent'] = attrs[2]; - graphicalNodeList[nodeName]['mtm'] = attrs[3]; - graphicalNodeList[nodeName]['status'] = attrs[4]; - break; - case 'blade': - graphicalNodeList[nodeName]['mpa'] = attrs[2]; - graphicalNodeList[nodeName]['unit'] = attrs[3]; - graphicalNodeList[nodeName]['status'] = attrs[4]; - break; - case 'systemx': - graphicalNodeList[nodeName]['rack'] = attrs[2]; - graphicalNodeList[nodeName]['unit'] = attrs[3]; - graphicalNodeList[nodeName]['mtm'] = attrs[4]; - graphicalNodeList[nodeName]['status'] = attrs[5]; - break; - default: - break; - } - } -} - -function createPhysicalLayout(nodeList) { - var flag = false; - - // When the graphical layout is shown, do not need to redraw - if (1 < $('#graphTab').children().length) { - return; - } - - // Save the new selected nodes - if (graphicalNodeList) { - for (var i in graphicalNodeList) { - flag = true; - break; - } - } - - bpaList = new Object(); - fspList = new Object(); - lparList = new Object(); - bladeList = new Object(); - selectNode = new Object(); - rackList = new Object(); - unknownList = new Array(); - - // There is no graphical data, get the info now - if (!flag) { - graphicalNodeList = new Object(); - initGraphicalData(); - } else { - getNodesAndDraw(); - } -} - -function getNodesAndDraw() { - var groupName = $.cookie('xcat_selectgrouponnodes'); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'nodels', - tgt : groupName, - args : '', - msg : '' - }, - success : function(data) { - for (var temp in data.rsp) { - var nodeName = data.rsp[temp][0]; - if ('' == nodeName) { - continue; - } - fillList(nodeName); - } - $('#graphTab').empty(); - createGraphical(); - } - }); -} - -function fillList(nodeName, defaultnodetype) { - var parentName = ''; - var mtm = ''; - var status = ''; - var nodeType = ''; - var mpa = ''; - var unit = ''; - var rack = ''; - if (!graphicalNodeList[nodeName]) { - parentName = ''; - mtm = ''; - status = ''; - nodeType = defaultnodetype; - } else { - status = graphicalNodeList[nodeName]['status']; - nodeType = graphicalNodeList[nodeName]['type']; - switch (nodeType) { - case 'frame': - case 'lpar,osi': - case 'lpar': - case 'osi': - case 'cec': - parentName = graphicalNodeList[nodeName]['parent']; - mtm = graphicalNodeList[nodeName]['mtm']; - break; - case 'blade': - mpa = graphicalNodeList[nodeName]['mpa']; - unit = graphicalNodeList[nodeName]['unit']; - break; - case 'systemx': - rack = graphicalNodeList[nodeName]['rack']; - unit = graphicalNodeList[nodeName]['unit']; - break; - default: - break; - } - - } - - if ('' == status) { - status = 'unknown'; - } - - switch (nodeType) { - case 'frame': - if (undefined == bpaList[nodeName]) { - bpaList[nodeName] = new Array(); - } - - break; - case 'lpar,osi': - case 'lpar': - case 'osi': - if ('' == parentName) { - break; - } - - if (undefined == fspList[parentName]) { - fillList(parentName, 'cec'); - } - - fspList[parentName]['children'].push(nodeName); - lparList[nodeName] = status; - - break; - case 'cec': - if (undefined != fspList[nodeName]) { - break; - } - - fspList[nodeName] = new Object(); - fspList[nodeName]['children'] = new Array(); - fspList[nodeName]['mtm'] = mtm; - - if ('' == parentName) { - break; - } - - if (undefined == bpaList[parentName]) { - fillList(parentName, 'frame'); - } - - bpaList[parentName].push(nodeName); - break; - case 'blade': - if (undefined == bladeList[mpa]) { - bladeList[mpa] = new Array(); - } - bladeList[mpa].push(nodeName + ',' + unit); - - break; - case 'systemx': - if (!rack) { - rack = '_notsupply_'; - } - - if (undefined == rackList[rack]) { - rackList[rack] = new Array(); - } - - rackList[rack].push(nodeName + ',' + unit); - - break; - default: - unknownList.push(nodeName); - break; - } -} - -function createGraphical() { - var tabArea = $('#graphTab'); - var selectNodeDiv = $('
                          '); - var temp = 0; - for (var i in selectNode) { - temp++; - break; - } - - // There is no selected LPAR, show the info bar - if (temp == 0) { - tabArea.append(createInfoBar('Hover over a CEC and select the LPARs to do operations against.')); - } else { - // Show selected LPARs - updateSelectNodeDiv(); - } - - // Add buttons - tabArea.append(createActionMenu()); - tabArea.append(selectNodeDiv); - createSystempGraphical(bpaList, fspList, tabArea); - createBladeGraphical(bladeList, tabArea); - createSystemxGraphical(rackList, tabArea); - addUnknownGraphical(unknownList, tabArea); -} - -/** - * Create the physical/graphical layout for System p machines - * - * @param bpa All BPA and their related FSPs - * @param fsp All FSP and their related LPARs - * @param area The element to append graphical layout - */ -function createSystempGraphical(bpa, fsp, area) { - var usedFsp = new Object(); - var graphTable = $('
                          '); - var elementNum = 0; - var row = null; - var showFlag = false; - - // There is a node in the BPA list, so show add the title and show all frames - for (var bpaName in bpa) { - showFlag = true; - $('#graphTab').append('system p
                          '); - $('#graphTab').append(graphTable); - break; - } - - for (var bpaName in bpa) { - if (0 == elementNum % 3) { - row = $(''); - graphTable.append(row); - } - - elementNum++; - - var td = $(''); - var frameDiv = $('
                          '); - frameDiv.append('
                          '); - - // For P7-IH, all the CECs are insert into the frame from bottom to up, - // so we have to show the CECs same as the physical layout - var tempBlankDiv = $('
                          '); - var tempHeight = 0; - for (var fspIndex in bpa[bpaName]) { - var fspName = bpa[bpaName][fspIndex]; - usedFsp[fspName] = 1; - - // This is the P7-IH, we should add the blank at the top - if ((0 == fspIndex) && ('9125-F2C' == fsp[fspName]['mtm'])) { - frameDiv.append(tempBlankDiv); - } - - frameDiv.append(createFspDiv(fspName, fsp[fspName]['mtm'], fsp)); - frameDiv.append(createFspTip(fspName, fsp[fspName]['mtm'], fsp)); - - tempHeight += calculateBlank(fsp[fspName]['mtm']); - } - - // tempHeight is the total height for all CECs, so we should minus BPA div - // height and CEC div heights - tempHeight = 428 - tempHeight; - tempBlankDiv.css('height', tempHeight); - td.append(frameDiv); - row.append(td); - } - - // Find the single FSP and sort descend by units - var singleFsp = new Array(); - for (var fspName in fsp) { - if (usedFsp[fspName]) { - continue; - } - - singleFsp.push([ fspName, fsp[fspName]['mtm'] ]); - } - - // If there is no frame, we should check if there is single CEC and show - // the title and add node area - if (!showFlag) { - for (var fspIndex in singleFsp) { - $('#graphTab').append('system p
                          '); - $('#graphTab').append(graphTable); - break; - } - } - - singleFsp.sort(function(a, b) { - var unitNumA = 4; - var unitNumB = 4; - if (hardwareInfo[a[1]]) { - unitNumA = hardwareInfo[a[1]][1]; - } - - if (hardwareInfo[b[1]]) { - unitNumB = hardwareInfo[b[1]][1]; - } - - return (unitNumB - unitNumA); - }); - - elementNum = 0; - for (var fspIndex in singleFsp) { - var fspName = singleFsp[fspIndex][0]; - if (0 == elementNum % 3) { - row = $(''); - graphTable.append(row); - } - elementNum++; - - var td = $(''); - td.append(createFspDiv(fspName, fsp[fspName]['mtm'], fsp)); - td.append(createFspTip(fspName, fsp[fspName]['mtm'], fsp)); - row.append(td); - } - - $('.tooltip input[type = checkbox]').bind('click', function() { - var lparName = $(this).attr('name'); - if ('' == lparName) { - return; - } - if (true == $(this).attr('checked')) { - changeNode(lparName, 'select'); - } else { - changeNode(lparName, 'unselect'); - } - - updateSelectNodeDiv(); - }); - - $('.fspDiv2, .fspDiv4, .fspDiv42').tooltip({ - position : "center right", - relative : true, - offset : [ 10, -40 ], - effect : "fade", - opacity : 0.9 - }); - - $('.tooltip a').bind('click', function() { - var lparName = $(this).html(); - $('#nodesDatatable #' + lparName).trigger('click'); - }); - - $('.fspDiv2, .fspDiv4, .fspDiv42').bind('click', function() { - var fspName = $(this).attr('value'); - var selectCount = 0; - for (var lparIndex in fspList[fspName]['children']) { - var lparName = fspList[fspName]['children'][lparIndex]; - if (selectNode[lparName]) { - selectCount++; - } - } - - // All LPARs are selected, so unselect nodes - if (selectCount == fspList[fspName]['children'].length) { - for (var lparIndex in fspList[fspName]['children']) { - var lparName = fspList[fspName]['children'][lparIndex]; - changeNode(lparName, 'unselect'); - } - } - - // No selected LPARs on the cec, so add all LPARs into selectNode hash - else { - for (var lparIndex in fspList[fspName]['children']) { - var lparName = fspList[fspName]['children'][lparIndex]; - changeNode(lparName, 'select'); - } - } - - updateSelectNodeDiv(); - }); - - $('.fspCheckbox').bind('click', function() { - var itemName = $(this).attr('name'); - name = itemName.substr(6); - - if ($(this).attr('checked')) { - selectNode[name] = 1; - } else { - delete selectNode[name]; - } - - updateSelectNodeDiv(); - }); -} - -/** - * Create the physical/graphical layout for blades - * - * @param blades The blade list in global - * @param area The element to append the graphical layout - */ -function createBladeGraphical(blades, area) { - var graphTable = $('
                          '); - var mpa = ''; - var bladeName = ''; - var index = 0; - var mpaNumber = 0; - var row; - var showFlag = false; - - // Only show the title and nodes when there are blade in the blade list - for (mpa in blades) { - showFlag = true; - break; - } - - if (showFlag) { - $('#graphTab').append('Blade
                          '); - $('#graphTab').append(graphTable); - } - // If there is no blade node, return directly - else { - return; - } - - for (mpa in blades) { - var tempArray = new Array(14); - var bladeInfo = new Array(); - var unit = 0; - if (0 == mpaNumber % 3) { - row = $(''); - graphTable.append(row); - } - - mpaNumber++; - - var td = $(''); - var chasisDiv = $('
                          '); - - // Fill the array with blade information, to create the empty slot - for (index in blades[mpa]) { - bladeInfo = blades[mpa][index].split(','); - unit = parseInt(bladeInfo[1]); - tempArray[unit - 1] = bladeInfo[0]; - - } - - // Draw the blades and empty slot in chasis - for (index = 0; index < 14; index++) { - if (tempArray[index]) { - bladeName = tempArray[index]; - chasisDiv.append('
                          '); - } else { - chasisDiv.append('
                          '); - } - } - - td.append(chasisDiv); - row.append(td); - } - -} - -/** - * Create the physical/graphical layout for System x machines - * - * @param xnodes The system x node list in global - * @param area The element to append graphical layout - */ -function createSystemxGraphical(xnodes, area) { - var graphTable = $('
                          '); - var xnodename = ''; - var index = 0; - var rack = ''; - var row; - var xNodeCount = 0; - var showflag = false; - - // Only the title and System x node when there are x nodes in the list - for (rack in rackList) { - showflag = true; - break; - } - - if (showflag) { - $('#graphTab').append('system x
                          '); - $('#graphTab').append(graphTable); - } - // There is nothing to show, return directly - else { - return; - } - - for (rack in rackList) { - for (index in rackList[rack]) { - var xNodeName = rackList[rack][index]; - if (0 == xNodeCount % 3) { - row = $(''); - graphTable.append(row); - } - xNodeCount++; - var td = $(''); - var xNodeDiv = '
                          '; - td.append(xNodeDiv); - row.append(td); - } - } -} - -function addUnknownGraphical(unknownNodes, tab) { - // Do not continue if no nodes were found - if (unknownNodes.length < 1) - return; - - var list = ""; - tab.append('
                          '); - for (var index in unknownNodes) { - list += unknownNodes[index] + ', '; - - } - - // Delete last comma - list = list.substr(0, list.length - 2); - tab.append(list); -} - -/** - * Update the LPARs background in CEC, LPAR area and selectNode - */ -function updateSelectNodeDiv() { - var temp = 0; - $('#selectNodeDiv').empty(); - - // Add buttons - if (selectNode.length) { - $('#selectNodeDiv').append('Nodes: '); - for (var lparName in selectNode) { - $('#selectNodeDiv').append(lparName + ' '); - temp++; - if (temp > 6) { - $('#selectNodeDiv').append('...'); - break; - } - } - } -} - -/** - * Create the action menu - */ -function createActionMenu() { - // Create action bar - var actionBar = $('
                          ').css("width", "400px"); - - // Power on - var powerOnLnk = $('Power on'); - powerOnLnk.click(function() { - var tgtNodes = getSelectNodes(); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rpower', - tgt : tgtNodes, - args : 'on', - msg : '' - } - }); - }); - - // Power off - var powerOffLnk = $('Power off'); - powerOffLnk.click(function() { - var tgtNodes = getSelectNodes(); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rpower', - tgt : tgtNodes, - args : 'off', - msg : '' - } - }); - }); - - // Delete - var deleteLnk = $('Delete'); - deleteLnk.click(function() { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadDeletePage(tgtNodes); - } - }); - - // Unlock - var unlockLnk = $('Unlock'); - unlockLnk.click(function() { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadUnlockPage(tgtNodes); - } - }); - - // Run script - var scriptLnk = $('Run script'); - scriptLnk.click(function() { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadScriptPage(tgtNodes); - } - }); - - // Update - var updateLnk = $('Update'); - updateLnk.click(function() { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadUpdatenodePage(tgtNodes); - } - }); - - // Set boot state - var setBootStateLnk = $('Set boot state'); - setBootStateLnk.click(function() { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadNodesetPage(tgtNodes); - } - }); - - // Boot to network - var boot2NetworkLnk = $('Boot to network'); - boot2NetworkLnk.click(function() { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadNetbootPage(tgtNodes); - } - }); - - // Remote console - var rconLnk = $('Open console'); - rconLnk.bind('click', function(event) { - var tgtNodes = getSelectNodes(); - if (tgtNodes) { - loadRconsPage(tgtNodes); - } - }); - - // Edit properties - var editProps = $('Edit properties'); - editProps.bind('click', function(event) { - for (var node in selectNode) { - loadEditPropsPage(node); - } - }); - - // Actions - var actionsLnk = 'Actions'; - var actsMenu = createMenu([ deleteLnk, powerOnLnk, powerOffLnk, scriptLnk ]); - - // Configurations - var configLnk = 'Configuration'; - var configMenu = createMenu([ unlockLnk, updateLnk, editProps ]); - - // Provision - var provLnk = 'Provision'; - var provMenu = createMenu([ boot2NetworkLnk, setBootStateLnk, rconLnk ]); - - // Create an action menu - var actionsMenu = createMenu([ [ actionsLnk, actsMenu ], - [ configLnk, configMenu ], [ provLnk, provMenu ] ]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - actionBar.css('margin-top', '10px'); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - return actionBar; -} - -/** - * Create the physical/graphical layout - */ -function createFspDiv(fspName, mtm, fsp) { - // Create FSP title - var lparStatusRow = ''; - var temp = ''; - - for (var lparIndex in fsp[fspName]['children']) { - // Show 8 lpars on one cec at most. - if (lparIndex >= 8) { - break; - } - - var lparName = fsp[fspName]['children'][lparIndex]; - var color = statusMap(lparList[lparName]); - lparStatusRow += ''; - } - - // Select the backgroud - var divClass = ''; - if ('' == mtm) { - temp = '8231-E2B'; - } else { - temp = mtm; - } - - if (!hardwareInfo[temp]){ - hardwareInfo[temp] = ['unkown', 2]; - } - - if (hardwareInfo[temp][1]) { - divClass += 'fspDiv' + hardwareInfo[temp][1]; - } else { - divClass += 'fspDiv4'; - } - - // Create return value - var retHtml = ''; - retHtml += '
                          '; - retHtml += '
                          ' + lparStatusRow - + '
                          '; - return retHtml; -} - -/** - * Create the physical/graphical FSP tooltip which is used to select the LPAR - */ -function createFspTip(fspName, mtm, fsp) { - var tip = $('
                          '); - var tempTable = $('
                          '); - var temp = ''; - if ('' == mtm) { - temp = 'unkown'; - } else { - temp = mtm; - } - - if (hardwareInfo[temp]) { - tip.append('

                          ' + fspName + '(' + hardwareInfo[temp][0] + ')


                          '); - } else { - tip.append('

                          ' + fspName + '


                          '); - } - - for (var lparIndex in fsp[fspName]['children']) { - var lparName = fsp[fspName]['children'][lparIndex]; - var color = statusMap(lparList[lparName]); - var row = ''; - row += '' + lparName + ''; - row += '' + lparList[lparName] + ''; - tempTable.append(row); - } - - tip.append(tempTable); - return tip; -} -/** - * Map the LPAR status into a color - * - * @param status LPAR status in nodelist table - * @return Corresponding color name - */ -function statusMap(status) { - var color = 'gainsboro'; - - switch (status) { - case 'alive': - case 'ready': - case 'pbs': - case 'sshd': - case 'booting': - case 'booted': - case 'ping': - color = 'green'; - break; - case 'noping': - case 'unreachable': - color = 'red'; - break; - default: - color = 'grey'; - break; - } - - return color; -} - -/** - * Select all LPAR checkboxes - */ -function selectAllLpars(checkbox) { - var temp = checkbox.attr('checked'); - $('#selectNodeTable input[type = checkbox]').attr('checked', temp); -} - -/** - * Export all LPAR names from selectNode - * - * @return lpars' string - */ -function getSelectNodes() { - var ret = ''; - for (var lparName in selectNode) { - ret += lparName + ','; - } - - return ret.substring(0, ret.length - 1); -} - -/** - * When the node is selected or unselected, update the area on CEC, update - * the global list, and update the tooltip table - */ -function changeNode(lparName, status) { - var imgUrl = ''; - var checkFlag = true; - if ('select' == status) { - selectNode[lparName] = 1; - imgUrl = 'url(images/nodes/s-' + statusMap(lparList[lparName]) - + '.gif)'; - checkFlag = true; - } else { - delete selectNode[lparName]; - imgUrl = 'url(images/nodes/' + statusMap(lparList[lparName]) + '.gif)'; - checkFlag = false; - } - $('#' + lparName + 'status').css('background-image', imgUrl); - $('.tooltip input[name="' + lparName + '"]').attr('checked', checkFlag); -} - -/** - * The P7-IH's CECs are insert from bottom to up, so we had to calculate the blank height - * - * @return Height for the CEC - */ -function calculateBlank(mtm) { - if ('' == mtm) { - return 24; - } - - if (!hardwareInfo[mtm]) { - return 24; - } - - switch (hardwareInfo[mtm][1]) { - case 1: - return 13; - break; - case 2: - return 24; - break; - case 4: - return 47; - break; - default: - return 0; - break; - } +var bpaList; +var fspList; +var lparList; +var bladeList; +var rackList; +var unknownList; +var graphicalNodeList; +var selectNode; + +/** + * Get all nodes useful attributes from remote server + * + * @param dataTypeIndex The index in the array which contains attributes we need. + * @param attrNullNode The target node list for this attribute + */ +function initGraphicalData() { + $('#graphTab').append(createLoader()); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'graph', + msg : '' + }, + success : function(data) { + data = decodeRsp(data); + if (!data.rsp[0]) { + return; + } + extractGraphicalData(data.rsp[0]); + getNodesAndDraw(); + } + }); +} + +/** + * Extract all nodes userful data into a hash, which will be used for creating graphical + * + * @param data The response from xCAT command 'nodels all nodetype.nodetype ppc.parent ...' + * @return nodes list for next time query + */ +function extractGraphicalData(data) { + var nodes = data.split(';'); + var attrs; + var nodeName; + + // Extract useful info into tempList + for (var i = 0; i < nodes.length; i++) { + attrs = nodes[i].split(':'); + nodeName = attrs[0]; + if (undefined == graphicalNodeList[nodeName]) { + graphicalNodeList[nodeName] = new Object(); + } + + graphicalNodeList[nodeName]['type'] = attrs[1].toLowerCase(); + switch (attrs[1].toLowerCase()) { + case 'cec': + case 'frame': + case 'lpar': + case 'lpar,osi': + case 'osi,lpar': + graphicalNodeList[nodeName]['parent'] = attrs[2]; + graphicalNodeList[nodeName]['mtm'] = attrs[3]; + graphicalNodeList[nodeName]['status'] = attrs[4]; + break; + case 'blade': + graphicalNodeList[nodeName]['mpa'] = attrs[2]; + graphicalNodeList[nodeName]['unit'] = attrs[3]; + graphicalNodeList[nodeName]['status'] = attrs[4]; + break; + case 'systemx': + graphicalNodeList[nodeName]['rack'] = attrs[2]; + graphicalNodeList[nodeName]['unit'] = attrs[3]; + graphicalNodeList[nodeName]['mtm'] = attrs[4]; + graphicalNodeList[nodeName]['status'] = attrs[5]; + break; + default: + break; + } + } +} + +function createPhysicalLayout(nodeList) { + var flag = false; + + // When the graphical layout is shown, do not need to redraw + if (1 < $('#graphTab').children().length) { + return; + } + + // Save the new selected nodes + if (graphicalNodeList) { + for (var i in graphicalNodeList) { + flag = true; + break; + } + } + + bpaList = new Object(); + fspList = new Object(); + lparList = new Object(); + bladeList = new Object(); + selectNode = new Object(); + rackList = new Object(); + unknownList = new Array(); + + // There is no graphical data, get the info now + if (!flag) { + graphicalNodeList = new Object(); + initGraphicalData(); + } else { + getNodesAndDraw(); + } +} + +function getNodesAndDraw() { + var groupName = $.cookie('xcat_selectgrouponnodes'); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'nodels', + tgt : groupName, + args : '', + msg : '' + }, + success : function(data) { + data = decodeRsp(data); + for (var temp in data.rsp) { + var nodeName = data.rsp[temp][0]; + if ('' == nodeName) { + continue; + } + fillList(nodeName); + } + $('#graphTab').empty(); + createGraphical(); + } + }); +} + +function fillList(nodeName, defaultnodetype) { + var parentName = ''; + var mtm = ''; + var status = ''; + var nodeType = ''; + var mpa = ''; + var unit = ''; + var rack = ''; + if (!graphicalNodeList[nodeName]) { + parentName = ''; + mtm = ''; + status = ''; + nodeType = defaultnodetype; + } else { + status = graphicalNodeList[nodeName]['status']; + nodeType = graphicalNodeList[nodeName]['type']; + switch (nodeType) { + case 'frame': + case 'lpar,osi': + case 'lpar': + case 'osi': + case 'cec': + parentName = graphicalNodeList[nodeName]['parent']; + mtm = graphicalNodeList[nodeName]['mtm']; + break; + case 'blade': + mpa = graphicalNodeList[nodeName]['mpa']; + unit = graphicalNodeList[nodeName]['unit']; + break; + case 'systemx': + rack = graphicalNodeList[nodeName]['rack']; + unit = graphicalNodeList[nodeName]['unit']; + break; + default: + break; + } + + } + + if ('' == status) { + status = 'unknown'; + } + + switch (nodeType) { + case 'frame': + if (undefined == bpaList[nodeName]) { + bpaList[nodeName] = new Array(); + } + + break; + case 'lpar,osi': + case 'lpar': + case 'osi': + if ('' == parentName) { + break; + } + + if (undefined == fspList[parentName]) { + fillList(parentName, 'cec'); + } + + fspList[parentName]['children'].push(nodeName); + lparList[nodeName] = status; + + break; + case 'cec': + if (undefined != fspList[nodeName]) { + break; + } + + fspList[nodeName] = new Object(); + fspList[nodeName]['children'] = new Array(); + fspList[nodeName]['mtm'] = mtm; + + if ('' == parentName) { + break; + } + + if (undefined == bpaList[parentName]) { + fillList(parentName, 'frame'); + } + + bpaList[parentName].push(nodeName); + break; + case 'blade': + if (undefined == bladeList[mpa]) { + bladeList[mpa] = new Array(); + } + bladeList[mpa].push(nodeName + ',' + unit); + + break; + case 'systemx': + if (!rack) { + rack = '_notsupply_'; + } + + if (undefined == rackList[rack]) { + rackList[rack] = new Array(); + } + + rackList[rack].push(nodeName + ',' + unit); + + break; + default: + unknownList.push(nodeName); + break; + } +} + +function createGraphical() { + var tabArea = $('#graphTab'); + var selectNodeDiv = $('
                          '); + var temp = 0; + for (var i in selectNode) { + temp++; + break; + } + + // There is no selected LPAR, show the info bar + if (temp == 0) { + tabArea.append(createInfoBar('Hover over a CEC and select the LPARs to do operations against.')); + } else { + // Show selected LPARs + updateSelectNodeDiv(); + } + + // Add buttons + tabArea.append(createActionMenu()); + tabArea.append(selectNodeDiv); + createSystempGraphical(bpaList, fspList, tabArea); + createBladeGraphical(bladeList, tabArea); + createSystemxGraphical(rackList, tabArea); + addUnknownGraphical(unknownList, tabArea); +} + +/** + * Create the physical/graphical layout for System p machines + * + * @param bpa All BPA and their related FSPs + * @param fsp All FSP and their related LPARs + * @param area The element to append graphical layout + */ +function createSystempGraphical(bpa, fsp, area) { + var usedFsp = new Object(); + var graphTable = $('
                          '); + var elementNum = 0; + var row = null; + var showFlag = false; + + // There is a node in the BPA list, so show add the title and show all frames + for (var bpaName in bpa) { + showFlag = true; + $('#graphTab').append('system p
                          '); + $('#graphTab').append(graphTable); + break; + } + + for (var bpaName in bpa) { + if (0 == elementNum % 3) { + row = $(''); + graphTable.append(row); + } + + elementNum++; + + var td = $(''); + var frameDiv = $('
                          '); + frameDiv.append('
                          '); + + // For P7-IH, all the CECs are insert into the frame from bottom to up, + // so we have to show the CECs same as the physical layout + var tempBlankDiv = $('
                          '); + var tempHeight = 0; + for (var fspIndex in bpa[bpaName]) { + var fspName = bpa[bpaName][fspIndex]; + usedFsp[fspName] = 1; + + // This is the P7-IH, we should add the blank at the top + if ((0 == fspIndex) && ('9125-F2C' == fsp[fspName]['mtm'])) { + frameDiv.append(tempBlankDiv); + } + + frameDiv.append(createFspDiv(fspName, fsp[fspName]['mtm'], fsp)); + frameDiv.append(createFspTip(fspName, fsp[fspName]['mtm'], fsp)); + + tempHeight += calculateBlank(fsp[fspName]['mtm']); + } + + // tempHeight is the total height for all CECs, so we should minus BPA div + // height and CEC div heights + tempHeight = 428 - tempHeight; + tempBlankDiv.css('height', tempHeight); + td.append(frameDiv); + row.append(td); + } + + // Find the single FSP and sort descend by units + var singleFsp = new Array(); + for (var fspName in fsp) { + if (usedFsp[fspName]) { + continue; + } + + singleFsp.push([ fspName, fsp[fspName]['mtm'] ]); + } + + // If there is no frame, we should check if there is single CEC and show + // the title and add node area + if (!showFlag) { + for (var fspIndex in singleFsp) { + $('#graphTab').append('system p
                          '); + $('#graphTab').append(graphTable); + break; + } + } + + singleFsp.sort(function(a, b) { + var unitNumA = 4; + var unitNumB = 4; + if (hardwareInfo[a[1]]) { + unitNumA = hardwareInfo[a[1]][1]; + } + + if (hardwareInfo[b[1]]) { + unitNumB = hardwareInfo[b[1]][1]; + } + + return (unitNumB - unitNumA); + }); + + elementNum = 0; + for (var fspIndex in singleFsp) { + var fspName = singleFsp[fspIndex][0]; + if (0 == elementNum % 3) { + row = $(''); + graphTable.append(row); + } + elementNum++; + + var td = $(''); + td.append(createFspDiv(fspName, fsp[fspName]['mtm'], fsp)); + td.append(createFspTip(fspName, fsp[fspName]['mtm'], fsp)); + row.append(td); + } + + $('.tooltip input[type = checkbox]').bind('click', function() { + var lparName = $(this).attr('name'); + if ('' == lparName) { + return; + } + if (true == $(this).attr('checked')) { + changeNode(lparName, 'select'); + } else { + changeNode(lparName, 'unselect'); + } + + updateSelectNodeDiv(); + }); + + $('.fspDiv2, .fspDiv4, .fspDiv42').tooltip({ + position : "center right", + relative : true, + offset : [ 10, -40 ], + effect : "fade", + opacity : 0.9 + }); + + $('.tooltip a').bind('click', function() { + var lparName = $(this).html(); + $('#nodesDatatable #' + lparName).trigger('click'); + }); + + $('.fspDiv2, .fspDiv4, .fspDiv42').bind('click', function() { + var fspName = $(this).attr('value'); + var selectCount = 0; + for (var lparIndex in fspList[fspName]['children']) { + var lparName = fspList[fspName]['children'][lparIndex]; + if (selectNode[lparName]) { + selectCount++; + } + } + + // All LPARs are selected, so unselect nodes + if (selectCount == fspList[fspName]['children'].length) { + for (var lparIndex in fspList[fspName]['children']) { + var lparName = fspList[fspName]['children'][lparIndex]; + changeNode(lparName, 'unselect'); + } + } + + // No selected LPARs on the cec, so add all LPARs into selectNode hash + else { + for (var lparIndex in fspList[fspName]['children']) { + var lparName = fspList[fspName]['children'][lparIndex]; + changeNode(lparName, 'select'); + } + } + + updateSelectNodeDiv(); + }); + + $('.fspCheckbox').bind('click', function() { + var itemName = $(this).attr('name'); + name = itemName.substr(6); + + if ($(this).attr('checked')) { + selectNode[name] = 1; + } else { + delete selectNode[name]; + } + + updateSelectNodeDiv(); + }); +} + +/** + * Create the physical/graphical layout for blades + * + * @param blades The blade list in global + * @param area The element to append the graphical layout + */ +function createBladeGraphical(blades, area) { + var graphTable = $('
                          '); + var mpa = ''; + var bladeName = ''; + var index = 0; + var mpaNumber = 0; + var row; + var showFlag = false; + + // Only show the title and nodes when there are blade in the blade list + for (mpa in blades) { + showFlag = true; + break; + } + + if (showFlag) { + $('#graphTab').append('Blade
                          '); + $('#graphTab').append(graphTable); + } + // If there is no blade node, return directly + else { + return; + } + + for (mpa in blades) { + var tempArray = new Array(14); + var bladeInfo = new Array(); + var unit = 0; + if (0 == mpaNumber % 3) { + row = $(''); + graphTable.append(row); + } + + mpaNumber++; + + var td = $(''); + var chasisDiv = $('
                          '); + + // Fill the array with blade information, to create the empty slot + for (index in blades[mpa]) { + bladeInfo = blades[mpa][index].split(','); + unit = parseInt(bladeInfo[1]); + tempArray[unit - 1] = bladeInfo[0]; + + } + + // Draw the blades and empty slot in chasis + for (index = 0; index < 14; index++) { + if (tempArray[index]) { + bladeName = tempArray[index]; + chasisDiv.append('
                          '); + } else { + chasisDiv.append('
                          '); + } + } + + td.append(chasisDiv); + row.append(td); + } + +} + +/** + * Create the physical/graphical layout for System x machines + * + * @param xnodes The system x node list in global + * @param area The element to append graphical layout + */ +function createSystemxGraphical(xnodes, area) { + var graphTable = $('
                          '); + var xnodename = ''; + var index = 0; + var rack = ''; + var row; + var xNodeCount = 0; + var showflag = false; + + // Only the title and System x node when there are x nodes in the list + for (rack in rackList) { + showflag = true; + break; + } + + if (showflag) { + $('#graphTab').append('system x
                          '); + $('#graphTab').append(graphTable); + } + // There is nothing to show, return directly + else { + return; + } + + for (rack in rackList) { + for (index in rackList[rack]) { + var xNodeName = rackList[rack][index]; + if (0 == xNodeCount % 3) { + row = $(''); + graphTable.append(row); + } + xNodeCount++; + var td = $(''); + var xNodeDiv = '
                          '; + td.append(xNodeDiv); + row.append(td); + } + } +} + +function addUnknownGraphical(unknownNodes, tab) { + // Do not continue if no nodes were found + if (unknownNodes.length < 1) + return; + + var list = ""; + tab.append('
                          '); + for (var index in unknownNodes) { + list += unknownNodes[index] + ', '; + + } + + // Delete last comma + list = list.substr(0, list.length - 2); + tab.append(list); +} + +/** + * Update the LPARs background in CEC, LPAR area and selectNode + */ +function updateSelectNodeDiv() { + var temp = 0; + $('#selectNodeDiv').empty(); + + // Add buttons + if (selectNode.length) { + $('#selectNodeDiv').append('Nodes: '); + for (var lparName in selectNode) { + $('#selectNodeDiv').append(lparName + ' '); + temp++; + if (temp > 6) { + $('#selectNodeDiv').append('...'); + break; + } + } + } +} + +/** + * Create the action menu + */ +function createActionMenu() { + // Create action bar + var actionBar = $('
                          ').css("width", "400px"); + + // Power on + var powerOnLnk = $('Power on'); + powerOnLnk.click(function() { + var tgtNodes = getSelectNodes(); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rpower', + tgt : tgtNodes, + args : 'on', + msg : '' + } + }); + }); + + // Power off + var powerOffLnk = $('Power off'); + powerOffLnk.click(function() { + var tgtNodes = getSelectNodes(); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rpower', + tgt : tgtNodes, + args : 'off', + msg : '' + } + }); + }); + + // Delete + var deleteLnk = $('Delete'); + deleteLnk.click(function() { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadDeletePage(tgtNodes); + } + }); + + // Unlock + var unlockLnk = $('Unlock'); + unlockLnk.click(function() { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadUnlockPage(tgtNodes); + } + }); + + // Run script + var scriptLnk = $('Run script'); + scriptLnk.click(function() { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadScriptPage(tgtNodes); + } + }); + + // Update + var updateLnk = $('Update'); + updateLnk.click(function() { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadUpdatenodePage(tgtNodes); + } + }); + + // Set boot state + var setBootStateLnk = $('Set boot state'); + setBootStateLnk.click(function() { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadNodesetPage(tgtNodes); + } + }); + + // Boot to network + var boot2NetworkLnk = $('Boot to network'); + boot2NetworkLnk.click(function() { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadNetbootPage(tgtNodes); + } + }); + + // Remote console + var rconLnk = $('Open console'); + rconLnk.bind('click', function(event) { + var tgtNodes = getSelectNodes(); + if (tgtNodes) { + loadRconsPage(tgtNodes); + } + }); + + // Edit properties + var editProps = $('Edit properties'); + editProps.bind('click', function(event) { + for (var node in selectNode) { + loadEditPropsPage(node); + } + }); + + // Actions + var actionsLnk = 'Actions'; + var actsMenu = createMenu([ deleteLnk, powerOnLnk, powerOffLnk, scriptLnk ]); + + // Configurations + var configLnk = 'Configuration'; + var configMenu = createMenu([ unlockLnk, updateLnk, editProps ]); + + // Provision + var provLnk = 'Provision'; + var provMenu = createMenu([ boot2NetworkLnk, setBootStateLnk, rconLnk ]); + + // Create an action menu + var actionsMenu = createMenu([ [ actionsLnk, actsMenu ], + [ configLnk, configMenu ], [ provLnk, provMenu ] ]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + actionBar.css('margin-top', '10px'); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + return actionBar; +} + +/** + * Create the physical/graphical layout + */ +function createFspDiv(fspName, mtm, fsp) { + // Create FSP title + var lparStatusRow = ''; + var temp = ''; + + for (var lparIndex in fsp[fspName]['children']) { + // Show 8 lpars on one cec at most. + if (lparIndex >= 8) { + break; + } + + var lparName = fsp[fspName]['children'][lparIndex]; + var color = statusMap(lparList[lparName]); + lparStatusRow += ''; + } + + // Select the backgroud + var divClass = ''; + if ('' == mtm) { + temp = '8231-E2B'; + } else { + temp = mtm; + } + + if (!hardwareInfo[temp]){ + hardwareInfo[temp] = ['unkown', 2]; + } + + if (hardwareInfo[temp][1]) { + divClass += 'fspDiv' + hardwareInfo[temp][1]; + } else { + divClass += 'fspDiv4'; + } + + // Create return value + var retHtml = ''; + retHtml += '
                          '; + retHtml += '
                          ' + lparStatusRow + + '
                          '; + return retHtml; +} + +/** + * Create the physical/graphical FSP tooltip which is used to select the LPAR + */ +function createFspTip(fspName, mtm, fsp) { + var tip = $('
                          '); + var tempTable = $('
                          '); + var temp = ''; + if ('' == mtm) { + temp = 'unkown'; + } else { + temp = mtm; + } + + if (hardwareInfo[temp]) { + tip.append('

                          ' + fspName + '(' + hardwareInfo[temp][0] + ')


                          '); + } else { + tip.append('

                          ' + fspName + '


                          '); + } + + for (var lparIndex in fsp[fspName]['children']) { + var lparName = fsp[fspName]['children'][lparIndex]; + var color = statusMap(lparList[lparName]); + var row = ''; + row += '' + lparName + ''; + row += '' + lparList[lparName] + ''; + tempTable.append(row); + } + + tip.append(tempTable); + return tip; +} +/** + * Map the LPAR status into a color + * + * @param status LPAR status in nodelist table + * @return Corresponding color name + */ +function statusMap(status) { + var color = 'gainsboro'; + + switch (status) { + case 'alive': + case 'ready': + case 'pbs': + case 'sshd': + case 'booting': + case 'booted': + case 'ping': + color = 'green'; + break; + case 'noping': + case 'unreachable': + color = 'red'; + break; + default: + color = 'grey'; + break; + } + + return color; +} + +/** + * Select all LPAR checkboxes + */ +function selectAllLpars(checkbox) { + var temp = checkbox.attr('checked'); + $('#selectNodeTable input[type = checkbox]').attr('checked', temp); +} + +/** + * Export all LPAR names from selectNode + * + * @return lpars' string + */ +function getSelectNodes() { + var ret = ''; + for (var lparName in selectNode) { + ret += lparName + ','; + } + + return ret.substring(0, ret.length - 1); +} + +/** + * When the node is selected or unselected, update the area on CEC, update + * the global list, and update the tooltip table + */ +function changeNode(lparName, status) { + var imgUrl = ''; + var checkFlag = true; + if ('select' == status) { + selectNode[lparName] = 1; + imgUrl = 'url(images/nodes/s-' + statusMap(lparList[lparName]) + + '.gif)'; + checkFlag = true; + } else { + delete selectNode[lparName]; + imgUrl = 'url(images/nodes/' + statusMap(lparList[lparName]) + '.gif)'; + checkFlag = false; + } + $('#' + lparName + 'status').css('background-image', imgUrl); + $('.tooltip input[name="' + lparName + '"]').attr('checked', checkFlag); +} + +/** + * The P7-IH's CECs are insert from bottom to up, so we had to calculate the blank height + * + * @return Height for the CEC + */ +function calculateBlank(mtm) { + if ('' == mtm) { + return 24; + } + + if (!hardwareInfo[mtm]) { + return 24; + } + + switch (hardwareInfo[mtm][1]) { + case 1: + return 13; + break; + case 2: + return 24; + break; + case 4: + return 47; + break; + default: + return 0; + break; + } } \ No newline at end of file diff --git a/xCAT-UI/js/nodes/rnetboot.js b/xCAT-UI/js/nodes/rnetboot.js index c745c8922..7f03e069b 100644 --- a/xCAT-UI/js/nodes/rnetboot.js +++ b/xCAT-UI/js/nodes/rnetboot.js @@ -1,220 +1,223 @@ -/** - * Load netboot page - * - * @param tgtNodes Targets to run rnetboot against - */ -function loadNetbootPage(tgtNodes) { - // Get node OS - var osHash = new Object(); - var nodes = tgtNodes.split(','); - for (var i in nodes) { - var os = getNodeAttr(nodes[i], 'os'); - var osBase = os.match(/[a-zA-Z]+/); - if (osBase) { - nodes[osBase] = 1; - } - } - - // Get nodes tab - var tab = getNodesTab(); - - // Generate new tab ID - var inst = 0; - var newTabId = 'netbootTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = 'netbootTab' + inst; - } - - // Create netboot form - var netbootForm = $('
                          '); - - // Create status bar - var statBarId = 'netbootStatusBar' + inst; - var statusBar = createStatusBar(statBarId).hide(); - - // Create loader - var loader = createLoader('netbootLoader'); - statusBar.find('div').append(loader); - - // Create info bar - var infoBar = createInfoBar('Cause the range of nodes to boot to network'); - netbootForm.append(statusBar, infoBar); - - // Create VM fieldset - var vmFS = $('
                          '); - var vmLegend = $('Virtual Machine'); - vmFS.append(vmLegend); - netbootForm.append(vmFS); - - var vmAttr = $('
                          '); - vmFS.append($('
                          ')); - vmFS.append(vmAttr); - - // Create options fieldset - var optionsFS = $('
                          '); - var optionsLegend = $('Options'); - optionsFS.append(optionsLegend); - netbootForm.append(optionsFS); - - var optionsAttr = $('
                          '); - optionsFS.append($('
                          ')); - optionsFS.append(optionsAttr); - - // Create target node or group input - var target = $('
                          '); - vmAttr.append(target); - - // Create options - var optsLabel = $(''); - var optsList = $('
                            '); - optionsAttr.append(optsList); - - // Create boot order checkbox - var opt = $('
                          • '); - var bootOrderChkBox = $(''); - opt.append(bootOrderChkBox); - opt.append('Set the boot device order'); - optsList.append(opt); - // Create boot order input - var bootOrder = $('
                          • '); - bootOrder.hide(); - optsList.append(bootOrder); - - // Create force reboot checkbox - optsList.append('
                          • Force reboot
                          • '); - // Create force shutdown checkbox - optsList.append('
                          • Force immediate shutdown of the partition
                          • '); - if (osHash['AIX']) { - // Create iscsi dump checkbox - optsList.append('
                          • Do a iscsi dump on AIX
                          • '); - } - - // Show boot order when checkbox is checked - bootOrderChkBox.bind('click', function(event) { - if ($(this).is(':checked')) { - bootOrder.show(); - } else { - bootOrder.hide(); - } - }); - - // Determine plugin - var tmp = tgtNodes.split(','); - for ( var i = 0; i < tmp.length; i++) { - var mgt = getNodeAttr(tmp[i], 'mgt'); - // If it is zvm - if (mgt == 'zvm') { - // Add IPL input - optsList.append('
                            '); - break; - } - } - - // Generate tooltips - netbootForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events : { - def : "mouseover,mouseout", - input : "mouseover,mouseout", - widget : "focus mouseover,blur mouseout", - tooltip : "mouseover,mouseout" - } - }); - - /** - * Ok - */ - var okBtn = createButton('Ok'); - okBtn.css({ - 'width': '80px', - 'display': 'block' - }); - okBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); - - // Check inputs - var ready = true; - var inputs = $("#" + newTabId + " input[type='text']:visible"); - for ( var i = 0; i < inputs.length; i++) { - if (!inputs.eq(i).val()) { - inputs.eq(i).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - inputs.eq(i).css('border', 'solid #BDBDBD 1px'); - } - } - - // Generate arguments - var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); - var optStr = ''; - var opt; - for ( var i = 0; i < chkBoxes.length; i++) { - opt = chkBoxes.eq(i).attr('name'); - optStr += '-' + opt; - - // If it is the boot order - if (opt == 's') { - // Get the boot order - optStr += ';' + $('#' + newTabId + ' input[name=bootOrder]').val(); - } - - // Append ; to end of string - if (i < (chkBoxes.length - 1)) { - optStr += ';'; - } - } - - // If no inputs are empty - if (ready) { - // Get nodes - var tgts = $('#' + newTabId + ' input[name=target]').val(); - - // Get IPL address - var ipl = $('#' + newTabId + ' input[name=ipl]'); - if (ipl) { - optStr += 'ipl=' + ipl.val(); - } - - // Disable all inputs and Ok button - $('#' + newTabId + ' input').attr('disabled', 'disabled'); - $(this).attr('disabled', 'true'); - - /** - * (1) Boot to network - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rnetboot', - tgt : tgts, - args : optStr, - msg : 'out=' + statBarId + ';cmd=rnetboot;tgt=' + tgts - }, - - success : updateStatusBar - }); - - // Show status bar - statusBar.show(); - } else { - // Show warning message - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this).parents('.ui-tabs-panel')); - } - }); - netbootForm.append(okBtn); - - // Append to discover tab - tab.add(newTabId, 'Boot', netbootForm, true); - - // Select new tab - tab.select(newTabId); +/** + * Load netboot page + * + * @param tgtNodes Targets to run rnetboot against + */ +function loadNetbootPage(tgtNodes) { + // Get node OS + var osHash = new Object(); + var nodes = tgtNodes.split(','); + for (var i in nodes) { + var os = getNodeAttr(nodes[i], 'os'); + var osBase = os.match(/[a-zA-Z]+/); + if (osBase) { + nodes[osBase] = 1; + } + } + + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'netbootTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'netbootTab' + inst; + } + + // Create netboot form + var netbootForm = $('
                            '); + + // Create status bar + var statBarId = 'netbootStatusBar' + inst; + var statusBar = createStatusBar(statBarId).hide(); + + // Create loader + var loader = createLoader('netbootLoader'); + statusBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Cause the range of nodes to boot to network'); + netbootForm.append(statusBar, infoBar); + + // Create VM fieldset + var vmFS = $('
                            '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + netbootForm.append(vmFS); + + var vmAttr = $('
                            '); + vmFS.append($('
                            ')); + vmFS.append(vmAttr); + + // Create options fieldset + var optionsFS = $('
                            '); + var optionsLegend = $('Options'); + optionsFS.append(optionsLegend); + netbootForm.append(optionsFS); + + var optionsAttr = $('
                            '); + optionsFS.append($('
                            ')); + optionsFS.append(optionsAttr); + + // Create target node or group input + var target = $('
                            '); + vmAttr.append(target); + + // Create options + var optsLabel = $(''); + var optsList = $('
                              '); + optionsAttr.append(optsList); + + // Create boot order checkbox + var opt = $('
                            • '); + var bootOrderChkBox = $(''); + opt.append(bootOrderChkBox); + opt.append('Set the boot device order'); + optsList.append(opt); + // Create boot order input + var bootOrder = $('
                            • '); + bootOrder.hide(); + optsList.append(bootOrder); + + // Create force reboot checkbox + optsList.append('
                            • Force reboot
                            • '); + // Create force shutdown checkbox + optsList.append('
                            • Force immediate shutdown of the partition
                            • '); + if (osHash['AIX']) { + // Create iscsi dump checkbox + optsList.append('
                            • Do a iscsi dump on AIX
                            • '); + } + + // Show boot order when checkbox is checked + bootOrderChkBox.bind('click', function(event) { + if ($(this).is(':checked')) { + bootOrder.show(); + } else { + bootOrder.hide(); + } + }); + + // Determine plugin + var tmp = tgtNodes.split(','); + for ( var i = 0; i < tmp.length; i++) { + var mgt = getNodeAttr(tmp[i], 'mgt'); + // If it is zvm + if (mgt == 'zvm') { + // Add IPL input + optsList.append('
                              '); + break; + } + } + + // Generate tooltips + netbootForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Ok + */ + var okBtn = createButton('Ok'); + okBtn.css({ + 'width': '80px', + 'display': 'block' + }); + okBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // Check inputs + var ready = true; + var inputs = $("#" + newTabId + " input[type='text']:visible"); + for ( var i = 0; i < inputs.length; i++) { + if (!inputs.eq(i).val()) { + inputs.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + inputs.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + // Generate arguments + var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); + var optStr = ''; + var opt; + for ( var i = 0; i < chkBoxes.length; i++) { + opt = chkBoxes.eq(i).attr('name'); + optStr += '-' + opt; + + // If it is the boot order + if (opt == 's') { + // Get the boot order + optStr += ';' + $('#' + newTabId + ' input[name=bootOrder]').val(); + } + + // Append ; to end of string + if (i < (chkBoxes.length - 1)) { + optStr += ';'; + } + } + + // If no inputs are empty + if (ready) { + // Get nodes + var tgts = $('#' + newTabId + ' input[name=target]').val(); + + // Get IPL address + var ipl = $('#' + newTabId + ' input[name=ipl]'); + if (ipl) { + optStr += 'ipl=' + ipl.val(); + } + + // Disable all inputs and Ok button + $('#' + newTabId + ' input').attr('disabled', 'disabled'); + $(this).attr('disabled', 'true'); + + /** + * (1) Boot to network + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rnetboot', + tgt : tgts, + args : optStr, + msg : 'out=' + statBarId + ';cmd=rnetboot;tgt=' + tgts + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + + // Show status bar + statusBar.show(); + } else { + // Show warning message + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this).parents('.ui-tabs-panel')); + } + }); + netbootForm.append(okBtn); + + // Append to discover tab + tab.add(newTabId, 'Boot', netbootForm, true); + + // Select new tab + tab.select(newTabId); } \ No newline at end of file diff --git a/xCAT-UI/js/nodes/rscan.js b/xCAT-UI/js/nodes/rscan.js index 5c9585274..06fdbff09 100644 --- a/xCAT-UI/js/nodes/rscan.js +++ b/xCAT-UI/js/nodes/rscan.js @@ -1,171 +1,174 @@ -/** - * Load rscan page - * - * @param tgtNodes Targets to run rscan against - */ -function loadRscanPage(tgtNodes) { - // Get node OS - var osHash = new Object(); - var nodes = tgtNodes.split(','); - for (var i in nodes) { - var os = getNodeAttr(nodes[i], 'os'); - var osBase = os.match(/[a-zA-Z]+/); - if (osBase) { - nodes[osBase] = 1; - } - } - - // Get nodes tab - var tab = getNodesTab(); - - // Generate new tab ID - var inst = 0; - var newTabId = 'rscanTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = 'rscanTab' + inst; - } - - // Create rscan form - var rscanForm = $('
                              '); - - // Create status bar - var statBarId = 'rscanStatusBar' + inst; - var statBar = createStatusBar(statBarId).hide(); - - // Create loader - var loader = createLoader('rscanLoader'); - statBar.find('div').append(loader); - - // Create info bar - var infoBar = createInfoBar('Collects node information from one or more hardware control points'); - rscanForm.append(statBar, infoBar); - - // Create VM fieldset - var vmFS = $('
                              '); - var vmLegend = $('Virtual Machine'); - vmFS.append(vmLegend); - rscanForm.append(vmFS); - - var vmAttr = $('
                              '); - vmFS.append($('
                              ')); - vmFS.append(vmAttr); - - // Create options fieldset - var optionsFS = $('
                              '); - var optionsLegend = $('Options'); - optionsFS.append(optionsLegend); - rscanForm.append(optionsFS); - - var optionsAttr = $('
                              '); - optionsFS.append($('
                              ')); - optionsFS.append(optionsAttr); - - // Create target node or group input - var target = $('
                              '); - vmAttr.append(target); - - // Create options - var optsList = $('
                                '); - optionsAttr.append(optsList); - - optsList.append('
                              • Updates and then prints out node definitions in the xCAT database for CEC/BPA
                              • '); - optsList.append('
                              • Writes output to xCAT database
                              • '); - optsList.append('
                              • XML format
                              • '); - optsList.append('
                              • Stanza formated output
                              • '); - - // Generate tooltips - rscanForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events : { - def : "mouseover,mouseout", - input : "mouseover,mouseout", - widget : "focus mouseover,blur mouseout", - tooltip : "mouseover,mouseout" - } - }); - - /** - * Ok - */ - var okBtn = createButton('Ok'); - okBtn.css({ - 'width': '80px', - 'display': 'block' - }); - okBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); - - // Check inputs - var ready = true; - var inputs = $("#" + newTabId + " input[type='text']"); - for ( var i = 0; i < inputs.length; i++) { - if (!inputs.eq(i).val()) { - inputs.eq(i).css('border', 'solid #FF0000 1px'); - ready = false; - } else { - inputs.eq(i).css('border', 'solid #BDBDBD 1px'); - } - } - - // Generate arguments - var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); - var optStr = ''; - var opt; - for ( var i = 0; i < chkBoxes.length; i++) { - opt = chkBoxes.eq(i).attr('name'); - optStr += '-' + opt; - - // Append ; to end of string - if (i < (chkBoxes.length - 1)) { - optStr += ';'; - } - } - - // If no inputs are empty - if (ready) { - // Get nodes - var tgts = $('#' + newTabId + ' input[name=target]').val(); - - // Disable all inputs and Ok button - $('#' + newTabId + ' input').attr('disabled', 'disabled'); - $(this).attr('disabled', 'true'); - - /** - * (1) Scan - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rscan', - tgt : tgts, - args : optStr, - msg : 'out=' + statBarId + ';cmd=rscan;tgt=' + tgts - }, - - success : updateStatusBar - }); - - // Show status bar - statBar.show(); - } else { - // Show warning message - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this).parents('.ui-tabs-panel')); - } - }); - rscanForm.append(okBtn); - - // Append to discover tab - tab.add(newTabId, 'Scan', rscanForm, true); - - // Select new tab - tab.select(newTabId); +/** + * Load rscan page + * + * @param tgtNodes Targets to run rscan against + */ +function loadRscanPage(tgtNodes) { + // Get node OS + var osHash = new Object(); + var nodes = tgtNodes.split(','); + for (var i in nodes) { + var os = getNodeAttr(nodes[i], 'os'); + var osBase = os.match(/[a-zA-Z]+/); + if (osBase) { + nodes[osBase] = 1; + } + } + + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'rscanTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'rscanTab' + inst; + } + + // Create rscan form + var rscanForm = $('
                                '); + + // Create status bar + var statBarId = 'rscanStatusBar' + inst; + var statBar = createStatusBar(statBarId).hide(); + + // Create loader + var loader = createLoader('rscanLoader'); + statBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Collects node information from one or more hardware control points'); + rscanForm.append(statBar, infoBar); + + // Create VM fieldset + var vmFS = $('
                                '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + rscanForm.append(vmFS); + + var vmAttr = $('
                                '); + vmFS.append($('
                                ')); + vmFS.append(vmAttr); + + // Create options fieldset + var optionsFS = $('
                                '); + var optionsLegend = $('Options'); + optionsFS.append(optionsLegend); + rscanForm.append(optionsFS); + + var optionsAttr = $('
                                '); + optionsFS.append($('
                                ')); + optionsFS.append(optionsAttr); + + // Create target node or group input + var target = $('
                                '); + vmAttr.append(target); + + // Create options + var optsList = $('
                                  '); + optionsAttr.append(optsList); + + optsList.append('
                                • Updates and then prints out node definitions in the xCAT database for CEC/BPA
                                • '); + optsList.append('
                                • Writes output to xCAT database
                                • '); + optsList.append('
                                • XML format
                                • '); + optsList.append('
                                • Stanza formated output
                                • '); + + // Generate tooltips + rscanForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Ok + */ + var okBtn = createButton('Ok'); + okBtn.css({ + 'width': '80px', + 'display': 'block' + }); + okBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + + // Check inputs + var ready = true; + var inputs = $("#" + newTabId + " input[type='text']"); + for ( var i = 0; i < inputs.length; i++) { + if (!inputs.eq(i).val()) { + inputs.eq(i).css('border', 'solid #FF0000 1px'); + ready = false; + } else { + inputs.eq(i).css('border', 'solid #BDBDBD 1px'); + } + } + + // Generate arguments + var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); + var optStr = ''; + var opt; + for ( var i = 0; i < chkBoxes.length; i++) { + opt = chkBoxes.eq(i).attr('name'); + optStr += '-' + opt; + + // Append ; to end of string + if (i < (chkBoxes.length - 1)) { + optStr += ';'; + } + } + + // If no inputs are empty + if (ready) { + // Get nodes + var tgts = $('#' + newTabId + ' input[name=target]').val(); + + // Disable all inputs and Ok button + $('#' + newTabId + ' input').attr('disabled', 'disabled'); + $(this).attr('disabled', 'true'); + + /** + * (1) Scan + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rscan', + tgt : tgts, + args : optStr, + msg : 'out=' + statBarId + ';cmd=rscan;tgt=' + tgts + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + + // Show status bar + statBar.show(); + } else { + // Show warning message + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this).parents('.ui-tabs-panel')); + } + }); + rscanForm.append(okBtn); + + // Append to discover tab + tab.add(newTabId, 'Scan', rscanForm, true); + + // Select new tab + tab.select(newTabId); } \ No newline at end of file diff --git a/xCAT-UI/js/nodes/updatenode.js b/xCAT-UI/js/nodes/updatenode.js index 943972e8e..d0a177a40 100644 --- a/xCAT-UI/js/nodes/updatenode.js +++ b/xCAT-UI/js/nodes/updatenode.js @@ -1,403 +1,409 @@ -/** - * Load updatenode page - * - * @param tgtNodes Targets to run updatenode against - */ -function loadUpdatenodePage(tgtNodes) { - // Get OS images - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'osimage', - msg : '' - }, - - success : setOSImageCookies - }); - - // Get node OS - var osHash = new Object(); - var nodes = tgtNodes.split(','); - for (var i in nodes) { - var os = getNodeAttr(nodes[i], 'os'); - var osBase = os.match(/[a-zA-Z]+/); - if (osBase) { - nodes[osBase] = 1; - } - } - - // Get nodes tab - var tab = getNodesTab(); - - // Generate new tab ID - var inst = 0; - var newTabId = 'updatenodeTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = 'updatenodeTab' + inst; - } - - // Create rscan form - var updatenodeForm = $('
                                  '); - - // Create status bar - var statBarId = 'updatenodeStatusBar' + inst; - var statusBar = createStatusBar(statBarId).hide(); - - // Create loader - var loader = createLoader('updatenodeLoader'); - statusBar.find('div').append(loader); - - // Create info bar - var infoBar = createInfoBar('Update nodes in an xCAT environment'); - updatenodeForm.append(statusBar, infoBar); - - // Create VM fieldset - var vmFS = $('
                                  '); - var vmLegend = $('Virtual Machine'); - vmFS.append(vmLegend); - updatenodeForm.append(vmFS); - - var vmAttr = $('
                                  '); - vmFS.append($('
                                  ')); - vmFS.append(vmAttr); - - // Create options fieldset - var optionsFS = $('
                                  '); - var optionsLegend = $('Options'); - optionsFS.append(optionsLegend); - updatenodeForm.append(optionsFS); - - var optionsAttr = $('
                                  '); - optionsFS.append($('
                                  ')); - optionsFS.append(optionsAttr); - - // Create target node or group input - var tgt = $('
                                  '); - vmAttr.append(tgt); - - // Create options - var optionsList = $('
                                    '); - optionsAttr.append(optionsList); - - // Create update all software checkbox (only AIX) - if (osHash['AIX']) { - var updateAllOption = $('
                                  • '); - var updateAllChkBox = $(''); - updateAllOption.append(updateAllChkBox); - optionsList.append(updateAllOption); - updateAllOption.append('Install or update all software contained in the source directory'); - - // Create source directory input - var allSwScrDirectory = $('
                                  • '); - // Browse server directory and files - var allSWSrcDirBrowse = createButton('Browse'); - allSWSrcDirBrowse.serverBrowser({ - onSelect : function(path) { - $('#allSwSrcDirectory').val(path); - }, - onLoad : function() { - return $('#allSwSrcDirectory').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [ { - text : 'Install', - image : 'desktop.png', - path : '/install' - } ], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - allSwScrDirectory.append(allSWSrcDirBrowse); - allSwScrDirectory.hide(); - optionsList.append(allSwScrDirectory); - - // Show source directory when checked - updateAllChkBox.bind('click', function(event) { - if ($(this).is(':checked')) { - allSwScrDirectory.show(); - } else { - allSwScrDirectory.hide(); - } - }); - } - - // Create update software checkbox - var updateOption = $('
                                  • '); - var updateChkBox = $(''); - optionsList.append(updateOption); - updateOption.append(updateChkBox); - updateOption.append('Update existing software'); - - // Create source directory input - var scrDirectory = $('
                                  • '); - // Browse server directory and files - var srcDirBrowse = createButton('Browse'); - srcDirBrowse.serverBrowser({ - onSelect : function(path) { - $('#srcDirectory').val(path); - }, - onLoad : function() { - return $('#srcDirectory').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [ { - text : 'Install', - image : 'desktop.png', - path : '/install' - } ], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - scrDirectory.append(srcDirBrowse); - scrDirectory.hide(); - optionsList.append(scrDirectory); - - // Create other packages input - var otherPkgs = $('
                                  • '); - otherPkgs.hide(); - optionsList.append(otherPkgs); - - // Create RPM flags input (only AIX) - var aixRpmFlags = $('
                                  • '); - aixRpmFlags.hide(); - optionsList.append(aixRpmFlags); - - // Create installp flags input (only AIX) - var aixInstallPFlags = $('
                                  • '); - aixInstallPFlags.hide(); - optionsList.append(aixInstallPFlags); - - // Create emgr flags input (only AIX) - var aixEmgrFlags = $('
                                  • '); - aixEmgrFlags.hide(); - optionsList.append(aixEmgrFlags); - - // Show flags when checked - updateChkBox.bind('click', function(event) { - if ($(this).is(':checked')) { - scrDirectory.show(); - otherPkgs.show(); - if (osHash['AIX']) { - aixRpmFlags.show(); - aixInstallPFlags.show(); - aixEmgrFlags.show(); - } - } else { - scrDirectory.hide(); - otherPkgs.hide(); - if (osHash['AIX']) { - aixRpmFlags.hide(); - aixInstallPFlags.hide(); - aixEmgrFlags.hide(); - } - } - }); - - // Create postscripts input - var postOption = $('
                                  • '); - var postChkBox = $(''); - optionsList.append(postOption); - postOption.append(postChkBox); - postOption.append('Run postscripts'); - var postscripts = $('
                                  • '); - postscripts.hide(); - optionsList.append(postscripts); - - // Show alternate source directory when checked - postChkBox.bind('click', function(event) { - if ($(this).is(':checked')) { - postscripts.show(); - } else { - postscripts.hide(); - } - }); - optionsList.append('
                                  • Distribute and synchronize files
                                  • '); - optionsList.append('
                                  • Update the ssh keys and host keys for the service nodes and compute nodes
                                  • '); - - // Create update OS checkbox - if (!osHash['AIX']) { - var osOption = $('
                                  • '); - var osChkBox = $(''); - optionsList.append(osOption); - osOption.append(osChkBox); - osOption.append('Update the operating system'); - - var os = $('
                                  • ').hide(); - var osLabel = $(''); - var osInput = $(''); - osInput.one('focus', function(){ - var tmp = $.cookie('xcat_osvers'); - if (tmp) { - // Turn on auto complete - $(this).autocomplete({ - source: tmp.split(',') - }); - } - }); - os.append(osLabel); - os.append(osInput); - optionsList.append(os); - - // Show alternate source directory when checked - osChkBox.bind('click', function(event) { - if ($(this).is(':checked')) { - os.show(); - } else { - os.hide(); - } - }); - } - - // Generate tooltips - updatenodeForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events : { - def : "mouseover,mouseout", - input : "mouseover,mouseout", - widget : "focus mouseover,blur mouseout", - tooltip : "mouseover,mouseout" - } - }); - - /** - * Ok - */ - var updateBtn = createButton('Update'); - updateBtn.css({ - 'width': '80px', - 'display': 'block' - }); - updateBtn.bind('click', function(event) { - // Remove any warning messages - $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); - var ready = true; - - // Generate arguments - var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); - var optionsStr = ''; - var option; - for ( var i = 0; i < chkBoxes.length; i++) { - option = chkBoxes.eq(i).attr('name'); - optionsStr += '-' + option; - - // If update all software is checked - if (option == 'S') { - var srcDir = $('#' + newTabId + ' input[name=allSwSrcDirectory]').val(); - if (srcDir) { - optionsStr += ';-d ' + srcDir; - } - } - - // If update software is checked - if (option == 'S') { - // Get source directory - var srcDirectory = $('#' + newTabId + ' input[name=srcDirectory]').val(); - if (srcDirectory) { - optionsStr += ';-d;' + srcDirectory; - } - - // Get otherpkgs - var otherpkgs = $('#' + newTabId + ' input[name=otherpkgs]').val(); - if (otherpkgs) { - optionsStr += ';otherpkgs=' + otherpkgs; - } - - // Get rpm_flags - var rpm_flags = $('#' + newTabId + ' input[name=rpm_flags]').val(); - if (rpm_flags) { - optionsStr += ';rpm_flags=' + rpm_flags; - } - - // Get installp_flags - var installp_flags = $('#' + newTabId + ' input[name=installp_flags]').val(); - if (installp_flags) { - optionsStr += ';installp_flags=' + installp_flags; - } - - // Get emgr_flags - var emgr_flags = $('#' + newTabId + ' input[name=emgr_flags]').val(); - if (emgr_flags) { - optionsStr += ';emgr_flags=' + emgr_flags; - } - } - - // If postscripts is checked - if (option == 'P') { - // Get postscripts - optionsStr += ';' + $('#' + newTabId + ' input[name=postscripts]').val(); - } - - // If operating system is checked - if (option == 'o') { - // Get the OS - optionsStr += ';' + $('#' + newTabId + ' input[name=os]').val(); - } - - // Append ; to end of string - if (i < (chkBoxes.length - 1)) { - optionsStr += ';'; - } - } - - // If no inputs are empty - if (ready) { - // Get nodes - var tgts = $('#' + newTabId + ' input[name=target]').val(); - - // Disable all inputs and Ok button - $('#' + newTabId + ' input').attr('disabled', 'disabled'); - $(this).attr('disabled', 'true'); - - /** - * (1) Boot to network - */ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'updatenode', - tgt : tgts, - args : optionsStr, - msg : 'out=' + statBarId + ';cmd=updatenode;tgt=' + tgts - }, - - success : updateStatusBar - }); - - // Show status bar - statusBar.show(); - } else { - // Show warning message - var warn = createWarnBar('You are missing some values'); - warn.prependTo($(this).parents('.ui-tabs-panel')); - } - }); - updatenodeForm.append(updateBtn); - - // Append to discover tab - tab.add(newTabId, 'Update', updatenodeForm, true); - - // Select new tab - tab.select(newTabId); +/** + * Load updatenode page + * + * @param tgtNodes Targets to run updatenode against + */ +function loadUpdatenodePage(tgtNodes) { + // Get OS images + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'osimage', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setOSImageCookies(data); + } + }); + + // Get node OS + var osHash = new Object(); + var nodes = tgtNodes.split(','); + for (var i in nodes) { + var os = getNodeAttr(nodes[i], 'os'); + var osBase = os.match(/[a-zA-Z]+/); + if (osBase) { + nodes[osBase] = 1; + } + } + + // Get nodes tab + var tab = getNodesTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'updatenodeTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'updatenodeTab' + inst; + } + + // Create rscan form + var updatenodeForm = $('
                                    '); + + // Create status bar + var statBarId = 'updatenodeStatusBar' + inst; + var statusBar = createStatusBar(statBarId).hide(); + + // Create loader + var loader = createLoader('updatenodeLoader'); + statusBar.find('div').append(loader); + + // Create info bar + var infoBar = createInfoBar('Update nodes in an xCAT environment'); + updatenodeForm.append(statusBar, infoBar); + + // Create VM fieldset + var vmFS = $('
                                    '); + var vmLegend = $('Virtual Machine'); + vmFS.append(vmLegend); + updatenodeForm.append(vmFS); + + var vmAttr = $('
                                    '); + vmFS.append($('
                                    ')); + vmFS.append(vmAttr); + + // Create options fieldset + var optionsFS = $('
                                    '); + var optionsLegend = $('Options'); + optionsFS.append(optionsLegend); + updatenodeForm.append(optionsFS); + + var optionsAttr = $('
                                    '); + optionsFS.append($('
                                    ')); + optionsFS.append(optionsAttr); + + // Create target node or group input + var tgt = $('
                                    '); + vmAttr.append(tgt); + + // Create options + var optionsList = $('
                                      '); + optionsAttr.append(optionsList); + + // Create update all software checkbox (only AIX) + if (osHash['AIX']) { + var updateAllOption = $('
                                    • '); + var updateAllChkBox = $(''); + updateAllOption.append(updateAllChkBox); + optionsList.append(updateAllOption); + updateAllOption.append('Install or update all software contained in the source directory'); + + // Create source directory input + var allSwScrDirectory = $('
                                    • '); + // Browse server directory and files + var allSWSrcDirBrowse = createButton('Browse'); + allSWSrcDirBrowse.serverBrowser({ + onSelect : function(path) { + $('#allSwSrcDirectory').val(path); + }, + onLoad : function() { + return $('#allSwSrcDirectory').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [ { + text : 'Install', + image : 'desktop.png', + path : '/install' + } ], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + allSwScrDirectory.append(allSWSrcDirBrowse); + allSwScrDirectory.hide(); + optionsList.append(allSwScrDirectory); + + // Show source directory when checked + updateAllChkBox.bind('click', function(event) { + if ($(this).is(':checked')) { + allSwScrDirectory.show(); + } else { + allSwScrDirectory.hide(); + } + }); + } + + // Create update software checkbox + var updateOption = $('
                                    • '); + var updateChkBox = $(''); + optionsList.append(updateOption); + updateOption.append(updateChkBox); + updateOption.append('Update existing software'); + + // Create source directory input + var scrDirectory = $('
                                    • '); + // Browse server directory and files + var srcDirBrowse = createButton('Browse'); + srcDirBrowse.serverBrowser({ + onSelect : function(path) { + $('#srcDirectory').val(path); + }, + onLoad : function() { + return $('#srcDirectory').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [ { + text : 'Install', + image : 'desktop.png', + path : '/install' + } ], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + scrDirectory.append(srcDirBrowse); + scrDirectory.hide(); + optionsList.append(scrDirectory); + + // Create other packages input + var otherPkgs = $('
                                    • '); + otherPkgs.hide(); + optionsList.append(otherPkgs); + + // Create RPM flags input (only AIX) + var aixRpmFlags = $('
                                    • '); + aixRpmFlags.hide(); + optionsList.append(aixRpmFlags); + + // Create installp flags input (only AIX) + var aixInstallPFlags = $('
                                    • '); + aixInstallPFlags.hide(); + optionsList.append(aixInstallPFlags); + + // Create emgr flags input (only AIX) + var aixEmgrFlags = $('
                                    • '); + aixEmgrFlags.hide(); + optionsList.append(aixEmgrFlags); + + // Show flags when checked + updateChkBox.bind('click', function(event) { + if ($(this).is(':checked')) { + scrDirectory.show(); + otherPkgs.show(); + if (osHash['AIX']) { + aixRpmFlags.show(); + aixInstallPFlags.show(); + aixEmgrFlags.show(); + } + } else { + scrDirectory.hide(); + otherPkgs.hide(); + if (osHash['AIX']) { + aixRpmFlags.hide(); + aixInstallPFlags.hide(); + aixEmgrFlags.hide(); + } + } + }); + + // Create postscripts input + var postOption = $('
                                    • '); + var postChkBox = $(''); + optionsList.append(postOption); + postOption.append(postChkBox); + postOption.append('Run postscripts'); + var postscripts = $('
                                    • '); + postscripts.hide(); + optionsList.append(postscripts); + + // Show alternate source directory when checked + postChkBox.bind('click', function(event) { + if ($(this).is(':checked')) { + postscripts.show(); + } else { + postscripts.hide(); + } + }); + optionsList.append('
                                    • Distribute and synchronize files
                                    • '); + optionsList.append('
                                    • Update the ssh keys and host keys for the service nodes and compute nodes
                                    • '); + + // Create update OS checkbox + if (!osHash['AIX']) { + var osOption = $('
                                    • '); + var osChkBox = $(''); + optionsList.append(osOption); + osOption.append(osChkBox); + osOption.append('Update the operating system'); + + var os = $('
                                    • ').hide(); + var osLabel = $(''); + var osInput = $(''); + osInput.one('focus', function(){ + var tmp = $.cookie('xcat_osvers'); + if (tmp) { + // Turn on auto complete + $(this).autocomplete({ + source: tmp.split(',') + }); + } + }); + os.append(osLabel); + os.append(osInput); + optionsList.append(os); + + // Show alternate source directory when checked + osChkBox.bind('click', function(event) { + if ($(this).is(':checked')) { + os.show(); + } else { + os.hide(); + } + }); + } + + // Generate tooltips + updatenodeForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + /** + * Ok + */ + var updateBtn = createButton('Update'); + updateBtn.css({ + 'width': '80px', + 'display': 'block' + }); + updateBtn.bind('click', function(event) { + // Remove any warning messages + $(this).parents('.ui-tabs-panel').find('.ui-state-error').remove(); + var ready = true; + + // Generate arguments + var chkBoxes = $("#" + newTabId + " input[type='checkbox']:checked"); + var optionsStr = ''; + var option; + for ( var i = 0; i < chkBoxes.length; i++) { + option = chkBoxes.eq(i).attr('name'); + optionsStr += '-' + option; + + // If update all software is checked + if (option == 'S') { + var srcDir = $('#' + newTabId + ' input[name=allSwSrcDirectory]').val(); + if (srcDir) { + optionsStr += ';-d ' + srcDir; + } + } + + // If update software is checked + if (option == 'S') { + // Get source directory + var srcDirectory = $('#' + newTabId + ' input[name=srcDirectory]').val(); + if (srcDirectory) { + optionsStr += ';-d;' + srcDirectory; + } + + // Get otherpkgs + var otherpkgs = $('#' + newTabId + ' input[name=otherpkgs]').val(); + if (otherpkgs) { + optionsStr += ';otherpkgs=' + otherpkgs; + } + + // Get rpm_flags + var rpm_flags = $('#' + newTabId + ' input[name=rpm_flags]').val(); + if (rpm_flags) { + optionsStr += ';rpm_flags=' + rpm_flags; + } + + // Get installp_flags + var installp_flags = $('#' + newTabId + ' input[name=installp_flags]').val(); + if (installp_flags) { + optionsStr += ';installp_flags=' + installp_flags; + } + + // Get emgr_flags + var emgr_flags = $('#' + newTabId + ' input[name=emgr_flags]').val(); + if (emgr_flags) { + optionsStr += ';emgr_flags=' + emgr_flags; + } + } + + // If postscripts is checked + if (option == 'P') { + // Get postscripts + optionsStr += ';' + $('#' + newTabId + ' input[name=postscripts]').val(); + } + + // If operating system is checked + if (option == 'o') { + // Get the OS + optionsStr += ';' + $('#' + newTabId + ' input[name=os]').val(); + } + + // Append ; to end of string + if (i < (chkBoxes.length - 1)) { + optionsStr += ';'; + } + } + + // If no inputs are empty + if (ready) { + // Get nodes + var tgts = $('#' + newTabId + ' input[name=target]').val(); + + // Disable all inputs and Ok button + $('#' + newTabId + ' input').attr('disabled', 'disabled'); + $(this).attr('disabled', 'true'); + + /** + * (1) Boot to network + */ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'updatenode', + tgt : tgts, + args : optionsStr, + msg : 'out=' + statBarId + ';cmd=updatenode;tgt=' + tgts + }, + + success : function(data) { + data = decodeRsp(data); + updateStatusBar(data); + } + }); + + // Show status bar + statusBar.show(); + } else { + // Show warning message + var warn = createWarnBar('You are missing some values'); + warn.prependTo($(this).parents('.ui-tabs-panel')); + } + }); + updatenodeForm.append(updateBtn); + + // Append to discover tab + tab.add(newTabId, 'Update', updatenodeForm, true); + + // Select new tab + tab.select(newTabId); } \ No newline at end of file diff --git a/xCAT-UI/js/provision/images.js b/xCAT-UI/js/provision/images.js index d1a6f174d..fd4967321 100644 --- a/xCAT-UI/js/provision/images.js +++ b/xCAT-UI/js/provision/images.js @@ -1,1435 +1,1462 @@ -/** - * Global variables - */ -var origAttrs = new Object(); // Original image attributes -var defAttrs; // Definable image attributes -var imgTableId = 'imagesDatatable'; // Images datatable ID -var softwareList = { - "rsct" : ["rsct.core.utils", "rsct.core", "src"], - "pe" : ["IBMJava2-142-ppc64-JRE", "ibm_lapi_ip_rh6p", "ibm_lapi_us_rh6p", "IBM_pe_license", "ibm_pe_rh6p", "ppe_pdb_ppc64_rh600", "sci_ppc_32bit_rh600", "sci_ppc_64bit_rh600", "vac.cmp", - "vac.lib", "vac.lic", "vacpp.cmp", "vacpp.help.pdf", "vacpp.lib", "vacpp.man", "vacpp.rte", "vacpp.rte.lnk", "vacpp.samples", "xlf.cmp", "xlf.help.pdf", "xlf.lib", "xlf.lic", "xlf.man", - "xlf.msg.rte", "xlf.rte", "xlf.rte.lnk", "xlf.samples", "xlmass.lib", "xlsmp.lib", "xlsmp.msg.rte", "xlsmp.rte"], - "gpfs" : ["gpfs.base", "gpfs.gpl", "gpfs.gplbin", "gpfs.msg.en_US"], - "essl" : ["essl.3232.rte", "essl.3264.rte", "essl.6464.rte", "essl.common", "essl.license", "essl.man", "essl.msg", "essl.rte", "ibm-java2", "pessl.common", "pessl.license", "pessl.man", - "pessl.msg", "pessl.rte.ppe"], - "loadl" : ["IBMJava2", "LoadL-full-license-RH6", "LoadL-resmgr-full-RH6", "LoadL-scheduler-full-RH6"], - "ganglia" : ["rrdtool", "ganglia", "ganglia-gmetad", "ganglia-gmond"], - "base" : ["createrepo"] -}; - -/** - * Load images page - */ -function loadImagesPage() { - // Set padding for images page - $('#imagesTab').css('padding', '20px 60px'); - - // Get images within the database - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsdef', - tgt : '', - args : '-t;osimage;-l', - msg : '' - }, - - success : loadImages - }); -} - -/** - * Load images within the database - * - * @param data Data returned from HTTP request - */ -function loadImages(data) { - // Data returned - var rsp = data.rsp; - if (rsp[0].indexOf('Could not find any object definitions') > -1) { - rsp = new Array(); - } - - // Image attributes hash - var attrs = new Object(); - // Image attributes - var headers = new Object(); - - // Clear hash table containing image attributes - origAttrs = ''; - - var image; - var args; - for (var i in rsp) { - // Get the image - var pos = rsp[i].indexOf('Object name:'); - if (pos > -1) { - var temp = rsp[i].split(': '); - image = jQuery.trim(temp[1]); - - // Create a hash for the image attributes - attrs[image] = new Object(); - i++; - } - - // Get key and value - args = rsp[i].split('='); - var key = jQuery.trim(args[0]); - var val = jQuery.trim(args[1]); - - // Create a hash table - attrs[image][key] = val; - headers[key] = 1; - } - - // Save attributes in hash table - origAttrs = attrs; - - // Sort headers - var sorted = new Array(); - for (var key in headers) { - sorted.push(key); - } - sorted.sort(); - - // Add column for check box and image name - sorted.unshift('', 'imagename'); - - // Create a datatable - var dTable = new DataTable(imgTableId); - dTable.init(sorted); - - // Go through each image - for (var img in attrs) { - // Create a row - var row = new Array(); - // Create a check box - var checkBx = ''; - // Push in checkbox and image name - row.push(checkBx, img); - - // Go through each header - for (var i = 2; i < sorted.length; i++) { - // Add the node attributes to the row - var key = sorted[i]; - var val = attrs[img][key]; - if (val) { - row.push(val); - } else { - row.push(''); - } - } - - // Add the row to the table - dTable.add(row); - } - - // Clear the tab before inserting the table - $('#imagesTab').children().remove(); - - // Create info bar for images tab - var info = createInfoBar('Double click on a cell to edit. Click outside the table to save changes. Hit the Escape key to ignore changes.'); - $('#imagesTab').append(info); - - /** - * The following actions are available for images: - * copy Linux distribution and edit image properties - */ - - // Copy CD into install directory - var copyCDLnk = $('Copy CD'); - copyCDLnk.click(function() { - openCopyCdDialog(); - }); - - // Generate stateless or statelite image - var generateLnk = $('Generate image'); - generateLnk.click(function() { - loadCreateImage(); - }); - - // Edit image attributes - var editLnk = $('Edit'); - editLnk.click(function() { - var tgtImages = getNodesChecked(imgTableId).split(','); - if (tgtImages) { - for (var i in tgtImages) { - openEditImagePage(tgtImages[i]); - } - } - }); - - // Add a row - var addLnk = $('Add'); - addLnk.click(function() { - openAddImageDialog(); - }); - - // Remove a row - var removeLnk = $('Remove'); - removeLnk.click(function() { - var images = getNodesChecked(imgTableId); - if (images) { - confirmImageDeleteDialog(images); - } - }); - - // Refresh image table - var refreshLnk = $('Refresh'); - refreshLnk.click(function() { - // Get images within the database - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsdef', - tgt : '', - args : '-t;osimage;-l', - msg : '' - }, - - success : loadImages - }); - }); - - // Insert table - $('#imagesTab').append(dTable.object()); - - // Turn table into a datatable - var myDataTable = $('#' + imgTableId).dataTable({ - 'iDisplayLength': 50, - 'bLengthChange': false, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - - // Set datatable width - $('#' + imgTableId + '_wrapper').css({ - 'width': '880px' - }); - - // Actions - var actionBar = $('
                                      ').css("width", "450px"); - var advancedLnk = 'Advanced'; - var advancedMenu = createMenu([copyCDLnk, generateLnk]); - - // Create an action menu - var actionsMenu = createMenu([refreshLnk, addLnk, editLnk, removeLnk, [advancedLnk, advancedMenu]]); - actionsMenu.superfish(); - actionsMenu.css('display', 'inline-block'); - actionBar.append(actionsMenu); - - // Set correct theme for action menu - actionsMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + imgTableId + '_wrapper').prepend(menuDiv); - menuDiv.append(actionBar); - $('#' + imgTableId + '_filter').appendTo(menuDiv); - - /** - * Enable editable columns - */ - - // Do not make 1st or 2nd columns editable - $('#' + imgTableId + ' td:not(td:nth-child(1),td:nth-child(2))').editable( - function(value, settings) { - // Get column index - var colPos = this.cellIndex; - - // Get row index - var dTable = $('#' + imgTableId).dataTable(); - var rowPos = dTable.fnGetPosition(this.parentNode); - - // Update datatable - dTable.fnUpdate(value, rowPos, colPos); - - // Get image name - var image = $(this).parent().find('td:eq(1)').text(); - - // Get table headers - var headers = $('#' + imgTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); - - // Get attribute name - var attrName = jQuery.trim(headers.eq(colPos).text()); - // Get column value - var value = $(this).text(); - // Build argument - var args = attrName + '=' + value; - - // Send command to change image attributes - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chdef', - tgt : '', - args : '-t;osimage;-o;' + image + ';' + args, - msg : 'out=imagesTab;tgt=' + image - }, - - success: showChdefOutput - }); - - return value; - }, { - onblur : 'submit', // Clicking outside editable area submits changes - type : 'textarea', // Input type to use - placeholder: ' ', - event : "dblclick", // Double click and edit - height : '30px' // The height of the text area - }); - - // Get definable node attributes - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'lsdef', - tgt : '', - args : '-t;osimage;-h', - msg : '' - }, - - success : setImageDefAttrs - }); -} - -/** - * Open dialog to confirm deleting image - * - * @param images Comma delimited image names - */ -function confirmImageDeleteDialog(images) { - // Make images list more readable - var dialogId = 'confirmImageRemove'; - var tmp = images.replace(new RegExp(',', 'g'), ', '); - var confirmDialog = $('
                                      ' - + '

                                      Are you sure you want to remove ' + tmp + '?

                                      ' - + '
                                      '); - - // Open dialog to confirm delete - confirmDialog.dialog({ - modal: true, - close: function(){ - $(this).remove(); - }, - title: 'Confirm', - width: 500, - buttons: { - "Ok": function(){ - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Add image to xCAT - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'rmdef', - tgt : '', - args : '-t;osimage;-o;' + images, - msg : dialogId - }, - - success : updateImageDialog - }); - }, - "Cancel": function(){ - $(this).dialog("close"); - } - } - }); -} - -/** - * Open a dialog to add an image - */ -function openAddImageDialog() { - // Create dialog to add image - var dialogId = 'addImage'; - var addImageForm = $('
                                      '); - - // Create info bar - var info = createInfoBar('Provide the following attributes for the image. The image name will be generated based on the attributes you will give.'); - - var imageFS = $('
                                      '); - var imageLegend = $('Image'); - imageFS.append(imageLegend); - var imageAttr = $('
                                      '); - imageFS.append($('
                                      ')); - imageFS.append(imageAttr); - - var optionFS = $('
                                      '); - var optionLegend = $('Options'); - optionFS.append(optionLegend); - var optionAttr = $('
                                      '); - optionFS.append($('
                                      ')); - optionFS.append(optionAttr); - - addImageForm.append(info, imageFS, optionFS); - - // Create inputs for image attributes - var imageName = $('
                                      '); - var imageType = $('
                                      '); - var architecture = $('
                                      '); - var osName = $('
                                      '); - var osVersion = $('
                                      '); - var profile = $('
                                      '); - var provisionMethod = $('
                                      '); - var provisionSelect = $(''); - provisionMethod.append(provisionSelect); - - // Create inputs for optional attributes - var otherpkgDirectory = $('
                                      '); - var otherpkgDirectoryInput = $(''); - otherpkgDirectory.append(otherpkgDirectoryInput); - otherpkgDirectoryInput.serverBrowser({ - onSelect : function(path) { - $('#addImage input[name="otherpkgdir"]').val(path); - }, - onLoad : function() { - return $('#addImage input[name="otherpkgdir"]').val(); - }, - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - var packageDirectory = $('
                                      '); - var packageDirectoryInput = $(''); - packageDirectory.append(packageDirectoryInput); - packageDirectoryInput.serverBrowser({ - onSelect : function(path) { - $('#addImage input[name="pkgdir"]').val(path); - }, - onLoad : function() { - return $('#addImage input[name="pkgdir"]').val(); - }, - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - var packageList = $('
                                      '); - var packageListInput = $(''); - packageList.append(packageListInput); - packageListInput.serverBrowser({ - onSelect : function(path) { - $('#addImage input[name="pkglist"]').val(path); - }, - onLoad : function() { - return $('#addImage input[name="pkglist"]').val(); - }, - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - var template = $('
                                      '); - var templateInput = $(''); - template.append(templateInput); - templateInput.serverBrowser({ - onSelect : function(path) { - $('#addImage input[name="template"]').val(path); - }, - onLoad : function() { - return $('#addImage input[name="template"]').val(); - }, - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - - imageAttr.append(imageName, imageType, architecture, osName, osVersion, profile, provisionMethod); - optionAttr.append(otherpkgDirectory, packageDirectory, packageList, template); - - // Generate tooltips - addImageForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to add image - addImageForm.dialog({ - title:'Add image', - modal: true, - close: function(){ - $(this).remove(); - }, - beight: 400, - width: 600, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - // Get image attributes - var imageType = $(this).find('input[name="imagetype"]'); - var architecture = $(this).find('input[name="osarch"]'); - var osName = $(this).find('input[name="osname"]'); - var osVersion = $(this).find('input[name="osvers"]'); - var profile = $(this).find('input[name="profile"]'); - var provisionMethod = $(this).find('select[name="provmethod"]'); - - // Get optional image attributes - var otherpkgDirectory = $(this).find('input[name="otherpkgdir"]'); - var pkgDirectory = $(this).find('input[name="pkgdir"]'); - var pkgList = $(this).find('input[name="pkglist"]'); - var template = $(this).find('input[name="template"]'); - - // Check that image attributes are provided before continuing - var ready = 1; - var inputs = new Array(imageType, architecture, osName, osVersion, profile, provisionMethod); - for (var i in inputs) { - if (!inputs[i].val()) { - inputs[i].css('border-color', 'red'); - ready = 0; - } else - inputs[i].css('border-color', ''); - } - - // If inputs are not complete, show warning message - if (!ready) { - var warn = createWarnBar('Please provide a value for each missing field.'); - warn.prependTo($(this)); - } else { - // Override image name - $(this).find('input[name="imagename"]').val(osVersion.val() + '-' + architecture.val() + '-' + provisionMethod.val() + '-' + profile.val()); - var imageName = $(this).find('input[name="imagename"]'); - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Create arguments to send via AJAX - var args = '-t;osimage;-o;' + imageName.val() + ';' + - 'imagetype=' + imageType.val() + ';' + - 'osarch=' + architecture.val() + ';' + - 'osname=' + osName.val() + ';' + - 'osvers=' + osVersion.val() + ';' + - 'profile=' + profile.val() + ';' + - 'provmethod=' + provisionMethod.val(); - - // Get optional attributes - if (otherpkgDirectory.val()) - args += ';otherpkgdir=' + otherpkgDirectory.val(); - if (pkgDirectory.val()) - args += ';pkgdir=' + pkgDirectory.val(); - if (pkgList.val()) - args += ';pkglist=' + pkgList.val(); - if (template.val()) - args += ';template=' + template.val(); - - // Add image to xCAT - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chdef', - tgt : '', - args : args, - msg : dialogId - }, - - success : updateImageDialog - }); - } - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Update image dialog - * - * @param data HTTP request data - */ -function updateImageDialog(data) { - var dialogId = data.msg; - var infoMsg; - - // Delete loader if one does exist - $('.ui-dialog #' + dialogId + ' img[src="images/loader.gif"]').remove(); - - // Create info message - if (jQuery.isArray(data.rsp)) { - infoMsg = ''; - - // If the data returned is more than 10 lines, get only the last line - var i, start; - if (data.rsp.length > 10) - start = data.rsp.length - 1; - else - start = 0; - - for (i = start; i < data.rsp.length; i++) - infoMsg += data.rsp[i] + '
                                      '; - } else { - infoMsg = data.rsp; - } - - // Create info bar with close button - var infoBar = $('
                                      ').css('margin', '5px 0px'); - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px' - }); - - // Create close button to close info bar - var close = $('').css({ - 'display': 'inline-block', - 'float': 'right' - }).click(function() { - $(this).parent().remove(); - }); - - var msg = $('

                                      ' + infoMsg + '

                                      ').css({ - 'display': 'inline-block', - 'width': '90%' - }); - - infoBar.append(icon, msg, close); - infoBar.prependTo($('.ui-dialog #' + dialogId)); -} - -/** - * Set definable image attributes - * - * @param data Data returned from HTTP request - */ -function setImageDefAttrs(data) { - // Clear hash table containing definable image attributes - defAttrs = new Array(); - - // Get definable attributes - var attrs = data.rsp[2].split(/\n/); - - // Go through each line - var attr, key, descr; - for (var i in attrs) { - attr = attrs[i]; - - // If the line is not empty - if (attr) { - // If the line has the attribute name - if (attr.indexOf(':') && attr.indexOf(' ')) { - // Get attribute name and description - key = jQuery.trim(attr.substring(0, attr.indexOf(':'))); - descr = jQuery.trim(attr.substring(attr.indexOf(':') + 1)); - descr = descr.replace(new RegExp('<', 'g'), '[').replace(new RegExp('>', 'g'), ']'); - - // Set hash table where key = attribute name and value = description - defAttrs[key] = descr; - } else { - // Append description to hash table - defAttrs[key] = defAttrs[key] + '\n' + attr.replace(new RegExp('<', 'g'), '[').replace(new RegExp('>', 'g'), ']'); - } - } // End of if - } // End of for -} - -/** - * Load create image page - */ -function loadCreateImage() { - // Get nodes tab - var tab = getProvisionTab(); - var tabId = 'createImageTab'; - - // Generate new tab ID - if ($('#' + tabId).size()) { - tab.select(tabId); - return; - } - - var imageOsVers = $.cookie("xcat_osvers").split(","); - var imageArch = $.cookie("xcat_osarchs").split(","); - var profiles = $.cookie("xcat_profiles").split(","); - - var createImgForm = $('
                                      '); - var createImgFS = $('
                                      ').append('Create Image'); - createImgForm.append(createImgFS); - - // Show info bar - var infoBar = createInfoBar('Specify the parameters for the image (stateless or statelite) you want to create, then click Create.'); - createImgFS.append(infoBar); - - // Drop down for OS versions - var osVerSelect = $(''); - for (var i in imageOsVers) - osVerSelect.append(''); - createImgFS.append($('
                                      ').append(osVerSelect)); - - // Drop down for OS architectures - var imgSelect = $(''); - for (var i in imageArch) - imgSelect.append(''); - createImgFS.append($('
                                      ').append(imgSelect)); - - // Netboot interface input - createImgFS.append($('
                                      ')); - - // Profile selector - var profileSelect = $('' + - '' + - '' + - '
                                      ')); - - // Create HPC software stack fieldset - createHpcFS(createImgForm); - - // The button used to create images is created here - var createImageBtn = createButton("Create"); - createImageBtn.bind('click', function(event) { - createImage(); - }); - - createImgForm.append(createImageBtn); - - // Add tab - tab.add(tabId, 'Create', createImgForm, true); - tab.select(tabId); - - // Check the selected OS version and OS arch for HPC stack - // If they are valid, show the HCP stack fieldset - hpcShow(); -} - -/** - * Create HPC fieldset - * - * @param container The container to hold the HPC fieldset - */ -function createHpcFS(container) { - var hpcFieldset = $('
                                      '); - hpcFieldset.append('HPC Software Stack'); - - var str = 'Before selecting the software, you should have the following already completed on your xCAT cluster:

                                      ' - + '1. If you are using the xCAT hierarchy, your service nodes are installed and running.
                                      ' - + '2. Your compute nodes are defined in xCAT, and you have verified your hardware control capabilities, ' - + 'gathered MAC addresses, and done all the other necessary preparations for a diskless install.
                                      ' - + '3. You should have a diskless image created with the base OS installed and verified it on at least one test node.
                                      ' - + '4. You should install the software on the management node and copy all correponding packages into the location "/install/custom/otherpkgs/" based on ' - + 'these documents.
                                      '; - hpcFieldset.append(createInfoBar(str)); - - // Advanced software - str = '
                                      • GPFS
                                      • ' + - '
                                      • RSCT
                                      • ' + - '
                                      • PE
                                      • ' + - '
                                      • ESSl & PESSL
                                      • ' + - '
                                      ' + - '
                                      • Ganglia
                                      • ' + - '
                                      '; - hpcFieldset.append(str); - - container.append($('
                                      ').append(hpcFieldset)); -} - -/** - * Check the dependance for ESSL and start the software check for ESSL - * - * @param softwareObject The checkbox object of ESSL - */ -function esslCheck(softwareObject) { - var softwareName = softwareObject.name; - if (!$('#createImageTab input[name=pe]').attr('checked')) { - var warnBar = createWarnBar('You must first select the PE'); - $(':checkbox[name=essl]').attr("checked", false); - - // Clear existing warnings and append new warning - $('#hpcsoft .ui-state-error').remove(); - $('#hpcsoft').prepend(warnBar); - - return; - } else { - softwareCheck(softwareObject); - } -} - -/** - * Check the parameters for the HPC software - * - * @param softwareObject Checkbox object of the HPC software - * @return True if the checkbox is checked, false otherwise - */ -function softwareCheck(softwareObject) { - var softwareName = softwareObject.name; - $('#createImageTab #' + softwareName + 'li .ui-state-error').remove(); - $('#createImageTab #' + softwareName + 'li').append(createLoader()); - var cmdString = genRpmCmd(softwareName); - $.ajax( { - url : 'lib/systemcmd.php', - dataType : 'json', - data : { - cmd : cmdString, - msg : softwareName - }, - success : function(data) { - if (rpmCheck(data.rsp, data.msg)) { - genLsCmd(data.msg); - $.ajax( { - url : 'lib/systemcmd.php', - dataType : 'json', - data : { - cmd : genLsCmd(data.msg), - msg : data.msg - }, - success : rpmCopyCheck - }); - } - } - }); -} - -/** - * Check if the RPMs are copied to the special location - * - * @param data Data returned from HTTP request - */ -function rpmCopyCheck(data) { - // Remove the loading image - var errorStr = ''; - var softwareName = data.msg; - - // Check the return information - var reg = /.+:(.+): No such.*/; - var resultArray = data.rsp.split("\n"); - for ( var i in resultArray) { - var temp = reg.exec(resultArray[i]); - if (temp) { - // Find out the path and RPM name - var pos = temp[1].lastIndexOf('/'); - var path = temp[1].substring(0, pos); - var rpmName = temp[1].substring(pos + 1).replace('*', ''); - errorStr += 'copy ' + rpmName + ' to ' + path + '
                                      '; - } - } - $('#createImageTab #' + softwareName + 'li').find('img').remove(); - - // No error, show the check image - if (!errorStr) { - var infoPart = '
                                      '; - $('#createImageTab #' + softwareName + 'li').append(infoPart); - } else { - // Show the error message - errorStr = 'To install the RSCT on your compute node. You should:
                                      ' + errorStr + '
                                      '; - var warnBar = createWarnBar(errorStr); - $(':checkbox[name=' + softwareName + ']').attr("checked", false); - - // Clear existing warnings and append new warning - $('#hpcsoft .ui-state-error').remove(); - $('#hpcsoft').prepend(warnBar); - } -} - -/** - * Generate the RPM command for rpmcheck - * - * @param softwareName The name of the software - * @return The RPM command - */ -function genRpmCmd(softwareName) { - var cmdString; - cmdString = 'rpm -q '; - for (var i in softwareList[softwareName]) { - cmdString += softwareList[softwareName][i] + ' '; - } - - for (var i in softwareList['base']) { - cmdString += softwareList['base'][i] + ' '; - } - - return cmdString; -} - -/** - * Check if the RPMs for the HPC software are copied to the special location - * - * @param softwareName The name of the software - */ -function genLsCmd(softwareName) { - var osvers = $('#createImageTab #osvers').val(); - var osarch = $('#createImageTab #osarch').val(); - var path = '/install/post/otherpkgs/' + osvers + '/' + osarch + '/' + softwareName; - var checkCmd = 'ls '; - - for (var i in softwareList[softwareName]) { - checkCmd += path + '/' + softwareList[softwareName][i] + '*.rpm '; - } - checkCmd += '2>&1'; - - return checkCmd; -} - -/** - * Check if all RPMs are installed - * - * @param checkInfo 'rpm -q' output - * @return True if all RPMs are installed, false otherwise - */ -function rpmCheck(checkInfo, name) { - var errorStr = ''; - - var checkArray = checkInfo.split('\n'); - for (var i in checkArray) { - if (checkArray[i].indexOf('not install') != -1) { - errorStr += checkArray[i] + '
                                      '; - } - } - - if (!errorStr) { - return true; - } - - errorStr = errorStr.substr(0, errorStr.length - 1); - $(':checkbox[name=' + name + ']').attr('checked', false); - - // Add the error - var warnBar = createWarnBar(errorStr); - $('#createImageTab #' + name + 'li').find('img').remove(); - - // Clear existing warnings and append new warning - $('#hpcsoft .ui-state-error').remove(); - $('#hpcsoft').prepend(warnBar); - - return; -} - -/** - * Check the option and decide whether to show the hpcsoft or not - */ -function hpcShow() { - // The current UI only supports RHELS 6 - // If you want to support all, delete the subcheck - if ($('#createImageTab #osvers').attr('value') != "rhels6" || $('#createImageTab #osarch').attr('value') != "ppc64" || $('#createImageTab #profile').attr('value') != "compute") { - $('#createImageTab #partlysupport').hide(); - } else { - $('#createImageTab #partlysupport').show(); - } -} - -/** - * Load set image properties page - * - * @param tgtImage Target image to set properties - */ -function openEditImagePage(tgtImage) { - // Get nodes tab - var tab = getProvisionTab(); - - // Generate new tab ID - var inst = 0; - var newTabId = 'editImageTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = 'editImageTab' + inst; - } - - // Open new tab - // Create set properties form - var setPropsForm = $('
                                      '); - - // Create info bar - var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); - setPropsForm.append(infoBar); - - // Create an input for each definable attribute - var div, label, input, value; - var attrIndex = 0; - // Set node attribute - origAttrs[tgtImage]['imagename'] = tgtImage; - for (var key in defAttrs) { - // If an attribute value exists - if (origAttrs[tgtImage][key]) { - // Set the value - value = origAttrs[tgtImage][key]; - } else { - value = ''; - } - - // Create label and input for attribute - div = $('
                                      ').css('display', 'inline'); - label = $('').css('vertical-align', 'middle'); - input = $('').css({ - 'margin-top': '5px', - 'float': 'none', - 'width': 'inherit' - }); - - // There is an element called groups that will override the defaults for the groups attribute. - // Hence, the input must have use CSS to override the float and width. - - // Split attributes into 2 per row - if (attrIndex > 0 && !(attrIndex % 2)) { - div.css('display', 'inline-block'); - } - - attrIndex++; - - // Create server browser - switch (key) { - case 'pkgdir': - input.serverBrowser({ - onSelect : function(path) { - $('#pkgdir').val(path); - }, - onLoad : function() { - return $('#pkgdir').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - break; - case 'otherpkgdir': - input.serverBrowser({ - onSelect : function(path) { - $('#otherpkgdir').val(path); - }, - onLoad : function() { - return $('#otherpkgdir').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - break; - case 'pkglist': - input.serverBrowser({ - onSelect : function(path) { - $('#pkglist').val(path); - }, - onLoad : function() { - return $('#pkglist').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/opt/xcat/share' // Limit user to only install directory - }); - break; - case 'otherpkglist': - input.serverBrowser({ - onSelect : function(path) { - $('#otherpkglist').val(path); - }, - onLoad : function() { - return $('#otherpkglist').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - break; - case 'template': - input.serverBrowser({ - onSelect : function(path) { - $('#template').val(path); - }, - onLoad : function() { - return $('#template').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [{ - text : 'Install', - image : 'desktop.png', - path : '/install' - }], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/opt/xcat/share' // Limit user to only install directory - }); - break; - default: - // Do nothing - } - - // Change border to blue onchange - input.bind('change', function(event) { - $(this).css('border-color', 'blue'); - }); - - div.append(label, input); - setPropsForm.append(div); - } - - // Change style for last division - div.css({ - 'display': 'block', - 'margin': '0px 0px 10px 0px' - }); - - // Generate tooltips - setPropsForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 500, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - } - }); - - /** - * Save - */ - var saveBtn = createButton('Save'); - saveBtn.bind('click', function(event) { - // Get all inputs - var inputs = $('#' + newTabId + ' input'); - - // Go through each input - var args = ''; - var attrName, attrVal; - inputs.each(function(){ - // If the border color is blue - if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { - // Change border color back to normal - $(this).css('border-color', ''); - - // Get attribute name and value - attrName = $(this).parent().find('label').text().replace(':', ''); - attrVal = $(this).val(); - - // Build argument string - if (args) { - // Handle subsequent arguments - args += ';' + attrName + '=' + attrVal; - } else { - // Handle the 1st argument - args += attrName + '=' + attrVal; - } - } - }); - - // Send command to change image attributes - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'chdef', - tgt : '', - args : '-t;osimage;-o;' + tgtImage + ';' + args, - msg : 'out=' + newTabId + ';tgt=' + tgtImage - }, - - success: showChdefOutput - }); - }); - setPropsForm.append(saveBtn); - - /** - * Cancel - */ - var cancelBtn = createButton('Cancel'); - cancelBtn.bind('click', function(event) { - // Close the tab - tab.remove($(this).parent().parent().attr('id')); - }); - setPropsForm.append(cancelBtn); - - // Append to discover tab - tab.add(newTabId, 'Edit', setPropsForm, true); - - // Select new tab - tab.select(newTabId); -} - -/** - * Load copy CD page - */ -function openCopyCdDialog() { - // Create copy Linux form - var dialogId = 'imageCopyCd'; - var copyLinuxForm = $('
                                      '); - - // Create info bar - var infoBar = createInfoBar('Copy Linux distributions and service levels from CDs or DVDs to the install directory.'); - copyLinuxForm.append(infoBar); - - // Create Linux ISO input - var iso = $('
                                      '); - var isoLabel = $('').css('vertical-align', 'middle'); - var isoInput = $('').css('width', '300px'); - iso.append(isoLabel); - iso.append(isoInput); - copyLinuxForm.append(iso); - - // Create architecture input - copyLinuxForm.append('
                                      '); - // Create distribution input - copyLinuxForm.append('
                                      '); - - /** - * Browse - */ - var browseBtn = createButton('Browse'); - iso.append(browseBtn); - // Browse server directory and files - browseBtn.serverBrowser({ - onSelect : function(path) { - $('#imageCopyCd #iso').val(path); - }, - onLoad : function() { - return $('#imageCopyCd #iso').val(); - }, - knownExt : [ 'exe', 'js', 'txt' ], - knownPaths : [ { - text : 'Install', - image : 'desktop.png', - path : '/install' - } ], - imageUrl : 'images/serverbrowser/', - systemImageUrl : 'images/serverbrowser/', - handlerUrl : 'lib/getpath.php', - title : 'Browse', - requestMethod : 'POST', - width : '500', - height : '300', - basePath : '/install' // Limit user to only install directory - }); - - // Generate tooltips - copyLinuxForm.find('div input[title],select[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - delay: 0, - predelay: 800, - events: { - def: "mouseover,mouseout", - input: "mouseover,mouseout", - widget: "focus mouseover,blur mouseout", - tooltip: "mouseover,mouseout" - }, - - // Change z index to show tooltip in front - onBeforeShow: function() { - this.getTip().css('z-index', $.topZIndex()); - } - }); - - // Open dialog to copy CD - copyLinuxForm.dialog({ - title:'Copy CD', - close: function(){ - $(this).remove(); - }, - modal: true, - width: 600, - buttons: { - "Copy": function() { - // Show loader - $('.ui-dialog #imageCopyCd').append(createLoader('')); - - // Change dialog buttons - $(this).dialog('option', 'buttons', { - 'Close': function() {$(this).dialog("close");} - }); - - // Get image attributes - var iso = $(this).find('input[name="iso"]'); - var arch = $(this).find('input[name="arch"]'); - var distro = $(this).find('input[name="distro"]'); - - // Send ajax request to copy ISO - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'copycds', - tgt : '', - args : '-n;' + distro.val() + ';-a;' + arch.val() + ';' + iso.val(), - msg : dialogId - }, - - success : updateImageDialog - }); - }, - "Cancel": function() { - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Use user input or select to create image - */ -function createImage() { - var osvers = $("#createImageTab #osvers").val(); - var osarch = $("#createImageTab #osarch").val(); - var profile = $("#createImageTab #profile").val(); - var bootInterface = $("#createImageTab #netbootif").val(); - var bootMethod = $("#createImageTab #bootmethod").val(); - - $('#createImageTab .ui-state-error').remove(); - // If there no input for the bootInterface - if (!bootInterface) { - var warnBar = createWarnBar('Please specify the netboot interface'); - $("#createImageTab").prepend(warnBar); - return; - } - - var createImageArgs = "createimage;" + osvers + ";" + osarch + ";" + profile + ";" + bootInterface + ";" + bootMethod + ";"; - - $("#createImageTab :checkbox:checked").each(function() { - createImageArgs += $(this).attr("name") + ","; - }); - - createImageArgs = createImageArgs.substring(0, (createImageArgs.length - 1)); - $.ajax({ - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : createImageArgs, - msg : '' - }, - success : function(data) { - - } - }); +/** + * Global variables + */ +var origAttrs = new Object(); // Original image attributes +var defAttrs; // Definable image attributes +var imgTableId = 'imagesDatatable'; // Images datatable ID +var softwareList = { + "rsct" : ["rsct.core.utils", "rsct.core", "src"], + "pe" : ["IBMJava2-142-ppc64-JRE", "ibm_lapi_ip_rh6p", "ibm_lapi_us_rh6p", "IBM_pe_license", "ibm_pe_rh6p", "ppe_pdb_ppc64_rh600", "sci_ppc_32bit_rh600", "sci_ppc_64bit_rh600", "vac.cmp", + "vac.lib", "vac.lic", "vacpp.cmp", "vacpp.help.pdf", "vacpp.lib", "vacpp.man", "vacpp.rte", "vacpp.rte.lnk", "vacpp.samples", "xlf.cmp", "xlf.help.pdf", "xlf.lib", "xlf.lic", "xlf.man", + "xlf.msg.rte", "xlf.rte", "xlf.rte.lnk", "xlf.samples", "xlmass.lib", "xlsmp.lib", "xlsmp.msg.rte", "xlsmp.rte"], + "gpfs" : ["gpfs.base", "gpfs.gpl", "gpfs.gplbin", "gpfs.msg.en_US"], + "essl" : ["essl.3232.rte", "essl.3264.rte", "essl.6464.rte", "essl.common", "essl.license", "essl.man", "essl.msg", "essl.rte", "ibm-java2", "pessl.common", "pessl.license", "pessl.man", + "pessl.msg", "pessl.rte.ppe"], + "loadl" : ["IBMJava2", "LoadL-full-license-RH6", "LoadL-resmgr-full-RH6", "LoadL-scheduler-full-RH6"], + "ganglia" : ["rrdtool", "ganglia", "ganglia-gmetad", "ganglia-gmond"], + "base" : ["createrepo"] +}; + +/** + * Load images page + */ +function loadImagesPage() { + // Set padding for images page + $('#imagesTab').css('padding', '20px 60px'); + + // Get images within the database + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : '-t;osimage;-l', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadImages(data); + } + }); +} + +/** + * Load images within the database + * + * @param data Data returned from HTTP request + */ +function loadImages(data) { + // Data returned + var rsp = data.rsp; + if (rsp[0].indexOf('Could not find any object definitions') > -1) { + rsp = new Array(); + } + + // Image attributes hash + var attrs = new Object(); + // Image attributes + var headers = new Object(); + + // Clear hash table containing image attributes + origAttrs = ''; + + var image; + var args; + for (var i in rsp) { + // Get the image + var pos = rsp[i].indexOf('Object name:'); + if (pos > -1) { + var temp = rsp[i].split(': '); + image = jQuery.trim(temp[1]); + + // Create a hash for the image attributes + attrs[image] = new Object(); + i++; + } + + // Get key and value + args = rsp[i].split('='); + var key = jQuery.trim(args[0]); + var val = jQuery.trim(args[1]); + + // Create a hash table + attrs[image][key] = val; + headers[key] = 1; + } + + // Save attributes in hash table + origAttrs = attrs; + + // Sort headers + var sorted = new Array(); + for (var key in headers) { + sorted.push(key); + } + sorted.sort(); + + // Add column for check box and image name + sorted.unshift('', 'imagename'); + + // Create a datatable + var dTable = new DataTable(imgTableId); + dTable.init(sorted); + + // Go through each image + for (var img in attrs) { + // Create a row + var row = new Array(); + // Create a check box + var checkBx = ''; + // Push in checkbox and image name + row.push(checkBx, img); + + // Go through each header + for (var i = 2; i < sorted.length; i++) { + // Add the node attributes to the row + var key = sorted[i]; + var val = attrs[img][key]; + if (val) { + row.push(val); + } else { + row.push(''); + } + } + + // Add the row to the table + dTable.add(row); + } + + // Clear the tab before inserting the table + $('#imagesTab').children().remove(); + + // Create info bar for images tab + var info = createInfoBar('Double click on a cell to edit. Click outside the table to save changes. Hit the Escape key to ignore changes.'); + $('#imagesTab').append(info); + + /** + * The following actions are available for images: + * copy Linux distribution and edit image properties + */ + + // Copy CD into install directory + var copyCDLnk = $('Copy CD'); + copyCDLnk.click(function() { + openCopyCdDialog(); + }); + + // Generate stateless or statelite image + var generateLnk = $('Generate image'); + generateLnk.click(function() { + loadCreateImage(); + }); + + // Edit image attributes + var editLnk = $('Edit'); + editLnk.click(function() { + var tgtImages = getNodesChecked(imgTableId).split(','); + if (tgtImages) { + for (var i in tgtImages) { + openEditImagePage(tgtImages[i]); + } + } + }); + + // Add a row + var addLnk = $('Add'); + addLnk.click(function() { + openAddImageDialog(); + }); + + // Remove a row + var removeLnk = $('Remove'); + removeLnk.click(function() { + var images = getNodesChecked(imgTableId); + if (images) { + confirmImageDeleteDialog(images); + } + }); + + // Refresh image table + var refreshLnk = $('Refresh'); + refreshLnk.click(function() { + // Get images within the database + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : '-t;osimage;-l', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadImages(data); + } + }); + }); + + // Insert table + $('#imagesTab').append(dTable.object()); + + // Turn table into a datatable + var myDataTable = $('#' + imgTableId).dataTable({ + 'iDisplayLength': 50, + 'bLengthChange': false, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + + // Set datatable width + $('#' + imgTableId + '_wrapper').css({ + 'width': '880px' + }); + + // Actions + var actionBar = $('
                                      ').css("width", "450px"); + var advancedLnk = 'Advanced'; + var advancedMenu = createMenu([copyCDLnk, generateLnk]); + + // Create an action menu + var actionsMenu = createMenu([refreshLnk, addLnk, editLnk, removeLnk, [advancedLnk, advancedMenu]]); + actionsMenu.superfish(); + actionsMenu.css('display', 'inline-block'); + actionBar.append(actionsMenu); + + // Set correct theme for action menu + actionsMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + imgTableId + '_wrapper').prepend(menuDiv); + menuDiv.append(actionBar); + $('#' + imgTableId + '_filter').appendTo(menuDiv); + + /** + * Enable editable columns + */ + + // Do not make 1st or 2nd columns editable + $('#' + imgTableId + ' td:not(td:nth-child(1),td:nth-child(2))').editable( + function(value, settings) { + // Get column index + var colPos = this.cellIndex; + + // Get row index + var dTable = $('#' + imgTableId).dataTable(); + var rowPos = dTable.fnGetPosition(this.parentNode); + + // Update datatable + dTable.fnUpdate(value, rowPos, colPos); + + // Get image name + var image = $(this).parent().find('td:eq(1)').text(); + + // Get table headers + var headers = $('#' + imgTableId).parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); + + // Get attribute name + var attrName = jQuery.trim(headers.eq(colPos).text()); + // Get column value + var value = $(this).text(); + // Build argument + var args = attrName + '=' + value; + + // Send command to change image attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : '-t;osimage;-o;' + image + ';' + args, + msg : 'out=imagesTab;tgt=' + image + }, + + success : function(data) { + data = decodeRsp(data); + showChdefOutput(data); + } + }); + + return value; + }, { + onblur : 'submit', // Clicking outside editable area submits changes + type : 'textarea', // Input type to use + placeholder: ' ', + event : "dblclick", // Double click and edit + height : '30px' // The height of the text area + }); + + // Get definable node attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : '-t;osimage;-h', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setImageDefAttrs(data); + } + }); +} + +/** + * Open dialog to confirm deleting image + * + * @param images Comma delimited image names + */ +function confirmImageDeleteDialog(images) { + // Make images list more readable + var dialogId = 'confirmImageRemove'; + var tmp = images.replace(new RegExp(',', 'g'), ', '); + var confirmDialog = $('
                                      ' + + '

                                      Are you sure you want to remove ' + tmp + '?

                                      ' + + '
                                      '); + + // Open dialog to confirm delete + confirmDialog.dialog({ + modal: true, + close: function(){ + $(this).remove(); + }, + title: 'Confirm', + width: 500, + buttons: { + "Ok": function(){ + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + // Add image to xCAT + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'rmdef', + tgt : '', + args : '-t;osimage;-o;' + images, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateImageDialog(data); + } + }); + }, + "Cancel": function(){ + $(this).dialog("close"); + } + } + }); +} + +/** + * Open a dialog to add an image + */ +function openAddImageDialog() { + // Create dialog to add image + var dialogId = 'addImage'; + var addImageForm = $('
                                      '); + + // Create info bar + var info = createInfoBar('Provide the following attributes for the image. The image name will be generated based on the attributes you will give.'); + + var imageFS = $('
                                      '); + var imageLegend = $('Image'); + imageFS.append(imageLegend); + var imageAttr = $('
                                      '); + imageFS.append($('
                                      ')); + imageFS.append(imageAttr); + + var optionFS = $('
                                      '); + var optionLegend = $('Options'); + optionFS.append(optionLegend); + var optionAttr = $('
                                      '); + optionFS.append($('
                                      ')); + optionFS.append(optionAttr); + + addImageForm.append(info, imageFS, optionFS); + + // Create inputs for image attributes + var imageName = $('
                                      '); + var imageType = $('
                                      '); + var architecture = $('
                                      '); + var osName = $('
                                      '); + var osVersion = $('
                                      '); + var profile = $('
                                      '); + var provisionMethod = $('
                                      '); + var provisionSelect = $(''); + provisionMethod.append(provisionSelect); + + // Create inputs for optional attributes + var otherpkgDirectory = $('
                                      '); + var otherpkgDirectoryInput = $(''); + otherpkgDirectory.append(otherpkgDirectoryInput); + otherpkgDirectoryInput.serverBrowser({ + onSelect : function(path) { + $('#addImage input[name="otherpkgdir"]').val(path); + }, + onLoad : function() { + return $('#addImage input[name="otherpkgdir"]').val(); + }, + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + var packageDirectory = $('
                                      '); + var packageDirectoryInput = $(''); + packageDirectory.append(packageDirectoryInput); + packageDirectoryInput.serverBrowser({ + onSelect : function(path) { + $('#addImage input[name="pkgdir"]').val(path); + }, + onLoad : function() { + return $('#addImage input[name="pkgdir"]').val(); + }, + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + var packageList = $('
                                      '); + var packageListInput = $(''); + packageList.append(packageListInput); + packageListInput.serverBrowser({ + onSelect : function(path) { + $('#addImage input[name="pkglist"]').val(path); + }, + onLoad : function() { + return $('#addImage input[name="pkglist"]').val(); + }, + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + var template = $('
                                      '); + var templateInput = $(''); + template.append(templateInput); + templateInput.serverBrowser({ + onSelect : function(path) { + $('#addImage input[name="template"]').val(path); + }, + onLoad : function() { + return $('#addImage input[name="template"]').val(); + }, + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + + imageAttr.append(imageName, imageType, architecture, osName, osVersion, profile, provisionMethod); + optionAttr.append(otherpkgDirectory, packageDirectory, packageList, template); + + // Generate tooltips + addImageForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to add image + addImageForm.dialog({ + title:'Add image', + modal: true, + close: function(){ + $(this).remove(); + }, + beight: 400, + width: 600, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + // Get image attributes + var imageType = $(this).find('input[name="imagetype"]'); + var architecture = $(this).find('input[name="osarch"]'); + var osName = $(this).find('input[name="osname"]'); + var osVersion = $(this).find('input[name="osvers"]'); + var profile = $(this).find('input[name="profile"]'); + var provisionMethod = $(this).find('select[name="provmethod"]'); + + // Get optional image attributes + var otherpkgDirectory = $(this).find('input[name="otherpkgdir"]'); + var pkgDirectory = $(this).find('input[name="pkgdir"]'); + var pkgList = $(this).find('input[name="pkglist"]'); + var template = $(this).find('input[name="template"]'); + + // Check that image attributes are provided before continuing + var ready = 1; + var inputs = new Array(imageType, architecture, osName, osVersion, profile, provisionMethod); + for (var i in inputs) { + if (!inputs[i].val()) { + inputs[i].css('border-color', 'red'); + ready = 0; + } else + inputs[i].css('border-color', ''); + } + + // If inputs are not complete, show warning message + if (!ready) { + var warn = createWarnBar('Please provide a value for each missing field.'); + warn.prependTo($(this)); + } else { + // Override image name + $(this).find('input[name="imagename"]').val(osVersion.val() + '-' + architecture.val() + '-' + provisionMethod.val() + '-' + profile.val()); + var imageName = $(this).find('input[name="imagename"]'); + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + // Create arguments to send via AJAX + var args = '-t;osimage;-o;' + imageName.val() + ';' + + 'imagetype=' + imageType.val() + ';' + + 'osarch=' + architecture.val() + ';' + + 'osname=' + osName.val() + ';' + + 'osvers=' + osVersion.val() + ';' + + 'profile=' + profile.val() + ';' + + 'provmethod=' + provisionMethod.val(); + + // Get optional attributes + if (otherpkgDirectory.val()) + args += ';otherpkgdir=' + otherpkgDirectory.val(); + if (pkgDirectory.val()) + args += ';pkgdir=' + pkgDirectory.val(); + if (pkgList.val()) + args += ';pkglist=' + pkgList.val(); + if (template.val()) + args += ';template=' + template.val(); + + // Add image to xCAT + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : args, + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateImageDialog(data); + } + }); + } + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Update image dialog + * + * @param data HTTP request data + */ +function updateImageDialog(data) { + var dialogId = data.msg; + var infoMsg; + + // Delete loader if one does exist + $('.ui-dialog #' + dialogId + ' img[src="images/loader.gif"]').remove(); + + // Create info message + if (jQuery.isArray(data.rsp)) { + infoMsg = ''; + + // If the data returned is more than 10 lines, get only the last line + var i, start; + if (data.rsp.length > 10) + start = data.rsp.length - 1; + else + start = 0; + + for (i = start; i < data.rsp.length; i++) + infoMsg += data.rsp[i] + '
                                      '; + } else { + infoMsg = data.rsp; + } + + // Create info bar with close button + var infoBar = $('
                                      ').css('margin', '5px 0px'); + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px' + }); + + // Create close button to close info bar + var close = $('').css({ + 'display': 'inline-block', + 'float': 'right' + }).click(function() { + $(this).parent().remove(); + }); + + var msg = $('

                                      ' + infoMsg + '

                                      ').css({ + 'display': 'inline-block', + 'width': '90%' + }); + + infoBar.append(icon, msg, close); + infoBar.prependTo($('.ui-dialog #' + dialogId)); +} + +/** + * Set definable image attributes + * + * @param data Data returned from HTTP request + */ +function setImageDefAttrs(data) { + // Clear hash table containing definable image attributes + defAttrs = new Array(); + + // Get definable attributes + var attrs = data.rsp[2].split(/\n/); + + // Go through each line + var attr, key, descr; + for (var i in attrs) { + attr = attrs[i]; + + // If the line is not empty + if (attr) { + // If the line has the attribute name + if (attr.indexOf(':') && attr.indexOf(' ')) { + // Get attribute name and description + key = jQuery.trim(attr.substring(0, attr.indexOf(':'))); + descr = jQuery.trim(attr.substring(attr.indexOf(':') + 1)); + descr = descr.replace(new RegExp('<', 'g'), '[').replace(new RegExp('>', 'g'), ']'); + + // Set hash table where key = attribute name and value = description + defAttrs[key] = descr; + } else { + // Append description to hash table + defAttrs[key] = defAttrs[key] + '\n' + attr.replace(new RegExp('<', 'g'), '[').replace(new RegExp('>', 'g'), ']'); + } + } // End of if + } // End of for +} + +/** + * Load create image page + */ +function loadCreateImage() { + // Get nodes tab + var tab = getProvisionTab(); + var tabId = 'createImageTab'; + + // Generate new tab ID + if ($('#' + tabId).size()) { + tab.select(tabId); + return; + } + + var imageOsVers = $.cookie("xcat_osvers").split(","); + var imageArch = $.cookie("xcat_osarchs").split(","); + var profiles = $.cookie("xcat_profiles").split(","); + + var createImgForm = $('
                                      '); + var createImgFS = $('
                                      ').append('Create Image'); + createImgForm.append(createImgFS); + + // Show info bar + var infoBar = createInfoBar('Specify the parameters for the image (stateless or statelite) you want to create, then click Create.'); + createImgFS.append(infoBar); + + // Drop down for OS versions + var osVerSelect = $(''); + for (var i in imageOsVers) + osVerSelect.append(''); + createImgFS.append($('
                                      ').append(osVerSelect)); + + // Drop down for OS architectures + var imgSelect = $(''); + for (var i in imageArch) + imgSelect.append(''); + createImgFS.append($('
                                      ').append(imgSelect)); + + // Netboot interface input + createImgFS.append($('
                                      ')); + + // Profile selector + var profileSelect = $('' + + '' + + '' + + '
                                      ')); + + // Create HPC software stack fieldset + createHpcFS(createImgForm); + + // The button used to create images is created here + var createImageBtn = createButton("Create"); + createImageBtn.bind('click', function(event) { + createImage(); + }); + + createImgForm.append(createImageBtn); + + // Add tab + tab.add(tabId, 'Create', createImgForm, true); + tab.select(tabId); + + // Check the selected OS version and OS arch for HPC stack + // If they are valid, show the HCP stack fieldset + hpcShow(); +} + +/** + * Create HPC fieldset + * + * @param container The container to hold the HPC fieldset + */ +function createHpcFS(container) { + var hpcFieldset = $('
                                      '); + hpcFieldset.append('HPC Software Stack'); + + var str = 'Before selecting the software, you should have the following already completed on your xCAT cluster:

                                      ' + + '1. If you are using the xCAT hierarchy, your service nodes are installed and running.
                                      ' + + '2. Your compute nodes are defined in xCAT, and you have verified your hardware control capabilities, ' + + 'gathered MAC addresses, and done all the other necessary preparations for a diskless install.
                                      ' + + '3. You should have a diskless image created with the base OS installed and verified it on at least one test node.
                                      ' + + '4. You should install the software on the management node and copy all correponding packages into the location "/install/custom/otherpkgs/" based on ' + + 'these documents.
                                      '; + hpcFieldset.append(createInfoBar(str)); + + // Advanced software + str = '
                                      • GPFS
                                      • ' + + '
                                      • RSCT
                                      • ' + + '
                                      • PE
                                      • ' + + '
                                      • ESSl & PESSL
                                      • ' + + '
                                      ' + + '
                                      • Ganglia
                                      • ' + + '
                                      '; + hpcFieldset.append(str); + + container.append($('
                                      ').append(hpcFieldset)); +} + +/** + * Check the dependance for ESSL and start the software check for ESSL + * + * @param softwareObject The checkbox object of ESSL + */ +function esslCheck(softwareObject) { + var softwareName = softwareObject.name; + if (!$('#createImageTab input[name=pe]').attr('checked')) { + var warnBar = createWarnBar('You must first select the PE'); + $(':checkbox[name=essl]').attr("checked", false); + + // Clear existing warnings and append new warning + $('#hpcsoft .ui-state-error').remove(); + $('#hpcsoft').prepend(warnBar); + + return; + } else { + softwareCheck(softwareObject); + } +} + +/** + * Check the parameters for the HPC software + * + * @param softwareObject Checkbox object of the HPC software + * @return True if the checkbox is checked, false otherwise + */ +function softwareCheck(softwareObject) { + var softwareName = softwareObject.name; + $('#createImageTab #' + softwareName + 'li .ui-state-error').remove(); + $('#createImageTab #' + softwareName + 'li').append(createLoader()); + var cmdString = genRpmCmd(softwareName); + $.ajax( { + url : 'lib/systemcmd.php', + dataType : 'json', + data : { + cmd : cmdString, + msg : softwareName + }, + success : function(data) { + if (rpmCheck(data.rsp, data.msg)) { + genLsCmd(data.msg); + $.ajax( { + url : 'lib/systemcmd.php', + dataType : 'json', + data : { + cmd : genLsCmd(data.msg), + msg : data.msg + }, + success : function(data) { + data = decodeRsp(data); + rpmCopyCheck(data); + } + }); + } + } + }); +} + +/** + * Check if the RPMs are copied to the special location + * + * @param data Data returned from HTTP request + */ +function rpmCopyCheck(data) { + // Remove the loading image + var errorStr = ''; + var softwareName = data.msg; + + // Check the return information + var reg = /.+:(.+): No such.*/; + var resultArray = data.rsp.split("\n"); + for ( var i in resultArray) { + var temp = reg.exec(resultArray[i]); + if (temp) { + // Find out the path and RPM name + var pos = temp[1].lastIndexOf('/'); + var path = temp[1].substring(0, pos); + var rpmName = temp[1].substring(pos + 1).replace('*', ''); + errorStr += 'copy ' + rpmName + ' to ' + path + '
                                      '; + } + } + $('#createImageTab #' + softwareName + 'li').find('img').remove(); + + // No error, show the check image + if (!errorStr) { + var infoPart = '
                                      '; + $('#createImageTab #' + softwareName + 'li').append(infoPart); + } else { + // Show the error message + errorStr = 'To install the RSCT on your compute node. You should:
                                      ' + errorStr + '
                                      '; + var warnBar = createWarnBar(errorStr); + $(':checkbox[name=' + softwareName + ']').attr("checked", false); + + // Clear existing warnings and append new warning + $('#hpcsoft .ui-state-error').remove(); + $('#hpcsoft').prepend(warnBar); + } +} + +/** + * Generate the RPM command for rpmcheck + * + * @param softwareName The name of the software + * @return The RPM command + */ +function genRpmCmd(softwareName) { + var cmdString; + cmdString = 'rpm -q '; + for (var i in softwareList[softwareName]) { + cmdString += softwareList[softwareName][i] + ' '; + } + + for (var i in softwareList['base']) { + cmdString += softwareList['base'][i] + ' '; + } + + return cmdString; +} + +/** + * Check if the RPMs for the HPC software are copied to the special location + * + * @param softwareName The name of the software + */ +function genLsCmd(softwareName) { + var osvers = $('#createImageTab #osvers').val(); + var osarch = $('#createImageTab #osarch').val(); + var path = '/install/post/otherpkgs/' + osvers + '/' + osarch + '/' + softwareName; + var checkCmd = 'ls '; + + for (var i in softwareList[softwareName]) { + checkCmd += path + '/' + softwareList[softwareName][i] + '*.rpm '; + } + checkCmd += '2>&1'; + + return checkCmd; +} + +/** + * Check if all RPMs are installed + * + * @param checkInfo 'rpm -q' output + * @return True if all RPMs are installed, false otherwise + */ +function rpmCheck(checkInfo, name) { + var errorStr = ''; + + var checkArray = checkInfo.split('\n'); + for (var i in checkArray) { + if (checkArray[i].indexOf('not install') != -1) { + errorStr += checkArray[i] + '
                                      '; + } + } + + if (!errorStr) { + return true; + } + + errorStr = errorStr.substr(0, errorStr.length - 1); + $(':checkbox[name=' + name + ']').attr('checked', false); + + // Add the error + var warnBar = createWarnBar(errorStr); + $('#createImageTab #' + name + 'li').find('img').remove(); + + // Clear existing warnings and append new warning + $('#hpcsoft .ui-state-error').remove(); + $('#hpcsoft').prepend(warnBar); + + return; +} + +/** + * Check the option and decide whether to show the hpcsoft or not + */ +function hpcShow() { + // The current UI only supports RHELS 6 + // If you want to support all, delete the subcheck + if ($('#createImageTab #osvers').attr('value') != "rhels6" || $('#createImageTab #osarch').attr('value') != "ppc64" || $('#createImageTab #profile').attr('value') != "compute") { + $('#createImageTab #partlysupport').hide(); + } else { + $('#createImageTab #partlysupport').show(); + } +} + +/** + * Load set image properties page + * + * @param tgtImage Target image to set properties + */ +function openEditImagePage(tgtImage) { + // Get nodes tab + var tab = getProvisionTab(); + + // Generate new tab ID + var inst = 0; + var newTabId = 'editImageTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = 'editImageTab' + inst; + } + + // Open new tab + // Create set properties form + var setPropsForm = $('
                                      '); + + // Create info bar + var infoBar = createInfoBar('Choose the properties you wish to change on the node. When you are finished, click Save.'); + setPropsForm.append(infoBar); + + // Create an input for each definable attribute + var div, label, input, value; + var attrIndex = 0; + // Set node attribute + origAttrs[tgtImage]['imagename'] = tgtImage; + for (var key in defAttrs) { + // If an attribute value exists + if (origAttrs[tgtImage][key]) { + // Set the value + value = origAttrs[tgtImage][key]; + } else { + value = ''; + } + + // Create label and input for attribute + div = $('
                                      ').css('display', 'inline'); + label = $('').css('vertical-align', 'middle'); + input = $('').css({ + 'margin-top': '5px', + 'float': 'none', + 'width': 'inherit' + }); + + // There is an element called groups that will override the defaults for the groups attribute. + // Hence, the input must have use CSS to override the float and width. + + // Split attributes into 2 per row + if (attrIndex > 0 && !(attrIndex % 2)) { + div.css('display', 'inline-block'); + } + + attrIndex++; + + // Create server browser + switch (key) { + case 'pkgdir': + input.serverBrowser({ + onSelect : function(path) { + $('#pkgdir').val(path); + }, + onLoad : function() { + return $('#pkgdir').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + break; + case 'otherpkgdir': + input.serverBrowser({ + onSelect : function(path) { + $('#otherpkgdir').val(path); + }, + onLoad : function() { + return $('#otherpkgdir').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + break; + case 'pkglist': + input.serverBrowser({ + onSelect : function(path) { + $('#pkglist').val(path); + }, + onLoad : function() { + return $('#pkglist').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/opt/xcat/share' // Limit user to only install directory + }); + break; + case 'otherpkglist': + input.serverBrowser({ + onSelect : function(path) { + $('#otherpkglist').val(path); + }, + onLoad : function() { + return $('#otherpkglist').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + break; + case 'template': + input.serverBrowser({ + onSelect : function(path) { + $('#template').val(path); + }, + onLoad : function() { + return $('#template').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [{ + text : 'Install', + image : 'desktop.png', + path : '/install' + }], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/opt/xcat/share' // Limit user to only install directory + }); + break; + default: + // Do nothing + } + + // Change border to blue onchange + input.bind('change', function(event) { + $(this).css('border-color', 'blue'); + }); + + div.append(label, input); + setPropsForm.append(div); + } + + // Change style for last division + div.css({ + 'display': 'block', + 'margin': '0px 0px 10px 0px' + }); + + // Generate tooltips + setPropsForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 500, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + } + }); + + /** + * Save + */ + var saveBtn = createButton('Save'); + saveBtn.bind('click', function(event) { + // Get all inputs + var inputs = $('#' + newTabId + ' input'); + + // Go through each input + var args = ''; + var attrName, attrVal; + inputs.each(function(){ + // If the border color is blue + if ($(this).css('border-left-color') == 'rgb(0, 0, 255)') { + // Change border color back to normal + $(this).css('border-color', ''); + + // Get attribute name and value + attrName = $(this).parent().find('label').text().replace(':', ''); + attrVal = $(this).val(); + + // Build argument string + if (args) { + // Handle subsequent arguments + args += ';' + attrName + '=' + attrVal; + } else { + // Handle the 1st argument + args += attrName + '=' + attrVal; + } + } + }); + + // Send command to change image attributes + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'chdef', + tgt : '', + args : '-t;osimage;-o;' + tgtImage + ';' + args, + msg : 'out=' + newTabId + ';tgt=' + tgtImage + }, + + success : function(data) { + data = decodeRsp(data); + showChdefOutput(data); + } + }); + }); + setPropsForm.append(saveBtn); + + /** + * Cancel + */ + var cancelBtn = createButton('Cancel'); + cancelBtn.bind('click', function(event) { + // Close the tab + tab.remove($(this).parent().parent().attr('id')); + }); + setPropsForm.append(cancelBtn); + + // Append to discover tab + tab.add(newTabId, 'Edit', setPropsForm, true); + + // Select new tab + tab.select(newTabId); +} + +/** + * Load copy CD page + */ +function openCopyCdDialog() { + // Create copy Linux form + var dialogId = 'imageCopyCd'; + var copyLinuxForm = $('
                                      '); + + // Create info bar + var infoBar = createInfoBar('Copy Linux distributions and service levels from CDs or DVDs to the install directory.'); + copyLinuxForm.append(infoBar); + + // Create Linux ISO input + var iso = $('
                                      '); + var isoLabel = $('').css('vertical-align', 'middle'); + var isoInput = $('').css('width', '300px'); + iso.append(isoLabel); + iso.append(isoInput); + copyLinuxForm.append(iso); + + // Create architecture input + copyLinuxForm.append('
                                      '); + // Create distribution input + copyLinuxForm.append('
                                      '); + + /** + * Browse + */ + var browseBtn = createButton('Browse'); + iso.append(browseBtn); + // Browse server directory and files + browseBtn.serverBrowser({ + onSelect : function(path) { + $('#imageCopyCd #iso').val(path); + }, + onLoad : function() { + return $('#imageCopyCd #iso').val(); + }, + knownExt : [ 'exe', 'js', 'txt' ], + knownPaths : [ { + text : 'Install', + image : 'desktop.png', + path : '/install' + } ], + imageUrl : 'images/serverbrowser/', + systemImageUrl : 'images/serverbrowser/', + handlerUrl : 'lib/getpath.php', + title : 'Browse', + requestMethod : 'POST', + width : '500', + height : '300', + basePath : '/install' // Limit user to only install directory + }); + + // Generate tooltips + copyLinuxForm.find('div input[title],select[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + delay: 0, + predelay: 800, + events: { + def: "mouseover,mouseout", + input: "mouseover,mouseout", + widget: "focus mouseover,blur mouseout", + tooltip: "mouseover,mouseout" + }, + + // Change z index to show tooltip in front + onBeforeShow: function() { + this.getTip().css('z-index', $.topZIndex()); + } + }); + + // Open dialog to copy CD + copyLinuxForm.dialog({ + title:'Copy CD', + close: function(){ + $(this).remove(); + }, + modal: true, + width: 600, + buttons: { + "Copy": function() { + // Show loader + $('.ui-dialog #imageCopyCd').append(createLoader('')); + + // Change dialog buttons + $(this).dialog('option', 'buttons', { + 'Close': function() {$(this).dialog("close");} + }); + + // Get image attributes + var iso = $(this).find('input[name="iso"]'); + var arch = $(this).find('input[name="arch"]'); + var distro = $(this).find('input[name="distro"]'); + + // Send ajax request to copy ISO + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'copycds', + tgt : '', + args : '-n;' + distro.val() + ';-a;' + arch.val() + ';' + iso.val(), + msg : dialogId + }, + + success : function(data) { + data = decodeRsp(data); + updateImageDialog(data); + } + }); + }, + "Cancel": function() { + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Use user input or select to create image + */ +function createImage() { + var osvers = $("#createImageTab #osvers").val(); + var osarch = $("#createImageTab #osarch").val(); + var profile = $("#createImageTab #profile").val(); + var bootInterface = $("#createImageTab #netbootif").val(); + var bootMethod = $("#createImageTab #bootmethod").val(); + + $('#createImageTab .ui-state-error').remove(); + // If there no input for the bootInterface + if (!bootInterface) { + var warnBar = createWarnBar('Please specify the netboot interface'); + $("#createImageTab").prepend(warnBar); + return; + } + + var createImageArgs = "createimage;" + osvers + ";" + osarch + ";" + profile + ";" + bootInterface + ";" + bootMethod + ";"; + + $("#createImageTab :checkbox:checked").each(function() { + createImageArgs += $(this).attr("name") + ","; + }); + + createImageArgs = createImageArgs.substring(0, (createImageArgs.length - 1)); + $.ajax({ + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : createImageArgs, + msg : '' + }, + success : function(data) { + data = decodeRsp(data); + } + }); } \ No newline at end of file diff --git a/xCAT-UI/js/provision/provision.js b/xCAT-UI/js/provision/provision.js index 00a059080..f3a394df5 100644 --- a/xCAT-UI/js/provision/provision.js +++ b/xCAT-UI/js/provision/provision.js @@ -1,252 +1,258 @@ -/** - * Global variables - */ -var provisionTabs; // Provision tabs - -/** - * Set the provision tab - * - * @param obj Tab object - */ -function setProvisionTab(obj) { - provisionTabs = obj; -} - -/** - * Get the provision tab - * - * @param Nothing - * @return Tab object - */ -function getProvisionTab() { - return provisionTabs; -} - -/** - * Load provision page - */ -function loadProvisionPage() { - // If the page is loaded - if ($('#content').children().length) { - // Do not load again - return; - } - - // Get OS image names - if (!$.cookie('xcat_imagenames')){ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'osimage', - msg : '' - }, - - success : setOSImageCookies - }); - } - - // Get groups - if (!$.cookie('xcat_groups')){ - $.ajax( { - url : 'lib/cmd.php', - dataType : 'json', - data : { - cmd : 'extnoderange', - tgt : '/.*', - args : 'subgroups', - msg : '' - }, - - success : setGroupsCookies - }); - } - - // Create info bar - var infoBar = createInfoBar('Select a platform to provision or re-provision a node on, then click Ok.'); - - // Create provision page - var provPg = $('
                                      '); - provPg.append(infoBar); - - // Create provision tab - var tab = new Tab('provisionPageTabs'); - setProvisionTab(tab); - tab.init(); - $('#content').append(tab.object()); - - // Create radio buttons for platforms - var hwList = $('
                                        Platforms available:
                                      '); - var esx = $('
                                    • ESX
                                    • '); - var kvm = $('
                                    • KVM
                                    • '); - var zvm = $('
                                    • z\/VM
                                    • '); - var ipmi = $('
                                    • iDataPlex
                                    • '); - var blade = $('
                                    • BladeCenter
                                    • '); - var hmc = $('
                                    • System p
                                    • '); - - hwList.append(esx); - hwList.append(kvm); - hwList.append(zvm); - hwList.append(blade); - hwList.append(ipmi); - hwList.append(hmc); - provPg.append(hwList); - - /** - * Ok - */ - var okBtn = createButton('Ok'); - okBtn.bind('click', function(event) { - // Get hardware that was selected - var hw = $(this).parent().find('input[name="hw"]:checked').val(); - - var inst = 0; - var newTabId = hw + 'ProvisionTab' + inst; - while ($('#' + newTabId).length) { - // If one already exists, generate another one - inst = inst + 1; - newTabId = hw + 'ProvisionTab' + inst; - } - - // Create an instance of the plugin - var title = ''; - var plugin; - switch (hw) { - case "kvm": - plugin = new kvmPlugin(); - title = 'KVM'; - break; - case "esx": - plugin = new esxPlugin(); - title = 'ESX'; - break; - case "blade": - plugin = new bladePlugin(); - title = 'BladeCenter'; - break; - case "hmc": - plugin = new hmcPlugin(); - title = 'System p'; - break; - case "ipmi": - plugin = new ipmiPlugin(); - title = 'iDataPlex'; - break; - case "zvm": - plugin = new zvmPlugin(); - title = 'z/VM'; - break; - } - - // Select tab - tab.add(newTabId, title, '', true); - tab.select(newTabId); - plugin.loadProvisionPage(newTabId); - }); - provPg.append(okBtn); - - // Create resources tab - var resrcPg = $('
                                      '); - - // Create info bar - var resrcInfoBar = createInfoBar('Select a platform to view its current resources.'); - resrcPg.append(resrcInfoBar); - - // Create radio buttons for platforms - var rsrcHwList = $('
                                        Platforms available:
                                      '); - esx = $('
                                    • ESX
                                    • '); - kvm = $('
                                    • KVM
                                    • '); - zvm = $('
                                    • z\/VM
                                    • '); - ipmi = $('
                                    • iDataPlex
                                    • '); - blade = $('
                                    • BladeCenter
                                    • '); - hmc = $('
                                    • System p
                                    • '); - - rsrcHwList.append(esx); - rsrcHwList.append(kvm); - rsrcHwList.append(zvm); - rsrcHwList.append(blade); - rsrcHwList.append(ipmi); - rsrcHwList.append(hmc); - - resrcPg.append(rsrcHwList); - - var okBtn = createButton('Ok'); - okBtn.bind('click', function(event) { - // Get hardware that was selected - var hw = $(this).parent().find('input[name="rsrcHw"]:checked').val(); - - // Generate new tab ID - var newTabId = hw + 'ResourceTab'; - if (!$('#' + newTabId).length) { - // Create loader - var loader = $('
                                      ').append(createLoader(hw + 'ResourceLoader')); - - // Create an instance of the plugin - var plugin = null; - var displayName = ""; - switch (hw) { - case "kvm": - plugin = new kvmPlugin(); - displayName = "KVM"; - break; - case "esx": - plugin = new esxPlugin(); - displayName = "ESX"; - break; - case "blade": - plugin = new bladePlugin(); - displayName = "BladeCenter"; - break; - case "hmc": - plugin = new hmcPlugin(); - displayName = "System p"; - break; - case "ipmi": - plugin = new ipmiPlugin(); - displayName = "iDataPlex"; - break; - case "zvm": - plugin = new zvmPlugin(); - displayName = "z\/VM"; - break; - } - - // Add resource tab and load resources - tab.add(newTabId, displayName, loader, true); - plugin.loadResources(); - } - - // Select tab - tab.select(newTabId); - }); - - resrcPg.append(okBtn); - - // Add provision tab - tab.add('provisionTab', 'Provision', provPg, false); - // Add image tab - tab.add('imagesTab', 'Images', '', false); - // Add resource tab - tab.add('resourceTab', 'Resources', resrcPg, false); - - // Load tabs onselect - $('#provisionPageTabs').bind('tabsselect', function(event, ui){ - // Load image page - if (!$('#imagesTab').children().length && ui.index == 1) { - $('#imagesTab').append($('
                                      ').append(createLoader(''))); - loadImagesPage(); - } - }); - - // Open the quick provision tab - if (window.location.search) { - tab.add('quickProvisionTab', 'Quick Provision', '', true); - tab.select('quickProvisionTab'); - - var provForm = $('
                                      '); - $('#quickProvisionTab').append(provForm); - appendProvisionSection('quick', provForm); - } +/** + * Global variables + */ +var provisionTabs; // Provision tabs + +/** + * Set the provision tab + * + * @param obj Tab object + */ +function setProvisionTab(obj) { + provisionTabs = obj; +} + +/** + * Get the provision tab + * + * @param Nothing + * @return Tab object + */ +function getProvisionTab() { + return provisionTabs; +} + +/** + * Load provision page + */ +function loadProvisionPage() { + // If the page is loaded + if ($('#content').children().length) { + // Do not load again + return; + } + + // Get OS image names + if (!$.cookie('xcat_imagenames')){ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'osimage', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setOSImageCookies(data); + } + }); + } + + // Get groups + if (!$.cookie('xcat_groups')){ + $.ajax( { + url : 'lib/cmd.php', + dataType : 'json', + data : { + cmd : 'extnoderange', + tgt : '/.*', + args : 'subgroups', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setGroupsCookies(data); + } + }); + } + + // Create info bar + var infoBar = createInfoBar('Select a platform to provision or re-provision a node on, then click Ok.'); + + // Create provision page + var provPg = $('
                                      '); + provPg.append(infoBar); + + // Create provision tab + var tab = new Tab('provisionPageTabs'); + setProvisionTab(tab); + tab.init(); + $('#content').append(tab.object()); + + // Create radio buttons for platforms + var hwList = $('
                                        Platforms available:
                                      '); + var esx = $('
                                    • ESX
                                    • '); + var kvm = $('
                                    • KVM
                                    • '); + var zvm = $('
                                    • z\/VM
                                    • '); + var ipmi = $('
                                    • iDataPlex
                                    • '); + var blade = $('
                                    • BladeCenter
                                    • '); + var hmc = $('
                                    • System p
                                    • '); + + hwList.append(esx); + hwList.append(kvm); + hwList.append(zvm); + hwList.append(blade); + hwList.append(ipmi); + hwList.append(hmc); + provPg.append(hwList); + + /** + * Ok + */ + var okBtn = createButton('Ok'); + okBtn.bind('click', function(event) { + // Get hardware that was selected + var hw = $(this).parent().find('input[name="hw"]:checked').val(); + + var inst = 0; + var newTabId = hw + 'ProvisionTab' + inst; + while ($('#' + newTabId).length) { + // If one already exists, generate another one + inst = inst + 1; + newTabId = hw + 'ProvisionTab' + inst; + } + + // Create an instance of the plugin + var title = ''; + var plugin; + switch (hw) { + case "kvm": + plugin = new kvmPlugin(); + title = 'KVM'; + break; + case "esx": + plugin = new esxPlugin(); + title = 'ESX'; + break; + case "blade": + plugin = new bladePlugin(); + title = 'BladeCenter'; + break; + case "hmc": + plugin = new hmcPlugin(); + title = 'System p'; + break; + case "ipmi": + plugin = new ipmiPlugin(); + title = 'iDataPlex'; + break; + case "zvm": + plugin = new zvmPlugin(); + title = 'z/VM'; + break; + } + + // Select tab + tab.add(newTabId, title, '', true); + tab.select(newTabId); + plugin.loadProvisionPage(newTabId); + }); + provPg.append(okBtn); + + // Create resources tab + var resrcPg = $('
                                      '); + + // Create info bar + var resrcInfoBar = createInfoBar('Select a platform to view its current resources.'); + resrcPg.append(resrcInfoBar); + + // Create radio buttons for platforms + var rsrcHwList = $('
                                        Platforms available:
                                      '); + esx = $('
                                    • ESX
                                    • '); + kvm = $('
                                    • KVM
                                    • '); + zvm = $('
                                    • z\/VM
                                    • '); + ipmi = $('
                                    • iDataPlex
                                    • '); + blade = $('
                                    • BladeCenter
                                    • '); + hmc = $('
                                    • System p
                                    • '); + + rsrcHwList.append(esx); + rsrcHwList.append(kvm); + rsrcHwList.append(zvm); + rsrcHwList.append(blade); + rsrcHwList.append(ipmi); + rsrcHwList.append(hmc); + + resrcPg.append(rsrcHwList); + + var okBtn = createButton('Ok'); + okBtn.bind('click', function(event) { + // Get hardware that was selected + var hw = $(this).parent().find('input[name="rsrcHw"]:checked').val(); + + // Generate new tab ID + var newTabId = hw + 'ResourceTab'; + if (!$('#' + newTabId).length) { + // Create loader + var loader = $('
                                      ').append(createLoader(hw + 'ResourceLoader')); + + // Create an instance of the plugin + var plugin = null; + var displayName = ""; + switch (hw) { + case "kvm": + plugin = new kvmPlugin(); + displayName = "KVM"; + break; + case "esx": + plugin = new esxPlugin(); + displayName = "ESX"; + break; + case "blade": + plugin = new bladePlugin(); + displayName = "BladeCenter"; + break; + case "hmc": + plugin = new hmcPlugin(); + displayName = "System p"; + break; + case "ipmi": + plugin = new ipmiPlugin(); + displayName = "iDataPlex"; + break; + case "zvm": + plugin = new zvmPlugin(); + displayName = "z\/VM"; + break; + } + + // Add resource tab and load resources + tab.add(newTabId, displayName, loader, true); + plugin.loadResources(); + } + + // Select tab + tab.select(newTabId); + }); + + resrcPg.append(okBtn); + + // Add provision tab + tab.add('provisionTab', 'Provision', provPg, false); + // Add image tab + tab.add('imagesTab', 'Images', '', false); + // Add resource tab + tab.add('resourceTab', 'Resources', resrcPg, false); + + // Load tabs onselect + $('#provisionPageTabs').bind('tabsselect', function(event, ui){ + // Load image page + if (!$('#imagesTab').children().length && ui.index == 1) { + $('#imagesTab').append($('
                                      ').append(createLoader(''))); + loadImagesPage(); + } + }); + + // Open the quick provision tab + if (window.location.search) { + tab.add('quickProvisionTab', 'Quick Provision', '', true); + tab.select('quickProvisionTab'); + + var provForm = $('
                                      '); + $('#quickProvisionTab').append(provForm); + appendProvisionSection('quick', provForm); + } } \ No newline at end of file diff --git a/xCAT-UI/js/service/service.js b/xCAT-UI/js/service/service.js index 3d49b98d9..0ffcaf7d4 100644 --- a/xCAT-UI/js/service/service.js +++ b/xCAT-UI/js/service/service.js @@ -1,2153 +1,2192 @@ -/** - * Global variables - */ -var serviceTabs; -var nodeName; -var nodePath; -var nodeStatus; -var gangliaTimer; - -/** - * Initialize service page - */ -function initServicePage() { - // Load theme - var theme = $.cookie('xcat_theme'); - if (theme) { - switch (theme) { - case 'cupertino': - includeCss("css/themes/jquery-ui-cupertino.css"); - break; - case 'dark_hive': - includeCss("css/themes/jquery-ui-dark_hive.css"); - break; - case 'redmond': - includeCss("css/themes/jquery-ui-redmond.css"); - break; - case 'start': - includeCss("css/themes/jquery-ui-start.css"); - break; - case 'sunny': - includeCss("css/themes/jquery-ui-sunny.css"); - break; - case 'ui_dark': - includeCss("css/themes/jquery-ui-ui_darkness.css"); - break; - default: - includeCss("css/themes/jquery-ui-start.css"); - } - } else { - includeCss("css/themes/jquery-ui-start.css"); - } - - // Load jQuery stylesheets - includeCss("css/jquery.dataTables.css"); - includeCss("css/superfish.css"); - includeCss("css/jstree.css"); - includeCss("css/jquery.jqplot.css"); - - // Load custom stylesheet - includeCss("css/style.css"); - - // Reuqired JQuery plugins - includeJs("js/jquery/jquery.dataTables.min.js"); - includeJs("js/jquery/jquery.cookie.min.js"); - includeJs("js/jquery/tooltip.min.js"); - includeJs("js/jquery/superfish.min.js"); - includeJs("js/jquery/jquery.jqplot.min.js"); - includeJs("js/jquery/jqplot.dateAxisRenderer.min.js"); - - // Custom plugins - includeJs("js/custom/esx.js"); - includeJs("js/custom/kvm.js"); - includeJs("js/custom/zvm.js"); - - // Enable settings link - $('#xcat_settings').click(function() { - openSettings(); - }); - - // Show service page - $("#content").children().remove(); - includeJs("js/service/utils.js"); - loadServicePage(); - - // Initialize tab index history - $.cookie('xcat_tabindex_history', '0,0', { path: '/xcat', secure:true }); -} - -/** - * Load service page - */ -function loadServicePage() { - // If the page is loaded - if ($('#content').children().length) { - // Do not load again - return; - } - - // Create manage and provision tabs - serviceTabs = new Tab(); - serviceTabs.init(); - $('#content').append(serviceTabs.object()); - - var manageTabId = 'manageTab'; - serviceTabs.add(manageTabId, 'Manage', '', false); - - // Get nodes owned by user - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'nodetype', - msg : '' - }, - - success : function(data) { - setUserNodes(data); - setMaxVM(); - getUserNodesDef(); - getNodesCurrentLoad(); - loadManagePage(manageTabId); - } - }); - - // Get OS image names - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - async : true, - data : { - cmd : 'tabdump', - tgt : '', - args : 'osimage', - msg : '' - }, - - success : function(data) { - setOSImageCookies(data); - } - }); - - // Get contents of hosts table - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - async : true, - data : { - cmd : 'tabdump', - tgt : '', - args : 'hosts', - msg : '' - }, - - success : function(data) { - setGroupCookies(data); - } - }); - - var provTabId = 'provisionTab'; - serviceTabs.add(provTabId, 'Provision', '', false); - loadServiceProvisionPage(provTabId); - - serviceTabs.select(manageTabId); -} - -/** - * Load the service portal's provision page - * - * @param tabId Tab ID where page will reside - */ -function loadServiceProvisionPage(tabId) { - // Create info bar - var infoBar = createInfoBar('Select a platform to provision a node on, then click Ok.'); - - // Create provision page - var provPg = $('
                                      '); - $('#' + tabId).append(infoBar, provPg); - - // Create radio buttons for platforms - var hwList = $('
                                        Platforms available:
                                      '); - var esx = $('
                                    • ESX
                                    • '); - var kvm = $('
                                    • KVM
                                    • '); - var zvm = $('
                                    • z\/VM
                                    • '); - - hwList.append(esx); - hwList.append(kvm); - hwList.append(zvm); - provPg.append(hwList); - - /** - * Ok - */ - var okBtn = createButton('Ok'); - okBtn.bind('click', function(event) { - var userName = $.cookie('xcat_username'); - var tmp = $.cookie('xcat_' + userName + '_usrnodes'); - - // Get maximun number for nodes from cookie - var nodes = ''; - var maxVM = 0; - if (tmp.length) { - nodes = tmp.split(','); - maxVM = parseInt($.cookie('xcat_' + userName + '_maxvm')); - - // Do not allow user to clone if the maximum number of VMs is reached - if (nodes.length >= maxVM) { - var warn = createWarnBar('You have reached the maximum number of virtual machines allowed (' + maxVM + '). Delete unused virtual machines or contact your system administrator request more virtual machines.'); - warn.prependTo($('#' + tabId)); - return; - } - } - - // Get hardware that was selected - var hw = $(this).parent().find('input[name="hw"]:checked').val(); - var newTabId = hw + 'ProvisionTab'; - - if ($('#' + newTabId).size() > 0){ - serviceTabs.select(newTabId); - } else { - var title = ''; - - // Create an instance of the plugin - var plugin = null; - switch (hw) { - case "kvm": - plugin = new kvmPlugin(); - title = 'KVM'; - break; - case "esx": - plugin = new esxPlugin(); - title = 'ESX'; - break; - case "blade": - plugin = new bladePlugin(); - title = 'BladeCenter'; - break; - case "hmc": - plugin = new hmcPlugin(); - title = 'System p'; - break; - case "ipmi": - plugin = new ipmiPlugin(); - title = 'iDataPlex'; - break; - case "zvm": - plugin = new zvmPlugin(); - title = 'z/VM'; - - // Get zVM host names - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - async : false, - data : { - cmd : 'webportal', - tgt : '', - args : 'lszvm', - msg : '' - }, - - success : function(data) { - setzVMCookies(data); - } - }); - - // Get master copies for clone - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - async : false, - data : { - cmd : 'webportal', - tgt : '', - args : 'lsgoldenimages', - msg : '' - }, - - success : function(data) { - setGoldenImagesCookies(data); - } - }); - - break; - } - - // Select tab - serviceTabs.add(newTabId, title, '', true); - serviceTabs.select(newTabId); - plugin.loadServiceProvisionPage(newTabId); - } - }); - provPg.append(okBtn); -} - -/** - * Load manage page - * - * @param tabId Tab ID where page will reside - */ -function loadManagePage(tabId) { - // Create manage form - var manageForm = $('
                                      '); - - // Append to manage tab - $('#' + tabId).append(manageForm); -} - -/** - * Get the user nodes definitions - */ -function getUserNodesDef() { - var userName = $.cookie('xcat_username'); - var userNodes = $.cookie('xcat_' + userName + '_usrnodes'); - if (userNodes) { - // Get nodes definitions - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'lsdef', - tgt : '', - args : userNodes, - msg : '' - }, - - success : loadNodesTable - }); - } else { - // Clear the tab before inserting the table - $('#manageTab').append(createWarnBar('No nodes were found belonging to you!')); - } -} - -/** - * Load user nodes definitions into a table - * - * @param data Data from HTTP request - */ -function loadNodesTable(data) { - // Clear the tab before inserting the table - $('#manageTab').children().remove(); - - // Nodes datatable ID - var nodesDTId = 'userNodesDT'; - - // Hash of node attributes - var attrs = new Object(); - // Node attributes - var headers = new Object(); - var node = null, args; - // Create hash of node attributes - for (var i in data.rsp) { - // Get node name - if (data.rsp[i].indexOf('Object name:') > -1) { - var temp = data.rsp[i].split(': '); - node = jQuery.trim(temp[1]); - - // Create a hash for the node attributes - attrs[node] = new Object(); - i++; - } - - // Get key and value - args = data.rsp[i].split('=', 2); - var key = jQuery.trim(args[0]); - var val = jQuery.trim(data.rsp[i].substring(data.rsp[i].indexOf('=') + 1, data.rsp[i].length)); - - // Create a hash table - attrs[node][key] = val; - headers[key] = 1; - } - - // Sort headers - var sorted = new Array(); - var attrs2show = new Array('arch', 'groups', 'hcp', 'hostnames', 'ip', 'os', 'userid', 'mgt'); - for (var key in headers) { - // Show node attributes - if (jQuery.inArray(key, attrs2show) > -1) { - sorted.push(key); - } - } - sorted.sort(); - - // Add column for check box, node, ping, power, monitor, and comments - sorted.unshift('', - 'node', - 'status', - 'power', - 'monitor', - 'comments'); - - // Create a datatable - var nodesDT = new DataTable(nodesDTId); - nodesDT.init(sorted); - - // Go through each node - for (var node in attrs) { - // Create a row - var row = new Array(); - - // Create a check box, node link, and get node status - var checkBx = $(''); - var nodeLink = $('' + node + '').bind('click', loadNode); - - // If there is no status attribute for the node, do not try to access hash table - // Else the code will break - var status = ''; - if (attrs[node]['status']) { - status = attrs[node]['status'].replace('sshd', 'ping'); - } - - // Push in checkbox, node, status, monitor, and power - row.push(checkBx, nodeLink, status, '', ''); - - // If the node attributes are known (i.e the group is known) - if (attrs[node]['groups']) { - // Put in comments - var comments = attrs[node]['usercomment']; - // If no comments exists, show 'No comments' and set icon image source - var iconSrc; - if (!comments) { - comments = 'No comments'; - iconSrc = 'images/nodes/ui-icon-no-comment.png'; - } else { - iconSrc = 'images/nodes/ui-icon-comment.png'; - } - - // Create comments icon - var tipID = node + 'Tip'; - var icon = $('').css({ - 'width': '18px', - 'height': '18px' - }); - - // Create tooltip - var tip = createCommentsToolTip(comments); - var col = $('').append(icon); - col.append(tip); - row.push(col); - - // Generate tooltips - icon.tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.8, - relative: true, - delay: 500 - }); - } else { - // Do not put in comments if attributes are not known - row.push(''); - } - - // Go through each header - for (var i = 6; i < sorted.length; i++) { - // Add the node attributes to the row - var key = sorted[i]; - - // Do not put comments and status in twice - if (key != 'usercomment' && key != 'status' && key.indexOf('statustime') < 0) { - var val = attrs[node][key]; - if (val) { - row.push($('' + val + '')); - } else { - row.push(''); - } - } - } - - // Add the row to the table - nodesDT.add(row); - } - - // Create info bar - var infoBar = createInfoBar('Manage and monitor your virtual machines.'); - $('#manageTab').append(infoBar); - - // Insert action bar and nodes datatable - $('#manageTab').append(nodesDT.object()); - - // Turn table into a datatable - $('#' + nodesDTId).dataTable({ - 'iDisplayLength': 50, - 'bLengthChange': false, - "bScrollCollapse": true, - "sScrollY": "400px", - "sScrollX": "110%", - "bAutoWidth": true, - "oLanguage": { - "oPaginate": { - "sNext": "", - "sPrevious": "" - } - } - }); - - // Set datatable header class to add color - // $('.datatable thead').attr('class', 'ui-widget-header'); - - // Do not sort ping, power, and comment column - $('#' + nodesDTId + ' thead tr th').click(function() { - getNodeAttrs(group); - }); - var checkboxCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(0)'); - var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); - var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); - var monitorCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); - var commentCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(5)'); - checkboxCol.unbind('click'); - pingCol.unbind('click'); - powerCol.unbind('click'); - monitorCol.unbind('click'); - commentCol.unbind('click'); - - // Refresh the node ping, power, and monitor status on-click - var nodes = getNodesShown(nodesDTId); - pingCol.find('span a').click(function() { - refreshNodeStatus(nodes); - }); - powerCol.find('span a').click(function() { - refreshPowerStatus(nodes); - }); - monitorCol.find('span a').click(function() { - refreshGangliaStatus(nodes); - }); - - // Create actions menu - // Power on - var powerOnLnk = $('Power on'); - powerOnLnk.click(function() { - var tgtNodes = getNodesChecked(nodesDTId); - if (tgtNodes) { - powerNode(tgtNodes, 'on'); - } - }); - - // Power off - var powerOffLnk = $('Power off'); - powerOffLnk.click(function() { - var tgtNodes = getNodesChecked(nodesDTId); - if (tgtNodes) { - powerNode(tgtNodes, 'off'); - } - }); - - // Power softoff - var powerSoftoffLnk = $('Shutdown'); - powerSoftoffLnk.click(function() { - var tgtNodes = getNodesChecked(nodesDTId); - if (tgtNodes) { - powerNode(tgtNodes, 'softoff'); - } - }); - - // Clone - var cloneLnk = $('Clone'); - cloneLnk.click(function() { - var tgtNodes = getNodesChecked(nodesDTId); - if (tgtNodes) { - cloneNode(tgtNodes); - } - }); - - // Delete - var deleteLnk = $('Delete'); - deleteLnk.click(function() { - var tgtNodes = getNodesChecked(nodesDTId); - if (tgtNodes) { - deleteNode(tgtNodes); - } - }); - - // Unlock - var unlockLnk = $('Unlock'); - unlockLnk.click(function() { - var tgtNodes = getNodesChecked(nodesDTId); - if (tgtNodes) { - unlockNode(tgtNodes); - } - }); - - // Create action bar - var actionBar = $('
                                      ').css('width', '370px'); - - // Prepend menu to datatable - var actionsLnk = $('Actions'); - var refreshLnk = $('Refresh'); - refreshLnk.click(function() { - // Get nodes owned by user - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'nodetype', - msg : '' - }, - - success : function(data) { - // Save nodes owned by user - setUserNodes(data); - getNodesCurrentLoad(); - - // Refresh nodes table - var userName = $.cookie('xcat_username'); - var userNodes = $.cookie('xcat_' + userName + '_usrnodes'); - if (userNodes) { - // Get nodes definitions - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'lsdef', - tgt : '', - args : userNodes, - msg : '' - }, - - success : loadNodesTable - }); - } else { - // Clear the tab before inserting the table - $('#manageTab').children().remove(); - $('#manageTab').append(createWarnBar('You are not managing any node. Try to provision a node.')); - } - } - }); - }); - - var actionMenu = createMenu([cloneLnk, deleteLnk, powerOnLnk, powerOffLnk, powerSoftoffLnk, unlockLnk]); - var menu = createMenu([[actionsLnk, actionMenu], refreshLnk]); - menu.superfish(); - actionBar.append(menu); - - // Set correct theme for action menu - actionMenu.find('li').hover(function() { - setMenu2Theme($(this)); - }, function() { - setMenu2Normal($(this)); - }); - - // Create a division to hold actions menu - var menuDiv = $(''); - $('#' + nodesDTId + '_wrapper').prepend(menuDiv); - menuDiv.append(actionBar); - $('#' + nodesDTId + '_filter').appendTo(menuDiv); - - // Get power and monitor status - var nodes = getNodesShown(nodesDTId); - refreshPowerStatus(nodes); - refreshGangliaStatus(nodes); -} - -/** - * Refresh ping status for each node - * - * @param nodes Nodes to get ping status - */ -function refreshNodeStatus(nodes) { - // Show ping loader - var nodesDTId = 'userNodesDT'; - var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); - pingCol.find('img').show(); - - // Get the node ping status - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'nodestat', - tgt : nodes, - args : '-u', - msg : '' - }, - - success : loadNodePing - }); -} - -/** - * Load node ping status for each node - * - * @param data Data returned from HTTP request - */ -function loadNodePing(data) { - var nodesDTId = 'userNodesDT'; - var datatable = $('#' + nodesDTId).dataTable(); - var rsp = data.rsp; - var args, rowPos, node, status; - - // Get all nodes within datatable - for (var i in rsp) { - args = rsp[i].split(':'); - - // args[0] = node and args[1] = status - node = jQuery.trim(args[0]); - status = jQuery.trim(args[1]).replace('sshd', 'ping'); - - // Get row containing node - rowPos = findRow(node, '#' + nodesDTId, 1); - - // Update ping status column - datatable.fnUpdate(status, rowPos, 2, false); - } - - // Hide status loader - var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); - pingCol.find('img').hide(); - adjustColumnSize(nodesDTId); -} - -/** - * Refresh power status for each node - * - * @param nodes Nodes to get power status - */ -function refreshPowerStatus(nodes) { - // Show power loader - var nodesDTId = 'userNodesDT'; - var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); - powerCol.find('img').show(); - - // Get power status - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'rpower', - tgt : nodes, - args : 'stat', - msg : '' - }, - - success : loadPowerStatus - }); -} - -/** - * Load power status for each node - * - * @param data Data returned from HTTP request - */ -function loadPowerStatus(data) { - var nodesDTId = 'userNodesDT'; - var datatable = $('#' + nodesDTId).dataTable(); - var power = data.rsp; - var rowPos, node, status, args; - - for (var i in power) { - // power[0] = nodeName and power[1] = state - args = power[i].split(':'); - node = jQuery.trim(args[0]); - status = jQuery.trim(args[1]); - - // Get the row containing the node - rowPos = findRow(node, '#' + nodesDTId, 1); - - // Update the power status column - datatable.fnUpdate(status, rowPos, 3, false); - } - - // Hide power loader - var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); - powerCol.find('img').hide(); - adjustColumnSize(nodesDTId); -} - -/** - * Refresh the status of Ganglia for each node - * - * @param nodes Nodes to get Ganglia status - */ -function refreshGangliaStatus(nodes) { - // Show ganglia loader - var nodesDTId = 'userNodesDT'; - var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); - gangliaCol.find('img').show(); - - // Get the status of Ganglia - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastatus;' + nodes, - msg : '' - }, - - success : loadGangliaStatus - }); -} - -/** - * Load the status of Ganglia for a given group - * - * @param data Data returned from HTTP request - */ -function loadGangliaStatus(data) { - // Get datatable - var nodesDTId = 'userNodesDT'; - var datatable = $('#' + nodesDTId).dataTable(); - var ganglia = data.rsp; - var rowNum, node, status; - - for ( var i in ganglia) { - // ganglia[0] = nodeName and ganglia[1] = state - node = jQuery.trim(ganglia[i][0]); - status = jQuery.trim(ganglia[i][1]); - - if (node) { - // Get the row containing the node - rowNum = findRow(node, '#' + nodesDTId, 1); - - // Update the power status column - datatable.fnUpdate(status, rowNum, 4); - } - } - - // Hide Ganglia loader - var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); - gangliaCol.find('img').hide(); - adjustColumnSize(nodesDTId); -} - -/** - * Load inventory for given node - * - * @param e Windows event - */ -function loadNode(e) { - if (!e) { - e = window.event; - } - - // Get node that was clicked - var node = (e.target) ? e.target.id : e.srcElement.id; - - // Create a new tab to show inventory - var tabId = node + '_inventory'; - - if(!$('#' + tabId).length) { - // Add new tab, only if one does not exist - var loader = createLoader(node + 'Loader'); - loader = $('
                                      ').append(loader); - serviceTabs.add(tabId, node, loader, true); - - // Get node inventory - var msg = 'out=' + tabId + ',node=' + node; - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'rinv', - tgt : node, - args : 'all', - msg : msg - }, - - success : function(data) { - var args = data.msg.split(','); - - // Get node - var node = args[1].replace('node=', ''); - - // Get the management plugin - var mgt = getNodeAttr(node, 'mgt'); - - // Create an instance of the plugin - var plugin; - switch (mgt) { - case "kvm": - plugin = new kvmPlugin(); - break; - case "esx": - plugin = new esxPlugin(); - break; - case "zvm": - plugin = new zvmPlugin(); - break; - } - - // Select tab - plugin.loadServiceInventory(data); - } - }); - } - - // Select new tab - serviceTabs.select(tabId); -} - -/** - * Set a cookie for group names - * - * @param data Data from HTTP request - */ -function setGroupCookies(data) { - if (data.rsp) { - var groups = new Array(); - - // Index 0 is the table header - var cols, name, ip, hostname, desc, selectable, comments, tmp; - for (var i = 1; i < data.rsp.length; i++) { - // Set default description and selectable - selectable = "no"; - desc = "No description"; - - // Split into columns: - // node, ip, hostnames, otherinterfaces, comments, disable - cols = data.rsp[i].split(','); - name = cols[0].replace(new RegExp('"', 'g'), ''); - ip = cols[1].replace(new RegExp('"', 'g'), ''); - hostname = cols[2].replace(new RegExp('"', 'g'), ''); - - // It should return: "description: All machines; network: 10.1.100.0/24;" - comments = cols[4].replace(new RegExp('"', 'g'), ''); - tmp = comments.split('|'); - for (var j = 0; j < tmp.length; j++) { - // Save description - if (tmp[j].indexOf('description:') > -1) { - desc = tmp[j].replace('description:', ''); - desc = jQuery.trim(desc); - } - - // Is the group selectable? - if (tmp[j].indexOf('selectable:') > -1) { - selectable = tmp[j].replace('selectable:', ''); - selectable = jQuery.trim(selectable); - } - } - - // Save groups that are selectable - if (selectable == "yes") - groups.push(name + ':' + ip + ':' + hostname + ':' + desc); - } - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (240 * 60 * 1000)); - $.cookie('xcat_srv_groups', groups, { expires: exDate, path: '/xcat', secure:true }); - } -} - -/** - * Set a cookie for the OS images - * - * @param data Data from HTTP request - */ -function setOSImageCookies(data) { - // Get response - var rsp = data.rsp; - - var imageNames = new Array(); - var profilesHash = new Object(); - var osVersHash = new Object(); - var osArchsHash = new Object(); - var imagePos = 0; - var profilePos = 0; - var osversPos = 0; - var osarchPos = 0; - var provMethodPos = 0; - var comments = 0; - var desc, selectable, tmp; - // Get column index for each attribute - var colNameArray = rsp[0].substr(1).split(','); - for (var i in colNameArray){ - switch (colNameArray[i]){ - case 'imagename': { - imagePos = i; - } - break; - - case 'profile':{ - profilePos = i; - } - break; - - case 'osvers':{ - osversPos = i; - } - break; - - case 'osarch':{ - osarchPos = i; - } - break; - - case 'comments':{ - comments = i; - } - break; - - case 'provmethod':{ - provMethodPos = i; - } - break; - - default : - break; - } - } - - // Go through each index - for (var i = 1; i < rsp.length; i++) { - // Get image name - var cols = rsp[i].split(','); - var osImage = cols[imagePos].replace(new RegExp('"', 'g'), ''); - var profile = cols[profilePos].replace(new RegExp('"', 'g'), ''); - var provMethod = cols[provMethodPos].replace(new RegExp('"', 'g'), ''); - var osVer = cols[osversPos].replace(new RegExp('"', 'g'), ''); - var osArch = cols[osarchPos].replace(new RegExp('"', 'g'), ''); - var osComments = cols[comments].replace(new RegExp('"', 'g'), ''); - - // Only save install boot - if (provMethod.indexOf('install') > -1) { - if (osComments) { - // Only enable images where description and selectable comments exist - // Set default description and selectable - selectable = "no"; - desc = "No description"; - - tmp = osComments.split('|'); - for (var j = 0; j < tmp.length; j++) { - // Save description - if (tmp[j].indexOf('description:') > -1) { - desc = tmp[j].replace('description:', ''); - desc = jQuery.trim(desc); - } - - // Is the image selectable? - if (tmp[j].indexOf('selectable:') > -1) { - selectable = tmp[j].replace('selectable:', ''); - selectable = jQuery.trim(selectable); - } - } - - // Save images that are selectable - if (selectable == "yes") - imageNames.push(osImage + ':' + desc); - } - - profilesHash[profile] = 1; - osVersHash[osVer] = 1; - osArchsHash[osArch] = 1; - } - } - - // Save image names in a cookie - $.cookie('xcat_srv_imagenames', imageNames); - - // Save profiles in a cookie - var tmp = new Array; - for (var key in profilesHash) { - tmp.push(key); - } - $.cookie('xcat_srv_profiles', tmp); - - // Save OS versions in a cookie - tmp = new Array; - for (var key in osVersHash) { - tmp.push(key); - } - $.cookie('xcat_srv_osvers', tmp); - - // Save OS architectures in a cookie - tmp = new Array; - for (var key in osArchsHash) { - tmp.push(key); - } - $.cookie('xcat_srv_osarchs', tmp); -} - - - -/** - * Set a cookie for user nodes - * - * @param data Data from HTTP request - */ -function setUserNodes(data) { - if (data.rsp) { - // Get user name that is logged in - var userName = $.cookie('xcat_username'); - var usrNodes = new Array(); - - // Ignore first columns because it is the header - for ( var i = 1; i < data.rsp.length; i++) { - // Go through each column - // where column names are: node, os, arch, profile, provmethod, supportedarchs, nodetype, comments, disable - var cols = data.rsp[i].split(','); - var node = cols[0].replace(new RegExp('"', 'g'), ''); - - // Comments can contain the owner and description - var comments = new Array(); - if (cols[7].indexOf(';') > -1) { - comments = cols[7].replace(new RegExp('"', 'g'), '').split(';'); - } else { - comments.push(cols[7].replace(new RegExp('"', 'g'), '')); - } - - // Extract the owner - var owner; - for (var j in comments) { - if (comments[j].indexOf('owner:') > -1) { - owner = comments[j].replace('owner:', ''); - - if (owner == userName) { - usrNodes.push(node); - } - - break; - } - } - } // End of for - - // Set cookie to expire in 240 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (240 * 60 * 1000)); - $.cookie('xcat_' + userName + '_usrnodes', usrNodes, { expires: exDate, path: '/xcat', secure:true }); - } // End of if -} - -/** - * Power on a given node - * - * @param tgtNodes Node to power on or off - * @param power2 Power node to given state - */ -function powerNode(tgtNodes, power2) { - // Show power loader - var nodesDTId = 'userNodesDT'; - var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); - powerCol.find('img').show(); - - var nodes = tgtNodes.split(','); - for (var n in nodes) { - // Get hardware that was selected - var hw = getUserNodeAttr(nodes[n], 'mgt'); - - // Change to power softoff (to gracefully shutdown) - switch (hw) { - case "blade": - break; - case "hmc": - break; - case "ipmi": - break; - case "zvm": - if (power2 == 'off') { - power2 = 'softoff'; - } - - break; - } - } - - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'rpower', - tgt : tgtNodes, - args : power2, - msg : tgtNodes - }, - - success : updatePowerStatus - }); -} - -/** - * Update power status of a node in the datatable - * - * @param data Data from HTTP request - */ -function updatePowerStatus(data) { - // Get datatable - var nodesDTId = 'userNodesDT'; - var dTable = $('#' + nodesDTId).dataTable(); - - // Get xCAT response - var rsp = data.rsp; - // Loop through each line - var node, status, rowPos, strPos; - for (var i in rsp) { - // Get node name - node = rsp[i].split(":")[0]; - - // If there is no error - if (rsp[i].indexOf("Error") < 0 || rsp[i].indexOf("Failed") < 0) { - // Get the row containing the node link - rowPos = findRow(node, '#' + nodesDTId, 1); - - // If it was power on, then the data return would contain "Starting" - strPos = rsp[i].indexOf("Starting"); - if (strPos > -1) { - status = 'on'; - } else { - status = 'off'; - } - - // Update the power status column - dTable.fnUpdate(status, rowPos, 3, false); - } else { - // Power on/off failed - alert(rsp[i]); - } - } - - var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); - powerCol.find('img').hide(); - adjustColumnSize(nodesDTId); -} - -/** - * Turn on monitoring for a given node - * - * @param node Node to monitor on or off - * @param monitor Monitor state, on or off - */ -function monitorNode(node, monitor) { - // Show ganglia loader - var nodesDTId = 'userNodesDT'; - var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); - gangliaCol.find('img').show(); - - if (monitor == 'on') { - if (node) { - // Check if ganglia RPMs are installed - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliacheck;' + node, - msg : node // Node range will be passed along in data.msg - }, - - /** - * Start ganglia on a given node range - * - * @param data - * Data returned from HTTP request - * @return Nothing - */ - success : function(data) { - // Get response - var out = data.rsp[0].split(/\n/); - - // Go through each line - var warn = false; - var warningMsg = ''; - for (var i in out) { - // If an RPM is not installed - if (out[i].indexOf('not installed') > -1) { - warn = true; - - if (warningMsg) { - warningMsg += '
                                      ' + out[i]; - } else { - warningMsg = out[i]; - } - } - } - - // If there are warnings - if (warn) { - // Create warning bar - var warningBar = createWarnBar(warningMsg); - warningBar.css('margin-bottom', '10px'); - warningBar.prependTo($('#nodesTab')); - } else { - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastart;' + data.msg, - msg : data.msg - }, - - success : function(data) { - // Remove any warnings - $('#nodesTab').find('.ui-state-error').remove(); - refreshGangliaStatus(data.msg); - } - }); - } // End of if (warn) - } // End of function(data) - }); - } - } else { - var args; - if (node) { - args = 'gangliastop;' + node; - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : args, - msg : node - }, - - success : function(data) { - refreshGangliaStatus(data.msg); - } - }); - } - } -} - -/** - * Open a dialog to clone node - * - * @param tgtNodes Nodes to clone - */ -function cloneNode(tgtNodes) { - var userName = $.cookie('xcat_username'); - var nodes = tgtNodes.split(','); - var tmp = $.cookie('xcat_' + userName + '_usrnodes'); - var usrNodes = tmp.split(','); - - var maxVM = parseInt($.cookie('xcat_' + userName + '_maxvm')); - - // Do not allow user to clone if the maximum number of VMs is reached - if (usrNodes.length >= maxVM) { - var warn = createWarnBar('You have reached the maximum number of virtual machines allowed (' + maxVM + '). Delete un-used virtual machines or contact your system administrator request more virtual machines.'); - warn.prependTo($('#manageTab')); - return; - } - - for (var n in nodes) { - // Get hardware that was selected - var hw = getUserNodeAttr(nodes[n], 'mgt'); - - // Create an instance of the plugin - var plugin; - switch (hw) { - case "kvm": - plugin = new kvmPlugin(); - break; - case "esx": - plugin = new esxPlugin(); - break; - case "zvm": - plugin = new zvmPlugin(); - break; - } - - // Clone node - plugin.serviceClone(nodes[n]); - } -} - - -/** - * Open a dialog to delete node - * - * @param tgtNodes Nodes to delete - */ -function deleteNode(tgtNodes) { - var nodes = tgtNodes.split(','); - - // Loop through each node and create target nodes string - var tgtNodesStr = ''; - for (var i in nodes) { - if (i == 0 && i == nodes.length - 1) { - // If it is the 1st and only node - tgtNodesStr += nodes[i]; - } else if (i == 0 && i != nodes.length - 1) { - // If it is the 1st node of many nodes, append a comma to the string - tgtNodesStr += nodes[i] + ', '; - } else { - if (i == nodes.length - 1) { - // If it is the last node, append nothing to the string - tgtNodesStr += nodes[i]; - } else { - // Append a comma to the string - tgtNodesStr += nodes[i] + ', '; - } - } - } - - // Confirm delete of node - var dialog = $('
                                      '); - var warn = createWarnBar('Are you sure you want to delete ' + tgtNodesStr + '?'); - dialog.append(warn); - - // Open dialog - dialog.dialog({ - title: "Confirm", - modal: true, - close: function(){ - $(this).remove(); - }, - width: 400, - buttons: { - "Yes": function(){ - // Create status bar and append to tab - var instance = 0; - var statBarId = 'deleteStat' + instance; - while ($('#' + statBarId).length) { - // If one already exists, generate another one - instance = instance + 1; - statBarId = 'deleteStat' + instance; - } - - var statBar = createStatusBar(statBarId); - var loader = createLoader(''); - statBar.find('div').append(loader); - statBar.prependTo($('#manageTab')); - - // Delete the virtual server - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'rmvm', - tgt : tgtNodes, - args : '', - msg : 'out=' + statBarId + ';cmd=rmvm;tgt=' + tgtNodes - }, - - success : function(data) { - var args = data.msg.split(';'); - var statBarId = args[0].replace('out=', ''); - var tgts = args[2].replace('tgt=', '').split(','); - - // Get data table - var nodesDTId = 'userNodesDT'; - var dTable = $('#' + nodesDTId).dataTable(); - var failed = false; - - // Create an info box to show output - var output = writeRsp(data.rsp, ''); - output.css('margin', '0px'); - // Remove loader and append output - $('#' + statBarId + ' img').remove(); - $('#' + statBarId + ' div').append(output); - - // If there was an error, do not continue - if (output.html().indexOf('Error') > -1) { - failed = true; - } - - // Update data table - var rowPos; - for (var i in tgts) { - if (!failed) { - // Get row containing the node link and delete it - rowPos = findRow(tgts[i], '#' + nodesDTId, 1); - dTable.fnDeleteRow(rowPos); - } - } - - // Refresh nodes owned by user - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'tabdump', - tgt : '', - args : 'nodetype', - msg : '' - }, - - success : function(data) { - setUserNodes(data); - } - }); - } - }); - - $(this).dialog("close"); - }, - "No": function() { - $(this).dialog("close"); - } - } - }); -} - -/** - * Unlock a node by setting the ssh keys - * - * @param tgtNodes Nodes to unlock - */ -function unlockNode(tgtNodes) { - var nodes = tgtNodes.split(','); - - // Loop through each node and create target nodes string - var tgtNodesStr = ''; - for (var i in nodes) { - if (i == 0 && i == nodes.length - 1) { - // If it is the 1st and only node - tgtNodesStr += nodes[i]; - } else if (i == 0 && i != nodes.length - 1) { - // If it is the 1st node of many nodes, append a comma to the string - tgtNodesStr += nodes[i] + ', '; - } else { - if (i == nodes.length - 1) { - // If it is the last node, append nothing to the string - tgtNodesStr += nodes[i]; - } else { - // Append a comma to the string - tgtNodesStr += nodes[i] + ', '; - } - } - } - - var dialog = $('
                                      '); - var infoBar = createInfoBar('Give the root password for this node range to setup its SSH keys.'); - dialog.append(infoBar); - - var unlockForm = $('
                                      ').css('margin', '5px'); - unlockForm.append('
                                      '); - unlockForm.append('
                                      '); - dialog.append(unlockForm); - - dialog.find('div input').css('margin', '5px'); - - // Generate tooltips - unlockForm.find('div input[title]').tooltip({ - position: "center right", - offset: [-2, 10], - effect: "fade", - opacity: 0.7, - predelay: 800, - events : { - def : "mouseover,mouseout", - input : "mouseover,mouseout", - widget : "focus mouseover,blur mouseout", - tooltip : "mouseover,mouseout" - } - }); - - // Open dialog - dialog.dialog({ - title: "Confirm", - modal: true, - close: function(){ - $(this).remove(); - }, - width: 450, - buttons: { - "Ok": function(){ - // Create status bar and append to tab - var instance = 0; - var statBarId = 'unlockStat' + instance; - while ($('#' + statBarId).length) { - // If one already exists, generate another one - instance = instance + 1; - statBarId = 'unlockStat' + instance; - } - - var statBar = createStatusBar(statBarId); - var loader = createLoader(''); - statBar.find('div').append(loader); - statBar.prependTo($('#manageTab')); - - // If a password is given - var password = unlockForm.find('input[name=password]:eq(0)'); - if (password.val()) { - // Setup SSH keys - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'unlock;' + tgtNodes + ';' + password.val(), - msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes - }, - - success : function(data) { - // Create an info box to show output - var output = writeRsp(data.rsp, ''); - output.css('margin', '0px'); - // Remove loader and append output - $('#' + statBarId + ' img').remove(); - $('#' + statBarId + ' div').append(output); - } - }); - - $(this).dialog("close"); - } - }, - "Cancel": function() { - $(this).dialog("close"); - } - } - }); -} - -/** - * Get nodes current load information - */ -function getNodesCurrentLoad(){ - var userName = $.cookie('xcat_username'); - var nodes = $.cookie('xcat_' + userName + '_usrnodes'); - - // Get nodes current status - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliacurrent;node;' + nodes, - msg : '' - }, - - success: saveNodeLoad - }); -} - -/** - * Save node load data - * - * @param status Data returned from HTTP request - */ -function saveNodeLoad(status){ - // Save node path and status for future use - nodePath = new Object(); - nodeStatus = new Object(); - - // Get nodes status - var nodes = status.rsp[0].split(';'); - - var i = 0, pos = 0; - var node = '', tmpStr = ''; - var tmpArry; - - for (i = 0; i < nodes.length; i++){ - tmpStr = nodes[i]; - pos = tmpStr.indexOf(':'); - node = tmpStr.substring(0, pos); - tmpArry = tmpStr.substring(pos + 1).split(','); - - switch(tmpArry[0]){ - case 'UNKNOWN':{ - nodeStatus[node] = -2; - } - break; - case 'ERROR':{ - nodeStatus[node] = -1; - } - break; - case 'WARNING':{ - nodeStatus[node] = 0; - nodePath[node] = tmpArry[1]; - } - break; - case 'NORMAL':{ - nodeStatus[node] = 1; - nodePath[node] = tmpArry[1]; - } - break; - } - } -} - -/** - * Get monitoring metrics and load into inventory fieldset - * - * @param node Node to collect metrics - */ -function getMonitorMetrics(node) { - // Inventory tab should have this fieldset already created - // e.g.
                                      - $('#' + node + '_monitor').children('div').remove(); - - // Before trying to get the metrics, check if Ganglia is running - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliastatus;' + node, - msg : '' - }, - - success: function(data) { - var ganglia = data.rsp; - var node, status; - - // Get the ganglia status - for (var i in ganglia) { - // ganglia[0] = nodeName and ganglia[1] = state - node = jQuery.trim(ganglia[i][0]); - status = jQuery.trim(ganglia[i][1]); - - if (node && status == 'on') { - // Get monitoring metrics - $.ajax({ - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'gangliashow;' + nodePath[node] + ';hour;_summary_', - msg : node - }, - - success: drawMonitoringCharts - }); - } else if (node && status == 'off') { - var info = createInfoBar('Ganglia monitoring is disabled for this node'); - $('#' + node + '_monitor').append(info.css('width', '300px')); - } - } // End of for - } // End of function - }); -} - -/** - * Draw monitoring charts based on node metrics - * - * @param data Data returned from HTTP request - */ -function drawMonitoringCharts(data){ - var nodeMetrics = new Object(); - var metricData = data.rsp[0].split(';'); - var node = data.msg; - - var metricName = ''; - var metricVal = ''; - var pos = 0; - - // Go through the metrics returned - for (var m = 0; m < metricData.length; m++){ - pos = metricData[m].indexOf(':'); - - // Get metric name - metricName = metricData[m].substr(0, pos); - nodeMetrics[metricName] = new Array(); - // Get metric values - metricVal = metricData[m].substr(pos + 1).split(','); - // Save node metrics - for (var i = 0; i < metricVal.length; i++){ - nodeMetrics[metricName].push(Number(metricVal[i])); - } - } - - drawLoadFlot(node, nodeMetrics['load_one'], nodeMetrics['cpu_num']); - drawCpuFlot(node, nodeMetrics['cpu_idle']); - drawMemFlot(node, nodeMetrics['mem_free'], nodeMetrics['mem_total']); - drawDiskFlot(node, nodeMetrics['disk_free'], nodeMetrics['disk_total']); - drawNetworkFlot(node, nodeMetrics['bytes_in'], nodeMetrics['bytes_out']); -} - -/** - * Draw load metrics flot - * - * @param node Node name - * @param loadpair Load timestamp and value pair - * @param cpupair CPU number and value pair - */ -function drawLoadFlot(node, loadPair, cpuPair){ - var load = new Array(); - var cpu = new Array(); - - var i = 0; - var yAxisMax = 0; - var interval = 1; - - // Append flot to node monitoring fieldset - var loadFlot = $('
                                      ').css({ - 'float': 'left', - 'height': '150px', - 'margin': '0 0 10px', - 'width': '300px' - }); - $('#' + node + '_monitor').append(loadFlot); - $('#' + node + '_load').empty(); - - // Parse load pair where: - // timestamp must be mutiplied by 1000 and Javascript timestamp is in ms - for (i = 0; i < loadPair.length; i += 2){ - load.push([loadPair[i] * 1000, loadPair[i + 1]]); - if (loadPair[i + 1] > yAxisMax){ - yAxisMax = loadPair[i + 1]; - } - } - - // Parse CPU pair - for (i = 0; i < cpuPair.length; i += 2){ - cpu.push([cpuPair[i] * 1000, cpuPair[i + 1]]); - if (cpuPair[i + 1] > yAxisMax){ - yAxisMax = cpuPair[i + 1]; - } - } - - interval = parseInt(yAxisMax / 3); - if (interval < 1){ - interval = 1; - } - - $.jqplot(node + '_load', [load, cpu],{ - title: ' Loads/Procs Last Hour', - axes:{ - xaxis:{ - renderer : $.jqplot.DateAxisRenderer, - numberTicks: 4, - tickOptions : { - formatString : '%R', - show : true - } - }, - yaxis: { - min : 0, - tickInterval : interval - } - }, - legend : { - show: true, - location: 'nw' - }, - series:[{label:'Load'}, {label: 'CPU Number'}], - seriesDefaults : {showMarker: false} - }); -} - -/** - * Draw CPU usage flot - * - * @param node Node name - * @param cpuPair CPU timestamp and value pair - */ -function drawCpuFlot(node, cpuPair){ - var cpu = new Array(); - - // Append flot to node monitoring fieldset - var cpuFlot = $('
                                      ').css({ - 'float': 'left', - 'height': '150px', - 'margin': '0 0 10px', - 'width': '300px' - }); - $('#' + node + '_monitor').append(cpuFlot); - $('#' + node + '_cpu').empty(); - - // Time stamp should by mutiplied by 1000 - // CPU idle comes from server, subtract 1 from idle - for(var i = 0; i < cpuPair.length; i +=2){ - cpu.push([(cpuPair[i] * 1000), (100 - cpuPair[i + 1])]); - } - - $.jqplot(node + '_cpu', [cpu],{ - title: 'CPU Use Last Hour', - axes:{ - xaxis:{ - renderer : $.jqplot.DateAxisRenderer, - numberTicks: 4, - tickOptions : { - formatString : '%R', - show : true - } - }, - yaxis: { - min : 0, - max : 100, - tickOptions:{formatString : '%d\%'} - } - }, - seriesDefaults : {showMarker: false} - }); -} - -/** - * Draw memory usage flot - * - * @param node Node name - * @param freePair Free memory timestamp and value pair - * @param totalPair Total memory timestamp and value pair - */ -function drawMemFlot(node, freePair, totalPair){ - var used = new Array(); - var total = new Array(); - var size = 0; - - // Append flot to node monitoring fieldset - var memoryFlot = $('
                                      ').css({ - 'float': 'left', - 'height': '150px', - 'margin': '0 0 10px', - 'width': '300px' - }); - $('#' + node + '_monitor').append(memoryFlot); - $('#' + node + '_memory').empty(); - - if(freePair.length < totalPair.length){ - size = freePair.length; - } else { - size = freePair.length; - } - - var tmpTotal, tmpUsed; - for(var i = 0; i < size; i+=2){ - tmpTotal = totalPair[i+1]; - tmpUsed = tmpTotal-freePair[i+1]; - tmpTotal = tmpTotal/1000000; - tmpUsed = tmpUsed/1000000; - total.push([totalPair[i]*1000, tmpTotal]); - used.push([freePair[i]*1000, tmpUsed]); - } - - $.jqplot(node + '_memory', [used, total],{ - title: 'Memory Use Last Hour', - axes:{ - xaxis:{ - renderer : $.jqplot.DateAxisRenderer, - numberTicks: 4, - tickOptions : { - formatString : '%R', - show : true - } - }, - yaxis: { - min : 0, - tickOptions:{formatString : '%.2fG'} - } - }, - legend : { - show: true, - location: 'nw' - }, - series:[{label:'Used'}, {label: 'Total'}], - seriesDefaults : {showMarker: false} - }); -} - -/** - * Draw disk usage flot - * - * @param node Node name - * @param freePair Free disk space (Ganglia only logs free data) - * @param totalPair Total disk space - */ -function drawDiskFlot(node, freePair, totalPair) { - var used = new Array(); - var total = new Array(); - var size = 0; - - // Append flot to node monitoring fieldset - var diskFlot = $('
                                      ').css({ - 'float' : 'left', - 'height' : '150px', - 'margin' : '0 0 10px', - 'width' : '300px' - }); - $('#' + node + '_monitor').append(diskFlot); - $('#' + node + '_disk').empty(); - - if (freePair.length < totalPair.length) { - size = freePair.length; - } else { - size = freePair.length; - } - - var tmpTotal, tmpUsed; - for ( var i = 0; i < size; i += 2) { - tmpTotal = totalPair[i + 1]; - tmpUsed = tmpTotal - freePair[i + 1]; - total.push([ totalPair[i] * 1000, tmpTotal ]); - used.push([ freePair[i] * 1000, tmpUsed ]); - } - - $.jqplot(node + '_disk', [ used, total ], { - title : 'Disk Use Last Hour', - axes : { - xaxis : { - renderer : $.jqplot.DateAxisRenderer, - numberTicks : 4, - tickOptions : { - formatString : '%R', - show : true - } - }, - yaxis : { - min : 0, - tickOptions : { - formatString : '%.2fG' - } - } - }, - legend : { - show : true, - location : 'nw' - }, - series : [ { - label : 'Used' - }, { - label : 'Total' - } ], - seriesDefaults : { - showMarker : false - } - }); -} - -/** - * Draw network usage flot - * - * @param node Node name - * @param freePair Free memory timestamp and value pair - * @param totalPair Total memory timestamp and value pair - */ -function drawNetworkFlot(node, inPair, outPair) { - var inArray = new Array(); - var outArray = new Array(); - var maxVal = 0; - var unitName = 'B'; - var divisor = 1; - - // Append flot to node monitoring fieldset - var diskFlot = $('
                                      ').css({ - 'float' : 'left', - 'height' : '150px', - 'margin' : '0 0 10px', - 'width' : '300px' - }); - $('#' + node + '_monitor').append(diskFlot); - $('#' + node + '_network').empty(); - - for (var i = 0; i < inPair.length; i += 2) { - if (inPair[i + 1] > maxVal) { - maxVal = inPair[i + 1]; - } - } - - for (var i = 0; i < outPair.length; i += 2) { - if (outPair[i + 1] > maxVal) { - maxVal = outPair[i + 1]; - } - } - - if (maxVal > 3000000) { - divisor = 1000000; - unitName = 'GB'; - } else if (maxVal >= 3000) { - divisor = 1000; - unitName = 'MB'; - } else { - // Do nothing - } - - for (i = 0; i < inPair.length; i += 2) { - inArray.push([ (inPair[i] * 1000), (inPair[i + 1] / divisor) ]); - } - - for (i = 0; i < outPair.length; i += 2) { - outArray.push([ (outPair[i] * 1000), (outPair[i + 1] / divisor) ]); - } - - $.jqplot(node + '_network', [ inArray, outArray ], { - title : 'Network Last Hour', - axes : { - xaxis : { - renderer : $.jqplot.DateAxisRenderer, - numberTicks : 4, - tickOptions : { - formatString : '%R', - show : true - } - }, - yaxis : { - min : 0, - tickOptions : { - formatString : '%d' + unitName - } - } - }, - legend : { - show : true, - location : 'nw' - }, - series : [ { - label : 'In' - }, { - label : 'Out' - } ], - seriesDefaults : { - showMarker : false - } - }); -} - -/** - * Get an attribute of a given node - * - * @param node The node - * @param attrName The attribute - * @return The attribute of the node - */ -function getNodeAttr(node, attrName) { - // Get the row - var row = $('[id=' + node + ']').parents('tr'); - - // Search for the column containing the attribute - var attrCol = null; - - var cols = row.parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); - // Loop through each column - for (var i in cols) { - // Find column that matches the attribute - if (cols.eq(i).html() == attrName) { - attrCol = cols.eq(i); - break; - } - } - - // If the column containing the attribute is found - if (attrCol) { - // Get the attribute column index - var attrIndex = attrCol.index(); - - // Get the attribute for the given node - var attr = row.find('td:eq(' + attrIndex + ')'); - return attr.text(); - } else { - return ''; - } -} - -/** - * Set the maximum number of VMs a user could have - */ -function setMaxVM() { - var userName = $.cookie('xcat_username'); - - $.ajax( { - url : 'lib/srv_cmd.php', - dataType : 'json', - data : { - cmd : 'webportal', - tgt : '', - args : 'getmaxvm;' + userName, - msg : '' - }, - - success : function(data) { - // Get response - var rsp = jQuery.trim(data.rsp); - rsp = rsp.replace('Max allowed:', ''); - - // Set cookie to expire in 60 minutes - var exDate = new Date(); - exDate.setTime(exDate.getTime() + (240 * 60 * 1000)); - $.cookie('xcat_' + userName + '_maxvm', rsp, { expires: exDate }); - } - }); +/** + * Global variables + */ +var serviceTabs; +var nodeName; +var nodePath; +var nodeStatus; +var gangliaTimer; + +/** + * Initialize service page + */ +function initServicePage() { + // Load theme + var theme = $.cookie('xcat_theme'); + if (theme) { + switch (theme) { + case 'cupertino': + includeCss("css/themes/jquery-ui-cupertino.css"); + break; + case 'dark_hive': + includeCss("css/themes/jquery-ui-dark_hive.css"); + break; + case 'redmond': + includeCss("css/themes/jquery-ui-redmond.css"); + break; + case 'start': + includeCss("css/themes/jquery-ui-start.css"); + break; + case 'sunny': + includeCss("css/themes/jquery-ui-sunny.css"); + break; + case 'ui_dark': + includeCss("css/themes/jquery-ui-ui_darkness.css"); + break; + default: + includeCss("css/themes/jquery-ui-start.css"); + } + } else { + includeCss("css/themes/jquery-ui-start.css"); + } + + // Load jQuery stylesheets + includeCss("css/jquery.dataTables.css"); + includeCss("css/superfish.css"); + includeCss("css/jstree.css"); + includeCss("css/jquery.jqplot.css"); + + // Load custom stylesheet + includeCss("css/style.css"); + + // Reuqired JQuery plugins + includeJs("js/jquery/jquery.dataTables.min.js"); + includeJs("js/jquery/jquery.cookie.min.js"); + includeJs("js/jquery/tooltip.min.js"); + includeJs("js/jquery/superfish.min.js"); + includeJs("js/jquery/jquery.jqplot.min.js"); + includeJs("js/jquery/jqplot.dateAxisRenderer.min.js"); + + // Custom plugins + includeJs("js/custom/esx.js"); + includeJs("js/custom/kvm.js"); + includeJs("js/custom/zvm.js"); + + // Enable settings link + $('#xcat_settings').click(function() { + openSettings(); + }); + + // Show service page + $("#content").children().remove(); + includeJs("js/service/utils.js"); + loadServicePage(); + + // Initialize tab index history + $.cookie('xcat_tabindex_history', '0,0', { path: '/xcat', secure:true }); +} + +/** + * Load service page + */ +function loadServicePage() { + // If the page is loaded + if ($('#content').children().length) { + // Do not load again + return; + } + + // Create manage and provision tabs + serviceTabs = new Tab(); + serviceTabs.init(); + $('#content').append(serviceTabs.object()); + + var manageTabId = 'manageTab'; + serviceTabs.add(manageTabId, 'Manage', '', false); + + // Get nodes owned by user + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'nodetype', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setUserNodes(data); + setMaxVM(); + getUserNodesDef(); + getNodesCurrentLoad(); + loadManagePage(manageTabId); + } + }); + + // Get OS image names + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + async : true, + data : { + cmd : 'tabdump', + tgt : '', + args : 'osimage', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setOSImageCookies(data); + } + }); + + // Get contents of hosts table + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + async : true, + data : { + cmd : 'tabdump', + tgt : '', + args : 'hosts', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setGroupCookies(data); + } + }); + + var provTabId = 'provisionTab'; + serviceTabs.add(provTabId, 'Provision', '', false); + loadServiceProvisionPage(provTabId); + + serviceTabs.select(manageTabId); +} + +/** + * Load the service portal's provision page + * + * @param tabId Tab ID where page will reside + */ +function loadServiceProvisionPage(tabId) { + // Create info bar + var infoBar = createInfoBar('Select a platform to provision a node on, then click Ok.'); + + // Create provision page + var provPg = $('
                                      '); + $('#' + tabId).append(infoBar, provPg); + + // Create radio buttons for platforms + var hwList = $('
                                        Platforms available:
                                      '); + var esx = $('
                                    • ESX
                                    • '); + var kvm = $('
                                    • KVM
                                    • '); + var zvm = $('
                                    • z\/VM
                                    • '); + + hwList.append(esx); + hwList.append(kvm); + hwList.append(zvm); + provPg.append(hwList); + + /** + * Ok + */ + var okBtn = createButton('Ok'); + okBtn.bind('click', function(event) { + var userName = $.cookie('xcat_username'); + var tmp = $.cookie('xcat_' + userName + '_usrnodes'); + + // Get maximun number for nodes from cookie + var nodes = ''; + var maxVM = 0; + if (tmp.length) { + nodes = tmp.split(','); + maxVM = parseInt($.cookie('xcat_' + userName + '_maxvm')); + + // Do not allow user to clone if the maximum number of VMs is reached + if (nodes.length >= maxVM) { + var warn = createWarnBar('You have reached the maximum number of virtual machines allowed (' + maxVM + '). Delete unused virtual machines or contact your system administrator request more virtual machines.'); + warn.prependTo($('#' + tabId)); + return; + } + } + + // Get hardware that was selected + var hw = $(this).parent().find('input[name="hw"]:checked').val(); + var newTabId = hw + 'ProvisionTab'; + + if ($('#' + newTabId).size() > 0){ + serviceTabs.select(newTabId); + } else { + var title = ''; + + // Create an instance of the plugin + var plugin = null; + switch (hw) { + case "kvm": + plugin = new kvmPlugin(); + title = 'KVM'; + break; + case "esx": + plugin = new esxPlugin(); + title = 'ESX'; + break; + case "blade": + plugin = new bladePlugin(); + title = 'BladeCenter'; + break; + case "hmc": + plugin = new hmcPlugin(); + title = 'System p'; + break; + case "ipmi": + plugin = new ipmiPlugin(); + title = 'iDataPlex'; + break; + case "zvm": + plugin = new zvmPlugin(); + title = 'z/VM'; + + // Get zVM host names + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + async : false, + data : { + cmd : 'webportal', + tgt : '', + args : 'lszvm', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setzVMCookies(data); + } + }); + + // Get master copies for clone + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + async : false, + data : { + cmd : 'webportal', + tgt : '', + args : 'lsgoldenimages', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setGoldenImagesCookies(data); + } + }); + + break; + } + + // Select tab + serviceTabs.add(newTabId, title, '', true); + serviceTabs.select(newTabId); + plugin.loadServiceProvisionPage(newTabId); + } + }); + provPg.append(okBtn); +} + +/** + * Load manage page + * + * @param tabId Tab ID where page will reside + */ +function loadManagePage(tabId) { + // Create manage form + var manageForm = $('
                                      '); + + // Append to manage tab + $('#' + tabId).append(manageForm); +} + +/** + * Get the user nodes definitions + */ +function getUserNodesDef() { + var userName = $.cookie('xcat_username'); + var userNodes = $.cookie('xcat_' + userName + '_usrnodes'); + if (userNodes) { + // Get nodes definitions + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : userNodes, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadNodesTable(data); + } + }); + } else { + // Clear the tab before inserting the table + $('#manageTab').append(createWarnBar('No nodes were found belonging to you!')); + } +} + +/** + * Load user nodes definitions into a table + * + * @param data Data from HTTP request + */ +function loadNodesTable(data) { + // Clear the tab before inserting the table + $('#manageTab').children().remove(); + + // Nodes datatable ID + var nodesDTId = 'userNodesDT'; + + // Hash of node attributes + var attrs = new Object(); + // Node attributes + var headers = new Object(); + var node = null, args; + // Create hash of node attributes + for (var i in data.rsp) { + // Get node name + if (data.rsp[i].indexOf('Object name:') > -1) { + var temp = data.rsp[i].split(': '); + node = jQuery.trim(temp[1]); + + // Create a hash for the node attributes + attrs[node] = new Object(); + i++; + } + + // Get key and value + args = data.rsp[i].split('=', 2); + var key = jQuery.trim(args[0]); + var val = jQuery.trim(data.rsp[i].substring(data.rsp[i].indexOf('=') + 1, data.rsp[i].length)); + + // Create a hash table + attrs[node][key] = val; + headers[key] = 1; + } + + // Sort headers + var sorted = new Array(); + var attrs2show = new Array('arch', 'groups', 'hcp', 'hostnames', 'ip', 'os', 'userid', 'mgt'); + for (var key in headers) { + // Show node attributes + if (jQuery.inArray(key, attrs2show) > -1) { + sorted.push(key); + } + } + sorted.sort(); + + // Add column for check box, node, ping, power, monitor, and comments + sorted.unshift('', + 'node', + 'status', + 'power', + 'monitor', + 'comments'); + + // Create a datatable + var nodesDT = new DataTable(nodesDTId); + nodesDT.init(sorted); + + // Go through each node + for (var node in attrs) { + // Create a row + var row = new Array(); + + // Create a check box, node link, and get node status + var checkBx = $(''); + var nodeLink = $('' + node + '').bind('click', loadNode); + + // If there is no status attribute for the node, do not try to access hash table + // Else the code will break + var status = ''; + if (attrs[node]['status']) { + status = attrs[node]['status'].replace('sshd', 'ping'); + } + + // Push in checkbox, node, status, monitor, and power + row.push(checkBx, nodeLink, status, '', ''); + + // If the node attributes are known (i.e the group is known) + if (attrs[node]['groups']) { + // Put in comments + var comments = attrs[node]['usercomment']; + // If no comments exists, show 'No comments' and set icon image source + var iconSrc; + if (!comments) { + comments = 'No comments'; + iconSrc = 'images/nodes/ui-icon-no-comment.png'; + } else { + iconSrc = 'images/nodes/ui-icon-comment.png'; + } + + // Create comments icon + var tipID = node + 'Tip'; + var icon = $('').css({ + 'width': '18px', + 'height': '18px' + }); + + // Create tooltip + var tip = createCommentsToolTip(comments); + var col = $('').append(icon); + col.append(tip); + row.push(col); + + // Generate tooltips + icon.tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.8, + relative: true, + delay: 500 + }); + } else { + // Do not put in comments if attributes are not known + row.push(''); + } + + // Go through each header + for (var i = 6; i < sorted.length; i++) { + // Add the node attributes to the row + var key = sorted[i]; + + // Do not put comments and status in twice + if (key != 'usercomment' && key != 'status' && key.indexOf('statustime') < 0) { + var val = attrs[node][key]; + if (val) { + row.push($('' + val + '')); + } else { + row.push(''); + } + } + } + + // Add the row to the table + nodesDT.add(row); + } + + // Create info bar + var infoBar = createInfoBar('Manage and monitor your virtual machines.'); + $('#manageTab').append(infoBar); + + // Insert action bar and nodes datatable + $('#manageTab').append(nodesDT.object()); + + // Turn table into a datatable + $('#' + nodesDTId).dataTable({ + 'iDisplayLength': 50, + 'bLengthChange': false, + "bScrollCollapse": true, + "sScrollY": "400px", + "sScrollX": "110%", + "bAutoWidth": true, + "oLanguage": { + "oPaginate": { + "sNext": "", + "sPrevious": "" + } + } + }); + + // Set datatable header class to add color + // $('.datatable thead').attr('class', 'ui-widget-header'); + + // Do not sort ping, power, and comment column + $('#' + nodesDTId + ' thead tr th').click(function() { + getNodeAttrs(group); + }); + var checkboxCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(0)'); + var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + var monitorCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + var commentCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(5)'); + checkboxCol.unbind('click'); + pingCol.unbind('click'); + powerCol.unbind('click'); + monitorCol.unbind('click'); + commentCol.unbind('click'); + + // Refresh the node ping, power, and monitor status on-click + var nodes = getNodesShown(nodesDTId); + pingCol.find('span a').click(function() { + refreshNodeStatus(nodes); + }); + powerCol.find('span a').click(function() { + refreshPowerStatus(nodes); + }); + monitorCol.find('span a').click(function() { + refreshGangliaStatus(nodes); + }); + + // Create actions menu + // Power on + var powerOnLnk = $('Power on'); + powerOnLnk.click(function() { + var tgtNodes = getNodesChecked(nodesDTId); + if (tgtNodes) { + powerNode(tgtNodes, 'on'); + } + }); + + // Power off + var powerOffLnk = $('Power off'); + powerOffLnk.click(function() { + var tgtNodes = getNodesChecked(nodesDTId); + if (tgtNodes) { + powerNode(tgtNodes, 'off'); + } + }); + + // Power softoff + var powerSoftoffLnk = $('Shutdown'); + powerSoftoffLnk.click(function() { + var tgtNodes = getNodesChecked(nodesDTId); + if (tgtNodes) { + powerNode(tgtNodes, 'softoff'); + } + }); + + // Clone + var cloneLnk = $('Clone'); + cloneLnk.click(function() { + var tgtNodes = getNodesChecked(nodesDTId); + if (tgtNodes) { + cloneNode(tgtNodes); + } + }); + + // Delete + var deleteLnk = $('Delete'); + deleteLnk.click(function() { + var tgtNodes = getNodesChecked(nodesDTId); + if (tgtNodes) { + deleteNode(tgtNodes); + } + }); + + // Unlock + var unlockLnk = $('Unlock'); + unlockLnk.click(function() { + var tgtNodes = getNodesChecked(nodesDTId); + if (tgtNodes) { + unlockNode(tgtNodes); + } + }); + + // Create action bar + var actionBar = $('
                                      ').css('width', '370px'); + + // Prepend menu to datatable + var actionsLnk = $('Actions'); + var refreshLnk = $('Refresh'); + refreshLnk.click(function() { + // Get nodes owned by user + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'nodetype', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + // Save nodes owned by user + setUserNodes(data); + getNodesCurrentLoad(); + + // Refresh nodes table + var userName = $.cookie('xcat_username'); + var userNodes = $.cookie('xcat_' + userName + '_usrnodes'); + if (userNodes) { + // Get nodes definitions + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'lsdef', + tgt : '', + args : userNodes, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadNodesTable(data); + } + }); + } else { + // Clear the tab before inserting the table + $('#manageTab').children().remove(); + $('#manageTab').append(createWarnBar('You are not managing any node. Try to provision a node.')); + } + } + }); + }); + + var actionMenu = createMenu([cloneLnk, deleteLnk, powerOnLnk, powerOffLnk, powerSoftoffLnk, unlockLnk]); + var menu = createMenu([[actionsLnk, actionMenu], refreshLnk]); + menu.superfish(); + actionBar.append(menu); + + // Set correct theme for action menu + actionMenu.find('li').hover(function() { + setMenu2Theme($(this)); + }, function() { + setMenu2Normal($(this)); + }); + + // Create a division to hold actions menu + var menuDiv = $(''); + $('#' + nodesDTId + '_wrapper').prepend(menuDiv); + menuDiv.append(actionBar); + $('#' + nodesDTId + '_filter').appendTo(menuDiv); + + // Get power and monitor status + var nodes = getNodesShown(nodesDTId); + refreshPowerStatus(nodes); + refreshGangliaStatus(nodes); +} + +/** + * Refresh ping status for each node + * + * @param nodes Nodes to get ping status + */ +function refreshNodeStatus(nodes) { + // Show ping loader + var nodesDTId = 'userNodesDT'; + var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + pingCol.find('img').show(); + + // Get the node ping status + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'nodestat', + tgt : nodes, + args : '-u', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadNodePing(data); + } + }); +} + +/** + * Load node ping status for each node + * + * @param data Data returned from HTTP request + */ +function loadNodePing(data) { + var nodesDTId = 'userNodesDT'; + var datatable = $('#' + nodesDTId).dataTable(); + var rsp = data.rsp; + var args, rowPos, node, status; + + // Get all nodes within datatable + for (var i in rsp) { + args = rsp[i].split(':'); + + // args[0] = node and args[1] = status + node = jQuery.trim(args[0]); + status = jQuery.trim(args[1]).replace('sshd', 'ping'); + + // Get row containing node + rowPos = findRow(node, '#' + nodesDTId, 1); + + // Update ping status column + datatable.fnUpdate(status, rowPos, 2, false); + } + + // Hide status loader + var pingCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(2)'); + pingCol.find('img').hide(); + adjustColumnSize(nodesDTId); +} + +/** + * Refresh power status for each node + * + * @param nodes Nodes to get power status + */ +function refreshPowerStatus(nodes) { + // Show power loader + var nodesDTId = 'userNodesDT'; + var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + powerCol.find('img').show(); + + // Get power status + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'rpower', + tgt : nodes, + args : 'stat', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadPowerStatus(data); + } + }); +} + +/** + * Load power status for each node + * + * @param data Data returned from HTTP request + */ +function loadPowerStatus(data) { + var nodesDTId = 'userNodesDT'; + var datatable = $('#' + nodesDTId).dataTable(); + var power = data.rsp; + var rowPos, node, status, args; + + for (var i in power) { + // power[0] = nodeName and power[1] = state + args = power[i].split(':'); + node = jQuery.trim(args[0]); + status = jQuery.trim(args[1]); + + // Get the row containing the node + rowPos = findRow(node, '#' + nodesDTId, 1); + + // Update the power status column + datatable.fnUpdate(status, rowPos, 3, false); + } + + // Hide power loader + var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + powerCol.find('img').hide(); + adjustColumnSize(nodesDTId); +} + +/** + * Refresh the status of Ganglia for each node + * + * @param nodes Nodes to get Ganglia status + */ +function refreshGangliaStatus(nodes) { + // Show ganglia loader + var nodesDTId = 'userNodesDT'; + var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').show(); + + // Get the status of Ganglia + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastatus;' + nodes, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + loadGangliaStatus(data); + } + }); +} + +/** + * Load the status of Ganglia for a given group + * + * @param data Data returned from HTTP request + */ +function loadGangliaStatus(data) { + // Get datatable + var nodesDTId = 'userNodesDT'; + var datatable = $('#' + nodesDTId).dataTable(); + var ganglia = data.rsp; + var rowNum, node, status; + + for ( var i in ganglia) { + // ganglia[0] = nodeName and ganglia[1] = state + node = jQuery.trim(ganglia[i][0]); + status = jQuery.trim(ganglia[i][1]); + + if (node) { + // Get the row containing the node + rowNum = findRow(node, '#' + nodesDTId, 1); + + // Update the power status column + datatable.fnUpdate(status, rowNum, 4); + } + } + + // Hide Ganglia loader + var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').hide(); + adjustColumnSize(nodesDTId); +} + +/** + * Load inventory for given node + * + * @param e Windows event + */ +function loadNode(e) { + if (!e) { + e = window.event; + } + + // Get node that was clicked + var node = (e.target) ? e.target.id : e.srcElement.id; + + // Create a new tab to show inventory + var tabId = node + '_inventory'; + + if(!$('#' + tabId).length) { + // Add new tab, only if one does not exist + var loader = createLoader(node + 'Loader'); + loader = $('
                                      ').append(loader); + serviceTabs.add(tabId, node, loader, true); + + // Get node inventory + var msg = 'out=' + tabId + ',node=' + node; + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'rinv', + tgt : node, + args : 'all', + msg : msg + }, + + success : function(data) { + data = decodeRsp(data); + var args = data.msg.split(','); + + // Get node + var node = args[1].replace('node=', ''); + + // Get the management plugin + var mgt = getNodeAttr(node, 'mgt'); + + // Create an instance of the plugin + var plugin; + switch (mgt) { + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + + // Select tab + plugin.loadServiceInventory(data); + } + }); + } + + // Select new tab + serviceTabs.select(tabId); +} + +/** + * Set a cookie for group names + * + * @param data Data from HTTP request + */ +function setGroupCookies(data) { + if (data.rsp) { + var groups = new Array(); + + // Index 0 is the table header + var cols, name, ip, hostname, desc, selectable, comments, tmp; + for (var i = 1; i < data.rsp.length; i++) { + // Set default description and selectable + selectable = "no"; + desc = "No description"; + + // Split into columns: + // node, ip, hostnames, otherinterfaces, comments, disable + cols = data.rsp[i].split(','); + name = cols[0].replace(new RegExp('"', 'g'), ''); + ip = cols[1].replace(new RegExp('"', 'g'), ''); + hostname = cols[2].replace(new RegExp('"', 'g'), ''); + + // It should return: "description: All machines; network: 10.1.100.0/24;" + comments = cols[4].replace(new RegExp('"', 'g'), ''); + tmp = comments.split('|'); + for (var j = 0; j < tmp.length; j++) { + // Save description + if (tmp[j].indexOf('description:') > -1) { + desc = tmp[j].replace('description:', ''); + desc = jQuery.trim(desc); + } + + // Is the group selectable? + if (tmp[j].indexOf('selectable:') > -1) { + selectable = tmp[j].replace('selectable:', ''); + selectable = jQuery.trim(selectable); + } + } + + // Save groups that are selectable + if (selectable == "yes") + groups.push(name + ':' + ip + ':' + hostname + ':' + desc); + } + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (240 * 60 * 1000)); + $.cookie('xcat_srv_groups', groups, { expires: exDate, path: '/xcat', secure:true }); + } +} + +/** + * Set a cookie for the OS images + * + * @param data Data from HTTP request + */ +function setOSImageCookies(data) { + // Get response + var rsp = data.rsp; + + var imageNames = new Array(); + var profilesHash = new Object(); + var osVersHash = new Object(); + var osArchsHash = new Object(); + var imagePos = 0; + var profilePos = 0; + var osversPos = 0; + var osarchPos = 0; + var provMethodPos = 0; + var comments = 0; + var desc, selectable, tmp; + // Get column index for each attribute + var colNameArray = rsp[0].substr(1).split(','); + for (var i in colNameArray){ + switch (colNameArray[i]){ + case 'imagename': { + imagePos = i; + } + break; + + case 'profile':{ + profilePos = i; + } + break; + + case 'osvers':{ + osversPos = i; + } + break; + + case 'osarch':{ + osarchPos = i; + } + break; + + case 'comments':{ + comments = i; + } + break; + + case 'provmethod':{ + provMethodPos = i; + } + break; + + default : + break; + } + } + + // Go through each index + for (var i = 1; i < rsp.length; i++) { + // Get image name + var cols = rsp[i].split(','); + var osImage = cols[imagePos].replace(new RegExp('"', 'g'), ''); + var profile = cols[profilePos].replace(new RegExp('"', 'g'), ''); + var provMethod = cols[provMethodPos].replace(new RegExp('"', 'g'), ''); + var osVer = cols[osversPos].replace(new RegExp('"', 'g'), ''); + var osArch = cols[osarchPos].replace(new RegExp('"', 'g'), ''); + var osComments = cols[comments].replace(new RegExp('"', 'g'), ''); + + // Only save install boot + if (provMethod.indexOf('install') > -1) { + if (osComments) { + // Only enable images where description and selectable comments exist + // Set default description and selectable + selectable = "no"; + desc = "No description"; + + tmp = osComments.split('|'); + for (var j = 0; j < tmp.length; j++) { + // Save description + if (tmp[j].indexOf('description:') > -1) { + desc = tmp[j].replace('description:', ''); + desc = jQuery.trim(desc); + } + + // Is the image selectable? + if (tmp[j].indexOf('selectable:') > -1) { + selectable = tmp[j].replace('selectable:', ''); + selectable = jQuery.trim(selectable); + } + } + + // Save images that are selectable + if (selectable == "yes") + imageNames.push(osImage + ':' + desc); + } + + profilesHash[profile] = 1; + osVersHash[osVer] = 1; + osArchsHash[osArch] = 1; + } + } + + // Save image names in a cookie + $.cookie('xcat_srv_imagenames', imageNames); + + // Save profiles in a cookie + var tmp = new Array; + for (var key in profilesHash) { + tmp.push(key); + } + $.cookie('xcat_srv_profiles', tmp); + + // Save OS versions in a cookie + tmp = new Array; + for (var key in osVersHash) { + tmp.push(key); + } + $.cookie('xcat_srv_osvers', tmp); + + // Save OS architectures in a cookie + tmp = new Array; + for (var key in osArchsHash) { + tmp.push(key); + } + $.cookie('xcat_srv_osarchs', tmp); +} + + + +/** + * Set a cookie for user nodes + * + * @param data Data from HTTP request + */ +function setUserNodes(data) { + if (data.rsp) { + // Get user name that is logged in + var userName = $.cookie('xcat_username'); + var usrNodes = new Array(); + + // Ignore first columns because it is the header + for ( var i = 1; i < data.rsp.length; i++) { + // Go through each column + // where column names are: node, os, arch, profile, provmethod, supportedarchs, nodetype, comments, disable + var cols = data.rsp[i].split(','); + var node = cols[0].replace(new RegExp('"', 'g'), ''); + + // Comments can contain the owner and description + var comments = new Array(); + if (cols[7].indexOf(';') > -1) { + comments = cols[7].replace(new RegExp('"', 'g'), '').split(';'); + } else { + comments.push(cols[7].replace(new RegExp('"', 'g'), '')); + } + + // Extract the owner + var owner; + for (var j in comments) { + if (comments[j].indexOf('owner:') > -1) { + owner = comments[j].replace('owner:', ''); + + if (owner == userName) { + usrNodes.push(node); + } + + break; + } + } + } // End of for + + // Set cookie to expire in 240 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (240 * 60 * 1000)); + $.cookie('xcat_' + userName + '_usrnodes', usrNodes, { expires: exDate, path: '/xcat', secure:true }); + } // End of if +} + +/** + * Power on a given node + * + * @param tgtNodes Node to power on or off + * @param power2 Power node to given state + */ +function powerNode(tgtNodes, power2) { + // Show power loader + var nodesDTId = 'userNodesDT'; + var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + powerCol.find('img').show(); + + var nodes = tgtNodes.split(','); + for (var n in nodes) { + // Get hardware that was selected + var hw = getUserNodeAttr(nodes[n], 'mgt'); + + // Change to power softoff (to gracefully shutdown) + switch (hw) { + case "blade": + break; + case "hmc": + break; + case "ipmi": + break; + case "zvm": + if (power2 == 'off') { + power2 = 'softoff'; + } + + break; + } + } + + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'rpower', + tgt : tgtNodes, + args : power2, + msg : tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + updatePowerStatus(data); + } + }); +} + +/** + * Update power status of a node in the datatable + * + * @param data Data from HTTP request + */ +function updatePowerStatus(data) { + // Get datatable + var nodesDTId = 'userNodesDT'; + var dTable = $('#' + nodesDTId).dataTable(); + + // Get xCAT response + var rsp = data.rsp; + // Loop through each line + var node, status, rowPos, strPos; + for (var i in rsp) { + // Get node name + node = rsp[i].split(":")[0]; + + // If there is no error + if (rsp[i].indexOf("Error") < 0 || rsp[i].indexOf("Failed") < 0) { + // Get the row containing the node link + rowPos = findRow(node, '#' + nodesDTId, 1); + + // If it was power on, then the data return would contain "Starting" + strPos = rsp[i].indexOf("Starting"); + if (strPos > -1) { + status = 'on'; + } else { + status = 'off'; + } + + // Update the power status column + dTable.fnUpdate(status, rowPos, 3, false); + } else { + // Power on/off failed + alert(rsp[i]); + } + } + + var powerCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(3)'); + powerCol.find('img').hide(); + adjustColumnSize(nodesDTId); +} + +/** + * Turn on monitoring for a given node + * + * @param node Node to monitor on or off + * @param monitor Monitor state, on or off + */ +function monitorNode(node, monitor) { + // Show ganglia loader + var nodesDTId = 'userNodesDT'; + var gangliaCol = $('#' + nodesDTId + '_wrapper .dataTables_scrollHead .datatable thead tr th:eq(4)'); + gangliaCol.find('img').show(); + + if (monitor == 'on') { + if (node) { + // Check if ganglia RPMs are installed + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliacheck;' + node, + msg : node // Node range will be passed along in data.msg + }, + + /** + * Start ganglia on a given node range + * + * @param data + * Data returned from HTTP request + * @return Nothing + */ + success : function(data) { + data = decodeRsp(data); + // Get response + var out = data.rsp[0].split(/\n/); + + // Go through each line + var warn = false; + var warningMsg = ''; + for (var i in out) { + // If an RPM is not installed + if (out[i].indexOf('not installed') > -1) { + warn = true; + + if (warningMsg) { + warningMsg += '
                                      ' + out[i]; + } else { + warningMsg = out[i]; + } + } + } + + // If there are warnings + if (warn) { + // Create warning bar + var warningBar = createWarnBar(warningMsg); + warningBar.css('margin-bottom', '10px'); + warningBar.prependTo($('#nodesTab')); + } else { + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastart;' + data.msg, + msg : data.msg + }, + + success : function(data) { + data = decodeRsp(data); + // Remove any warnings + $('#nodesTab').find('.ui-state-error').remove(); + refreshGangliaStatus(data.msg); + } + }); + } // End of if (warn) + } // End of function(data) + }); + } + } else { + var args; + if (node) { + args = 'gangliastop;' + node; + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : args, + msg : node + }, + + success : function(data) { + data = decodeRsp(data); + refreshGangliaStatus(data.msg); + } + }); + } + } +} + +/** + * Open a dialog to clone node + * + * @param tgtNodes Nodes to clone + */ +function cloneNode(tgtNodes) { + var userName = $.cookie('xcat_username'); + var nodes = tgtNodes.split(','); + var tmp = $.cookie('xcat_' + userName + '_usrnodes'); + var usrNodes = tmp.split(','); + + var maxVM = parseInt($.cookie('xcat_' + userName + '_maxvm')); + + // Do not allow user to clone if the maximum number of VMs is reached + if (usrNodes.length >= maxVM) { + var warn = createWarnBar('You have reached the maximum number of virtual machines allowed (' + maxVM + '). Delete un-used virtual machines or contact your system administrator request more virtual machines.'); + warn.prependTo($('#manageTab')); + return; + } + + for (var n in nodes) { + // Get hardware that was selected + var hw = getUserNodeAttr(nodes[n], 'mgt'); + + // Create an instance of the plugin + var plugin; + switch (hw) { + case "kvm": + plugin = new kvmPlugin(); + break; + case "esx": + plugin = new esxPlugin(); + break; + case "zvm": + plugin = new zvmPlugin(); + break; + } + + // Clone node + plugin.serviceClone(nodes[n]); + } +} + + +/** + * Open a dialog to delete node + * + * @param tgtNodes Nodes to delete + */ +function deleteNode(tgtNodes) { + var nodes = tgtNodes.split(','); + + // Loop through each node and create target nodes string + var tgtNodesStr = ''; + for (var i in nodes) { + if (i == 0 && i == nodes.length - 1) { + // If it is the 1st and only node + tgtNodesStr += nodes[i]; + } else if (i == 0 && i != nodes.length - 1) { + // If it is the 1st node of many nodes, append a comma to the string + tgtNodesStr += nodes[i] + ', '; + } else { + if (i == nodes.length - 1) { + // If it is the last node, append nothing to the string + tgtNodesStr += nodes[i]; + } else { + // Append a comma to the string + tgtNodesStr += nodes[i] + ', '; + } + } + } + + // Confirm delete of node + var dialog = $('
                                      '); + var warn = createWarnBar('Are you sure you want to delete ' + tgtNodesStr + '?'); + dialog.append(warn); + + // Open dialog + dialog.dialog({ + title: "Confirm", + modal: true, + close: function(){ + $(this).remove(); + }, + width: 400, + buttons: { + "Yes": function(){ + // Create status bar and append to tab + var instance = 0; + var statBarId = 'deleteStat' + instance; + while ($('#' + statBarId).length) { + // If one already exists, generate another one + instance = instance + 1; + statBarId = 'deleteStat' + instance; + } + + var statBar = createStatusBar(statBarId); + var loader = createLoader(''); + statBar.find('div').append(loader); + statBar.prependTo($('#manageTab')); + + // Delete the virtual server + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'rmvm', + tgt : tgtNodes, + args : '', + msg : 'out=' + statBarId + ';cmd=rmvm;tgt=' + tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + var args = data.msg.split(';'); + var statBarId = args[0].replace('out=', ''); + var tgts = args[2].replace('tgt=', '').split(','); + + // Get data table + var nodesDTId = 'userNodesDT'; + var dTable = $('#' + nodesDTId).dataTable(); + var failed = false; + + // Create an info box to show output + var output = writeRsp(data.rsp, ''); + output.css('margin', '0px'); + // Remove loader and append output + $('#' + statBarId + ' img').remove(); + $('#' + statBarId + ' div').append(output); + + // If there was an error, do not continue + if (output.html().indexOf('Error') > -1) { + failed = true; + } + + // Update data table + var rowPos; + for (var i in tgts) { + if (!failed) { + // Get row containing the node link and delete it + rowPos = findRow(tgts[i], '#' + nodesDTId, 1); + dTable.fnDeleteRow(rowPos); + } + } + + // Refresh nodes owned by user + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'tabdump', + tgt : '', + args : 'nodetype', + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + setUserNodes(data); + } + }); + } + }); + + $(this).dialog("close"); + }, + "No": function() { + $(this).dialog("close"); + } + } + }); +} + +/** + * Unlock a node by setting the ssh keys + * + * @param tgtNodes Nodes to unlock + */ +function unlockNode(tgtNodes) { + var nodes = tgtNodes.split(','); + + // Loop through each node and create target nodes string + var tgtNodesStr = ''; + for (var i in nodes) { + if (i == 0 && i == nodes.length - 1) { + // If it is the 1st and only node + tgtNodesStr += nodes[i]; + } else if (i == 0 && i != nodes.length - 1) { + // If it is the 1st node of many nodes, append a comma to the string + tgtNodesStr += nodes[i] + ', '; + } else { + if (i == nodes.length - 1) { + // If it is the last node, append nothing to the string + tgtNodesStr += nodes[i]; + } else { + // Append a comma to the string + tgtNodesStr += nodes[i] + ', '; + } + } + } + + var dialog = $('
                                      '); + var infoBar = createInfoBar('Give the root password for this node range to setup its SSH keys.'); + dialog.append(infoBar); + + var unlockForm = $('
                                      ').css('margin', '5px'); + unlockForm.append('
                                      '); + unlockForm.append('
                                      '); + dialog.append(unlockForm); + + dialog.find('div input').css('margin', '5px'); + + // Generate tooltips + unlockForm.find('div input[title]').tooltip({ + position: "center right", + offset: [-2, 10], + effect: "fade", + opacity: 0.7, + predelay: 800, + events : { + def : "mouseover,mouseout", + input : "mouseover,mouseout", + widget : "focus mouseover,blur mouseout", + tooltip : "mouseover,mouseout" + } + }); + + // Open dialog + dialog.dialog({ + title: "Confirm", + modal: true, + close: function(){ + $(this).remove(); + }, + width: 450, + buttons: { + "Ok": function(){ + // Create status bar and append to tab + var instance = 0; + var statBarId = 'unlockStat' + instance; + while ($('#' + statBarId).length) { + // If one already exists, generate another one + instance = instance + 1; + statBarId = 'unlockStat' + instance; + } + + var statBar = createStatusBar(statBarId); + var loader = createLoader(''); + statBar.find('div').append(loader); + statBar.prependTo($('#manageTab')); + + // If a password is given + var password = unlockForm.find('input[name=password]:eq(0)'); + if (password.val()) { + // Setup SSH keys + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'unlock;' + tgtNodes + ';' + password.val(), + msg : 'out=' + statBarId + ';cmd=unlock;tgt=' + tgtNodes + }, + + success : function(data) { + data = decodeRsp(data); + // Create an info box to show output + var output = writeRsp(data.rsp, ''); + output.css('margin', '0px'); + // Remove loader and append output + $('#' + statBarId + ' img').remove(); + $('#' + statBarId + ' div').append(output); + } + }); + + $(this).dialog("close"); + } + }, + "Cancel": function() { + $(this).dialog("close"); + } + } + }); +} + +/** + * Get nodes current load information + */ +function getNodesCurrentLoad(){ + var userName = $.cookie('xcat_username'); + var nodes = $.cookie('xcat_' + userName + '_usrnodes'); + + // Get nodes current status + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliacurrent;node;' + nodes, + msg : '' + }, + + success: function(data) { + data = decodeRsp(data); + saveNodeLoad(data); + } + }); +} + +/** + * Save node load data + * + * @param status Data returned from HTTP request + */ +function saveNodeLoad(status){ + // Save node path and status for future use + nodePath = new Object(); + nodeStatus = new Object(); + + // Get nodes status + var nodes = status.rsp[0].split(';'); + + var i = 0, pos = 0; + var node = '', tmpStr = ''; + var tmpArry; + + for (i = 0; i < nodes.length; i++){ + tmpStr = nodes[i]; + pos = tmpStr.indexOf(':'); + node = tmpStr.substring(0, pos); + tmpArry = tmpStr.substring(pos + 1).split(','); + + switch(tmpArry[0]){ + case 'UNKNOWN':{ + nodeStatus[node] = -2; + } + break; + case 'ERROR':{ + nodeStatus[node] = -1; + } + break; + case 'WARNING':{ + nodeStatus[node] = 0; + nodePath[node] = tmpArry[1]; + } + break; + case 'NORMAL':{ + nodeStatus[node] = 1; + nodePath[node] = tmpArry[1]; + } + break; + } + } +} + +/** + * Get monitoring metrics and load into inventory fieldset + * + * @param node Node to collect metrics + */ +function getMonitorMetrics(node) { + // Inventory tab should have this fieldset already created + // e.g.
                                      + $('#' + node + '_monitor').children('div').remove(); + + // Before trying to get the metrics, check if Ganglia is running + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliastatus;' + node, + msg : '' + }, + + success: function(data) { + data = decodeRsp(data); + var ganglia = data.rsp; + var node, status; + + // Get the ganglia status + for (var i in ganglia) { + // ganglia[0] = nodeName and ganglia[1] = state + node = jQuery.trim(ganglia[i][0]); + status = jQuery.trim(ganglia[i][1]); + + if (node && status == 'on') { + // Get monitoring metrics + $.ajax({ + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'gangliashow;' + nodePath[node] + ';hour;_summary_', + msg : node + }, + + success: function(data) { + data = decodeRsp(data); + drawMonitoringCharts(data); + } + }); + } else if (node && status == 'off') { + var info = createInfoBar('Ganglia monitoring is disabled for this node'); + $('#' + node + '_monitor').append(info.css('width', '300px')); + } + } // End of for + } // End of function + }); +} + +/** + * Draw monitoring charts based on node metrics + * + * @param data Data returned from HTTP request + */ +function drawMonitoringCharts(data){ + var nodeMetrics = new Object(); + var metricData = data.rsp[0].split(';'); + var node = data.msg; + + var metricName = ''; + var metricVal = ''; + var pos = 0; + + // Go through the metrics returned + for (var m = 0; m < metricData.length; m++){ + pos = metricData[m].indexOf(':'); + + // Get metric name + metricName = metricData[m].substr(0, pos); + nodeMetrics[metricName] = new Array(); + // Get metric values + metricVal = metricData[m].substr(pos + 1).split(','); + // Save node metrics + for (var i = 0; i < metricVal.length; i++){ + nodeMetrics[metricName].push(Number(metricVal[i])); + } + } + + drawLoadFlot(node, nodeMetrics['load_one'], nodeMetrics['cpu_num']); + drawCpuFlot(node, nodeMetrics['cpu_idle']); + drawMemFlot(node, nodeMetrics['mem_free'], nodeMetrics['mem_total']); + drawDiskFlot(node, nodeMetrics['disk_free'], nodeMetrics['disk_total']); + drawNetworkFlot(node, nodeMetrics['bytes_in'], nodeMetrics['bytes_out']); +} + +/** + * Draw load metrics flot + * + * @param node Node name + * @param loadpair Load timestamp and value pair + * @param cpupair CPU number and value pair + */ +function drawLoadFlot(node, loadPair, cpuPair){ + var load = new Array(); + var cpu = new Array(); + + var i = 0; + var yAxisMax = 0; + var interval = 1; + + // Append flot to node monitoring fieldset + var loadFlot = $('
                                      ').css({ + 'float': 'left', + 'height': '150px', + 'margin': '0 0 10px', + 'width': '300px' + }); + $('#' + node + '_monitor').append(loadFlot); + $('#' + node + '_load').empty(); + + // Parse load pair where: + // timestamp must be mutiplied by 1000 and Javascript timestamp is in ms + for (i = 0; i < loadPair.length; i += 2){ + load.push([loadPair[i] * 1000, loadPair[i + 1]]); + if (loadPair[i + 1] > yAxisMax){ + yAxisMax = loadPair[i + 1]; + } + } + + // Parse CPU pair + for (i = 0; i < cpuPair.length; i += 2){ + cpu.push([cpuPair[i] * 1000, cpuPair[i + 1]]); + if (cpuPair[i + 1] > yAxisMax){ + yAxisMax = cpuPair[i + 1]; + } + } + + interval = parseInt(yAxisMax / 3); + if (interval < 1){ + interval = 1; + } + + $.jqplot(node + '_load', [load, cpu],{ + title: ' Loads/Procs Last Hour', + axes:{ + xaxis:{ + renderer : $.jqplot.DateAxisRenderer, + numberTicks: 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis: { + min : 0, + tickInterval : interval + } + }, + legend : { + show: true, + location: 'nw' + }, + series:[{label:'Load'}, {label: 'CPU Number'}], + seriesDefaults : {showMarker: false} + }); +} + +/** + * Draw CPU usage flot + * + * @param node Node name + * @param cpuPair CPU timestamp and value pair + */ +function drawCpuFlot(node, cpuPair){ + var cpu = new Array(); + + // Append flot to node monitoring fieldset + var cpuFlot = $('
                                      ').css({ + 'float': 'left', + 'height': '150px', + 'margin': '0 0 10px', + 'width': '300px' + }); + $('#' + node + '_monitor').append(cpuFlot); + $('#' + node + '_cpu').empty(); + + // Time stamp should by mutiplied by 1000 + // CPU idle comes from server, subtract 1 from idle + for(var i = 0; i < cpuPair.length; i +=2){ + cpu.push([(cpuPair[i] * 1000), (100 - cpuPair[i + 1])]); + } + + $.jqplot(node + '_cpu', [cpu],{ + title: 'CPU Use Last Hour', + axes:{ + xaxis:{ + renderer : $.jqplot.DateAxisRenderer, + numberTicks: 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis: { + min : 0, + max : 100, + tickOptions:{formatString : '%d\%'} + } + }, + seriesDefaults : {showMarker: false} + }); +} + +/** + * Draw memory usage flot + * + * @param node Node name + * @param freePair Free memory timestamp and value pair + * @param totalPair Total memory timestamp and value pair + */ +function drawMemFlot(node, freePair, totalPair){ + var used = new Array(); + var total = new Array(); + var size = 0; + + // Append flot to node monitoring fieldset + var memoryFlot = $('
                                      ').css({ + 'float': 'left', + 'height': '150px', + 'margin': '0 0 10px', + 'width': '300px' + }); + $('#' + node + '_monitor').append(memoryFlot); + $('#' + node + '_memory').empty(); + + if(freePair.length < totalPair.length){ + size = freePair.length; + } else { + size = freePair.length; + } + + var tmpTotal, tmpUsed; + for(var i = 0; i < size; i+=2){ + tmpTotal = totalPair[i+1]; + tmpUsed = tmpTotal-freePair[i+1]; + tmpTotal = tmpTotal/1000000; + tmpUsed = tmpUsed/1000000; + total.push([totalPair[i]*1000, tmpTotal]); + used.push([freePair[i]*1000, tmpUsed]); + } + + $.jqplot(node + '_memory', [used, total],{ + title: 'Memory Use Last Hour', + axes:{ + xaxis:{ + renderer : $.jqplot.DateAxisRenderer, + numberTicks: 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis: { + min : 0, + tickOptions:{formatString : '%.2fG'} + } + }, + legend : { + show: true, + location: 'nw' + }, + series:[{label:'Used'}, {label: 'Total'}], + seriesDefaults : {showMarker: false} + }); +} + +/** + * Draw disk usage flot + * + * @param node Node name + * @param freePair Free disk space (Ganglia only logs free data) + * @param totalPair Total disk space + */ +function drawDiskFlot(node, freePair, totalPair) { + var used = new Array(); + var total = new Array(); + var size = 0; + + // Append flot to node monitoring fieldset + var diskFlot = $('
                                      ').css({ + 'float' : 'left', + 'height' : '150px', + 'margin' : '0 0 10px', + 'width' : '300px' + }); + $('#' + node + '_monitor').append(diskFlot); + $('#' + node + '_disk').empty(); + + if (freePair.length < totalPair.length) { + size = freePair.length; + } else { + size = freePair.length; + } + + var tmpTotal, tmpUsed; + for ( var i = 0; i < size; i += 2) { + tmpTotal = totalPair[i + 1]; + tmpUsed = tmpTotal - freePair[i + 1]; + total.push([ totalPair[i] * 1000, tmpTotal ]); + used.push([ freePair[i] * 1000, tmpUsed ]); + } + + $.jqplot(node + '_disk', [ used, total ], { + title : 'Disk Use Last Hour', + axes : { + xaxis : { + renderer : $.jqplot.DateAxisRenderer, + numberTicks : 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis : { + min : 0, + tickOptions : { + formatString : '%.2fG' + } + } + }, + legend : { + show : true, + location : 'nw' + }, + series : [ { + label : 'Used' + }, { + label : 'Total' + } ], + seriesDefaults : { + showMarker : false + } + }); +} + +/** + * Draw network usage flot + * + * @param node Node name + * @param freePair Free memory timestamp and value pair + * @param totalPair Total memory timestamp and value pair + */ +function drawNetworkFlot(node, inPair, outPair) { + var inArray = new Array(); + var outArray = new Array(); + var maxVal = 0; + var unitName = 'B'; + var divisor = 1; + + // Append flot to node monitoring fieldset + var diskFlot = $('
                                      ').css({ + 'float' : 'left', + 'height' : '150px', + 'margin' : '0 0 10px', + 'width' : '300px' + }); + $('#' + node + '_monitor').append(diskFlot); + $('#' + node + '_network').empty(); + + for (var i = 0; i < inPair.length; i += 2) { + if (inPair[i + 1] > maxVal) { + maxVal = inPair[i + 1]; + } + } + + for (var i = 0; i < outPair.length; i += 2) { + if (outPair[i + 1] > maxVal) { + maxVal = outPair[i + 1]; + } + } + + if (maxVal > 3000000) { + divisor = 1000000; + unitName = 'GB'; + } else if (maxVal >= 3000) { + divisor = 1000; + unitName = 'MB'; + } else { + // Do nothing + } + + for (i = 0; i < inPair.length; i += 2) { + inArray.push([ (inPair[i] * 1000), (inPair[i + 1] / divisor) ]); + } + + for (i = 0; i < outPair.length; i += 2) { + outArray.push([ (outPair[i] * 1000), (outPair[i + 1] / divisor) ]); + } + + $.jqplot(node + '_network', [ inArray, outArray ], { + title : 'Network Last Hour', + axes : { + xaxis : { + renderer : $.jqplot.DateAxisRenderer, + numberTicks : 4, + tickOptions : { + formatString : '%R', + show : true + } + }, + yaxis : { + min : 0, + tickOptions : { + formatString : '%d' + unitName + } + } + }, + legend : { + show : true, + location : 'nw' + }, + series : [ { + label : 'In' + }, { + label : 'Out' + } ], + seriesDefaults : { + showMarker : false + } + }); +} + +/** + * Get an attribute of a given node + * + * @param node The node + * @param attrName The attribute + * @return The attribute of the node + */ +function getNodeAttr(node, attrName) { + // Get the row + var row = $('[id=' + node + ']').parents('tr'); + + // Search for the column containing the attribute + var attrCol = null; + + var cols = row.parents('.dataTables_scroll').find('.dataTables_scrollHead thead tr:eq(0) th'); + // Loop through each column + for (var i in cols) { + // Find column that matches the attribute + if (cols.eq(i).html() == attrName) { + attrCol = cols.eq(i); + break; + } + } + + // If the column containing the attribute is found + if (attrCol) { + // Get the attribute column index + var attrIndex = attrCol.index(); + + // Get the attribute for the given node + var attr = row.find('td:eq(' + attrIndex + ')'); + return attr.text(); + } else { + return ''; + } +} + +/** + * Set the maximum number of VMs a user could have + */ +function setMaxVM() { + var userName = $.cookie('xcat_username'); + + $.ajax( { + url : 'lib/srv_cmd.php', + dataType : 'json', + data : { + cmd : 'webportal', + tgt : '', + args : 'getmaxvm;' + userName, + msg : '' + }, + + success : function(data) { + data = decodeRsp(data); + // Get response + var rsp = jQuery.trim(data.rsp); + rsp = rsp.replace('Max allowed:', ''); + + // Set cookie to expire in 60 minutes + var exDate = new Date(); + exDate.setTime(exDate.getTime() + (240 * 60 * 1000)); + $.cookie('xcat_' + userName + '_maxvm', rsp, { expires: exDate }); + } + }); } \ No newline at end of file diff --git a/xCAT-UI/js/ui.js b/xCAT-UI/js/ui.js index 771fa5d65..3b5da6ba2 100644 --- a/xCAT-UI/js/ui.js +++ b/xCAT-UI/js/ui.js @@ -1,1068 +1,1101 @@ -/** - * Tab constructor - * - * @param tabId - * Tab ID - * @param tabName - * Tab name - * @return Nothing - */ -var Tab = function(tabId) { - this.tabId = tabId; - this.tabName = null; - this.tab = null; -}; - -/** - * Initialize the tab - * - * @param tabName Tab name to initialize - */ -Tab.prototype.init = function() { - // Create a division containing the tab - this.tab = $('
                                      '); - var tabList = $('
                                        '); - var tabItem = $('
                                      • Dummy tab item
                                      • '); - tabList.append(tabItem); - this.tab.append(tabList); - - // Create a template with close button - var tabs = this.tab.tabs(); - - tabs.bind('tabsselect', function(event, ui){ - // Save the order tabs were selected - var order; - if ($.cookie('xcat_tabindex_history')) { - order = $.cookie('xcat_tabindex_history').split(','); - order[1] = order[0]; // Set index 1 to last selected tab - order[0] = ui.index; // Set index 0 to currently selected tab - } else { - // Create an array to track the tab selected - order = new Array; - order[0] = ui.index; - order[1] = ui.index; - } - - $.cookie('xcat_tabindex_history', order, { path: '/xcat', secure:true }); - }); - - // Remove dummy tab - this.tab.tabs("remove", 0); - - // Hide tab - this.tab.hide(); -}; - -/** - * Return the tab object - * - * @return Object representing the tab - */ -Tab.prototype.object = function() { - return this.tab; -}; - -/** - * Add a new tab - * - * @param tabId Tab ID - * @param tabName Tab name - * @param tabCont Tab content - * @param closeable Is tab closeable - */ -Tab.prototype.add = function(tabId, tabName, tabCont, closeable) { - // Show tab - if (this.tab.css("display") == "none") { - this.tab.show(); - } - - var newTab = $('
                                        '); - newTab.append(tabCont); - this.tab.append(newTab); - this.tab.tabs("add", "#" + tabId, tabName); - - // Append close button - if (closeable) { - var header = this.tab.find('ul.ui-tabs-nav a[href="#' + tabId +'"]').parent(); - header.append(''); - - // Get this tab - var tabs = this.tab; - var tabLink = 'a[href="\#' + tabId + '"]'; - var thisTab = $(tabLink, tabs).parent(); - - // Close tab when close button is clicked - thisTab.find('span.tab-close').bind('click', function(event) { - var tabIndex = ($('li', tabs).index(thisTab)); - - // Do not remove first tab - if (tabIndex != 0) { - // Go back to last tab if user is trying to close currently selected tab - if (tabs.tabs('option', 'selected') == tabIndex) { - // Get last selected tab from history - var order = $.cookie('xcat_tabindex_history').split(','); - if (order[1]) { - tabs.tabs('select', parseInt(order[1])); - } else { - tabs.tabs('select', 0); - } - } - - tabs.tabs('remove', tabIndex); - } - }); - } -}; - -/** - * Select a tab - * - * @param id Tab ID to select - */ -Tab.prototype.select = function(id) { - this.tab.tabs("select", "#" + id); -}; - -/** - * Remove a tab - * - * @param id Tab ID to remove - */ -Tab.prototype.remove = function(id) { - var selectorStr = 'a[href="\#' + id + '"]'; - var selectTab = $(selectorStr, this.tab).parent(); - var index = ($('li', this.tab).index(selectTab)); - this.tab.tabs("remove", index); -}; - -/** - * Table constructor - * - * @param tabId Tab ID - * @param tabName Tab name - */ -var Table = function(tableId) { - if ($('#' + tableId).length) { - this.tableId = tableId; - this.table = $('#' + tableId); - } else { - this.tableId = tableId; - this.table = null; - } -}; - -/** - * Initialize the table - * - * @param Headers Array of table headers - */ -Table.prototype.init = function(headers) { - // Create a table - this.table = $('
                                        '); - var thead = $(''); - var headRow = $(''); - - // Append headers - for (var i in headers) { - headRow.append('' + headers[i] + ''); - } - - thead.append(headRow); - this.table.append(thead); - - // Append table body - var tableBody = $(''); - this.table.append(tableBody); -}; - -/** - * Return the table object - * - * @return Object representing the table - */ -Table.prototype.object = function() { - return this.table; -}; - -/** - * Add a row to the table - * - * @param rowCont Array of table row contents - */ -Table.prototype.add = function(rowCont) { - // Create table row - var tableRow = $(''); - - // Create a column for each content - var tableCol; - for (var i in rowCont) { - tableCol = $(''); - tableCol.append(rowCont[i]); - tableRow.append(tableCol); - } - - // Append table row to table - this.table.find('tbody').append(tableRow); -}; - -/** - * Add a footer to the table - * - * @param rowCont Array of table row contents - */ -Table.prototype.addFooter = function(rowCont) { - // Create table row - var tableFoot = $(''); - tableFoot.append(rowCont); - - // Append table row to table - this.table.append(tableFoot); -}; - -/** - * Remove a row from the table - */ -Table.prototype.remove = function(id) { - // To be continued -}; - -/** - * Datatable class constructor - * - * @param tabId Tab ID - * @param tabName Tab name - */ -var DataTable = function(tableId) { - this.dataTableId = tableId; - this.dataTable = null; -}; - -/** - * Initialize the datatable - * - * @param Headers Array of table headers - */ -DataTable.prototype.init = function(headers) { - // Create a table - this.dataTable = $('
                                        '); - var thead = $(''); - var headRow = $(''); - - // Append headers - for (var i in headers) { - headRow.append('' + headers[i] + ''); - } - - thead.append(headRow); - this.dataTable.append(thead); - - // Append table body - var tableBody = $(''); - this.dataTable.append(tableBody); -}; - -/** - * Return the datatable object - * - * @return Object representing the table - */ -DataTable.prototype.object = function() { - return this.dataTable; -}; - -/** - * Add a row to the datatable - * - * @param rowCont Array of table row contents - */ -DataTable.prototype.add = function(rowCont) { - // Create table row - var tableRow = $(''); - - // Create a column for each content - var tableCol; - for (var i in rowCont) { - tableCol = $(''); - tableCol.append(rowCont[i]); - tableRow.append(tableCol); - } - - // Append table row to table - this.dataTable.find('tbody').append(tableRow); -}; - -/** - * Create status bar - * - * @param barId Status bar ID - */ -function createStatusBar(barId) { - // Do not change the background color or color! This is handled by the theme - // the user selects. - var statusBar = $('
                                        ').css({ - 'margin-bottom': '5px', - 'min-height': '30px', - 'max-height': '150px', - 'overflow': 'auto' - }); - - // Create info icon - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px', - 'vertical-align': 'top' - }); - - // Create message section - var msg = $('
                                        ').css({ - 'display': 'inline-block', - 'margin': '10px 0px', - 'width': '90%' - }); - - // Create hide button - var hide = $('').css({ - 'display': 'inline-block', - 'float': 'right', - 'cursor': 'pointer' - }).click(function() { - // Remove info box on-click - $(this).parent().hide(); - }); - - statusBar.append(icon); - statusBar.append(msg); - statusBar.append(hide); - return statusBar; -} - -/** - * Create info bar - * - * @param msg Info message - * @return Info bar - */ -function createInfoBar(msg) { - // Do not change the background color or color! This is handled by the theme - // the user selects. - var infoBar = $('
                                        ').css({ - 'margin': '5px 0px' - }); - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px' - }); - var barMsg = $('

                                        ' + msg + '

                                        ').css({ - 'display': 'inline-block', - 'width': '90%' - }); - - infoBar.append(icon); - infoBar.append(barMsg); - return infoBar; -} - -/** - * Create warning bar - * - * @param msg Warning message - * @return Warning bar - */ -function createWarnBar(msg) { - var warnBar = $('
                                        '); - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px' - }); - var barMsg = $('

                                        ' + msg + '

                                        ').css({ - 'display': 'inline-block', - 'width': '90%' - }); - - warnBar.append(icon); - warnBar.append(barMsg); - return warnBar; -} - -/** - * Create a loader - * - * @param loaderId Loader ID - */ -function createLoader(loaderId) { - var loader = $(''); - return loader; -} - -/** - * Create a button - * - * @param name Name of the button - */ -function createButton(name) { - var button = $('').button(); - return button; -} - -/** - * Create a menu - * - * @param items An array of items to go into the menu - * @return A division containing the menu - */ -function createMenu(items) { - var menu = $('
                                          '); - - // Loop through each item - for ( var i in items) { - // Append item to menu - var item = $('
                                        • '); - - // If it is a sub menu - if (items[i] instanceof Array) { - // 1st index = Sub menu title - item.append(items[i][0]); - // 2nd index = Sub menu - item.append(items[i][1]); - } else { - item.append(items[i]); - } - - menu.append(item); - } - - return menu; -} - -/** - * Initialize the page - */ -function initPage() { - // Load theme - var theme = $.cookie('xcat_theme'); - if (theme) { - switch (theme) { - case 'cupertino': - includeCss("css/themes/jquery-ui-cupertino.css"); - break; - case 'dark_hive': - includeCss("css/themes/jquery-ui-dark_hive.css"); - break; - case 'redmond': - includeCss("css/themes/jquery-ui-redmond.css"); - break; - case 'start': - includeCss("css/themes/jquery-ui-start.css"); - break; - case 'sunny': - includeCss("css/themes/jquery-ui-sunny.css"); - break; - case 'ui_dark': - includeCss("css/themes/jquery-ui-ui_darkness.css"); - break; - default: - includeCss("css/themes/jquery-ui-start.css"); - } - } else { - includeCss("css/themes/jquery-ui-start.css"); - } - - // Load jQuery stylesheets - includeCss("css/jquery.dataTables.css"); - includeCss("css/superfish.css"); - // includeCss("css/jstree.css"); - includeCss("css/jquery.jqplot.css"); - - // Load custom stylesheet - includeCss("css/style.css"); - - // JQuery plugins - includeJs("js/jquery/jquery.dataTables.min.js"); - includeJs("js/jquery/jquery.form.min.js"); - includeJs("js/jquery/jquery.jeditable.min.js"); - includeJs("js/jquery/jquery.contextmenu.min.js"); - includeJs("js/jquery/superfish.min.js"); - includeJs("js/jquery/hoverIntent.min.js"); - // includeJs("js/jquery/jquery.jstree.min.js"); - includeJs("js/jquery/tooltip.min.js"); - includeJs("js/jquery/jquery.serverBrowser.min.js"); - includeJs("js/jquery/jquery.jqplot.min.js"); - includeJs("js/jquery/jqplot.pieRenderer.min.js"); - includeJs("js/jquery/jqplot.barRenderer.min.js"); - includeJs("js/jquery/jqplot.pointLabels.min.js"); - includeJs("js/jquery/jqplot.categoryAxisRenderer.min.js"); - includeJs("js/jquery/jqplot.dateAxisRenderer.min.js"); - includeJs("js/jquery/jquery.topzindex.min.js"); - - // Page plugins - includeJs("js/configure/configure.js"); - includeJs("js/monitor/monitor.js"); - includeJs("js/nodes/nodes.js"); - includeJs("js/provision/provision.js"); - - // Custom plugins - includeJs("js/custom/esx.js"); - includeJs("js/custom/kvm.js"); - includeJs("js/custom/blade.js"); - includeJs("js/custom/ipmi.js"); - includeJs("js/custom/zvm.js"); - includeJs("js/custom/hmc.js"); - includeJs("js/custom/customUtils.js"); - - // Enable settings link - $('#xcat_settings').click(function() { - openSettings(); - }); - - // Set header to theme - var background = '', color = ''; - var theme = $.cookie('xcat_theme'); - if (theme) { - switch (theme) { - case 'cupertino': - background = '#3BAAE3'; - color = 'white'; - break; - case 'dark_hive': - background = '#0972A5'; - break; - case 'redmond': - background = '#F5F8F9'; - color = '#E17009'; - break; - case 'start': - background = '#6EAC2C'; - break; - case 'sunny': - background = 'white'; - color = '#0074C7'; - break; - case 'ui_dark': - background = '#F58400'; - break; - default: - background = '#6EAC2C'; - } - } else { - background = '#6EAC2C'; - } - - $('#header').addClass('ui-state-default'); - $('#header').css('border', '0px'); - - // Set theme to user span - $('#login_user').css('color', color); - - // Style for selected page - var style = { - 'background-color': background, - 'color': color - }; - - // Get the page being loaded - var url = window.location.pathname; - var page = url.replace('/xcat/', ''); - var headers = $('#header ul li a'); - - // Show the page - $("#content").children().remove(); - if (page == 'configure.php') { - includeJs("js/configure/update.js"); - includeJs("js/configure/service.js"); - includeJs("js/configure/users.js"); - includeJs("js/configure/files.js"); - headers.eq(1).css(style); - loadConfigPage(); - } else if (page == 'provision.php') { - includeJs("js/provision/images.js"); - headers.eq(2).css(style); - loadProvisionPage(); - } else if (page == 'help.php') { - includeJs("js/help/help.js"); - headers.eq(4).css(style); - loadHelpPage(); - } else { - // Load nodes page by default - includeJs("js/nodes/nodeset.js"); - includeJs("js/nodes/rnetboot.js"); - includeJs("js/nodes/updatenode.js"); - includeJs("js/nodes/rscan.js"); - headers.eq(0).css(style); - loadNodesPage(); - } -} - -/** - * Include javascript file in - * - * @param file File to include - */ -function includeJs(file) { - var script = $("head script[src='" + file + "']"); - - // If does not contain the javascript - if (!script.length) { - // Append the javascript to - var script = $(''); - script.attr( { - type : 'text/javascript', - src : file - }); - - $('head').append(script); - } -} - -/** - * Include CSS link in - * - * @param file File to include - */ -function includeCss(file) { - var link = $("head link[href='" + file + "']"); - - // If does not contain the link - if (!link.length) { - // Append the CSS link to - var link = $(''); - link.attr( { - type : 'text/css', - rel : 'stylesheet', - href : file - }); - - $('head').append(link); - } -} - -/** - * Write ajax response to a paragraph - * - * @param rsp Ajax response - * @param pattern Pattern to replace with a break - * @return Paragraph containing ajax response - */ -function writeRsp(rsp, pattern) { - // Create paragraph to hold ajax response - var prg = $('
                                          ');
                                          -
                                          -    for ( var i in rsp) {
                                          -        if (rsp[i]) {
                                          -            // Create regular expression for given pattern
                                          -            // Replace pattern with break
                                          -            if (pattern) {
                                          -                rsp[i] = rsp[i].replace(new RegExp(pattern, 'g'), '
                                          '); - prg.append(rsp[i]); - } else { - prg.append(rsp[i]); - prg.append('
                                          '); - } - } - } - - return prg; -} - -/** - * Open a dialog and show given message - * - * @param type Type of dialog, i.e. warn or info - * @param msg Message to show - */ -function openDialog(type, msg) { - var msgDialog = $('
                                          '); - var title = ""; - if (type == "warn") { - // Create warning message - msgDialog.append(createWarnBar(msg)); - title = "Warning"; - } else { - // Create info message - msgDialog.append(createInfoBar(msg)); - title = "Info"; - } - - // Open dialog - msgDialog.dialog({ - title: title, - modal: true, - close: function(){ - $(this).remove(); - }, - width: 500, - buttons: { - "Ok": function(){ - $(this).dialog("close"); - } - } - }); -} - -/** - * Create an iframe to hold the output of an xCAT command - * - * @param src The URL of the document to show in the iframe - * @return Info box containing the iframe - */ -function createIFrame(src) { - // Put an iframe inside an info box - var infoBar = $('
                                          ').css({ - 'margin-bottom': '5px' - }); - - // Create info and close icons - var icon = $('').css({ - 'display': 'inline-block', - 'margin': '10px 5px' - }); - var close = $('').css({ - 'display': 'inline-block', - 'float': 'right', - 'margin': '10px 5px' - }).click(function() { - // Remove info box on-click - $(this).parent().remove(); - }); - - var iframe = $('').attr('src', src).css({ - 'display': 'block', - 'border': '0px', - 'margin': '10px', - 'width': '100%' - }); - - var loader = createLoader('iLoader').css({ - 'display': 'block', - 'margin': '10px 0px' - }); - - infoBar.append(icon); - infoBar.append($('
                                          ').append(loader, iframe)); - infoBar.append(close); - - // Remove loader when done - iframe.load(function() { - loader.remove(); - }); - - return infoBar; -} - - -/** - * Open dialog to set xCAT UI settings - */ -function openSettings() { - // Create form to add node range - var dialog = $('
                                          '); - var info = createInfoBar('Select from the following options'); - dialog.append(info); - - var style = { - 'color': 'blue', - 'cursor': 'pointer', - 'padding': '5px' - }; - - var changeThemeOption = $('').css(style); - dialog.append(changeThemeOption); - - var changePasswordOption = $('').css(style); - dialog.append(changePasswordOption); - - // Open form as a dialog - dialog.dialog({ - modal: true, - close: function(){ - $(this).remove(); - }, - title: 'Settings', - width: 400, - buttons: { - "Cancel": function(){ - $(this).dialog("close"); - } - } - }); - - // Bind to click event - changeThemeOption.click(function() { - dialog.dialog("close"); - changeTheme(); - }); - - changePasswordOption.click(function() { - dialog.dialog("close"); - changePassword(); - }); -} - -/** - * Open dialog to change xCAT theme - */ -function changeTheme() { - // Create form to add node range - var dialog = $('
                                          '); - var info = createInfoBar('Select the xCAT theme you desire'); - dialog.append(info); - - // Create select drop down for themes - var oList = $('
                                            '); - oList.append($('
                                          1. Cupertino
                                          2. ')); - oList.append($('
                                          3. Dark Hive
                                          4. ')); - oList.append($('
                                          5. Redmond
                                          6. ')); - oList.append($('
                                          7. Start (default)
                                          8. ')); - oList.append($('
                                          9. Sunny
                                          10. ')); - oList.append($('
                                          11. UI Darkness
                                          12. ')); - dialog.append(oList); - - if ($.cookie('xcat_theme')) { - // Select theme - oList.find('input[value="' + $.cookie('xcat_theme') + '"]').attr('checked', true); - } else { - oList.find('input[value="start"]').attr('checked', true); - } - - // Open form as a dialog - dialog.dialog({ - modal: true, - close: function(){ - $(this).remove(); - }, - title: 'xCAT Theme', - width: 400, - buttons: { - "Ok": function(){ - // Save selected theme - var theme = $(this).find('input[name="theme"]:checked').val(); - $.cookie('xcat_theme', theme); // Do not expire cookie, keep it as long as possible - - // Show instructions to apply theme - $(this).empty(); - var info = createInfoBar('You will need to reload this page in order for changes to take effect'); - $(this).append(info); - - // Only show close button - $(this).dialog("option", "buttons", { - "Close" : function() { - $(this).dialog( "close" ); - } - }); - }, - "Cancel": function(){ - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Open dialog to change user password - */ -function changePassword() { - // Create form to add node range - var dialog = $('
                                            '); - var info = createInfoBar('Change your password'); - dialog.append(info); - - dialog.append('
                                            '); - dialog.append('
                                            '); - - // Open form as a dialog - dialog.dialog({ - modal: true, - close: function(){ - $(this).remove(); - }, - title: 'Change Password', - width: 400, - buttons: { - "Ok": function(){ - // Remove any warning messages - $(this).find('.ui-state-error').remove(); - - var errorMessage = ""; - - // Check each input is provided - $('#changePassword input').each(function() { - if (!$(this).val()) { - errorMessage = "Please provide a value for each missing input!"; - } - }); - - // Do not continue if error found - if (errorMessage) { - dialog.prepend(createWarnBar(errorMessage)); - return; - } - - // Check new and confirm passwords match - var user = $.cookie('xcat_username'); - var newPassword = $('#changePassword input[name="newPassword"]').val(); - var confirmPassword = $('#changePassword input[name="confirmPassword"]').val(); - if (newPassword != confirmPassword) { - dialog.prepend(createWarnBar("Please confirm new password!")); - return; - } - - // Change dialog buttons - $('#changePassword').dialog('option', 'buttons', { - 'Close':function(){ - $('#changePassword').dialog('destroy').remove(); - } - }); - - // Send request to change password - var url = window.location.pathname; - var page = url.replace('/xcat/', ''); - var url = 'lib/cmd.php'; - // Service portal does not have access to cmd.php - if (page == 'service.php') - url = 'lib/srv_cmd.php'; - $.ajax( { - url : url, - dataType : 'json', - data : { - cmd : 'webrun', - tgt : '', - args : 'passwd;' + user + ';' + newPassword, - msg : '' - }, - - success : function (data) { - // Show response message - var rspMessage = ""; - for (var i in data.rsp) - rspMessage += data.rsp[i] + "
                                            "; - - $('#changePassword').prepend(createInfoBar(rspMessage)); - } - }); - }, - "Cancel": function(){ - $(this).dialog( "close" ); - } - } - }); -} - -/** - * Adjust datatable column size - * - * @param tableId Table ID - */ -function adjustColumnSize(tableId) { - var dTable = $('#' + tableId).dataTable(); - dTable.fnAdjustColumnSizing(); -} - -/** - * Set menu theme - * - * @param menu Menu object - */ -function setMenu2Theme(menu) { - // On hover - var background = '', color = ''; - var theme = $.cookie('xcat_theme'); - if (theme) { - switch (theme) { - case 'cupertino': - background = '#3BAAE3'; - color = 'white'; - break; - case 'dark_hive': - background = '#0972A5'; - break; - case 'redmond': - background = '#F5F8F9'; - color = '#E17009'; - break; - case 'start': - background = '#6EAC2C'; - break; - case 'sunny': - background = 'white'; - color = '#0074C7'; - break; - case 'ui_dark': - background = '#F58400'; - break; - default: - background = '#6EAC2C'; - } - } else { - background = '#6EAC2C'; - } - - menu.css('background', background); - menu.find('a:eq(0)').css('color', color); -} - -/** - * Set menu back to normal before applying theme - * - * @param menu Menu object - */ -function setMenu2Normal(menu) { - // Change back to normal - menu.css('background', ''); - menu.find('a:eq(0)').css('color', ''); -} - -/** - * Get nodes that are checked in a given datatable - * - * @param datatableId The datatable ID - * @return Nodes that were checked - */ -function getNodesChecked(datatableId) { - var tgts = ''; - - // Get nodes that were checked - var nodes = $('#' + datatableId + ' input[type=checkbox]:checked'); - for (var i in nodes) { - var tgtNode = nodes.eq(i).attr('name'); - - if (tgtNode) { - tgts += tgtNode; - - // Add a comma at the end - if (i < nodes.length - 1) { - tgts += ','; - } - } - } - - return tgts; -} - -/** - * Check if return message contains errors - * - * @param msg Return message - * @return 0 If return message contains no errors - * -1 If return message contains errors - */ -function containErrors(msg) { - if (msg.indexOf('Failed') > -1 || msg.indexOf('Error') > -1) { - return -1; - } else { - return 0; - } -} - -/** - * Check if a value is an integer - * - * @param value Value to be checked - * @returns true If value is an integer - false If value is not an integer - */ -function isInteger(value){ - if ((parseFloat(value) == parseInt(value)) && !isNaN(value)) { - return true; - } else { - return false; - } -} +/** + * Tab constructor + * + * @param tabId + * Tab ID + * @param tabName + * Tab name + * @return Nothing + */ +var Tab = function(tabId) { + this.tabId = tabId; + this.tabName = null; + this.tab = null; +}; + +/** + * Initialize the tab + * + * @param tabName Tab name to initialize + */ +Tab.prototype.init = function() { + // Create a division containing the tab + this.tab = $('
                                            '); + var tabList = $('
                                              '); + var tabItem = $('
                                            • Dummy tab item
                                            • '); + tabList.append(tabItem); + this.tab.append(tabList); + + // Create a template with close button + var tabs = this.tab.tabs(); + + tabs.bind('tabsselect', function(event, ui){ + // Save the order tabs were selected + var order; + if ($.cookie('xcat_tabindex_history')) { + order = $.cookie('xcat_tabindex_history').split(','); + order[1] = order[0]; // Set index 1 to last selected tab + order[0] = ui.index; // Set index 0 to currently selected tab + } else { + // Create an array to track the tab selected + order = new Array; + order[0] = ui.index; + order[1] = ui.index; + } + + $.cookie('xcat_tabindex_history', order, { path: '/xcat', secure:true }); + }); + + // Remove dummy tab + this.tab.tabs("remove", 0); + + // Hide tab + this.tab.hide(); +}; + +/** + * Return the tab object + * + * @return Object representing the tab + */ +Tab.prototype.object = function() { + return this.tab; +}; + +/** + * Add a new tab + * + * @param tabId Tab ID + * @param tabName Tab name + * @param tabCont Tab content + * @param closeable Is tab closeable + */ +Tab.prototype.add = function(tabId, tabName, tabCont, closeable) { + // Show tab + if (this.tab.css("display") == "none") { + this.tab.show(); + } + + var newTab = $('
                                              '); + newTab.append(tabCont); + this.tab.append(newTab); + this.tab.tabs("add", "#" + tabId, tabName); + + // Append close button + if (closeable) { + var header = this.tab.find('ul.ui-tabs-nav a[href="#' + tabId +'"]').parent(); + header.append(''); + + // Get this tab + var tabs = this.tab; + var tabLink = 'a[href="\#' + tabId + '"]'; + var thisTab = $(tabLink, tabs).parent(); + + // Close tab when close button is clicked + thisTab.find('span.tab-close').bind('click', function(event) { + var tabIndex = ($('li', tabs).index(thisTab)); + + // Do not remove first tab + if (tabIndex != 0) { + // Go back to last tab if user is trying to close currently selected tab + if (tabs.tabs('option', 'selected') == tabIndex) { + // Get last selected tab from history + var order = $.cookie('xcat_tabindex_history').split(','); + if (order[1]) { + tabs.tabs('select', parseInt(order[1])); + } else { + tabs.tabs('select', 0); + } + } + + tabs.tabs('remove', tabIndex); + } + }); + } +}; + +/** + * Select a tab + * + * @param id Tab ID to select + */ +Tab.prototype.select = function(id) { + this.tab.tabs("select", "#" + id); +}; + +/** + * Remove a tab + * + * @param id Tab ID to remove + */ +Tab.prototype.remove = function(id) { + var selectorStr = 'a[href="\#' + id + '"]'; + var selectTab = $(selectorStr, this.tab).parent(); + var index = ($('li', this.tab).index(selectTab)); + this.tab.tabs("remove", index); +}; + +/** + * Table constructor + * + * @param tabId Tab ID + * @param tabName Tab name + */ +var Table = function(tableId) { + if ($('#' + tableId).length) { + this.tableId = tableId; + this.table = $('#' + tableId); + } else { + this.tableId = tableId; + this.table = null; + } +}; + +/** + * Initialize the table + * + * @param Headers Array of table headers + */ +Table.prototype.init = function(headers) { + // Create a table + this.table = $('
                                              '); + var thead = $(''); + var headRow = $(''); + + // Append headers + for ( var i in headers) { + headRow.append('' + headers[i] + ''); + } + + thead.append(headRow); + this.table.append(thead); + + // Append table body + var tableBody = $(''); + this.table.append(tableBody); +}; + +/** + * Return the table object + * + * @return Object representing the table + */ +Table.prototype.object = function() { + return this.table; +}; + +/** + * Add a row to the table + * + * @param rowCont Array of table row contents + */ +Table.prototype.add = function(rowCont) { + // Create table row + var tableRow = $(''); + + // Create a column for each content + var tableCol; + for ( var i in rowCont) { + tableCol = $(''); + tableCol.append(rowCont[i]); + tableRow.append(tableCol); + } + + // Append table row to table + this.table.find('tbody').append(tableRow); +}; + +/** + * Add a footer to the table + * + * @param rowCont Array of table row contents + */ +Table.prototype.addFooter = function(rowCont) { + // Create table row + var tableFoot = $(''); + tableFoot.append(rowCont); + + // Append table row to table + this.table.append(tableFoot); +}; + +/** + * Remove a row from the table + */ +Table.prototype.remove = function(id) { + // To be continued +}; + +/** + * Datatable class constructor + * + * @param tabId Tab ID + * @param tabName Tab name + */ +var DataTable = function(tableId) { + this.dataTableId = tableId; + this.dataTable = null; +}; + +/** + * Initialize the datatable + * + * @param Headers Array of table headers + */ +DataTable.prototype.init = function(headers) { + // Create a table + this.dataTable = $('
                                              '); + var thead = $(''); + var headRow = $(''); + + // Append headers + for ( var i in headers) { + headRow.append('' + headers[i] + ''); + } + + thead.append(headRow); + this.dataTable.append(thead); + + // Append table body + var tableBody = $(''); + this.dataTable.append(tableBody); +}; + +/** + * Return the datatable object + * + * @return Object representing the table + */ +DataTable.prototype.object = function() { + return this.dataTable; +}; + +/** + * Add a row to the datatable + * + * @param rowCont Array of table row contents + */ +DataTable.prototype.add = function(rowCont) { + // Create table row + var tableRow = $(''); + + // Create a column for each content + var tableCol; + for ( var i in rowCont) { + tableCol = $(''); + tableCol.append(rowCont[i]); + tableRow.append(tableCol); + } + + // Append table row to table + this.dataTable.find('tbody').append(tableRow); +}; + +/** + * Create status bar + * + * @param barId Status bar ID + */ +function createStatusBar(barId) { + // Do not change the background color or color! This is handled by the theme + // the user selects. + var statusBar = $('
                                              ').css({ + 'margin-bottom': '5px', + 'min-height': '30px', + 'max-height': '150px', + 'overflow': 'auto' + }); + + // Create info icon + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px', + 'vertical-align': 'top' + }); + + // Create message section + var msg = $('
                                              ').css({ + 'display': 'inline-block', + 'margin': '10px 0px', + 'width': '90%' + }); + + // Create hide button + var hide = $('').css({ + 'display': 'inline-block', + 'float': 'right', + 'cursor': 'pointer' + }).click(function() { + // Remove info box on-click + $(this).parent().hide(); + }); + + statusBar.append(icon); + statusBar.append(msg); + statusBar.append(hide); + return statusBar; +} + +/** + * Create info bar + * + * @param msg Info message + * @return Info bar + */ +function createInfoBar(msg) { + // Do not change the background color or color! This is handled by the theme + // the user selects. + var infoBar = $('
                                              ').css({ + 'margin': '5px 0px' + }); + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px' + }); + var barMsg = $('

                                              ' + msg + '

                                              ').css({ + 'display': 'inline-block', + 'width': '90%' + }); + + infoBar.append(icon); + infoBar.append(barMsg); + return infoBar; +} + +/** + * Create warning bar + * + * @param msg Warning message + * @return Warning bar + */ +function createWarnBar(msg) { + var warnBar = $('
                                              '); + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px' + }); + var barMsg = $('

                                              ' + msg + '

                                              ').css({ + 'display': 'inline-block', + 'width': '90%' + }); + + warnBar.append(icon); + warnBar.append(barMsg); + return warnBar; +} + +/** + * Create a loader + * + * @param loaderId Loader ID + */ +function createLoader(loaderId) { + var loader = $(''); + return loader; +} + +/** + * Create a button + * + * @param name Name of the button + */ +function createButton(name) { + var button = $('').button(); + return button; +} + +/** + * Create a menu + * + * @param items An array of items to go into the menu + * @return A division containing the menu + */ +function createMenu(items) { + var menu = $('
                                                '); + + // Loop through each item + for ( var i in items) { + // Append item to menu + var item = $('
                                              • '); + + // If it is a sub menu + if (items[i] instanceof Array) { + // 1st index = Sub menu title + item.append(items[i][0]); + // 2nd index = Sub menu + item.append(items[i][1]); + } else { + item.append(items[i]); + } + + menu.append(item); + } + + return menu; +} + +/** + * Initialize the page + */ +function initPage() { + // Load theme + var theme = $.cookie('xcat_theme'); + if (theme) { + switch (theme) { + case 'cupertino': + includeCss("css/themes/jquery-ui-cupertino.css"); + break; + case 'dark_hive': + includeCss("css/themes/jquery-ui-dark_hive.css"); + break; + case 'redmond': + includeCss("css/themes/jquery-ui-redmond.css"); + break; + case 'start': + includeCss("css/themes/jquery-ui-start.css"); + break; + case 'sunny': + includeCss("css/themes/jquery-ui-sunny.css"); + break; + case 'ui_dark': + includeCss("css/themes/jquery-ui-ui_darkness.css"); + break; + default: + includeCss("css/themes/jquery-ui-start.css"); + } + } else { + includeCss("css/themes/jquery-ui-start.css"); + } + + // Load jQuery stylesheets + includeCss("css/jquery.dataTables.css"); + includeCss("css/superfish.css"); + includeCss("css/jstree.css"); + includeCss("css/jquery.jqplot.css"); + + // Load custom stylesheet + includeCss("css/style.css"); + + // JQuery plugins + includeJs("js/jquery/jquery.dataTables.min.js"); + includeJs("js/jquery/jquery.form.min.js"); + includeJs("js/jquery/jquery.jeditable.min.js"); + includeJs("js/jquery/jquery.contextmenu.min.js"); + includeJs("js/jquery/superfish.min.js"); + includeJs("js/jquery/hoverIntent.min.js"); + includeJs("js/jquery/jquery.jstree.min.js"); + includeJs("js/jquery/tooltip.min.js"); + includeJs("js/jquery/jquery.serverBrowser.min.js"); + includeJs("js/jquery/jquery.jqplot.min.js"); + includeJs("js/jquery/jqplot.pieRenderer.min.js"); + includeJs("js/jquery/jqplot.dateAxisRenderer.min.js"); + includeJs("js/jquery/jquery.topzindex.min.js"); + + // Page plugins + includeJs("js/configure/configure.js"); + includeJs("js/monitor/monitor.js"); + includeJs("js/nodes/nodes.js"); + includeJs("js/provision/provision.js"); + + // Custom plugins + includeJs("js/custom/zvm.js"); + includeJs("js/custom/customUtils.js"); + + // Enable settings link + $('#xcat_settings').click(function() { + openSettings(); + }); + + // Set header to theme + var background = '', color = ''; + var theme = $.cookie('xcat_theme'); + if (theme) { + switch (theme) { + case 'cupertino': + background = '#3BAAE3'; + color = 'white'; + break; + case 'dark_hive': + background = '#0972A5'; + break; + case 'redmond': + background = '#F5F8F9'; + color = '#E17009'; + break; + case 'start': + background = '#6EAC2C'; + break; + case 'sunny': + background = 'white'; + color = '#0074C7'; + break; + case 'ui_dark': + background = '#F58400'; + break; + default: + background = '#6EAC2C'; + } + } else { + background = '#6EAC2C'; + } + + $('#header').addClass('ui-state-default'); + $('#header').css('border', '0px'); + + // Set theme to user span + $('#login_user').css('color', color); + + // Style for selected page + var style = { + 'background-color': background, + 'color': color + }; + + // Get the page being loaded + var url = window.location.pathname; + var page = url.replace('/xcat/', ''); + var headers = $('#header ul li a'); + + // Show the page + $("#content").children().remove(); + if (page == 'configure.php') { + includeJs("js/configure/update.js"); + includeJs("js/configure/service.js"); + includeJs("js/configure/users.js"); + includeJs("js/configure/files.js"); + headers.eq(1).css(style); + loadConfigPage(); + } else if (page == 'provision.php') { + includeJs("js/provision/images.js"); + headers.eq(2).css(style); + loadProvisionPage(); + } else if (page == 'help.php') { + includeJs("js/help/help.js"); + headers.eq(4).css(style); + loadHelpPage(); + } else { + // Load nodes page by default + includeJs("js/nodes/nodeset.js"); + includeJs("js/nodes/rnetboot.js"); + includeJs("js/nodes/updatenode.js"); + includeJs("js/nodes/rscan.js"); + headers.eq(0).css(style); + loadNodesPage(); + } +} + +/** + * Include javascript file in + * + * @param file File to include + */ +function includeJs(file) { + var script = $("head script[src='" + file + "']"); + + // If does not contain the javascript + if (!script.length) { + // Append the javascript to + var script = $(''); + script.attr( { + type : 'text/javascript', + src : file + }); + + $('head').append(script); + } +} + +/** + * Include CSS link in + * + * @param file File to include + */ +function includeCss(file) { + var link = $("head link[href='" + file + "']"); + + // If does not contain the link + if (!link.length) { + // Append the CSS link to + var link = $(''); + link.attr( { + type : 'text/css', + rel : 'stylesheet', + href : file + }); + + $('head').append(link); + } +} + +/** + * Write ajax response to a paragraph + * + * @param rsp Ajax response + * @param pattern Pattern to replace with a break + * @return Paragraph containing ajax response + */ +function writeRsp(rsp, pattern) { + // Create paragraph to hold ajax response + var prg = $('
                                                ');
                                                +
                                                +    for ( var i in rsp) {
                                                +        if (rsp[i]) {
                                                +            // Create regular expression for given pattern
                                                +            // Replace pattern with break
                                                +            if (pattern) {
                                                +                rsp[i] = rsp[i].replace(new RegExp(pattern, 'g'), '
                                                '); + prg.append(rsp[i]); + } else { + prg.append(rsp[i]); + prg.append('
                                                '); + } + } + } + + return prg; +} + +/** + * Open a dialog and show given message + * + * @param type Type of dialog, i.e. warn or info + * @param msg Message to show + */ +function openDialog(type, msg) { + var msgDialog = $('
                                                '); + var title = ""; + if (type == "warn") { + // Create warning message + msgDialog.append(createWarnBar(msg)); + title = "Warning"; + } else { + // Create info message + msgDialog.append(createInfoBar(msg)); + title = "Info"; + } + + // Open dialog + msgDialog.dialog({ + title: title, + modal: true, + close: function(){ + $(this).remove(); + }, + width: 500, + buttons: { + "Ok": function(){ + $(this).dialog("close"); + } + } + }); +} + +/** + * Create an iframe to hold the output of an xCAT command + * + * @param src The URL of the document to show in the iframe + * @return Info box containing the iframe + */ +function createIFrame(src) { + // Put an iframe inside an info box + var infoBar = $('
                                                ').css({ + 'margin-bottom': '5px' + }); + + // Create info and close icons + var icon = $('').css({ + 'display': 'inline-block', + 'margin': '10px 5px' + }); + var close = $('').css({ + 'display': 'inline-block', + 'float': 'right', + 'margin': '10px 5px' + }).click(function() { + // Remove info box on-click + $(this).parent().remove(); + }); + + var iframe = $('').attr('src', src).css({ + 'display': 'block', + 'border': '0px', + 'margin': '10px', + 'width': '100%' + }); + + var loader = createLoader('iLoader').css({ + 'display': 'block', + 'margin': '10px 0px' + }); + + infoBar.append(icon); + infoBar.append($('
                                                ').append(loader, iframe)); + infoBar.append(close); + + // Remove loader when done + iframe.load(function() { + loader.remove(); + }); + + return infoBar; +} + + +/** + * Open dialog to set xCAT UI settings + */ +function openSettings() { + // Create form to add node range + var dialog = $('
                                                '); + var info = createInfoBar('Select from the following options'); + dialog.append(info); + + var style = { + 'color': 'blue', + 'cursor': 'pointer', + 'padding': '5px' + }; + + var changeThemeOption = $('').css(style); + dialog.append(changeThemeOption); + + var changePasswordOption = $('').css(style); + dialog.append(changePasswordOption); + + // Open form as a dialog + dialog.dialog({ + modal: true, + close: function(){ + $(this).remove(); + }, + title: 'Settings', + width: 400, + buttons: { + "Cancel": function(){ + $(this).dialog("close"); + } + } + }); + + // Bind to click event + changeThemeOption.click(function() { + dialog.dialog("close"); + changeTheme(); + }); + + changePasswordOption.click(function() { + dialog.dialog("close"); + changePassword(); + }); +} + +/** + * Open dialog to change xCAT theme + */ +function changeTheme() { + // Create form to add node range + var dialog = $('
                                                '); + var info = createInfoBar('Select the xCAT theme you desire'); + dialog.append(info); + + // Create select drop down for themes + var oList = $('
                                                  '); + oList.append($('
                                                1. Cupertino
                                                2. ')); + oList.append($('
                                                3. Dark Hive
                                                4. ')); + oList.append($('
                                                5. Redmond
                                                6. ')); + oList.append($('
                                                7. Start (default)
                                                8. ')); + oList.append($('
                                                9. Sunny
                                                10. ')); + oList.append($('
                                                11. UI Darkness
                                                12. ')); + dialog.append(oList); + + if ($.cookie('xcat_theme')) { + // Select theme + oList.find('input[value="' + $.cookie('xcat_theme') + '"]').attr('checked', true); + } else { + oList.find('input[value="start"]').attr('checked', true); + } + + // Open form as a dialog + dialog.dialog({ + modal: true, + close: function(){ + $(this).remove(); + }, + title: 'xCAT Theme', + width: 400, + buttons: { + "Ok": function(){ + // Save selected theme + var theme = $(this).find('input[name="theme"]:checked').val(); + $.cookie('xcat_theme', theme); // Do not expire cookie, keep it as long as possible + + // Show instructions to apply theme + $(this).empty(); + var info = createInfoBar('You will need to reload this page in order for changes to take effect'); + $(this).append(info); + + // Only show close button + $(this).dialog("option", "buttons", { + "Close" : function() { + $(this).dialog( "close" ); + } + }); + }, + "Cancel": function(){ + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Open dialog to change user password + */ +function changePassword() { + // Create form to add node range + var dialog = $('
                                                  '); + var info = createInfoBar('Change your password'); + dialog.append(info); + + dialog.append('
                                                  '); + dialog.append('
                                                  '); + + // Open form as a dialog + dialog.dialog({ + modal: true, + close: function(){ + $(this).remove(); + }, + title: 'Change Password', + width: 400, + buttons: { + "Ok": function(){ + // Remove any warning messages + $(this).find('.ui-state-error').remove(); + + var errorMessage = ""; + + // Check each input is provided + $('#changePassword input').each(function() { + if (!$(this).val()) { + errorMessage = "Please provide a value for each missing input!"; + } + }); + + // Do not continue if error found + if (errorMessage) { + dialog.prepend(createWarnBar(errorMessage)); + return; + } + + // Check new and confirm passwords match + var user = $.cookie('xcat_username'); + var newPassword = $('#changePassword input[name="newPassword"]').val(); + var confirmPassword = $('#changePassword input[name="confirmPassword"]').val(); + if (newPassword != confirmPassword) { + dialog.prepend(createWarnBar("Please confirm new password!")); + return; + } + + // Change dialog buttons + $('#changePassword').dialog('option', 'buttons', { + 'Close':function(){ + $('#changePassword').dialog('destroy').remove(); + } + }); + + // Send request to change password + var url = window.location.pathname; + var page = url.replace('/xcat/', ''); + var url = 'lib/cmd.php'; + // Service portal does not have access to cmd.php + if (page == 'service.php') + url = 'lib/srv_cmd.php'; + $.ajax( { + url : url, + dataType : 'json', + data : { + cmd : 'webrun', + tgt : '', + args : 'passwd;' + user + ';' + newPassword, + msg : '' + }, + + success : function (data) { + // Show response message + var rspMessage = ""; + for (var i in data.rsp) + rspMessage += data.rsp[i] + "
                                                  "; + + $('#changePassword').prepend(createInfoBar(rspMessage)); + } + }); + }, + "Cancel": function(){ + $(this).dialog( "close" ); + } + } + }); +} + +/** + * Adjust datatable column size + * + * @param tableId Table ID + */ +function adjustColumnSize(tableId) { + var dTable = $('#' + tableId).dataTable(); + dTable.fnAdjustColumnSizing(); +} + +/** + * Set menu theme + * + * @param menu Menu object + */ +function setMenu2Theme(menu) { + // On hover + var background = '', color = ''; + var theme = $.cookie('xcat_theme'); + if (theme) { + switch (theme) { + case 'cupertino': + background = '#3BAAE3'; + color = 'white'; + break; + case 'dark_hive': + background = '#0972A5'; + break; + case 'redmond': + background = '#F5F8F9'; + color = '#E17009'; + break; + case 'start': + background = '#6EAC2C'; + break; + case 'sunny': + background = 'white'; + color = '#0074C7'; + break; + case 'ui_dark': + background = '#F58400'; + break; + default: + background = '#6EAC2C'; + } + } else { + background = '#6EAC2C'; + } + + menu.css('background', background); + menu.find('a:eq(0)').css('color', color); +} + +/** + * Set menu back to normal before applying theme + * + * @param menu Menu object + */ +function setMenu2Normal(menu) { + // Change back to normal + menu.css('background', ''); + menu.find('a:eq(0)').css('color', ''); +} + +/** + * Get nodes that are checked in a given datatable + * + * @param datatableId The datatable ID + * @return Nodes that were checked + */ +function getNodesChecked(datatableId) { + var tgts = ''; + + // Get nodes that were checked + var nodes = $('#' + datatableId + ' input[type=checkbox]:checked'); + for (var i in nodes) { + var tgtNode = nodes.eq(i).attr('name'); + + if (tgtNode) { + tgts += tgtNode; + + // Add a comma at the end + if (i < nodes.length - 1) { + tgts += ','; + } + } + } + + return tgts; +} + +/** + * Check if return message contains errors + * + * @param msg Return message + * @return 0 If return message contains no errors + * -1 If return message contains errors + */ +function containErrors(msg) { + if (msg.indexOf('Failed') > -1 || msg.indexOf('Error') > -1) { + return -1; + } else { + return 0; + } +} + +/** + * Check if a value is an integer + * + * @param value Value to be checked + * @returns true If value is an integer + false If value is not an integer + */ +function isInteger(value){ + if ((parseFloat(value) == parseInt(value)) && !isNaN(value)) { + return true; + } else { + return false; + } +} + +/** + * Remove html encoding from php htmlentities() conversion + * + * @param encodedString + */ +function decodeHTML(encodedString) { + var txt = document.createElement("textarea"); + txt.innerHTML = encodedString; + return txt.value; +} + +/** + * Remove html encoding from rsp.data array and rsp.msg + * + * @param encodedData from server response + */ +function decodeRsp(encodedData) { + /* response data has rsp and msg. msg is a string. + rsp is a string, or array of strings or array of arrays with strings*/ + if ( (typeof encodedData.rsp != "undefined") && (encodedData.rsp.length > 0) ) { + if (encodedData.rsp.constructor === Array) { + for (var i in encodedData.rsp) { + if (encodedData.rsp[i].constructor === Array) { + for (var j in encodedData.rsp[i]) { + encodedData.rsp[i][j] = decodeHTML(encodedData.rsp[i][j]); + } + } else { + encodedData.rsp[i] = decodeHTML(encodedData.rsp[i]); + } + } + } else { + encodedData.rsp = decodeHTML(encodedData.rsp); + } + } + /* msg is a string */ + if ( (typeof encodedData.msg != "undefined") && (encodedData.msg.length > 0) ){ + encodedData.msg = decodeHTML(encodedData.msg); + } + return encodedData; +} diff --git a/xCAT-UI/lib/cmd.php b/xCAT-UI/lib/cmd.php index f63f83000..db9a3a23b 100644 --- a/xCAT-UI/lib/cmd.php +++ b/xCAT-UI/lib/cmd.php @@ -1,225 +1,241 @@ -children() as $child) { - foreach ($child->children() as $data) { - if($data->name) { - $node = $data->name; - - if ($data->data->contents) { - $cont = $data->data->contents; - } else { - $cont = $data->data; - } - - if ($data->data->desc) { - $cont = $data->data->desc . ": " . $cont; - } - - $cont = str_replace(":|:", "\n", $cont); - array_push($rsp, "$node: $cont"); - } else if (strlen("$data") > 2) { - $data = str_replace(":|:", "\n", $data); - array_push($rsp, "$data"); - } - } - } - } - - // Reply in the form of JSON - $rtn = array("rsp" => $rsp, "msg" => $msg); - echo json_encode($rtn); -} - -/** - * Extract the output for a webrun command - * - * @param $xml The XML output from docmd() - * @return An array containing the output - */ -function extractWebrun($xml) { - $rsp = array(); - $i = 0; - - // Extract data returned - foreach($xml->children() as $nodes){ - foreach($nodes->children() as $node){ - // Get the node name - $name = $node->name; - - // Get the content - $status = $node->data; - $status = str_replace(":|:", "\n", $status); - - // Add to return array - $rsp[$i] = array("$name", "$status"); - $i++; - } - } - - return $rsp; -} - -/** - * Extract the output for a nodels command - * - * @param $xml The XML output from docmd() - * @return An array containing the output - */ -function extractNodels($xml) { - $rsp = array(); - $i = 0; - - // Extract data returned - foreach($xml->children() as $nodes){ - foreach($nodes->children() as $node){ - // Get the node name - $name = $node->name; - // Get the content - $status = $node->data->contents; - $status = str_replace(":|:", "\n", $status); - - $description = $node->data->desc; - // Add to return array - $rsp[$i] = array("$name", "$status", "$description"); - $i++; - } - } - - return $rsp; -} - -/** - * Extract the output for a extnoderange command - * - * @param $xml The XML output from docmd() - * @return The nodes and groups - */ -function extractExtnoderange($xml) { - $rsp = array(); - - // Extract data returned - foreach ($xml->xcatresponse->intersectinggroups as $group) { - array_push($rsp, "$group"); - } - - return $rsp; -} +children() as $child) { + foreach ($child->children() as $data) { + if($data->name) { + $node = $data->name; + + if ($data->data->contents) { + $cont = $data->data->contents; + } else { + $cont = $data->data; + } + + if ($data->data->desc) { + $cont = $data->data->desc . ": " . $cont; + } + + $cont = str_replace(":|:", "\n", $cont); + array_push($rsp, "$node: $cont"); + } else if (strlen("$data") > 2) { + $data = str_replace(":|:", "\n", $data); + array_push($rsp, "$data"); + } + } + } + } + // Remove any HTML that could be used for XSS attacks + foreach ($rsp as $key => &$value) { + $whatami = gettype($value); + if ("string" != $whatami) { + //echo "found a non string in rsp array \n"; + foreach ($value as $key2 => $value2){ + //echo "Key2:$key2 Value2 type:",gettype($value2)," value2 data: $value2 \n"; + $value[$key2] = htmlentities($value2, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + } else { + //echo "Key:$key Value type:",gettype($value)," value data: $value \n"; + $rsp[$key] = htmlentities($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + //echo "New value: $rsp[$key] \n"; + } + } + $msg = htmlentities($msg, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // Reply in the form of JSON + $rtn = array("rsp" => $rsp, "msg" => $msg); + echo json_encode($rtn); +} + +/** + * Extract the output for a webrun command + * + * @param $xml The XML output from docmd() + * @return An array containing the output + */ +function extractWebrun($xml) { + $rsp = array(); + $i = 0; + + // Extract data returned + foreach($xml->children() as $nodes){ + foreach($nodes->children() as $node){ + // Get the node name + $name = $node->name; + + // Get the content + $status = $node->data; + $status = str_replace(":|:", "\n", $status); + + // Add to return array + $rsp[$i] = array("$name", "$status"); + $i++; + } + } + + return $rsp; +} + +/** + * Extract the output for a nodels command + * + * @param $xml The XML output from docmd() + * @return An array containing the output + */ +function extractNodels($xml) { + $rsp = array(); + $i = 0; + + // Extract data returned + foreach($xml->children() as $nodes){ + foreach($nodes->children() as $node){ + // Get the node name + $name = $node->name; + // Get the content + $status = $node->data->contents; + $status = str_replace(":|:", "\n", $status); + + $description = $node->data->desc; + // Add to return array + $rsp[$i] = array("$name", "$status", "$description"); + $i++; + } + } + + return $rsp; +} + +/** + * Extract the output for a extnoderange command + * + * @param $xml The XML output from docmd() + * @return The nodes and groups + */ +function extractExtnoderange($xml) { + $rsp = array(); + + // Extract data returned + foreach ($xml->xcatresponse->intersectinggroups as $group) { + array_push($rsp, "$group"); + } + + return $rsp; +} ?> \ No newline at end of file diff --git a/xCAT-UI/lib/functions.php b/xCAT-UI/lib/functions.php index 20c27efa6..f46a7554c 100644 --- a/xCAT-UI/lib/functions.php +++ b/xCAT-UI/lib/functions.php @@ -299,7 +299,7 @@ function isAuthenticated() { */ function isRootAcess() { if (is_logged() && $_SESSION["xcatpassvalid"]) { - $testacc = docmd('tabdump', '', array('policy', '-w', "name==" . $_SESSION["username"]), array()); + $testacc = docmd('tabdump', '', array('-w', "name==" . $_SESSION["username"],'policy' ), array()); if (isset($testacc->{'xcatresponse'}->{'data'}->{1})) { $result = $testacc->{'xcatresponse'}->{'data'}->{1}; $result = str_replace('"', '', $result); diff --git a/xCAT-UI/lib/srv_cmd.php b/xCAT-UI/lib/srv_cmd.php index c1b226369..ecb4fc1a4 100644 --- a/xCAT-UI/lib/srv_cmd.php +++ b/xCAT-UI/lib/srv_cmd.php @@ -1,190 +1,206 @@ -children() as $child) { - foreach ($child->children() as $data) { - if($data->name) { - $node = $data->name; - - if($data->data->contents){ - $cont = $data->data->contents; - } else { - $cont = $data->data; - } - - $cont = str_replace(":|:", "\n", $cont); - array_push($rsp, "$node: $cont"); - } else if (strlen("$data") > 2) { - $data = str_replace(":|:", "\n", $data); - array_push($rsp, "$data"); - } - } - } - } - - // Reply in the form of JSON - $rtn = array("rsp" => $rsp, "msg" => $msg); - echo json_encode($rtn); -} - -/** - * Extract the output for a webrun command - * - * @param $xml The XML output from docmd() - * @return An array containing the output - */ -function extractWebrun($xml) { - $rsp = array(); - $i = 0; - - // Extract data returned - foreach($xml->children() as $nodes){ - foreach($nodes->children() as $node){ - // Get the node name - $name = $node->name; - - // Get the content - $status = $node->data; - $status = str_replace(":|:", "\n", $status); - - // Add to return array - $rsp[$i] = array("$name", "$status"); - $i++; - } - } - - return $rsp; -} - -/** - * Extract the output for a nodels command - * - * @param $xml The XML output from docmd() - * @return An array containing the output - */ -function extractNodels($xml) { - $rsp = array(); - $i = 0; - - // Extract data returned - foreach($xml->children() as $nodes){ - foreach($nodes->children() as $node){ - // Get the node name - $name = $node->name; - // Get the content - $status = $node->data->contents; - $status = str_replace(":|:", "\n", $status); - - $description = $node->data->desc; - // Add to return array - $rsp[$i] = array("$name", "$status", "$description"); - $i++; - } - } - - return $rsp; -} - -/** - * Extract the output for a extnoderange command - * - * @param $xml The XML output from docmd() - * @return The nodes and groups - */ -function extractExtnoderange($xml) { - $rsp = array(); - - // Extract data returned - foreach ($xml->xcatresponse->intersectinggroups as $group) { - array_push($rsp, "$group"); - } - - return $rsp; -} +children() as $child) { + foreach ($child->children() as $data) { + if($data->name) { + $node = $data->name; + + if($data->data->contents){ + $cont = $data->data->contents; + } else { + $cont = $data->data; + } + + $cont = str_replace(":|:", "\n", $cont); + array_push($rsp, "$node: $cont"); + } else if (strlen("$data") > 2) { + $data = str_replace(":|:", "\n", $data); + array_push($rsp, "$data"); + } + } + } + } + // Remove any HTML that could be used for XSS attacks + foreach ($rsp as $key => &$value) { + $whatami = gettype($value); + if ("string" != $whatami) { + //echo "found a non string in rsp array \n"; + foreach ($value as $key2 => $value2){ + //echo "Key2:$key2 Value2 type:",gettype($value2)," value2 data: $value2 \n"; + $value[$key2] = htmlentities($value2, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + } else { + //echo "Key:$key Value type:",gettype($value)," value data: $value \n"; + $rsp[$key] = htmlentities($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + //echo "New value: $rsp[$key] \n"; + } + } + $msg = htmlentities($msg, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // Reply in the form of JSON + $rtn = array("rsp" => $rsp, "msg" => $msg); + echo json_encode($rtn); +} + +/** + * Extract the output for a webrun command + * + * @param $xml The XML output from docmd() + * @return An array containing the output + */ +function extractWebrun($xml) { + $rsp = array(); + $i = 0; + + // Extract data returned + foreach($xml->children() as $nodes){ + foreach($nodes->children() as $node){ + // Get the node name + $name = $node->name; + + // Get the content + $status = $node->data; + $status = str_replace(":|:", "\n", $status); + + // Add to return array + $rsp[$i] = array("$name", "$status"); + $i++; + } + } + + return $rsp; +} + +/** + * Extract the output for a nodels command + * + * @param $xml The XML output from docmd() + * @return An array containing the output + */ +function extractNodels($xml) { + $rsp = array(); + $i = 0; + + // Extract data returned + foreach($xml->children() as $nodes){ + foreach($nodes->children() as $node){ + // Get the node name + $name = $node->name; + // Get the content + $status = $node->data->contents; + $status = str_replace(":|:", "\n", $status); + + $description = $node->data->desc; + // Add to return array + $rsp[$i] = array("$name", "$status", "$description"); + $i++; + } + } + + return $rsp; +} + +/** + * Extract the output for a extnoderange command + * + * @param $xml The XML output from docmd() + * @return The nodes and groups + */ +function extractExtnoderange($xml) { + $rsp = array(); + + // Extract data returned + foreach ($xml->xcatresponse->intersectinggroups as $group) { + array_push($rsp, "$group"); + } + + return $rsp; +} ?> \ No newline at end of file diff --git a/xCAT-UI/lib/systemcmd.php b/xCAT-UI/lib/systemcmd.php index 7990aa035..94bbc4444 100644 --- a/xCAT-UI/lib/systemcmd.php +++ b/xCAT-UI/lib/systemcmd.php @@ -1,36 +1,39 @@ -Please login before continuing!"); - exit; -} - -if (isset($_GET["cmd"])) { - // HTTP GET requests - $cmd = $_GET["cmd"]; - $msg = NULL; - $ret = ""; - - if (isset($_GET["msg"])) { - $msg = $_GET["msg"]; - } - - if ($cmd == "ostype") { - $ret = strtolower(PHP_OS); - } else { - $ret = shell_exec($cmd); - } - - echo json_encode(array("rsp"=>$ret, "msg" => $msg)); -} +Please login before continuing!"); + exit; +} + +if (isset($_GET["cmd"])) { + // HTTP GET requests + $cmd = $_GET["cmd"]; + $msg = NULL; + $ret = ""; + + if (isset($_GET["msg"])) { + $msg = $_GET["msg"]; + } + + if ($cmd == "ostype") { + $ret = strtolower(PHP_OS); + } else { + $ret = shell_exec($cmd); + } + + // Remove any HTML that could be used for XSS attacks + $ret = htmlentities($ret, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + $msg = htmlentities($msg, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + echo json_encode(array("rsp"=>$ret, "msg" => $msg)); +} ?> \ No newline at end of file diff --git a/xCAT-UI/lib/uploadfile.php b/xCAT-UI/lib/uploadfile.php new file mode 100644 index 000000000..fa382c587 --- /dev/null +++ b/xCAT-UI/lib/uploadfile.php @@ -0,0 +1,63 @@ + \ No newline at end of file diff --git a/xCAT-UI/lib/zCmd.php b/xCAT-UI/lib/zCmd.php index c2a21eebf..c77f4b5fe 100644 --- a/xCAT-UI/lib/zCmd.php +++ b/xCAT-UI/lib/zCmd.php @@ -1,142 +1,159 @@ -children() as $child) { - foreach ($child->children() as $data) { - $data = str_replace(":|:", "\n", $data); - array_push($rsp, "$data"); - } - } - } - - // Create virtual server - else if (strncasecmp($cmd, "mkvm", 4) == 0) { - // Directory /var/tmp permissions = 777 - // You can write anything to that directory - $userEntry = "/var/tmp/$tgt.txt"; - $handle = fopen($userEntry, 'w') or die("Cannot open $userEntry"); - fwrite($handle, $att); - fclose($handle); - - // CLI command: mkvm gpok3 /tmp/gpok3.txt - // Create user entry - array_unshift($arr, $userEntry); - $xml = docmd($cmd, $tgt, $arr, NULL); - foreach ($xml->children() as $child) { - foreach ($child->children() as $data) { - $data = str_replace(":|:", "\n", $data); - array_push($rsp, "$data"); - } - } - } - - // Run shell script - // This is a typical command used by all platforms. It is put here because - // most of the code needed are already here - else if (strncasecmp($cmd, "xdsh", 4) == 0) { - // Directory /var/tmp permissions = 777 - // You can write anything to that directory - $msgArgs = explode(";", $msg); - $inst = str_replace("out=scriptStatusBar", "", $msgArgs[0]); - $script = "/var/tmp/script$inst.sh"; - - // Write to file - $handle = fopen($script, 'w') or die("Cannot open $script"); - fwrite($handle, $att); - fclose($handle); - - // Change it to executable - chmod($script, 0777); - - // CLI command: xdsh gpok3 -e /var/tmp/gpok3.sh - // Create user entry - array_push($arr, $script); - $xml = docmd($cmd, $tgt, $arr, NULL); - foreach ($xml->children() as $child) { - foreach ($child->children() as $data) { - $data = str_replace(":|:", "\n", $data); - array_push($rsp, "$data"); - } - } - - // Remove this file - unlink($script); - } - - // Reply in the form of JSON - $rtn = array("rsp" => $rsp, "msg" => $msg); - echo json_encode($rtn); -} +children() as $child) { + foreach ($child->children() as $data) { + $data = str_replace(":|:", "\n", $data); + array_push($rsp, "$data"); + } + } + } + + // Create virtual server + else if (strncasecmp($cmd, "mkvm", 4) == 0) { + // Directory /var/tmp permissions = 777 + // You can write anything to that directory + $userEntry = "/var/tmp/$tgt.txt"; + $handle = fopen($userEntry, 'w') or die("Cannot open $userEntry"); + fwrite($handle, $att); + fclose($handle); + + // CLI command: mkvm gpok3 /tmp/gpok3.txt + // Create user entry + array_unshift($arr, $userEntry); + $xml = docmd($cmd, $tgt, $arr, NULL); + foreach ($xml->children() as $child) { + foreach ($child->children() as $data) { + $data = str_replace(":|:", "\n", $data); + array_push($rsp, "$data"); + } + } + } + + // Run shell script + // This is a typical command used by all platforms. It is put here because + // most of the code needed are already here + else if (strncasecmp($cmd, "xdsh", 4) == 0) { + // Directory /var/tmp permissions = 777 + // You can write anything to that directory + $msgArgs = explode(";", $msg); + $inst = str_replace("out=scriptStatusBar", "", $msgArgs[0]); + $script = "/var/tmp/script$inst.sh"; + + // Write to file + $handle = fopen($script, 'w') or die("Cannot open $script"); + fwrite($handle, $att); + fclose($handle); + + // Change it to executable + chmod($script, 0777); + + // CLI command: xdsh gpok3 -e /var/tmp/gpok3.sh + // Create user entry + array_push($arr, $script); + $xml = docmd($cmd, $tgt, $arr, NULL); + foreach ($xml->children() as $child) { + foreach ($child->children() as $data) { + $data = str_replace(":|:", "\n", $data); + array_push($rsp, "$data"); + } + } + + // Remove this file + unlink($script); + } + + // Remove any HTML that could be used for XSS attacks + foreach ($rsp as $key => &$value) { + $whatami = gettype($value); + if ("string" != $whatami) { + //echo "found a non string in rsp array \n"; + foreach ($value as $key2 => $value2){ + //echo "Key2:$key2 Value2 type:",gettype($value2)," value2 data: $value2 \n"; + $value[$key2] = htmlentities($value2, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + } else { + //echo "Key:$key Value type:",gettype($value)," value data: $value \n"; + $rsp[$key] = htmlentities($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + //echo "New value: $rsp[$key] \n"; + } + } + $msg = htmlentities($msg, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + + // Reply in the form of JSON + $rtn = array("rsp" => $rsp, "msg" => $msg); + echo json_encode($rtn); +} ?> \ No newline at end of file diff --git a/xCAT-UI/xcat/plugins/web.pm b/xCAT-UI/xcat/plugins/web.pm index cd74bde6b..bf0a65b14 100644 --- a/xCAT-UI/xcat/plugins/web.pm +++ b/xCAT-UI/xcat/plugins/web.pm @@ -83,7 +83,8 @@ sub process_request { 'cecsetup' => \&web_cecsetup, 'deletefile' => \&web_deletefile, 'createfolder' => \&web_createfolder, - 'getrepospace' => \&web_getrepospace + 'getrepospace' => \&web_getrepospace, + 'verifynode' => \&web_verifynode, ); # Check whether the request is authorized or not @@ -1982,8 +1983,8 @@ sub web_summary { while (my ($key, $value) = each(%{$attrs})) { web_attrcount($value->[0]->{'os'}, \%oshash); web_attrcount($value->[0]->{'arch'}, \%archhash); - web_attrcount($value->[0]->{'provmethod'}, \%provhash); - web_attrcount($value->[0]->{'nodetype'}, \%typehash); + web_attrcount($value->[0]->{'provmethod'},, \%provhash); + web_attrcount($value->[0]->{'nodetype'},, \%typehash); } $attrs = $nodelistTab->getNodesAttribs(\@nodes, ['status']); @@ -2716,4 +2717,27 @@ sub web_getrepospace() { $callback->({ info => $space }); } +sub web_verifynode() { + my ( $request, $callback, $sub_req ) = @_; + my $cmdOpts = ''; + my $out; + + # Loop to handle all options passed. We don't know how many they will pass + # so we look for 'end' to signify the end. We set a loop variable to 500 which + # we know is much larger than the expected number of arguments. This prevents + # the loop from going on forever should there be an error on the javascript side + # an the 'end' was not passed. + for ( my $i = 0 ; $i < 500 ; $i++ ) { + if ( $request->{arg}->[$i] ne 'end' ) { + $cmdOpts = "$cmdOpts $request->{arg}->[$i]"; + } else { + last; + } + } + + $out = `/opt/xcat/bin/verifynode $cmdOpts`; + + $callback->( { info => $out }); +} + 1; diff --git a/xCAT-client/bin/mkdummyimage b/xCAT-client/bin/mkdummyimage new file mode 100644 index 000000000..facf511c9 --- /dev/null +++ b/xCAT-client/bin/mkdummyimage @@ -0,0 +1,286 @@ +#!/usr/bin/perl +############################################################################### +# IBM(c) 2015 EPL license http://www.eclipse.org/legal/epl-v10.html +############################################################################### +# COMPONENT: mkdummyimage +# +# This script creates a dummy image. +############################################################################### + +use strict; +use warnings; + +use File::Basename; +use File::Path; +use File::Spec; +use File::Temp; +use Getopt::Long; +use MIME::Base64; +use Sys::Hostname; +use Socket; + +my $version = "1.0"; + +my $defaultName = 'dummy.img'; # Default image file name +my $dest = ''; # Target destination (filespec) +my $displayHelp = 0; # Display help information +my $eckdImage = 0; # ECKD image is wanted +my $fbaImage = 0; # FBA image is wanted +my $remoteHost = ''; # Remote host transfer information +my $remoteUser = ''; # Remote user transfer information +my $verbose = 0; # Verbose flag - 0: quiet, 1: verbose +my $versionOpt = 0; # Show version information flag + +# set the usage message +my $usage_string = "Usage:\n + $0 [ --eckd | --fba ] [ -V | --verbose ] + [ -d | --destination ] [-R | --remotehost ]\n + or\n + $0 -v | --version\n + or\n + $0 -h | --help\n + The following options are supported:\n + -d | --destination + File specification of the image file to be created. + Either the filename only or a fully qualified name. + If the destination is within the xCAT MN then + it must be within the directory specified by + the installdir property in the xCAT site table. + The default file name is $defaultName.\n + --eckd Create an empty ECKD disk image. This is the + default image type if neither --eckd or --fba + is specifed.\n + --fba Create an empty FBA disk image\n + -h | --help Display help information\n + -R | --remotehost + Indicates that the destination specified on the + command invocation is the file specification on a + host other than the management node. + The 'host' operand is the IP address or DNS host + name of the target server. A user may be specified + in a similar way as it is specified with the SCP + command using the format 'user\@host' where 'user' + is the name of the user on the remote system and + 'host' operand is as previously stated. The image + is created in the directory specified by the + tmpdir property in the site table and then moved + to the remote host using SCP. After moving the + image off the xCAT MN, the image is removed + from the xCAT MN. Prior to specifying the command, + the keystore on the remote host must be set up with + the public key of the xCAT MN.\n + -v | --version Display the version of this script.\n + -V | --verbose Display verbose processing messages.\n"; + + +#------------------------------------------------------- + +=head3 createImage + + Description : Create the dummy image file. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = createImage(); + +=cut + +#------------------------------------------------------- +sub createImage{ + my $rc = 0; + my $buildDir = ''; + my $header; + + # Create a temporary directory in which to create the image. + my $tmpDir = `/opt/xcat/sbin/tabdump site| grep '^"tmpdir",' | sed s/'^"tmpdir","'//g| sed s/'".*'//g`; + chomp $tmpDir; + if ( $tmpDir eq '' ) { + print "Error: The 'tmpdir' value is missing from the site table.\n"; + $rc = 11; + goto FINISH_createImage; + } + $buildDir = mkdtemp( "$tmpDir/image.$$.XXXXXX" ); + + # Create the image + my $imageFile = $defaultName; + open( my $fh, '>', "$buildDir/$imageFile" ) or die "Could not open file '$buildDir/$imageFile'"; + if ( $verbose ) { + print "Creating the image in $buildDir as $imageFile.\n"; + } + + if ( $eckdImage ) { + $header = "xCAT CKD Disk Image:"; + $header = "$header 0 CYL"; + } + elsif ( $fbaImage ) { + $header = "xCAT FBA Disk Image:"; + $header = "$header 0 BLK"; + } + $header = "$header HLen: 0055"; # Header size increased by x from 0055 + $header = "$header GZIP: 0"; + printf $fh "%-512s", "$header"; + close( $fh ); + + # Move the image to the target location. + if ( $remoteHost ne '' ) { + # Do a remote transfer + my $remoteTarget = $remoteHost . ':' . $dest; + if ( $remoteUser ne '' ) { + $remoteTarget = "$remoteUser\@$remoteTarget"; + } + my $scpVerbose = ''; + if ( $verbose ) { + print "Moving the image $imageFile to the remote system $remoteHost as $dest.\n"; + $scpVerbose = '-v' + } + $rc = system( "/usr/bin/scp $scpVerbose -B $buildDir/$imageFile $remoteTarget" ); + if ( $rc ) { + $rc = $rc >> 8; + print "Error: Unable to copy the image $buildDir/$imageFile to the remote host: $remoteHost, rc: $rc\n"; + $rc = 30; + goto FINISH_createImage; + } + } else { + # Move the file to a local directory + $rc = system( "cp $buildDir/$imageFile $dest" ); + if ( $rc ) { + $rc = $rc >> 8; + print "Error: Unable to copy the image $buildDir/$imageFile to the destination: $dest, rc: $rc\n"; + $rc = 40; + goto FINISH_createImage; + } + } + + if ( $remoteHost ne '' ) { + print "Image created on $remoteHost: $dest\n"; + } else { + print "Image created: $dest\n"; + } + +FINISH_createImage: + if ( -d $buildDir ) { + rmtree $buildDir; + } + return $rc; +} + + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 prepares a special image file that contains no disk\ncontents.\n\n"; + print $usage_string; + return; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $rc = 0; +my $out; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( + 'eckd' => \$eckdImage, + 'd|destination=s' => \$dest, + 'fba' => \$fbaImage, + 'h|help' => \$displayHelp, + 'R|remotehost=s' => \$remoteHost, + 'v|version' => \$versionOpt, + 'V|verbose' => \$verbose )) { + print $usage_string; + goto FINISH_main; +} + +if ( $versionOpt ) { + print "Version: $version\n"; +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH_main; +} + +if ( !$eckdImage and !$fbaImage ) { + $eckdImage = 1; +} + +if ( $remoteHost ne '' ) { + $remoteHost =~ s/^\s+|\s+$//g; # trim blanks from both ends of the value + my $spaceCnt = scalar( () = $remoteHost =~ /\s+/g ); + if ( $spaceCnt != 0 ) { + print "Error: Remote host value is not a single word.\n"; + $rc = 1; + goto FINISH_main; + } + if ( $remoteHost =~ /@/ ) { + my @parts = split( /@/, $remoteHost ); + if ( substr( $remoteHost, -1) eq '@' or + @parts > 2 or + !defined $parts[0] or $parts[0] eq '' or + !defined $parts[1] or $parts[1] eq '' ) { + print "Error: Remote host value is not valid.\n" . + " It should be in the form of 'hostname' or 'user\@hostname'.\n"; + $rc = 2; + goto FINISH_main; + } + $remoteUser = $parts[0]; + $remoteHost = $parts[1]; + } else { + $remoteUser = 'root'; + } +} + +# Determine the destination and make certain it is valid. +my $installDir = `/opt/xcat/sbin/tabdump site| grep '^"installdir",' | sed s/'^"installdir","'//g| sed s/'".*'//g`; +chomp $installDir; +if ( $installDir eq '' ) { + print "Error: The 'installDir' value is missing from the site table.\n"; + $rc = 10; + goto FINISH_main; +} + +if ( $dest eq '' ){ + # Set default destination for either a local or remote location. + if ( $remoteHost eq '' ) { + $dest = "$installDir/$defaultName"; + } else { + $dest = "$defaultName"; + } +} else { + # Can only verify local destination here. SCP will verify the remote destination. + if ( $remoteHost eq '' ) { + if ( $dest !~ /\// ) { + $dest = "$installDir/$dest"; + } else { + if ( $dest !~ /^$installDir/ ) { + print "Error: The destination is not within the install directory specified in the site table.\n"; + $rc = 20; + goto FINISH_main; + } + } + } +} + +# Create the image file. +createImage(); + +FINISH_main: +exit $rc; + diff --git a/xCAT-client/bin/verifynode b/xCAT-client/bin/verifynode new file mode 100644 index 000000000..66c29ffdb --- /dev/null +++ b/xCAT-client/bin/verifynode @@ -0,0 +1,3007 @@ +#!/usr/bin/perl +############################################################################### +# IBM (C) Copyright 2016 Eclipse Public License +# http://www.eclipse.org/org/documents/epl-v10.html +############################################################################### +# COMPONENT: verifynode +# +# Verify the capabilities of a node. +############################################################################### + +package xCAT_plugin::verifynode; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +$XML::Simple::PREFERRED_PARSER='XML::Parser'; + +use strict; +use warnings; + +use Data::Dumper qw(Dumper); +use File::Basename; +use File::Path; +use File::Spec; +use File::Temp; +use Getopt::Long; +use Getopt::Long qw(GetOptionsFromString); +use lib "$::XCATROOT/lib/perl"; +use MIME::Base64; +use POSIX qw/strftime/; +use Scalar::Util qw( looks_like_number ); +use Sys::Hostname; +use Sys::Syslog qw( :DEFAULT setlogsock ); +use Socket; +use Text::Wrap; +use Time::HiRes qw( gettimeofday sleep ); +use Unicode::UCD qw(charinfo); + +use xCAT::zvmCPUtils; +use xCAT::zvmMsgs; +use xCAT::zvmUtils; + +my $version = "1"; + +my $comments = ''; # Comments for the IVP run +my $dataFile = ''; # Input data file +my $decode = ''; # String specified by the --decode operand. +my $disable = ''; # Disable scheduled IVP from running. +my $displayHelp = 0; # Display help information +my $driver = ''; # Name of driver script +my $driverLoc = '/var/opt/xcat/ivp'; # Local directory containing the driver script, if it exists +my $encode = ''; # String specified by the --encode operand. +my $enable = ''; # Enable scheduled IVP to run. +my $endTime = ''; # Human readable end time +my %hosts; # Information on host nodes known by xCAT MN +my $id = ''; # Id of the IVP run +my %ignored; # List of ignored messages +my $ignoreCnt = 0; # Number of times we ignored a message +my $issueCmdOverIUCV = ''; # Issue a command to a node over IUCV +my $issueCmdToNode = ''; # Issue a command to a node using IUCV or SSH +my %localIPs; # Hash of the Local IP addresses for the xCAT MN system +my $logDir = '/var/log/xcat/ivp'; # Log location +my $logFile = ''; # Log file name and location +my $logFileHandle; # File handle for the log file. +my @ivpSummary; # Summary output lines for an IVP run. +my $locApplSystemRole = '/var/lib/sspmod/appliance_system_role'; +my %mnInfo; # Managed node environment information +my %msgsToIgnore; # Hash of messages to ignore +my $needToFinishLogFile = 0; # If 1 then finishLogFile() needs to be called +my $nodeName = ''; # Name of node to be verified +my $notify = 0; # Notify a user +my $notifyOnErrorOrWarning = 0; # Indicates a log file should be sent if the need to notify + # a user was determined. +my $openstackIP = ''; # IP address of the OpenStack node +my $openstackUser = 'root'; # User on the OpenStack system that can access the + # OpenStack configuration files +my $orchParms = ''; # Orchestrator parms to be stored in the zvmivp table +my $prepParms = ''; # Parameters to pass to the preparation script +my $remove = 0; # Remove a scheduled IVP +my $runBasicIVP = 0; # Run the basic xCAT IVP +my $runCron = 0; # Cron job invocation to run a possible set of IVPs + # on a periodic basis. +my $runFullIVP = 0; # Run the full end to end IVP (compute node and xCAT) +my $schedule = ''; # Hours to schedule an automated IVP +my $scheduledType = ''; # Type of IVP to schedule ('fullivp' or 'basicivp') +my $startTime = ''; # Human readable start time +my $tgtOS = ''; # Target Node's distro & version +my $todayDate = ''; # Today's date in the form 'yyyy-mm-dd' +my $verifyAccess = 0; # Verify system can be accessed +my $verifyCaptureReqs = 0; # Verify systems meets capture requirements +my $verifyCloudInit = 0; # Verify cloud-init installed +my $verifyXcatconf4z = 0; # Verify xcatconf4z +my $versionOpt = 0; # Show version information flag +my $warnErrCnt = 0; # Count of warnings and error messages for a verify set +my $zhcpTarget = ''; # ZHCP server that is related to this verification +my $zvmHost = ''; # z/VM host related to this verification +my $zxcatParms = ''; # Parmeters to pass to zxcatIVP + +# Routines to drive based on the selected test. +my %verifySets = ( + '$decode ne \'\'' => + [ '', # Do not show a function banner. + 'encodeDecodeString()', + ], + '$disable and ! $remove and $schedule eq \'\'' => + [ 'Disabling an IVP run', + 'enableDisableRun( $id )', + ], + '$enable and ! $remove and $schedule eq \'\'' => + [ 'Enabling an IVP run', + 'enableDisableRun( $id )', + ], + '$encode ne \'\'' => + [ 'Encoding a string', + 'encodeDecodeString()', + ], + '$issueCmdOverIUCV ne \'\'' => + [ 'Issue a command to a node using IUCV', + 'cmdOnVM( \'onlyIUCV\' )', + ], + '$issueCmdToNode ne \'\'' => + [ 'Issue a command to a node using IUCV or SSH', + 'cmdOnVM( \'either\' )', + ], + '$remove' => + [ 'Remove an automated periodic IVP', + 'removeIVP( $id )', + ], + '$runBasicIVP' => + [ 'Verifying the basic general setup of xCAT', + 'setupLogFile()', + 'addLogHeader( \'basicivp\' )', + 'verifyBasicXCAT()', + 'finishLogFile()', + ], + '$runCron' => + [ 'Cron job for automated verification', + 'driveCronList()', + ], + '$runFullIVP' => + [ 'Verifying the full end-to-end setup of the xCAT and the compute node', + 'setupLogFile()', + 'addLogHeader( \'fullivp\' )', + 'checkForDefault( \'openstackIP\', \'--openstackIP was not specified\', \'xCAT_master\' )', + 'verifyAccess( $openstackIP ); $rc = 0', + 'getDistro( $openstackIP )', + 'runPrepScript()', + 'runDriverScript()', + 'finishLogFile()', + ], + '$schedule ne \'\'' => + [ 'Scheduling an automated periodic IVP', + 'scheduleIVP()', + ], + '$verifyAccess' => + [ 'Verifying access to the system', + 'verifyPower( $nodeName )', + 'verifyAccess( $nodeName )' + ], + '$verifyCaptureReqs' => + [ 'Verifying system meets the image requirements', + 'verifyPower( $nodeName )', + 'verifyAccess( $nodeName )', + 'getDistro( $nodeName )', + 'verifyDistro()', + ], + '$verifyCloudInit' => + [ 'Verifying cloud-init is installed and configured', + 'verifyPower( $nodeName )', + 'verifyAccess( $nodeName )', + 'getDistro( $nodeName )', + 'verifyService($nodeName, "cloud-config,2,3,4,5 cloud-final,2,3,4,5 cloud-init,2,3,4,5 cloud-init-local,2,3,4,5")', + 'showCILevel()', + ], + '$verifyXcatconf4z' => + [ 'Verifying xcatconf4z is installed and configured', + 'verifyPower( $nodeName )', + 'verifyAccess( $nodeName )', + 'getDistro( $nodeName )', + 'verifyXcatconf4z( $nodeName )', + ], + '$test' => + [ 'Script test functon.', + 'verifyLogResponse()', + ], + ); + + +# set the usage message +my $usage_string = "Usage:\n + $0 -n \n + or\n + $0 \n + or\n + $0 -v\n + or\n + $0 [ -h | --help ]\n + The following options are supported:\n + -a | --access + Verify access from xCAT MN to node. + --basicivp + Run the basic xCAT IVP. + --capturereqs + Verify that node meets system capture + requirements for OpenStack. + --cmdoveriucv + Issue a command to a node using IUCV. + Specify a period for when combining this + option with the --file option. + --cmdtonode Issue a command to a node using IUCV or SSH + if IUCV is not available. + Specify a period for when combining this + option with the --file option. + -c | --cloudinit + Verify that node has cloud-init installed. + --comments + Comments to specify on the IVP run. + --cron + Driven as a cron job to run a set of periodic + IVPs. + --decode + Decode an encoded string. This is used to + view encoded values in the zvmivp table. + --disable Indicates that the IVP being scheduled should + be disabled from running. + --enable Indicates that the IVP being scheduled should + be enabled to run. This is the default when + neither --disable or --enable is specified. + --encode + Encode a string. This used for debug in order + to create a string to compare to an encoded + string from the zvmivp table. The must be + an ASCII string. This function does not support + encoding a UTF-8 character string. + --file File location containing additional input. + When used with the --cmdoveriucv or --cmdtonode + operands, this file contains the command to issue. + --fullivp + Run the full end-to-end IVP. This + verification checks the compute node and builds + a driver script which is then used to check + the xCAT environment based on the values + gathered in the driver script. + -h | --help + Display help information. + -i | --id + ID to associate with the IVP run. + --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'. + --notify + Notification of IVP failures should be sent + to a z/VM userid. + -n | --node + Node name of system to verify. + --openstackip + IP address of the OpenStack compute node. + --openstackuser + User to use to access the compute node. + The user defaults to 'root'. + --orchparms + Parameters to be stored in the zvmivp table. These + parameters are passed to this routine when a + scheduled IVP is driven. + --prepparms + Parameters to pass to the preparation script as + part of a full IVP. + --remove + Indicates that an IVP in the zvmivp table should be + removed. The id is specified with the --id + operand. + --schedule + List of hours (in 24 hour format) to periodically + run the IVP. Multiple hours are separated by blanks. + For example, \'0 6 12 18 20\'. + --type < basicivp | fullivp > + Type of IVP to be scheduled. + -v + Display the version of this script. + -x | --xcatconf4z + Verify that node has xcatconf4z installed. + --zxcatparms + Parameters to pass to the zxcatIVP script.\n"; + + +#------------------------------------------------------- + +=head3 addLogHeader + + Description : Add a header to the log file. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = addLogHeader(); + +=cut + +#------------------------------------------------------- +sub addLogHeader { + my ( $hType ) = @_; + + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + + # Generate the header based on the header type. + if ( $hType eq 'basicivp' ) { + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "This log file was created for a basic xCAT IVP. It consists of output from ". + "zxcatIVP that validates the xCAT management node (MN) and the ZHCP Agents that are used by ". + "the xCAT MN node to manage the z/VM hosts." ); + } elsif ( $hType eq 'fullivp' ) { + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "This log file was created for a full xCAT IVP which validates the z/VM OpenStack ". + "configuration files, the xCAT management node (MN) and the ZHCP Agents that are used by ". + "the xCAT MN node to manage the z/VM host. The log file contains the following output: output ". + "from the preparation script that validated the OpenStack properties, the contents of the ". + "driver script created by the preparation script to drive the second part of the IVP, output ". + "of the zxcatIVP run that is driven by the driver script to complete the full IVP." ); + } else { + logResponse( 'DF', 'GENERIC_RESPONSE', "VerifyNode log file." ); + } + + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + + return 0; +} + + +#------------------------------------------------------- + +=head3 checkForDefault + + Description : Determine if a default is needed for + the specified property and set it if + it is needed. Currently, this routine + is only used for the master IP address. + Arguments : Name of property + String to use in a DFLT01 message. + Default or function to perform. + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = checkForDefault( 'openStackIP', '--ip was not specified', 'xCAT_master' ); + +=cut + +#------------------------------------------------------- +sub checkForDefault { + my ( $prop, $reasonString, $default ) = @_; + my $additionalInfo = ''; + my $rc = 0; + my $val; + + eval( "\$val = \$$prop" ); + if ( $val eq '' ) { + if ( $default eq 'xCAT_master' ) { + $default = ''; + if ( exists $mnInfo{'ip'} ) { + $default = $mnInfo{'ip'}; + $additionalInfo = 'The value came from the IP address used as the xCAT MN\'s ' . + 'master IP address that was specified in the site table.'; + } else { + # Use one of the local IPv4 IPs. We don't support IPv6. + if ( keys %localIPs > 0 ) { + # Can't find the MN IP so use the first local IPv4 IP. + foreach my $ip ( keys %localIPs ) { + if ( $ip =~ /:/ ) { + next; + } else { + $default = $ip; + $additionalInfo = 'The function could not determine the master IP address for ' . + 'the xCAT MN so the first local IPv4 address was used.'; + last; + } + } + if ( $default eq '' ) { + $default = '127.0.0.1'; + $additionalInfo = 'The function could not determine the IP address for the xCAT MN ' . + 'and could not find any IPv4 addresses on the system so \'127.0.0.1\' will be used ' . + 'in the hope that it will work.'; + } + } else { + # Don't know the local IPs. + # Default to the IPv4 loopback and let it fail. + $default = '127.0.0.1'; + $additionalInfo = 'The function cound not determine the IP addresses on the ' . + 'xCAT MN system so \'127.0.0.1\' was used.'; + } + } + } + + if ( $rc == 0 ) { + # Found a default, set it in the property. + eval( "\$$prop = \'$default\'" ); + logResponse( 'DF', 'DFLT01', $reasonString, $default, $additionalInfo ); + } + } + + return $rc; +} + + +#------------------------------------------------------- + +=head3 cmdOnVM + + Description : Send a command to a node and show the result. + Arguments : Target function, either 'onlyIUCV' or 'either'. + Returns : Return code: + 0 - Normal Linux success + 255 - Unable to SSH to system + non-zero - command error + Output from the command or a error string on an SSH failure. + Example : $rc = cmdOnVM( 'onlyIUCV' ); + $rc = cmdOnVM( 'either' ); + +=cut + +#------------------------------------------------------- +sub cmdOnVM { + my ( $targetFunc ) = @_; + my $cmd = ''; + my $hcp = ''; + my @lines; + my $rc = 0; + my $out = ''; + my $sudo; + my $user; + my $userid = ''; + + # Get the command to issue. + if ( $targetFunc eq 'onlyIUCV' ) { + if ( $issueCmdOverIUCV ne '.' ) { + $cmd = $issueCmdOverIUCV; + } + } else { + if ( $issueCmdToNode ne '.' ) { + $cmd = $issueCmdToNode; + } + } + + # Use the data file if it was specified. + if ( $dataFile ne '' ) { + if ( -e $dataFile ) { + open my $handle, '<', $dataFile; + chomp( @lines = <$handle> ); + close $handle; + if ( $lines[0] ne '' ) { + $cmd = $lines[0]; + } else { + logResponse( 'DF', 'GENERIC_RESPONSE', "$dataFile does not have anything in the first line."); + goto FINISH_cmdUsingIUCV; + } + } else { + logResponse( 'DF', 'GENERIC_RESPONSE', "$dataFile does not exist."); + goto FINISH_cmdUsingIUCV; + } + } + + # Verify that we have a command to issue. + if ( $cmd eq '' ) { + logResponse( 'DF', 'GENERIC_RESPONSE', "A command to issue was not specified."); + goto FINISH_cmdUsingIUCV; + } + + ($user, $sudo) = xCAT::zvmUtils->getSudoer(); + + # Get the required properties (hcp, userid, user) from the node + $out = `/opt/xcat/bin/lsdef $nodeName -i hcp,userid`; + $rc = $?; + if ( $rc eq 0 ) { + my $fndNode = 0; + @lines = split( '\n', $out ); + my $host = ''; + foreach my $line ( @lines ) { + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string + if ( $line =~ /Object name:/ ) { + $fndNode = 1; + next; + } elsif ( $line =~ /hcp\=/ ) { + ($hcp) = $line =~ m/hcp\=(.*)/; + next; + } elsif ( $line =~ /userid\=/ ) { + ($userid) = $line =~ m/userid\=(.*)/; + next; + } + } + if ( $fndNode != 1 ) { + logResponse( 'DF', 'GENERIC_RESPONSE', "'$nodeName' is not an xCAT node." ); + goto FINISH_cmdUsingIUCV; + } + } else { + $rc = logResponse( 'DFS', 'GNRL01', "$cmd", $rc, $out ); + goto FINISH_cmdUsingIUCV; + } + + # Send the command through IUCV + logResponse( 'DF', '*NONFORMATTED*', "\n**************************\nData for the function call\n**************************" ); + logResponse( 'DF', '*NONFORMATTED*', " Node: $nodeName" ); + if ( $targetFunc eq 'onlyIUCV' ) { + logResponse( 'DF', '*NONFORMATTED*', "z/VM userid: $userid" ); + logResponse( 'DF', '*NONFORMATTED*', " HCP: $hcp" ); + logResponse( 'DF', '*NONFORMATTED*', " User: $user" ); + logResponse( 'DF', '*NONFORMATTED*', " Function: xCAT::zvmUtils->execcmdthroughIUCV" ); + } else { + logResponse( 'DF', '*NONFORMATTED*', " User: $user" ); + logResponse( 'DF', '*NONFORMATTED*', " Function: xCAT::zvmUtils->execcmdonVM" ); + } + logResponse( 'DF', '*NONFORMATTED*', " Cmd: $cmd" ); + + logResponse( 'DF', '*NONFORMATTED*', "\n*********************\nInvoking the function\n*********************" ); + if ( $targetFunc eq 'onlyIUCV' ) { + $out = xCAT::zvmUtils->execcmdthroughIUCV( $user, $hcp, $userid, $cmd ); + } else { + $out = xCAT::zvmUtils->execcmdonVM( $user, $nodeName, $cmd ); + } + logResponse( 'DF', '*NONFORMATTED*', "\n******\nResult\n******\n$out" ); + +FINISH_cmdUsingIUCV: + return $rc; +} + + +#------------------------------------------------------- + +=head3 driveCronList + + Description : Run thru the list of IVPs and run any that + are due to be run. + Arguments : None. + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = driveCronList(); + +=cut + +#------------------------------------------------------- +sub driveCronList { + my $attemptedRuns = 0; + my $disabledRuns = 0; + my @ivps; + my $out = ''; + my $rc = 0; + my $totalRunsInTable = 0; + + # Determine the timestamp and other time related information. + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); + $year = $year + 1900; + $todayDate = sprintf( "%04d-%02d-%02d", $year, $mon + 1, $mday ); + $startTime = sprintf( "%02d:%02d:%02d on %04d-%02d-%02d", + $hour, $min, $sec, $year, $mon + 1, $mday ); + my $currTime = sprintf( "%04d-%02d-%02d_%02d:%02d:%02d", + $year, $mon + 1, $mday, $hour, $min, $sec); + my $newLastRun = "$year $yday $hour"; + + logResponse( 'DFS', '*NONFORMATTED*', "CRON job run started at $startTime." ); + + # Read the z/VM IVP table to get the list of known IVP runs. + $out = `/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"`; + $rc = $?; + my @lines = split( '\n', $out ); + $totalRunsInTable = scalar( @lines ); + if ( $totalRunsInTable != 0 ) { + my $row = 0; + my $nowTotalHours = ( ( $year ) * 8784 ) + ( $yday * 24 ) + $hour; + foreach my $line ( @lines ) { + $row++; + chomp( $line ); + my @field = split( ',', $line ); + for ( my $i=0; $i < scalar( @field ); $i++ ) { + $field[$i] =~ s/^\"|\"$//g; # trim quotes from both ends of the string + } + + # Process the fields from the zvmIVP table. Each row is related to a + # scheduled run. + # Field 0 - Unique IVP ID + # 1 - IP property for the OpenStack system + # 2 - Schedule, hours to run the IVP + # 3 - Last time IVP was run, ( year day hour ) + # 4 - Type of run, e.g. basicivp, fullivp + # 5 - OpenStack user to access the config files + # 6 - Parameters to pass to the orchestrator, verifynode + # 7 - Parameters to pass to the preparator script, prep_zxcatIVP + # 8 - Parameters to pass to the main IVP script, zxcatIVP.pl + # 9 - Comments related to the IVP run + # 10 - Disabled, 1 or YES equals disabled + my %ivpRun; + if ( defined $field[0] and $field[0] ne '' ) { + $ivpRun{'id'} = $field[0]; + } else { + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row in the ivp table missing a ". + "required property, \'id\'. The row will be treated as disabled." ); + $disabledRuns++; + next; + } + + if ( defined $field[10] and ($field[10] eq '1' or uc( $field[10] ) eq 'YES') ) { + # IVP run is disabled + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table is ". + "disabled and will not be processed." ); + $disabledRuns++; + next; + } + + if ( defined $field[1] and $field[1] ne '' ) { + $ivpRun{'ip'} = $field[1]; + } else { + # Assume this is a local xCAT IVP run + $ivpRun{'ip'} = 'xcat'; + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table " . + "has no value specified for the \'ip\' property. " . + "The IVP run will be against the system which is " . + "running the xCAT management node." ); + } + + if ( defined $field[2] and $field[2] ne '' ) { + $ivpRun{'schedule'} = hexDecode( $field[2] ); + } else { + # Default run time is 1 AM + $ivpRun{'schedule'} = '1'; + } + + my $lRYear; + my $lRYDay; + my $lRHour; + + if ( defined $field[3] and $field[3] ne '' ) { + $ivpRun{'last_run'} = $field[3]; + my @parts = split ( '\s', $ivpRun{'last_run'} ); + if ( defined $parts[0] and $parts[0] ne '' ) { + $lRYear = $parts[0]; + } else { + # Missing year, set default to immediately, 0. + $lRYear = 0; + } + if ( defined $parts[1] and $parts[1] ne '' ) { + $lRYDay = $parts[1]; + } else { + # Missing day number, set default to immediately, 0. + $lRYDay = 0; + } + if ( defined $parts[2] and $parts[2] ne '' ) { + $lRHour = $parts[2]; + } else { + # Missing hour, set default to immediately, -1. + $lRHour = 0; + } + } else { + # Force a schedule run + $lRYear = 0; + $lRYDay = 0; + $lRHour = -1; + } + $ivpRun{'last_run'} = "$lRYear $lRYDay $lRHour"; + + # Weed out any runs that are not due to be run. + # Calculate the difference (since 2000) between the last run hour and the current hour, + # ignoring leap years by saying every year is a leap year. + my $totalHourDiff = $nowTotalHours - (( ( $lRYear ) * 8784 ) + ( $lRYDay * 24 ) + $lRHour); + + if ( $totalHourDiff == 0 ) { + # Same hour of the same day of the same year for some reason so ignore this IVP row. + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ". + "is being ignored because it was already run this hour." ); + next; + } else { + # Determine the range of hours that are of interest within a 24 hour time period. Anything + # that is more than 24 hours old is automatically overdue and should be run. + if ( $totalHourDiff < 24) { + my $todayStartHour; + my $yesterdayStartHour; + if ( $yday > $lRYDay ) { + # Last run yesterday + $todayStartHour = 0; + $yesterdayStartHour = $lRHour + 1; + } else { + # Last run today + $todayStartHour = $hour - $totalHourDiff; + $yesterdayStartHour = 24; + } + + # Less than a day so we need to worry about which scheduled hours were missed. + my $overdue = 0; + my @schedHours = split ( '\s', $ivpRun{'schedule'} ); + foreach my $sHour ( @schedHours ) { + if ( $sHour == 0 and $sHour ne '0' ) { + # not numeric, ignore + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ". + "contains as schedule hour that is non-numeric: $sHour. ". + "The value will be ignored." ); + next; + } + if (( $sHour >= $yesterdayStartHour ) or + ( $sHour > $todayStartHour and $sHour <= $hour ) ) { + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ". + "is due to be run. Scheduled hour: $sHour, last run: $lRHour." ); + $overdue = 1; + last; + } + } + if ( $overdue == 0 ) { + # did not find a scheduled hour that is due + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table is not ". + "due to be run this hour." ); + next; + } + } else { + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table ". + "is due to be run. Last run over 24 hours ago." ); + } + } + + if ( defined $field[4] and $field[4] ne '' ) { + $ivpRun{'type_of_run'} = lc( $field[4] ); + } else { + $ivpRun{'type_of_run'} = 'not specified'; + } + if ( $ivpRun{'type_of_run'} ne 'basicivp' and $ivpRun{'type_of_run'} ne 'fullivp' ) { + my $badValue = $ivpRun{'type_of_run'}; + if ( exists $mnInfo{'ip'} and ( $ivpRun{'ip'} eq $mnInfo{'ip'} )) { + if (( $mnInfo{'role'} eq 'controller' ) or + ( $mnInfo{'role'} eq 'compute' ) or + ( $mnInfo{'role'} eq 'compute_mn' )) { + $ivpRun{'type_of_run'} = 'fullivp'; + } else { + $ivpRun{'type_of_run'} = 'basicivp'; + } + } else { + $ivpRun{'type_of_run'} = 'basicivp'; + } + logResponse( 'DS', 'GENERIC_RESPONSE', "Row $row, ID($ivpRun{'id'}), in the ivp table contains ". + "an unknown run type value of \'$badValue\'. Instead ". + "a run type of \'$ivpRun{'type_of_run'}\' will be performed." ); + } + + if ( defined $field[5] and $field[5] ne '' ) { + $ivpRun{'access_user'} = hexDecode( $field[5] ); + } else { + $ivpRun{'access_user'} = ''; + } + + if ( defined $field[6] and $field[6] ne '' ) { + $ivpRun{'orch_parms'} = hexDecode( $field[6] ); + } else { + $ivpRun{'orch_parms'} = ''; + } + + if ( defined $field[7] and $field[7] ne '' ) { + $ivpRun{'prep_parms'} = hexDecode( $field[7] ); + } else { + $ivpRun{'prep_parms'} = ''; + } + + if ( defined $field[8] and $field[8] ne '' ) { + # Ensure that it is reencoded to pass along to do the actual work. + $ivpRun{'main_ivp_parms'} = hexEncode( $field[8] ); + } else { + $ivpRun{'main_ivp_parms'} = ''; + } + + if ( defined $field[9] and $field[9] ne '' ) { + $ivpRun{'comments'} = hexDecode( $field[9] ); + } else { + $ivpRun{'comments'} = ''; + } + + push @ivps, \%ivpRun; + } + } else { + # Table is empty. Create a single run for the z/VM related to the + # xCAT MN. + logResponse( 'DS', 'GENERIC_RESPONSE', "The ivp table is empty. Default IVPs will be enabled." ); + if ( exists $mnInfo{'node'} ) { + # First IVP is a full IVP for an OpenStack related xCAT at 1 am. + # Otherwise it is a basic IVP. + my %ivpRun; + $ivpRun{'id'} = 1; + if ( exists $mnInfo{'ip'} ) { + $ivpRun{'ip'} = $mnInfo{'ip'}; + } else { + $ivpRun{'ip'} = 'xcat'; + } + $ivpRun{'schedule'} = '1'; + if ( $mnInfo{'role'} eq 'controller' or $mnInfo{'role'} eq 'compute' or $mnInfo{'role'} eq 'compute_mn' ) { + $ivpRun{'type_of_run'} = 'fullivp'; + $ivpRun{'access_user'} = 'root'; + } else { + $ivpRun{'type_of_run'} = 'basicivp'; + $ivpRun{'access_user'} = ''; + } + $ivpRun{'orch_parms'} = ''; + $ivpRun{'prep_parms'} = ''; + $ivpRun{'main_ivp_parms'} = ''; + $ivpRun{'comments'} = 'Default Full IVP run'; + $ivpRun{'defaultRun'} = 'run'; + push @ivps, \%ivpRun; + $totalRunsInTable++; + + # Second IVP is a basic IVP run each hour except for 1 am when a full IVP is run. + my %ivpRun2; + $ivpRun2{'id'} = 2; + if ( exists $mnInfo{'ip'} ) { + $ivpRun2{'ip'} = $mnInfo{'ip'}; + } else { + $ivpRun2{'ip'} = 'xcat'; + } + $ivpRun2{'schedule'} = '0 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23'; + $ivpRun2{'type_of_run'} = 'basicivp'; + $ivpRun2{'access_user'} = ''; + $ivpRun2{'orch_parms'} = ''; + $ivpRun2{'prep_parms'} = ''; + $ivpRun2{'main_ivp_parms'} = ''; + $ivpRun2{'comments'} = 'Default Basic IVP run'; + $ivpRun2{'defaultRun'} = 'runLater'; + push @ivps, \%ivpRun2; + $totalRunsInTable++; + } else { + # xCAT MN does not exist as a node so we will only do a basic IVP the system. + # This is a very abnormal case. We deal in the abnormal so let's run some type + # of IVP but not define it in the table. + logResponse( 'DFS', 'MN03' ); + my %ivpRun; + $ivpRun{'id'} = 1; + if ( exists $mnInfo{'ip'} ) { + $ivpRun{'ip'} = $mnInfo{'ip'}; + } else { + $ivpRun{'ip'} = 'xcat'; + } + $ivpRun{'schedule'} = '0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23'; + $ivpRun{'type_of_run'} = 'basicivp'; + $ivpRun{'access_user'} = ''; + $ivpRun{'orch_parms'} = ''; + $ivpRun{'prep_parms'} = ''; + $ivpRun{'main_ivp_parms'} = ''; + $ivpRun{'comments'} = 'Default Basic IVP run'; + $ivpRun{'defaultRun'} = 'noTableUpdate'; + push @ivps, \%ivpRun; + $totalRunsInTable++; + } + } + + # Handle pruning, if necessary. + if ( $hour == 0 ) { + pruneLogs( $year, $mon + 1, $mday ); + } + + # Go thru the list of IVPs and drive a run for each IVP + # that is due to be run. + foreach my $ivp ( @ivps ) { + my %ivpRun = %$ivp; + + # Build the verify node command. + my $cmd = '/opt/xcat/bin/verifynode -n xcat --notify'; + + if ( $ivpRun{'type_of_run'} eq 'fullivp' ) { + $cmd = "$cmd --fullivp --openstackip \'$ivpRun{'ip'}\' --openstackuser \'" . hexEncode( $ivpRun{'access_user'} ) . "\'"; + } else { + $cmd = "$cmd --basicivp"; + } + + if ( $ivpRun{'orch_parms'} ne '' ) { + $cmd = "$cmd $ivpRun{'orch_parms'}"; # Don't hex encode orchestrator parms + } + + if ( $ivpRun{'prep_parms'} ne '' ) { + $cmd = "$cmd --prepparms \'" . hexEncode( $ivpRun{'prep_parms'} ) . "\'"; + } + + if ( $ivpRun{'main_ivp_parms'} ne '' ) { + $cmd = "$cmd --zxcatparms \'" . hexEncode( $ivpRun{'main_ivp_parms'} ) . "\'"; + } + + # Update the table to indicate the time we start the IVP run. + if ( ! exists $ivpRun{'defaultRun'} ) { + my $chtabCmd = "/opt/xcat/sbin/chtab id=\'$ivpRun{'id'}\' zvmivp.last_run=\'$newLastRun\'"; + $out = `$chtabCmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for IVP ID($ivpRun{'id'}), ip: $ivpRun{'ip'}, rc: $rc, out: $out\n" ); + } + } elsif ( $ivpRun{'defaultRun'} ne 'noTableUpdate' ) { + logResponse( 'DS', '*NONFORMATTED*', "Adding IVP ID($ivpRun{'id'}) to the z/VM IVP table: $ivpRun{'comments'}\n" ); + my $chtabCmd = "/opt/xcat/sbin/chtab id=\'$ivpRun{'id'}\' ". + "zvmivp.ip=\'$ivpRun{'ip'}\' ". + "zvmivp.schedule=\'$ivpRun{'schedule'}\' ". + "zvmivp.last_run=\'$newLastRun\' ". + "zvmivp.type_of_run=\'$ivpRun{'type_of_run'}\' ". + "zvmivp.access_user=\'" . hexEncode( $ivpRun{'access_user'} ) . "\' ". + "zvmivp.comments=\'" . hexEncode( $ivpRun{'comments'} ) . "\' "; + $out = `$chtabCmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($ivpRun{'id'}), ip: $ivpRun{'ip'}, rc: $rc, out: $out\n" ); + } + + # Skip running this one if it is marked 'runLater'. + # A basic IVP is not needed if a full IVP is going to be run. + if ( $ivpRun{'defaultRun'} eq 'runLater' ) { + logResponse( 'DS', '*NONFORMATTED*', "Ignoring IVP ID($ivpRun{'id'}) for this hour. It will be run next hour. IVP comment: $ivpRun{'comments'}\n" ); + next; + } + } + + # Drive the run. + $attemptedRuns++; + my $logComments = ''; + if ( $ivpRun{'comments'} eq '' ) { + $logComments = "Running IVP ID($ivpRun{'id'})"; + } else { + $logComments = "Running IVP ID($ivpRun{'id'}) - $ivpRun{'comments'}"; + } + logResponse( 'DS', '*NONFORMATTED*', "$logComments" ); + $out = `$cmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DS', '*NONFORMATTED*', "The IVP run for ID($ivpRun{'id'}) returned a ". + "non-zero return code, rc: $rc\n" ); + } + } + + logResponse( 'DFS', '*NONFORMATTED*', "IVP CRON results: $totalRunsInTable runs considered, $disabledRuns runs disabled, $attemptedRuns runs attempted" ); + $rc = 0; + return $rc; +} + + +#------------------------------------------------------- + +=head3 enableDisableRun + + Description : Enable or Disable command line function. + Arguments : ID of the IVP to be disabled + Global input set by command operands are used + by this routine: + $disable - Disable a run. + $enable - Enable a run. + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = enableDisableRun(); + +=cut + +#------------------------------------------------------- +sub enableDisableRun { + my ( $id ) = @_; + my %ids; + my $rc = 0; + + if ( $id eq '' ) { + logResponse( 'DF', 'OPER01', '--id' ); + goto FINISH_enableDisableRun; + } + + # Get the list of IVP ids in the zvmivp table. + my $cmd = '/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"'; + my $out = `$cmd`; + $rc = $?; + if ( $rc ne 0 ) { + logResponse( 'DF', 'GNRL04', $cmd, $rc, $out ); + goto FINISH_enableDisableRun; + } + + my @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + chomp( $line ); + my ($ivpId, $junk) = split( ',', $line, 2 ); + $ivpId =~ s/^\"|\"$//g; # trim quotes from both ends of the string + $ivpId =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string + if ( $ivpId ne '' ) { + $ids{$ivpId} = 1; + } + } + + # Validate the specified id. + if ( ! exists $ids{$id} ) { + logResponse( 'DF', 'ID03', $id ); + } + + my $action; + my $disableFlag = ''; + if ( $disable ) { + $disableFlag = 'YES'; + $action = 'disable'; + } else { + $action = 'enable'; + } + + # Update the table. + logResponse( 'DS', '*NONFORMATTED*', "Updating the z/VM IVP table (zvmivp) to $action $id" ); + my $chtabCmd = "/opt/xcat/sbin/chtab id='$id' zvmivp.disable=\'$disableFlag\'"; + $out = `$chtabCmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($id), rc: $rc, out: $out\n" ); + $rc = 1; + } + +FINISH_enableDisableRun: + return $rc; +} + + +#------------------------------------------------------- + +=head3 encodeDecodeString + + Description : Encode and decode command line function. + Arguments : Global input set by command operands are used + by this routine: + $decode - String to be decoded if not ''. + $encode - String to be encoded if not ''. + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = encodeDecodeString(); + +=cut + +#------------------------------------------------------- +sub encodeDecodeString { + my $newVal = ''; + if ( $decode ne '' ) { + binmode(STDOUT, ":utf8"); + print( "Value is: '" . hexDecode( $decode ) . "'\n" ); + } + + if ( $encode ne '' ) { + print( "Value is: '" . hexEncode( $encode ) . "'\n" ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 finishLogFile + + Description : Complete the log file for the run and + send it to the notify user if necessary. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = finishLogFile(); + +=cut + +#------------------------------------------------------- +sub finishLogFile { + my $rc = 0; + $needToFinishLogFile = 0; + + # Add the summary information to the log file. + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); + $endTime = sprintf("%02d:%02d:%02d on %04d-%02d-%02d", + $hour, $min, $sec, $year + 1900, $mon + 1, $mday ); + + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + logResponse( 'DF', 'GENERIC_RESPONSE', 'Results of the IVP run:\n' ); + logResponse( 'DF', 'GENERIC_RESPONSE', "Run started at $startTime and ended at $endTime." ); + foreach my $line ( @ivpSummary ) { + logResponse( 'DF', 'GENERIC_RESPONSE', $line ); + } + if ( $ignoreCnt != 0 ){ + logResponse( 'DFS', 'GENERIC_RESPONSE', "The orchestrator script (verifynode) ignored messages $ignoreCnt times." ); + my @ignoreArray = sort keys %ignored; + my $ignoreList = join ( ', ', @ignoreArray ); + logResponse( 'DFS', 'GENERIC_RESPONSE', "Message Ids of ignored messages by verifynode: $ignoreList" ); + } + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + + # Notify the z/VM userid, if necessary. + if ( $notify and $notifyOnErrorOrWarning ) { + notifyUser(); + } + +FINISH_finishLogFile: + return $rc; +} + + +#------------------------------------------------------- + +=head3 getDistro + + Description : Create the dummy image file. + Arguments : Node name or IP address + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = getDistro(); + +=cut + +#------------------------------------------------------- +sub getDistro { + my ( $nodeOrIP ) = @_; + my $rc = 0; + + $tgtOS = xCAT::zvmUtils->getOsVersion( $::SUDOER, $nodeOrIP ); + +FINISH_getDistro: + return $rc; +} + + +#------------------------------------------------------- + +=head3 getLocalIPs + + Description : Get the IP addresses from ifconfig. + Arguments : Node name or IP address + Returns : return code (Currently always 0) + 0 - No error + non-zero - Terminating error detected. + Hash of local IP addresses + Example : $rc = getLocalIPs(); + +=cut + +#------------------------------------------------------- +sub getLocalIPs { + my $ip; + my $junk; + my $rc = 0; + + my $out = `/sbin/ip addr | grep -e '^\\s*inet' -e '^\\s*inet6'`; + my @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + my @parts = split( ' ', $line ); + ($ip) = split( '/', $parts[1], 2 ); + $localIPs{$ip} = 1; + } + +FINISH_getLocalIPs: + return $rc; +} + + +#------------------------------------------------------- + +=head3 getMNInfo + + Description : Get information related to the xCAT MN + and the z/VM system where it is running. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = getMNInfo(); + +=cut + +#------------------------------------------------------- +sub getMNInfo { + my $cmd; + my $hostNode; + my @lines; + my $masterIP = ''; + my $out = ''; + my @parts; + my $rc = 0; + my $xcatMN; + + # Get the list of Local IP addresses + getLocalIPs(); + + # Get information related to all of the host nodes + $cmd = '/opt/xcat/bin/lsdef -w mgt=zvm -w hosttype=zvm -i hcp'; + $out = `$cmd`; + $rc = $?; + if ( $rc eq 0 ) { + @lines = split( '\n', $out ); + my $host = ''; + foreach my $line ( @lines ) { + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string + if ( $line =~ /Object name: / ) { + ($host) = $line =~ m/Object name: (.*)/; + next; + } elsif ( $line =~ /hcp\=/ ) { + ($hosts{$host}{'hcp'}) = $line =~ m/hcp\=(.*)/; + next; + } + } + } else { + $rc = logResponse( 'DFS', 'GNRL01', "$cmd", $rc, $out ); + $notify = 1; + } + + # Get key info related to the xCAT MN and the notify targets from the site table. + $mnInfo{'PruneIVP'} = 7; + $cmd = "/opt/xcat/sbin/tabdump site | grep -e '^\"master\"' -e '^\"zvmnotify\"' -e '^\"zvmpruneivp\"'"; + $out = `$cmd`; + $rc = $?; + if ( $rc == 0 ) { + @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string + @parts = split( ',', $line ); + $parts[1] =~ s/\"//g; + if ( $parts[0] =~ /^\"master\"/ ) { + $masterIP = $parts[1]; + } elsif ( $parts[0] =~ /^\"zvmnotify\"/ ) { + my @notifyInfo = split( ';', $parts[1] ); + my $notifyHost = ''; + my $notifyUser = ''; + foreach my $hostUser ( @notifyInfo ) { + ($notifyHost) = $hostUser =~ m/(.*)\(/; + ($notifyUser) = $hostUser =~ m/\((.*)\)/; + if ( defined $notifyHost and $notifyHost ne '' and defined $notifyUser and $notifyUser ne '' ) { + $hosts{$notifyHost}{'notifyUser'} = uc( $notifyUser ); + } else { + $rc = logResponse( 'DFS', 'SITE01', 'zvmnotify', $parts[1], $hostUser ); + $notify = 1; + } + } + } elsif ( $parts[0] =~ /^\"zvmpruneivp\"/ ) { + $mnInfo{'PruneIVP'} = $parts[1]; + } + } + } else { + # Unable to read the site table + $rc = logResponse( 'DFS', 'GNRL01', "$cmd", $rc, $out ); + $notify = 1; + } + + # Find a node that has the same address as the master IP and then find + # the host node that relates to that node. + if ( $masterIP eq '' ) { + # Unable to get the master value + $rc = logResponse( 'DFS', 'SITE02', 'master' ); + } else { + $cmd = "/opt/xcat/bin/lsdef -w ip=$masterIP -i hcp"; + $out = `$cmd`; + $rc = $?; + if ( $rc == 0 ) { + @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string + if ( $line =~ /Object name: / ) { + ($xcatMN) = $line =~ m/Object name: (.*)/; + $mnInfo{'node'} = $xcatMN; + $mnInfo{'ip'} = $masterIP; + next; + } elsif ( $line =~ /hcp\=/ ) { + ($mnInfo{'hcp'}) = $line =~ m/hcp\=(.*)/; + next; + } + } + } else { + $rc = logResponse( 'DFS', 'MN01', $masterIP, $cmd, $rc, $out ); + } + + # Find the host node which uses the hcp. + foreach $hostNode ( keys %hosts ) { + if ( exists $hosts{$hostNode}{'hcp'} and $hosts{$hostNode}{'hcp'} eq $mnInfo{'hcp'} ) { + if ( exists $hosts{$hostNode}{'notify'} ) { + $hosts{$hostNode}{'notify'} = $hosts{$hostNode}{'notify'} . 'm'; + } else { + $hosts{$hostNode}{'notify'} = 'm'; + } + $mnInfo{'host'} = $hostNode; + } + } + } + + # The following is useful in a z/VM CMA system only. + # As a safety measure, in case the site table is lost or does not have zvmnotify property, + # read the OPNCLOUD application system role file to find the value of the 'notify' property + # and remember it with the xCAT MN information. + # Also, determine the system role. + if ( -e $locApplSystemRole ) { + $out = `cat /var/lib/sspmod/appliance_system_role | grep -e '^notify\=' -e '^role\='`; + @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + $line =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string + my ($key, $value) = split( '=', $line, 2 ); + #($mnInfo{'notifyUser'}) = $line =~ m/^notify\=(.*)/; + if ( $key eq 'role' ) { + $mnInfo{'role'} = lc( $value ); + } + if ( $key eq 'notify' ) { + $mnInfo{'notifyUser'} = uc( $value ); + } + } + } else { + # Not CMA. Indicate role is not set. + $mnInfo{'role'} = ''; + } +} + + +#------------------------------------------------------- + +=head3 getOpenStackLevel + + Description : Get the OpenStack version level as a + name. + Arguments : IP address of OpenStack compute node. + Returns : string - OpenStack version name + (e.g. NEWTON), or + null string - error + Example : $level = getOpenStackLevel(); + +=cut + +#------------------------------------------------------- +sub getOpenStackLevel { + my ( $nodeOrIP, $openstackUser ) = @_; + my $cmd; + my $level = ''; + my $numWords = 0; + my $out = ''; + my @parts; + my $rc; + my %openStackVersion; + + # Get the list of known versions + $cmd = 'cat /opt/xcat/openstack.versions'; + $out = `$cmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DFS', 'GNRL01', $cmd, $rc, $out ); + $level = ''; + goto FINISH_getOpenStackLevel; + } + + my @versionLines = split ( /\n/, $out ); + foreach my $versionLine ( @versionLines ) { + if ( $versionLine =~ /^#/ ) { next; } + @parts = split( ' ', $versionLine ); + $openStackVersion{$parts[0]} = $parts[1]; + } + + # Get the version information from the OpenStack node + $cmd = "nova-manage --version 2>&1"; + ($rc, $out) = sshToNode( $openstackIP, $openstackUser, $cmd ); + if ( $rc != 0 ) { + # SSH failed, message already sent. + goto FINISH_getOpenStackLevel; + } + $out =~ s/^\s+|\s+$//g; # trim blanks from both ends of the string + ++$numWords while $out =~ /\S+/g; + if ( $numWords != 1 ) { + logResponse( 'DFS', 'GOSL01', $cmd, $openstackIP, $rc, $out ); + goto FINISH_getOpenStackLevel; + } + + @parts = split( '\.', $out ); + if ( !exists $parts[0] or !exists $parts[1] ) { + logResponse( 'DFS', 'GOSL01', $cmd, $openstackIP, $rc, $out ); + goto FINISH_getOpenStackLevel; + } elsif ( exists $openStackVersion{"$parts[0]."} ) { + $level = $openStackVersion{"$parts[0]."} + } elsif ( exists $openStackVersion{"$parts[0].$parts[1]."} ) { + $level = $openStackVersion{"$parts[0].$parts[1]."} + } else { + logResponse( 'DFS', 'GOSL02', $cmd, $openstackIP, $rc, $out ); + goto FINISH_getOpenStackLevel; + } + +FINISH_getOpenStackLevel: + return $level; +} + + +#------------------------------------------------------- + +=head3 hexDecode + + Description : Convert a string of printable hex + characters (4 hex characters per actual + character) into the actual string that + it represents. The string should + begin with 'HexEncoded:' which indicates + that it is encoded. The routine tolerates + getting called with a string that is not + not encoded and will return the string that + was passed to it instead of trying to decode + it. + Arguments : printable hex value + Returns : Perl string + Example : $rc = hexDecode(); + +=cut + +#------------------------------------------------------- +sub hexDecode { + my ( $hexVal ) = @_; + my $result = ''; + + if ( $hexVal =~ /^HexEncoded:/ ) { + ($hexVal) = $hexVal =~ m/HexEncoded:(.*)/; + my @hexes = unpack( "(a4)*", $hexVal); + for ( my $i = 0; $i < scalar(@hexes); $i++ ) { + $result .= chr( hex( $hexes[$i] ) ); + } + } else { + $result = $hexVal; + } + + return $result; +} + + +#------------------------------------------------------- + +=head3 hexEncode + + Description : Convert a string into a string of + printable hex characters in which each + character in the original string is + represented by the 2 byte (4 char) hex + code value. The string is preceded by + 'HexEncoded:' to indicate that it has + been encoded. The routine will tolerate + getting called with a string that has + already been encoded and not reencode it. + Arguments : ASCII or Unicode string + Returns : Hex string + Example : $rc = hexEncode(); + +=cut + +#------------------------------------------------------- +sub hexEncode { + my ( $str ) = @_; + my $hex; + my $result = $str; # All work done within the result variable. + + if ( $result ne '' ) { + # Encode the string if is not already encoded. Otherwise, leave it alone. + if ( $result !~ /^HexEncoded:/ ) { + $result =~ s/(.)/sprintf("%04x",ord($1))/eg; + $result = "HexEncoded:$result"; + } + } + + return $result; +} + + +#------------------------------------------------------- + +=head3 logResponse + + Description : Build and log the response. + Arguments : Target destination for the response. This is + string which can contain a charater for each + possible destination, e.g 'DSF'. The values are: + D - send to STDOUT + S - send to syslog + F - send to the log file + The default is 'DF' so that we send the output + to STDOUT and put it in the log file. + message ID or special flag: + *NONFORMATTED* indicates that the message build should not + be invoked but instead the message substitutions are + lines for the message to be produced. + *RESET* indicates that the message counter should be reset. + Array of message substitutions + Returns : 0 - No error, general response or info message detected. + 1 - Non-terminating message detected. + 2 - Terminating message detected. + Example : $rc = logResponse( 'D', 'VX01' ); + $rc = logResponse( '', 'VX03', $nodeName, $sub2); + $rc = logResponse( 'DFS', 'VX03', $nodeName, 'sub2a'); + +=cut + +#------------------------------------------------------- +sub logResponse { + my ( $dest, $msgId, @msgSubs ) = @_; + my $rc = 0; + my $extraInfo = ''; + my $msg = ''; + my @msgLines; + my $sev; + my $line; + + if ( $msgId eq '*RESET*' ) { + $warnErrCnt = 0; + goto FINISH_logResponse; + } elsif ( $msgId eq '*NONFORMATTED*' ) { + $rc = 0; + if ( @msgSubs ) { + foreach my $line ( @msgSubs ) { + $msg = "$msg$line\n"; + } + } else { + $msg = '\n'; + } + $sev = 0; + } else { + ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('VERIFYNODE', $msgId, \@msgSubs); + if ( defined $msgsToIgnore{$msgId} ) { + # Ignore this message id + $ignored{$msgId} = 1; + $ignoreCnt += 1; + print( "Message $msgId is being ignored but would have occurred here.\n" ); + goto FINISH_logResponse; + } elsif ( defined $msgsToIgnore{$sev} ) { + # Ignoring all messages of this severity. + $ignored{$msgId} = 1; + $ignoreCnt += 1; + print( "Message $msgId is being ignored but would have occurred here.\n" ); + goto FINISH_logResponse; + } + } + + if ( $sev >= 4 ) { + $warnErrCnt += 1; + } + + # Send the message to the requested destination. + if ( $dest =~ 'D' ) { + print "$msg"; # Send message to STDOUT + } + if ( $dest =~ 'F' and defined $logFileHandle ) { + print $logFileHandle $msg; + } + if ( $dest =~ 'S' ) { + my $logMsg = $msg; + $logMsg =~ s/\t//g; + $logMsg =~ s/\n/ /g; + syslog( 'err', $logMsg ); + } + + # Send the extra info to the requested destination (never send it to syslog). + if ( $extraInfo ne '' ) { + if ( $dest =~ 'D' ) { + print "$extraInfo"; + } + if ( $dest =~ 'F' and defined $logFileHandle ) { + print $logFileHandle $msg; + } + } + +FINISH_logResponse: + return $rc; +} + + +#------------------------------------------------------- + +=head3 notifyUser + + Description : Notify a z/VM user. Send a message + and the log as a SPOOL file. + Arguments : Node name or IP address + Returns : 0 - Always ok. + Example : $rc = notifyUser(); + +=cut + +#------------------------------------------------------- +sub notifyUser { + my $cmd; + my $host; + my @hostOrder; + my $msg = ''; + my $out = ''; + my $rc = 0; + my $tempFile; + my $th; + + # Determine who we should notify based on the host related to the verification. + # The driver script would have set the host related to an full IVP (if we can trust it). + # If we cannot get to the host related to the run then we fall back to sending + # the notification to the user on the host in which the xCAT MN is running. + # If that fails then "tough luck, Chuck". + my $mnAdded = 0; + foreach $host ( keys %hosts ) { + if ( exists $hosts{$host}{'notify'} and exists $hosts{$host}{'hcp'} and exists $hosts{$host}{'notifyUser'} ) { + if ( $hosts{$host}{'notify'} =~ /d/ ) { + # Driver script specified hosts go on the top of the stack. + push @hostOrder, $host; + if ( $hosts{$host}{'notify'} =~ /m/ ) { + # The host related to the MN is already in the list + $mnAdded = 1; + } + } elsif ( $hosts{$host}{'notify'} =~ /m/ ) { + # Management node systems go on the bottom as a fall back plan. + unshift @hostOrder, $host; + $mnAdded = 1; + } + } + } + if ( $mnAdded == 0 and exists $mnInfo{'hcp'} and exists $mnInfo{'notifyUser'} ) { + # Did not find a host related to this management node but we have the + # necessary information to send the notification so add a dummy host + # to the hosts hash and add the dummy host to the end of the hostOrder + # stack to be used when all other notification attemps fail. + $host = '*DEFAULT_MN_HOST*'; + $hosts{$host}{'notify'} = 'm'; + $hosts{$host}{'notifyUser'} = $mnInfo{'notifyUser'}; + $hosts{$host}{'hcp'} = $mnInfo{'hcp'}; + unshift @hostOrder, $host; + } + + # Prepare the message. + $msg = "The xCAT IVP detected some possible issues. A log file is being sent to your reader."; + + # Prepare the log file by removing tabs and splitting the log file at 80 character lines. + $tempFile = mktemp( "$logDir/$logFile.XXXXXX" ); + `expand $logDir/$logFile | fold -w 80 1>$tempFile`; + + # Send the message and log file to the first entity in the host order list that lets us + # successfully do so. + my $done = 0; + my $failed = 0; + foreach $host ( @hostOrder ) { + # Check if zHCP's punch is online and online it if it is not. + ($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', 'cat /sys/bus/ccw/drivers/vmur/0.0.000d/online' ); + chomp( $out ); + if ($rc != 0 or $out != 1) { + $cmd = '/sbin/cio_ignore -r 000d;/sbin/chccwdev -e 000d'; + ($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', $cmd ); + chomp( $out ); + if ( $rc != 0 or !( $out =~ m/Done$/i ) ) { + $rc = logResponse( 'DFS', 'GNRL03', $hosts{$host}{'hcp'}, $cmd, $rc, $out ); + next; + } + ($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', 'which udevadm &> /dev/null && udevadm settle || udevsettle' ); + # Don't worry about the udevadm settle. If it remains a problem then we will see it on the next command to ZHCP. + } + + # Send log file. It will exist temporarily on the ZHCP agent in the /tmp directory + # under its original log file name. + $rc = xCAT::zvmUtils->sendFile( 'root', $hosts{$host}{'hcp'}, "$tempFile", "/tmp/$logFile" ); + if ( $rc != 0 ) { + # An error is not a problem because the zhcp node could be logged off. + next; + } + + # Punch the file from the ZHCP agent to the target user. + $out = xCAT::zvmCPUtils->punch2Reader( 'root', $hosts{$host}{'hcp'}, $hosts{$host}{'notifyUser'}, "/tmp/$logFile", 'XCAT_IVP.RESULTS', '-t', 'A' ); + if ( $out ne 'Done' ) { + $rc = logResponse( 'DFS', 'GNRL02', "/tmp/$logFile", $hosts{$host}{'hcp'}, $hosts{$host}{'notifyUser'}, $host, "$logDir/$logFile", $out ); + $failed = 1; + } + + # Clean up the ZHCP node. + ($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', "rm -f /tmp/$logFile" ); + if ( $rc != 0 ) { + $rc = logResponse( 'DFS', 'GNRL03', $hosts{$host}{'hcp'}, "rm -f /tmp/$logFile", $rc, $out ); + next; + } + + # Try another host if we failed to send the file. + if ( $failed ) { + $failed = 0; + next; + } + + # Send a message if the user is logged on the system. + ($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', "vmcp query user $hosts{$host}{'notifyUser'}" ); + if ( $rc != 0 ) { + # User is either not logged on or we had a problem issuing the command. Not important, leave. + last; + } + $out = uc( $out ); + if ( $out =~ /^$hosts{$host}{'notifyUser'}/ ) { + my ($userStatus, $junk) = split '\s', $out, 2; + chomp( $userStatus ); + $userStatus =~ s/^\s+|\s+$//g; # trim both ends of the string + if ( $userStatus eq 'SSI' or $userStatus eq 'DSC' ) { + logResponse( 'DFS', 'MSG01', $hosts{$host}{'notifyUser'}, $host, $userStatus ); + last; + } + } + + # Don't worry if message still does not work. It is only a message. + ($rc, $out) = sshToNode( $hosts{$host}{'hcp'}, '', "vmcp message $hosts{$host}{'notifyUser'} $msg" ); + last; + } + + # Remove the temporary log file from the xCAT MN system. + $out = `rm -f $tempFile`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DFS', 'GNRL01', "rm -f $tempFile", $rc, $out ); + } + + $rc = 0; + return $rc; +} + + +#------------------------------------------------------- + + +=head3 pruneLogs + + Description : Compress and prune log files. The number of days to wait + before pruning is specified in the site table with the + zvmpruneivp property. If the property does not exist + or Management node information does not exist because of + some reason then the default is to prune logs that are + 7 days or older. + Arguments : Current year + Current month (numeric) + Current day (Julian day, numeric) + Returns : None. + Example : pruneLogs( $year, $mon + 1, $mday ); + +=cut + +#------------------------------------------------------- +sub pruneLogs{ + my ( $year, $month, $day ) = @_; + + my @lines; + my $pruneOffset = 0; + my $out; + my $pruneDate = ''; + my $rc; + my $removed = 0; + my $zipOut; + my $zipped = 0; + logResponse( 'DFS', '*NONFORMATTED*', "CRON job is pruning log files." ); + + # Determine the prune date value. + if ( ! exists $mnInfo{'PruneIVP'} or $mnInfo{'PruneIVP'} eq '' ) { + $pruneOffset = 7; + } else { + if ( looks_like_number( $mnInfo{'PruneIVP'} ) ) { + $pruneOffset = $mnInfo{'PruneIVP'}; + } else { + $pruneOffset = 7; + logResponse( 'DFS', 'TP01', 'zvmpruneivp', 'site', 'not numeric', $mnInfo{'PruneIVP'}, $pruneOffset ); + } + } + $pruneDate = strftime "%Y-%m-%d", 0, 0, 0, $day - $pruneOffset, $month-1, $year-1900; + logResponse( 'DFS', '*NONFORMATTED*', "Prune date: $pruneDate." ); + + # Zip any log files that are not for today. Current log files are similar to + # /var/log/xcat/ivp/IVP_XCAT_2016-09-19_01:01:03.log and will have .gz appended to the name + # after they have been zipped. + $out = `ls -1 /var/log/xcat/ivp/*.log`; + $rc = $?; + if ( $rc == 0 ) { + @lines = split( '\n', $out ); + foreach my $fqFile ( @lines ) { + chomp( $fqFile ); + my ($file) = $fqFile =~ /\/var\/log\/xcat\/ivp\/(.*)/; + my @parts = split( '_', $file ); + if ( ! defined $parts[0] or $parts[0] ne 'IVP' or + ! defined $parts[1] or ! defined $parts[2] or + $parts[2] eq $todayDate ) { + next; + } + #logResponse( 'DFS', 'GENERIC_RESPONSE', "ZIPPING: $file\n" ); + $zipOut = `gzip $fqFile 2>&1`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DF', 'GENERIC_RESPONSE', "Unable to gzip $fqFile, rc: $rc, out: $out" ); + } else { + $zipped++; + } + } + } + + # Prune any gzipped files that are too old. + $out = `ls -1 /var/log/xcat/ivp/*.log.gz`; + $rc = $?; + if ( $rc == 0 ) { + @lines = split( '\n', $out ); + foreach my $fqFile ( @lines ) { + chomp( $fqFile ); + my ($file) = $fqFile =~ /\/var\/log\/xcat\/ivp\/(.*)/; + my @parts = split( '_', $file ); + if ( ! defined $parts[0] or $parts[0] ne 'IVP' or + ! defined $parts[1] or ! defined $parts[2] or + $parts[2] ge $todayDate ) { + next; + } + if ( $parts[2] ge $pruneDate ) { + next; + } + + #logResponse( 'DFS', 'GENERIC_RESPONSE', "REMOVING: $file\n" ); + $zipOut = `rm $fqFile 2>&1`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DF', 'GENERIC_RESPONSE', "Unable to remove \'rm $fqFile\', rc: $rc, out: $out" ); + } else { + $removed++; + } + } + } + + logResponse( 'DFS', '*NONFORMATTED*', "IVP log pruning completed, zipped: $zipped, pruned: $removed." ); + return; +} + + +#------------------------------------------------------- + +=head3 removeIVP + + Description : Remove an automated IVP. + Arguments : ID of the IVP to be removed + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = removeIVP(); + +=cut + +#------------------------------------------------------- +sub removeIVP { + my ( $id ) = @_; + my $cmd; + my $out; + my $rc; + my $ivpId; + my %ids; + my $junk; + + # Get the list of IVP ids in the zvmivp table. + $cmd = '/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"'; + $out = `$cmd`; + $rc = $?; + if ( $rc ne 0 ) { + logResponse( 'DF', 'GNRL04', $cmd, $rc, $out ); + goto FINISH_removeIVP; + } + my @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + chomp( $line ); + my ($ivpId, $junk) = split( ',', $line, 2 ); + $ivpId =~ s/^\"|\"$//g; # trim quotes from both ends of the string + $ivpId =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string + if ( $ivpId ne '' ) { + $ids{$ivpId} = 1; + } + } + + # Validate the specified id. + if ( $id eq '' or ! exists $ids{$id} ) { + logResponse( 'DF', 'ID03', $id ); + } + + # Update the table. + logResponse( 'DS', '*NONFORMATTED*', "Updating the z/VM IVP table (zvmivp) to remove $id" ); + my $chtabCmd = "/opt/xcat/sbin/chtab -d id=\'$id\' zvmivp"; + $out = `$chtabCmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($id), rc: $rc, out: $out\n" ); + $rc = 1; + } + +FINISH_removeIVP: + return $rc; +} + + +#------------------------------------------------------- + +=head3 runDriverScript + + Description : Run a downloaded driver script. + Arguments : This routine is driven from the action + table, verifySets. It takes global + input that can be set as command + input or by precursor routines. + The input variables include: + $driver - Driver script name + $driverLoc - Location of the driver script + $hosts - Infor for defined host nodes + $infoCnt - Count of warnings that were generated + $warningCnt - Count of warnings that were generated + $zxcatParms - Command line parms to + pass to zxcatIVP + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = runDriverScript(); + +=cut + +#------------------------------------------------------- +sub runDriverScript{ + my $infoCnt = 0; + my @lines; + my $out; + my $rc = 0; + my $warningCnt = 0; + + # Analyze the driver script to determine the z/VM host that it intends to support. + my $hostNodeZhcp = ''; + my $hostNode = `cat $driverLoc/$driver | grep "^export zxcatIVP_hostNode\=" | sed '/^export zxcatIVP_hostNode\=*/!d; s///; s/\"//g;q'`; + chomp( $hostNode ); + + if ( $hostNode ne '' ) { + if ( exists $hosts{$hostNode}{'hcp'} ) { + if ( exists $hosts{$hostNode}{'notify'} ) { + $hosts{$hostNode}{'notify'} = $hosts{$hostNode}{'notify'} . 'd'; + } else { + $hosts{$hostNode}{'notify'} = 'd'; + } + $zhcpTarget = $hosts{$hostNode}{'hcp'}; + $zvmHost = $hostNode; + push @ivpSummary, "The ZHCP related to the host, \'$hostNode\', identified in the driver script has been identified ". + "as \'$zhcpTarget\'."; + } else { + $rc = logResponse( 'DFS', 'DRIV03', $zvmHost ); + $notify = 1; + } + } else { + # Driver script did not specify the host node. + $rc = logResponse( 'DFS', 'DRIV02', "$driverLoc/$driver", 'zxcatIVP_hostNode' ); + $notify = 1; + } + + # Analyze the driver script to determine the ZHCP node that it intends to support. + my $zhcpNode = `cat $driverLoc/$driver | grep "^export zxcatIVP_zhcpNode\=" | sed '/^export zxcatIVP_zhcpNode\=*/!d; s///; s/\"//g;q'`; + chomp( $zhcpNode ); + my $zhcpHcp = `/opt/xcat/bin/lsdef $zhcpNode | grep "hcp\=" | sed '/hcp\=*/!d; s///; s/\"//g;q'`; + if ( $zhcpHcp ne '' ) { + $zhcpHcp =~ s/^\s+|\s+$//g; # trim both ends of the string + $zhcpTarget = $zhcpHcp; + foreach $hostNode ( keys %hosts ) { + if ( exists $hosts{$hostNode}{'hcp'} and $hosts{$hostNode}{'hcp'} eq $zhcpHcp ) { + if ( exists $hosts{$hostNode}{'notify'} ) { + $hosts{$hostNode}{'notify'} = $hosts{$hostNode}{'notify'} . 'd'; + } else { + $hosts{$hostNode}{'notify'} = 'd'; + } + } + } + } + + my $exportCmd = ''; + if ( $zxcatParms ne '' ) { + $ENV{zxcatIVP_moreCmdOps}=$zxcatParms; + } else { + delete $ENV{zxcatIVP_moreCmdOps}; + } + + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "Using the driver script to drive the IVP. The output from the run follows." ); + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + $out = `chmod +x $driverLoc/$driver`; + $out = `$driverLoc/$driver`; + $rc = $?; + if ( $rc != 0 ) { + $rc = logResponse( 'DFS', 'DRIV01', "$driverLoc/$driver", $rc, $out ); + $notify = 1; + goto FINISH_runDriverScript; + } + + # Determine how many warnings and information messages were generated so that we can + # produce a summary message upon completion of the IVP. + @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^Warning \(/ ) { $warningCnt++; } + if ( $line =~ /^Info \(/ ) { $infoCnt++; } + } + push @ivpSummary, "The driver script generated $warningCnt warnings and $infoCnt information messages."; + logResponse( 'DF', '*NONFORMATTED*', $out ); + +FINISH_runDriverScript: + if ( $warningCnt != 0 ) { + $notify = 1; + } + return $rc; +} + + +#------------------------------------------------------- + +=head3 runPrepScript + + Description : Push the appropriate level of preparation + script to a compute node and run it to + validate the system and build the driver + script. + Arguments : Global defaults set as command line parameters provide input + to this routine: + $driver - Name of driver script + $driverLoc - Location of the driver script + $infoCnt - Count of warnings that were generated + $openstackIP - IP for OpenStack compute node + $openstackUser - User for OpenStack compute node + $prepParms - Parms for preparation script + $warnErrCnt - Count of warnings and errors that were generated + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = runPrepScript(); + +=cut + +#------------------------------------------------------- +sub runPrepScript { + my $cmd = ''; + my $infoCnt = 0; + my $line; + my @lines; + my $out = ''; + my $rc = 0; + my $retRC = 0; + my $tmpDir = ''; + my $warningCnt = 0; + + # Determine the name of the driver script based on the IP address for the compute node. + $driver = "zxcatIVPDriver_$openstackIP.sh"; + + # Create the local IVP directory. + if ( !-d $driverLoc ) { + $out = `mkdir -m a=rwx,g=rx,o= -p $driverLoc`; + $rc = $?; + if ( $rc != 0 ) { + $rc = logResponse( 'DFS', 'PREP01', $driverLoc, $rc, $out ); + $notify = 1; + goto FINISH_runPrepScript; + } + } + + # Determine the OpenStack level. + my $level = getOpenStackLevel( $openstackIP, $openstackUser ); + if ( $level eq '' ) { + $notify = 1; + $rc = logResponse( 'DFS', 'PREP06', $openstackIP ); + goto FINISH_runPrepScript; + } + my $levelLetter = substr( $level, 0, 1); + push @ivpSummary, "OpenStack system at $openstackIP is running the Nova $level release."; + + # Determine the appropriate preparation script to send to the compute node. + my $prepDir = '/opt/xcat/share/xcat/tools/zvm'; + my $prepScript = "prep_zxcatIVP_$level.pl"; + logResponse( 'DF', 'GENERIC_RESPONSE', "Attempting to push $prepScript to the OpenStack system ". + "at $openstackIP. It will be used to validate the OpenStack environment and create ". + "a driver script to be used for the validation on the xCAT management node." ); + my $runPrepScript = 1; + + # Create a directory on the compute node to hold the preparation script and driver script. + $cmd = 'mktemp -d /tmp/xCAT.XXXXXXXXXX'; + ($rc, $tmpDir) = sshToNode( $openstackIP, $openstackUser, $cmd ); + if ( $rc != 0 ) { + # Unable to create the directory. Let's see if we have an old driver script that we can use. + $rc = logResponse( 'DFS', 'PREP02', $openstackIP, "$driverLoc/$driver", $cmd, $rc, $out ); + $notify = 1; + goto FINISH_runPrepScript; + } else { + # Push the preparation script to the compute node and run it. + chomp($tmpDir); + $cmd = "/usr/bin/scp $prepDir/$prepScript $openstackUser\@$openstackIP:$tmpDir"; + $out = `/usr/bin/scp "$prepDir/$prepScript" "$openstackUser\@$openstackIP:$tmpDir"`; + $rc = $?; + if ( $rc != 0 ) { + # Unable to push the preparation script. Let's see if we have an old driver script that we can use. + $rc = logResponse( 'DFS', 'PREP02', $openstackIP, "$driverLoc/$driver", $cmd, $rc, $out ); + $notify = 1; + goto FINISH_runPrepScript; + } + } + + if ( $runPrepScript ) { + # Run the preparation script. + ($rc, $out) = sshToNode( $openstackIP, $openstackUser, "chmod +x $tmpDir/$prepScript"); + ($rc, $out) = sshToNode( $openstackIP, $openstackUser, "$tmpDir/$prepScript '--driver' $tmpDir/$driver $prepParms" ); + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "The output from the preparation script that is run on the compute node follows. ". + "The preparation script validates the z/VM related OpenStack configuration file properties." ); + logResponse( 'DF', '*NONFORMATTED*', '*******************************************************************************' ); + logResponse( 'DF', '*NONFORMATTED*', $out ); + + # Determine how many warnings were generated so that we can produce a summary message upon completion of the IVP. + if ( $levelLetter gt 'L' ) { + @lines = split( '\n', $out ); + foreach $line ( @lines ) { + if ( $line =~ /^Warning \(/ ) { $warningCnt++; } + if ( $line =~ /^Info \(/ ) { $infoCnt++; } + } + } else { + @lines = split( '\n', $out ); + foreach $line ( @lines ) { + if ( $line =~ /^Warning:/ ) { $warningCnt++; } + if ( $line =~ /^Info:/ ) { $infoCnt++; } + } + } + push @ivpSummary, "The preparation script generated $warningCnt warnings and $infoCnt information messages."; + + # Pull back the driver script. + if ( -e "$driverLoc/$driver" ) { + $out = `mv -f $driverLoc/$driver $driverLoc/$driver.old`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'S', 'GENERIC_RESPONSE', "Unable to move $driverLoc/$driver to $driverLoc/$driver.old, rc: $rc" ); + $notify = 1; + } + } + + $cmd = "/usr/bin/scp $openstackUser\@$openstackIP:$tmpDir/$driver $driverLoc/$driver"; + $out = `/usr/bin/scp "$openstackUser\@$openstackIP:$tmpDir/$driver" "$driverLoc/$driver"`; + $rc = $?; + if ( $rc != 0 ) { + $rc = logResponse( 'DFS', 'PREP04', "$driverLoc/$driver", $openstackIP, $cmd, $rc, $out ); + $notify = 1; + goto FINISH_runPrepScript; + } + + $cmd = "chmod 660 $driverLoc/$driver 2>&1"; + $out = `$cmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'FS', 'PREP07', "$driverLoc/$driver", $cmd, $rc, $out ); + $notify = 1; + } + } + +FINISH_runPrepScript: + if ( !-s "$driverLoc/$driver" && -s "$driverLoc/$driver.old" ) { + # New driver does not exist but old driver exists, use the old one. + $out = `mv -f $driverLoc/$driver.old $driverLoc/$driver`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'S', 'GENERIC_RESPONSE', "Unable to move $driverLoc/$driver.old to $driverLoc/$driver, rc: $rc" ); + } + } + + if ( -s "$driverLoc/$driver" ) { + $retRC = 0; + # Add the driver script to the log file. + $out = `cat $driverLoc/$driver`; + logResponse( 'F', '*NONFORMATTED*', '*******************************************************************************' ); + logResponse( 'F', 'GENERIC_RESPONSE_NOINDENT', "The contents of the driver script used for the rest of this IVP follows. ". + "The driver script is used to run the rest of the IVP on the xCAT Management Node." ); + logResponse( 'F', 'GENERIC_RESPONSE_NOINDENT', 'Note: Any line which set a password related property has been removed.' ); + logResponse( 'F', '*NONFORMATTED*', '*******************************************************************************' ); + $out = `echo "$out" | grep -v "^export zxcatIVP_xcatUserPw"`; + logResponse( 'F', '*NONFORMATTED*', $out ); + } + + if ( $warningCnt != 0 ) { + $notify = 1; + } + + if ( $tmpDir ne '' ) { + $cmd = "rm -Rf $tmpDir 2>&1"; + ($rc, $out) = sshToNode( $openstackIP, $openstackUser, "$cmd"); + $rc = $?; + if ( $rc != 0 ) { + $rc = logResponse( 'DFS', 'CLNUP01', $openstackIP, $tmpDir, $cmd, $rc, $out ); + $notify = 1; + } + } + return $retRC; +} + + +#------------------------------------------------------- + +=head3 scheduleIVP + + Description : Schedule an automated IVP. + Arguments : Global defaults set as command line parameters provide input + to this routine: + $comments - Comments for IVP + $disable - Disable option + $id - ID to update or 'NEW' + $openstackIP - IP for OpenStack compute node + $openstackUser - User for OpenStack compute node + $orchParms - Parms for this script when IVP run + $prepParms - Parms for preparation script + $schedule - Hours to schedule + $scheduledType - Type of IVP + $zxcatParms - Parms for zxcatIVP script + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = scheduleIVP(); + +=cut + +#------------------------------------------------------- +sub scheduleIVP { + my $cmd; + my $out; + my $rc; + my $ivpId; + my %ids; + my $junk; + + # Get the list of IVP ids in the zvmivp table. + $cmd = '/opt/xcat/sbin/tabdump zvmivp | grep -v "^#"'; + $out = `$cmd`; + $rc = $?; + if ( $rc ne 0 ) { + logResponse( 'DF', 'GNRL04', $cmd, $rc, $out ); + goto FINISH_scheduleIVP; + } + my @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + chomp( $line ); + my ($ivpId, $junk) = split( ',', $line, 2 ); + $ivpId =~ s/^\"|\"$//g; # trim quotes from both ends of the string + $ivpId =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string + if ( $ivpId ne '' ) { + $ids{$ivpId} = 1; + } + } + + # Validate the specified id. Either generate a new ID if this is a new + # request (specified as 'new' or omitted or use the id that was passed. + if ( $id eq '' or $id eq 'NEW' ) { + # Generate the new Id + my $i; + for ( $i = 10; $i < 10010; $i++ ) { + if ( ! exists $ids{$i} ) { + last; + } + } + if ( $i <= 10010 ) { + $id = $i; + } else { + # Could not find an available number in the first 10000 IDs. + logResponse( 'DF', 'ID01' ); + goto FINISH_scheduleIVP; + } + } else { + # Validate the Id + if ( ! exists $ids{$id} ) { + logResponse( 'DF', 'ID02', $id ); + } + } + + if ( $scheduledType eq '' ) { + logResponse( 'DF', 'OPER01', '--type' ); + goto FINISH_scheduleIVP; + } + $scheduledType = lc( $scheduledType ); + if ( $scheduledType ne 'basicivp' and $scheduledType ne 'fullivp' ) { + logResponse( 'DF', 'OPER02', '--type', $scheduledType, '\'basicivp\' or \'fullivp\'' ); + goto FINISH_scheduleIVP; + } + if ( $scheduledType eq 'basicivp' ) { + # Ignore any FULLIVP parms + $openstackIP = ''; + $openstackUser = ''; + $prepParms = ''; + } + + # Normalize the schedule. + my %hours; + my @parts = split( ' ', $schedule ); + foreach my $hour ( @parts ) { + $hour =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string + $hours{$hour} = 1; + } + $schedule = ''; + for ( my $hour = 0; $hour <= 23; $hour++ ) { + if ( exists $hours{$hour} ) { + $schedule = "$schedule $hour"; + delete $hours{$hour}; + } + } + $schedule =~ s/^\s+|\s+$//g; # trim spaces from both ends of the string + my (@leftOver) = keys %hours; + if ( scalar @leftOver != 0 ) { + my $badValues = join( '\' and \'', @leftOver ); + logResponse( 'DF', 'OPER02', '--schedule', $badValues, '0-23' ); + goto FINISH_scheduleIVP; + } + + if ( $comments =~ /,/ ) { + $comments =~ s/,//g; # trim commas from the string + } + + my $disableFlag = ''; + if ( $disable == 1 ) { + $disableFlag = 'YES'; + } + + # Update the table. + logResponse( 'DS', '*NONFORMATTED*', "Updating the z/VM IVP table (zvmivp) for $id" ); + my $chtabCmd = "/opt/xcat/sbin/chtab id=\'$id\' ". + "zvmivp.ip=\'$openstackIP\' ". + "zvmivp.schedule=\'$schedule\' ". + "zvmivp.last_run=\'\' ". + "zvmivp.type_of_run=\'$scheduledType\' ". + "zvmivp.access_user=\'" . hexEncode( $openstackUser ) . "\' ". + "zvmivp.orch_parms=\'" . hexEncode( $orchParms ) . "\' ". + "zvmivp.prep_parms=\'" . hexEncode( $prepParms ) . "\' ". + "zvmivp.main_ivp_parms=\'" . hexEncode( $zxcatParms ) . "\' ". + "zvmivp.comments=\'" . hexEncode( $comments ) . "\' ". + "zvmivp.disable=\'$disableFlag\'"; + $out = `$chtabCmd`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DS', '*NONFORMATTED*', "\'$chtabCmd\' failed for ID($id), rc: $rc, out: $out\n" ); + $rc = 1; + } + +FINISH_scheduleIVP: + return $rc; +} + +#------------------------------------------------------- + +=head3 setupLogFile + + Description : Set up the log file for this run. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = setupLogFile(); + +=cut + +#------------------------------------------------------- +sub setupLogFile { + my $out; + my $rc = 0; + my $ipString = ''; + + # Indicate that the log file will need to be finished. + # This is a safety in case something goes wrong during + # the IVP run and causes the program to break. + $needToFinishLogFile = 1; + + # Determine the IP address to associate with the log file. + if ( $openstackIP eq '' ) { + # Must be doing either a generic zxcatIVP or a full + # IVP for the compute node sharing the system with this + # xCAT MN. Use the string "XCAT". + $ipString = 'XCAT'; + } else { + if ( exists $localIPs{$openstackIP} ) { + $ipString = 'XCAT'; + } else { + $ipString = $openstackIP; + } + } + + # Determine the timestamp to use for the log. + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); + $startTime = sprintf("%02d:%02d:%02d on %04d-%02d-%02d", + $hour, $min, $sec, $year + 1900, $mon + 1, $mday ); + my $currTime = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", + $year + 1900, $mon + 1, $mday, $hour, $min, $sec); + + # If the log directory does not exist then create it. + if ( !-d $logDir ) { + # Directory did not exist. Create it. + $out = `mkdir -p $logDir`; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DFS', 'GENERIC_RESPONSE', 'Failed to set up the log directory. IVP will run without a log file.' ); + $rc = 0; # Continue processing. + goto FINISH_setupLogFile; + } + } + + # Create the log file. + $logFile = "IVP\_$ipString\_$currTime\.log"; + open $logFileHandle, ">", "$logDir/$logFile"; + $rc = $?; + if ( $rc != 0 ) { + logResponse( 'DFS', 'GENERIC_RESPONSE', "Failed to open the log file: $logFile. IVP will run without a log file. $!" ); + $rc = 0; # Continue processing. + goto FINISH_setupLogFile; + } + $out = `chmod 644 "$logDir/$logFile"`; + + push @ivpSummary, "The following logfile was created for this run: $logFile"; + +FINISH_setupLogFile: + return $rc; +} + + +#------------------------------------------------------- + +=head3 showCILevel + + Description : Show the cloud-init version installed + in the node. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = showCILevel(); + +=cut + +#------------------------------------------------------- +sub showCILevel { + my $ciLevel = ''; + my $out = ''; + my $rc = 0; + + # Attempted to obtain the version using the routine + # accessible through the $PATH variable. + ($rc, $out) = sshToNode( $nodeName, '', 'cloud-init --version 2>&1'); + if ( $rc == 255 ) { + logResponse( 'DF', 'GENERIC_RESPONSE', $out ); + goto FINISH_showCILevel; + } elsif ( $rc == 0 ) { + if ( $out =~ /^cloud-init / ) { + ($ciLevel) = $out =~ /^cloud-init (.*)/; + my $msgText = "Version of cloud-init: $ciLevel"; + logResponse( 'DF', 'GENERIC_RESPONSE', $msgText ); + # Add code to compare the version to what is available in xcat. + goto FINISH_showCILevel; + } + } + + # Attempt to locate the version in the various cloud-init scripts + # in the target node. + # Add support to look for versions on the node if the invocation + # using the path did not show the version. + +FINISH_showCILevel: + return $rc; +} + + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + print "$0 verify the capabilities of a node.\n\n"; + print $usage_string; + return; +} + + +#------------------------------------------------------- + +=head3 sshToNode + + Description : SSH to a node and issue a command. + Check for SSH errors. + Arguments : Node name or IP address/DNS name + user if we need to specify SUDO + Command to issue + Options (char string): + q - Quiet, Don't generate an error message on failure + Returns : Return code: + 0 - Normal Linux success + 255 - Unable to SSH to system + non-zero - command error + Output from the command or a error string on an SSH failure. + Example : ($rc, $out) = sshToNode( $nodeName, $user, $command ); + +=cut + +#------------------------------------------------------- +sub sshToNode{ + my ( $nodeName, $user, $cmd, $options ) = @_; + my $rc = 0; + my $out = ''; + + if ( ! defined $options ) { + $options = ''; + } + + if ( $user eq '' ) { + $out = `ssh $nodeName -qoBatchMode=yes '$cmd'`; + $rc = $? >> 8; + } else { + $out = `ssh $user\@$nodeName -qoBatchMode=yes '$cmd'`; + $rc = $? >> 8; + } + + if ( $rc == 255 and $options !~ /q/ ) { + logResponse( 'DFS', 'VSTN01', $nodeName ); # Keep $rc = 255. + $out = "Unable to SSH to $nodeName"; + } + + return ($rc, $out); +} + + +#------------------------------------------------------- + +=head3 verifyAccess + + Description : Verify the xCAT MN can access a system. + Arguments : Node name or IP address + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyAccess(); + +=cut + +#------------------------------------------------------- +sub verifyAccess{ + my ( $nodeOrIP ) = @_; + + my $rc = 0; + my $out = ''; + + ($rc, $out) = sshToNode( $nodeOrIP, '', 'pwd 2>/dev/null' ); + goto FINISH_verifyAccess if ( $rc == 255 ); # Already generated a message + if ( $rc != 0 ) { + $rc = logResponse( 'DFS', 'VA01' ); + } + +FINISH_verifyAccess: + return $rc; +} + + +#------------------------------------------------------- + +=head3 verifyBasicXCAT + + Description : Verify the basic setup of xCAT. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyBasicXCAT(); + +=cut + +#------------------------------------------------------- +sub verifyBasicXCAT{ + my $infoCnt = 0; + my @lines; + my $rc = 0; + my $warningCnt = 0; + + $rc = logResponse( 'DF', 'GENERIC_RESPONSE', 'The IVP will be invoked with BYPASS messages suppressed. '. + 'This is because a basic IVP run will produce a number of BYPASS messages due to the fact that '. + 'it is not driven with a full set of configuration operands causing it to avoid some tests. '. + 'Instead you will see an information line indicating that the message was ignored.' ); + $ENV{'zxcatIVP_bypassMsg'} = 0; # No bypass messages + $ENV{'zxcatIVP_moreCmdOps'} = $zxcatParms; + my $out = `/opt/xcat/bin/zxcatIVP.pl`; + + $rc = logResponse( 'DF', '*NONFORMATTED*', $out ); + # Determine how many warnings were generated so that we can produce a summary message upon completion of the IVP. + @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + if ( $line =~ /^Warning/ ) { $warningCnt++; } + if ( $line =~ /^Info/ ) { $infoCnt++; } + } + push @ivpSummary, "The zxcatIVP.pl script generated $warningCnt warnings and $infoCnt information messages."; + +FINISH_verifyBasicXCAT: + if ( $warningCnt != 0 ) { + $notify = 1; + } + return $rc; +} + + +#------------------------------------------------------- + +=head3 verifyDistro + + Description : Verify that the distro is supported. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyDistro(); + +=cut + +#------------------------------------------------------- +sub verifyDistro{ + my $rc = 0; + + my $supported = xCAT::zvmUtils->isOSVerSupported( $tgtOS ); + if ( !$supported ) { + $rc = logResponse( 'DF', 'VD01', ($tgtOS) ); + } + + return $rc; +} + + +#------------------------------------------------------- + +=head3 verifyLogResponse + + Description : Script test function to test functioning + of the logResponse() routine. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyLogResponse(); + +=cut + +#------------------------------------------------------- +sub verifyLogResponse{ + my $rc = 0; + my $sub2 = 'sub2'; + + $rc = logResponse( 'DF', 'VX01' ); + print "rc: $rc\n"; + logResponse( 'DF', 'VPF01', ($nodeName, 0, 'result message') ); + $rc = logResponse( 'DF', 'VX03', $nodeName, $sub2); + print "rc: $rc\n"; + $rc = logResponse( 'DF', 'VX03', $nodeName, 'sub2a'); + print "rc: $rc\n"; + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyPower + + Description : Verify the node is powered on. + Arguments : None + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyPower(); + +=cut + +#------------------------------------------------------- +sub verifyPower { + my ( $nodeName ) = @_; + my $rc = 0; + my $powerState; + + my $out = `/opt/xcat/bin/rpower $nodeName stat 2>&1`; + $rc = $?; + if ( $rc != 0 ) { + $rc = logResponse( 'DF', 'VPF01', ($nodeName, $rc, $out) ); + goto FINISH_verifyPower if ( $rc > 0 ); + } + + $out = lc( $out ); + if ( $out =~ /$nodeName: / ) { + chomp $out; + ($powerState) = $out =~ /$nodeName: (.*)/; + } + + if ( $powerState ne 'on' ) { + $rc = logResponse( 'DFS', 'VP02', ($nodeName) ); + } + +FINISH_verifyPower: + return $rc; +} + + +#------------------------------------------------------- + +=head3 verifyService + + Description : Verify that specified services are running. + Arguments : Node name or IP address + Blank delimitted list of service names + service name begins with the name and + has a comma separated list of run levels + which are to be verified as 'on' + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyService(); + +=cut + +#------------------------------------------------------- +sub verifyService { + my ( $nodeOrIP, $namesAndLevels ) = @_; + my @serviceList = split ( / /, $namesAndLevels ); + + my $rc = 0; + my $serviceOut = ''; + + # Get the list of service names and run levels into an array. + + # Get the list of configured services. The output looks similar to: + # service_name 0:off 1:off 2:off 3:off 4:off 5:off 6:off + ($rc, $serviceOut) = sshToNode( $nodeOrIP, '', "chkconfig --list 2>&1" ); + goto FINISH_verifyService if ( $rc == 255 ); + + # Look for missing services + my @missingService; + my @foundService; + my @nonrunningService; + foreach my $serviceInfo ( @serviceList ) { + my @offLevels; + my $levels; + my @serviceRunLevels = split( /,/, $serviceInfo ); + my $service = shift @serviceRunLevels; + ($levels) = $serviceOut =~ /^$service(.*)\s(.*)\n/m; + $levels =~ s/^\s+|\s+$//g if ( defined $levels ); + if (( defined $levels ) and ( $levels ne '' )) { + # Verify the run levels are enabled. + foreach my $level ( @serviceRunLevels ) { + if ( $levels !~ /$level:on/ ) { + push @offLevels, $level; + } + } + if ( @offLevels ) { + $levels = join(", ", @offLevels); + $rc = logResponse( 'DF', 'VS06', $service, $levels ); + goto FINISH_verifyService if ( $rc > 0 ); + } else { + push @foundService, $service; + } + } else { + push @nonrunningService, $service; + } + } + + if ( @foundService ) { + my $list = join(", ", @foundService); + $rc = logResponse( 'DF', 'GENERIC_RESPONSE', "The following services are configured to start: $list" ); + goto FINISH_verifyService if ( $rc > 0 ); + } + if ( @nonrunningService ) { + my $list = join(", ", @nonrunningService); + $rc = logResponse( 'DF', 'VS05', $list ); + goto FINISH_verifyService if ( $rc > 0 ); + } + +FINISH_verifyService: + return $rc; +} + + +#------------------------------------------------------- + +=head3 verifyXcatconf4z + + Description : Verify xcatconf4z is properly installed + and is the correct version. + Arguments : Node name or IP address + Returns : 0 - No error + non-zero - Terminating error detected. + Example : $rc = verifyXcatconf4z(); + +=cut + +#------------------------------------------------------- +sub verifyXcatconf4z{ + my ( $nodeOrIP ) = @_; + my $rc = 0; + my $out; + my ($mnVersion, $tgtVersion); + + # Verify service is installed + $rc = verifyService( $nodeOrIP, 'xcatconf4z,2,3,5' ); + goto FINISH_verifyXcatconf4z if ( $rc == 255 ); + + # Get the xCAT MN's xcatconf4z version level. + $out = `/opt/xcat/share/xcat/scripts/xcatconf4z version`; + if ( $out =~ /xcatconf4z version: / ) { + chomp $out; + ($mnVersion) = $out =~ /xcatconf4z version: (.*)/; + } else { + $rc = logResponse( 'DFS', 'VX01' ); + goto FINISH_verifyXcatconf4z if ( $rc > 0 ); + } + + # Verify that the node contains xcatconf4z in the correct location and is executable. + #$out = `ssh $nodeOrIP 'ls -al /opt/xcatconf4z 2>&1'`; + ($rc, $out) = sshToNode( $nodeOrIP, '', 'ls -al /opt/xcatconf4z 2>&1'); + goto FINISH_verifyXcatconf4z if ( $rc == 255 ); + if ( $out =~ /No such file or directory/ ) { + $rc = logResponse( 'DF', 'VX06' ); + goto FINISH_verifyXcatconf4z if ( $rc > 0 ); + } else { + # Verify that it is executable + } + + # Get the node's xcatconf4z version level. + #$out = `ssh $nodeOrIP '/opt/xcatconf4z version'`; + ($rc, $out) = sshToNode( $nodeOrIP, '', '/opt/xcatconf4z version'); + goto FINISH_verifyXcatconf4z if ( $rc == 255 ); + if ( $out =~ /xcatconf4z version: / ) { + chomp $out; + ($tgtVersion) = $out =~ /xcatconf4z version: (.*)/; + } else { + $rc = logResponse( 'DF', 'VX02' ); + goto FINISH_verifyXcatconf4z if ( $rc > 0 ); + } + + # Verify the version is up to date. + if ( defined $tgtVersion ) { + if ( $mnVersion > $tgtVersion ) { + $rc = logResponse( 'DF', 'VX03', ($tgtVersion, $mnVersion) ); + goto FINISH_verifyXcatconf4z if ( $rc > 0 ); + } + } + + # Verify that authorized senders is setup. + #$out = `ssh $nodeOrIP '/opt/xcatconf4z status'`; + ($rc, $out) = sshToNode( $nodeOrIP, '', '/opt/xcatconf4z status'); + goto verifyXcatconf4z if ( $rc == 255 ); + if ( $out =~ /xcatconf4z is disabled / ) { + $rc = logResponse( 'DF', 'VX04' ); + goto FINISH_verifyXcatconf4z if ( $rc > 0 ); + } else { + # Get list of authorized users and put out an info message. + } + + # Verify that mkisofs is available + ($rc, $out) = sshToNode( $nodeOrIP, '', 'stat /opt/bin/mkisofs'); + goto verifyXcatconf4z if ( $rc == 255 ); + if ( $rc != 0 ) { + $rc = logResponse( 'DF', 'VX07' ); + goto FINISH_verifyXcatconf4z if ( $rc > 0 ); + } + +FINISH_verifyXcatconf4z: + return $rc; +} + + +#***************************************************************************** +# Main routine +#***************************************************************************** +my $ignoreOpt; +my $rc = 0; +my $thisScript = $0; +my $out; + +# Parse the arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); +if (!GetOptions( + 'a|access' => \$verifyAccess, + 'basicivp' => \$runBasicIVP, + 'capturereqs' => \$verifyCaptureReqs, + 'c|cloudinit' => \$verifyCloudInit, + 'comments=s' => \$comments, + 'cron' => \$runCron, + 'decode=s' => \$decode, + 'disable' => \$disable, + 'enable' => \$enable, + 'encode=s' => \$encode, + 'file=s' => \$dataFile, + 'fullivp' => \$runFullIVP, + 'h|help' => \$displayHelp, + 'i|id=s' => \$id, + 'ignore=s' => \$ignoreOpt, + 'cmdoveriucv=s' => \$issueCmdOverIUCV, + 'cmdtonode=s' => \$issueCmdToNode, + 'n|node=s' => \$nodeName, + 'notify' => \$notifyOnErrorOrWarning, + 'openstackuser=s' => \$openstackUser, + 'openstackip=s' => \$openstackIP, + 'orchparms=s' => \$orchParms, + 'prepparms=s' => \$prepParms, + 'remove' => \$remove, + 'schedule=s' => \$schedule, + 'type=s' => \$scheduledType, + 'v|version' => \$versionOpt, + 'x|xcatconf4z' => \$verifyXcatconf4z, + 'zxcatparms=s' => \$zxcatParms, + )) { + print $usage_string; +} + +if ( $versionOpt ) { + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "Version: $version\n" ); +} + +if ( $displayHelp ) { + showHelp(); +} + +if ( $displayHelp or $versionOpt ) { + goto FINISH_main; +} + +# Convert any encoded operand values back to their unencoded value. +my @convertibleParms = ( 'comments', 'nodeName', 'openstackUser', 'orchParms', 'prepParms', 'zxcatParms' ); +foreach my $parmName ( @convertibleParms ) { + eval( "\$$parmName = hexDecode( \$$parmName )" ); +} + +if ( $schedule eq '' and $orchParms ne '' ) { + # If not scheduling an IVP run and orchestrator parms are present then use them. + my $unrecognizedOps = ''; + $rc = GetOptionsFromString( + $orchParms, + 'ignore=s' => \$ignoreOpt, + ); + if ( $rc == 0 ) { + print "Unrecognized option in --orchParms. ". + "Only --ignore is allowed as an orchestrator option for an immediate run.\n"; + $rc = 2; + goto FINISH_main; + } +} + +# Handle messages to ignore. +if ( defined( $ignoreOpt ) ) { + # Make hash from the specified ignore operands + $ignoreOpt = uc( $ignoreOpt ); + my @ingoreList; + if ( $ignoreOpt =~ ',' ) { + @ingoreList = split( ',', $ignoreOpt ); + } else { + @ingoreList = split( ' ', $ignoreOpt ); + } + + %msgsToIgnore = map { $_ => 1 } @ingoreList; + + # Convert general severity type operands to their numeric value. + if ( $msgsToIgnore{'BYPASS'} ) { + delete $msgsToIgnore{'BYPASS'}; + $msgsToIgnore{'2'} = 1; + } + if ( $msgsToIgnore{'INFO'} ) { + delete $msgsToIgnore{'INFO'}; + $msgsToIgnore{'3'} = 1; + } + if ( $msgsToIgnore{'WARNING'} ) { + delete $msgsToIgnore{'WARNING'}; + $msgsToIgnore{'4'} = 1; + } + if ( $msgsToIgnore{'ERROR'} ) { + delete $msgsToIgnore{'ERROR'}; + $msgsToIgnore{'5'} = 1; + } +} + +if ( $openstackIP ne '' ) { + $openstackIP = uc( $openstackIP ); +} + +# Handle the combinations of disable and enable options. +if ( $disable eq '' ) { + $disable = 0; +} +if ( $enable eq '' ) { + $enable = 0; +} +if ( $disable == 1 and $enable == 1 ) { + $rc = logResponse( 'DFS', 'OPER03', '--disable and --enable' ); + goto FINISH_main; +} + +# Determine the information about the xCAT managed node and the z/VM +# host environment where it is running (if running on z/VM). +getMNInfo(); + +if ( $decode ne '' or + $disable or + $enable or + $encode ne '' or + $runFullIVP or + $runCron or + $runBasicIVP or + $remove or + $schedule ne '' ) { + # IVP runs do not need the node. +} elsif ( $nodeName eq '' ) { + $rc = logResponse( 'DFS', 'OPER01', '-n or --node' ); + goto FINISH_main; +} + +($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer(); + +# Setup for a test run of logResponse. Normally, commented out. +#($verifyAccess, $verifyCaptureReqs, $verifyCloudInit, $verifyXcatconf4z) = 0; +#my $test = 1; + +# Run the selected tests +foreach my $verify (keys %verifySets) { + if ( eval $verify ) { + if ( $verifySets{$verify}[0] ne '' ) { + logResponse( 'D', 'GENERIC_RESPONSE_NOINDENT', "\n********************************************************************\n" ); + logResponse( 'D', 'GENERIC_RESPONSE_NOINDENT', "$verifySets{$verify}[0]\n" ); + logResponse( 'D', 'GENERIC_RESPONSE_NOINDENT', "********************************************************************\n" ); + } + my $size = @{$verifySets{$verify}}; + logResponse( 'D', '*RESET*' ); + for (my $i = 1; $i < $size; $i++) { + ($rc) = eval $verifySets{$verify}[$i]; + if ( ! defined $rc ) { + logResponse( 'DFS', 'PERL01', $thisScript, $verifySets{$verify}[$i], $@ ); + $rc = 666; + last; + } + + if ( $rc > 0 ) { + last; + } + } + + if ( $needToFinishLogFile ) { + finishLogFile(); + } + + if ( $warnErrCnt == 0 ) { + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "\nProcessing completed." ); + } else { + logResponse( 'DF', 'GENERIC_RESPONSE_NOINDENT', "\nProcessing completed with warning or error messages. " . + "See previous messages for information.\n" ); + } + } +} + +FINISH_main: +exit $rc; \ No newline at end of file diff --git a/xCAT-client/bin/zvmMsg b/xCAT-client/bin/zvmMsg new file mode 100644 index 000000000..d7b451c26 --- /dev/null +++ b/xCAT-client/bin/zvmMsg @@ -0,0 +1,113 @@ +#!/usr/bin/perl +# IBM(c) 2016 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + Provides more information related to an xCAT message + for z/VM. These are messages which are similar to the + following: + Warning (ZXCATIVP:VN01) Network VSW3 was not found as a network. + These message were originally added for the z/VM IVP and + are in the form: + (group:msgId) + + The information provided consists of: + Explanation of the message, + System Action, and + User Action. + +=cut + +#------------------------------------------------------- +package xCAT::zvmMsgs; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +$XML::Simple::PREFERRED_PARSER='XML::Parser'; + +use Getopt::Long; +use lib "$::XCATROOT/lib/perl"; +use Text::Wrap; +use xCAT::zvmMsgs; +use strict; +use warnings; +1; + +# set the usage message +my $usage_string = "Usage:\n + $0 -h + $0 --help + $0 --group --id \n + Use this script to display extra information for messages related + to z/VM that have the format: + (:) + for example: + Warning (ZXCATIVP:VN01) Network VSW3 was not found.\n + The following options are supported: + --group + specified the identifier of the group to which the message belongs. + The group id is the first part of the identifer, e.g. + 'Warning (ZXCATIVP:VDS01)' belongs to the 'ZXCATIVP' group. + The group id is case sensitive. This operand should be specified + along with the --id operand. + -h | --help + Displays usage information. + --id + specifies the message identifier. The message id is the second part + of the identifier, e.g. 'Warning (ZXCATIVP:VDS01)' where 'VDS01' + is the message id. The message identifier is case sensitive. This + operand should be specified along with the --group operand to fully + identify the message.\n"; + +#***************************************************************************** +# Main routine +#***************************************************************************** +# Parse the arguments +my $displayHelp = 0; +my $group = ''; +my $msgId = ''; +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( 'bundling' ); + +if ( !GetOptions( + 'h|help' => \$displayHelp, + 'group=s' => \$group, + 'id=s' => \$msgId, + )) { + print $usage_string; + goto FINISH; +} + +if ( $displayHelp ) { + print $usage_string; + goto FINISH; +} + +if ( $group eq '' ) { + print "--group operand was not specified.\n"; + print $usage_string; +} elsif ( $msgId eq '' ) { + print "--id operand was not specified.\n"; + print $usage_string; +} else { + my $extraInfo; + my $msg; + my $recAction; + my $sev; + ( $recAction, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg( $group, $msgId ); + if ( $msg =~ ': Message was not found!' ) { + # Oops, we could not find the message. + print "$msg\n"; + } else { + if ( $extraInfo ne '' ) { + print "Additional information for message $msgId in group $group:\n"; + print "$extraInfo\n"; + } + } +} + +FINISH: +exit; \ No newline at end of file diff --git a/xCAT-client/bin/zxcatCopyCloneList.pl b/xCAT-client/bin/zxcatCopyCloneList.pl new file mode 100644 index 000000000..defab5cbc --- /dev/null +++ b/xCAT-client/bin/zxcatCopyCloneList.pl @@ -0,0 +1,353 @@ +#!/usr/bin/perl +############################################################################### +# IBM (C) Copyright 2015, 2016 Eclipse Public License +# http://www.eclipse.org/org/documents/epl-v10.html +############################################################################### +# COMPONENT: zxcatCopyCloneList.pl +# +# This is a program to copy the "DOCLONE COPY" file from the 193 disk to +# /opt/xcat/doclone.txt +############################################################################### + +use strict; +#use warnings; +use Capture::Tiny ':all'; +use Getopt::Long; + +my $file_location = '/var/opt/xcat/'; +my $source_file = 'DOCLONE.COPY'; +my $file_name = 'doclone.txt'; +my $tempPattern = 'doclone.XXXXXXXX'; +my $source_vdev = '193'; +my $version = "1.0"; +my $out; +my $err; +my $returnvalue; +my $displayHelp = 0; # Display help information +my $versionOpt = 0; # Show version information flag + +my $usage_string = "This script copies the DOCLONE COPY from the MAINT 193 +to the $file_location$file_name\n\n + Usage:\n + $0 [ -v ] + $0 [ -h | --help ]\n + The following options are supported:\n + -h | --help Display help information\n + -v Display the version of this script.\n"; + +# Copied this routine from sspmodload.pl +# This will get the Linux address of the vdev +sub get_disk($) +{ + my ($id_user) = @_; + my $id = hex $id_user; + my $hex_id = sprintf '%x', $id; + my $completed = 1; + my $dev_path = sprintf '/sys/bus/ccw/drivers/dasd-eckd/0.0.%04x', $id; + if (!-d $dev_path) { + $dev_path = sprintf '/sys/bus/ccw/drivers/dasd-fba/0.0.%04x', $id; + } + if (!-d $dev_path) { + print "(Error) Unable to find a path to the $source_vdev in /sys/bus/ccw/drivers/\n"; + } + -d $dev_path or return undef; + + #offline the disk so that a new online will pick up the current file + my @sleepTimes = ( 1, 2, 3, 5, 8, 15, 22, 34, 60); + system("echo 0 > $dev_path/online"); + + my $dev_block = "$dev_path/block"; + + #wait if the disk directory is still there + if (-d $dev_block) { + $completed = 0; + foreach (@sleepTimes) { + system("echo 0 > $dev_path/online"); + sleep $_; + if (!-d $dev_block) { + $completed = 1; + last; + } + } + } + + if (!$completed) { + print "(Error) The 193 disk failed to complete the offline!\n"; + return undef; + } + + system("echo 1 > $dev_path/online"); + # Bring the device online if offline + if (!-d $dev_block) { + $completed = 0; + foreach (@sleepTimes) { + system("echo 1 > $dev_path/online"); + sleep $_; + if (-d $dev_block) { + $completed = 1; + last; + } + } + if (!$completed) { + print "(Error) The 193 disk failed to come online!\n"; + return undef; + } + } + if (opendir(my $dir, $dev_block)) { + my $dev; + while ($dev = readdir $dir) { + last if (!( $dev eq '.' || $dev eq '..' ) ); + } + closedir $dir; + if (!defined $dev) { + print "(Error) undefined $dev\n"; + } + defined $dev ? "/dev/$dev" : undef; + } else { + print "(Error) Unable to opendir $dev_block\n"; + return undef; + } +} + +# *********************************************************** +# Mainline. Parse any arguments, usually no arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); + +GetOptions( + 'h|help' => \$displayHelp, + 'v' => \$versionOpt ); + +if ( $versionOpt ) { + print "Version: $version\n"; + exit 0; +} + +if ( $displayHelp ) { + print $usage_string; + exit 0; +} + +my $tempFileName = ''; +my $rc = 0; +my $oldFileExists = 0; +my $dev = get_disk($source_vdev); +if (defined($dev)) { + # make sure directory exists + if (!-d $file_location) { + $returnvalue = mkdir "$file_location", 0755; + if (!$returnvalue) { + print "(Error) mkdir $file_location failed with errno:$!"; + $rc = 1; + goto MAIN_EXIT; + + } + } + my $oldFiletime; + # Create a temp file name to use while validating + $tempFileName = `/bin/mktemp -p $file_location $tempPattern`; + chomp($tempFileName); + # if we are overwriting an existing file, save time stamp + if (-e "$file_location$file_name") { + # stat will return results in $returnvalue + ( $out, $err, $returnvalue ) = eval { capture { `stat \'-c%y\' $file_location$file_name` } }; + chomp($out); + chomp($err); + chomp($returnvalue); + if (length($err) > 0) { + print "(Error) Cannot stat the $file_location$file_name\n$err\n"; + $rc = 1; + goto MAIN_EXIT; + } + $oldFileExists = 1; + $oldFiletime = $returnvalue; + } + + ( $out, $err, $returnvalue ) = eval { capture { `/sbin/cmsfscp -d $dev -a $source_file $tempFileName` } }; + chomp($out); + chomp($err); + chomp($returnvalue); + if (length($err) > 0) { + # skip any blksize message for other blksize + if ($err =~ 'does not match device blksize') { + } else { + print "(Error) Cannot copy $source_file\n$err\n"; + $rc = 1; + goto MAIN_EXIT; + } + } + + if ($oldFileExists == 1) { + ( $out, $err, $returnvalue ) = eval { capture { `stat \'-c%y\' $tempFileName` } }; + chomp($out); + chomp($err); + chomp($returnvalue); + if (length($err) > 0) { + print "(Error) Cannot stat the $tempFileName\n$err\n"; + $rc = 1; + goto MAIN_EXIT; + } + if ($oldFiletime eq $returnvalue) { + print "The $source_file copied to temporary file $tempFileName is the same time stamp as original.\n"; + } else { + print "$source_file copied to temporary file $tempFileName successfully\n"; + } + } else { + print "$source_file copied to temporary file $tempFileName successfully\n"; + } + + print "Validating $tempFileName contents for proper syntax...\n"; + if (-f "$tempFileName") { + $out = `cat $tempFileName`; + } else { + print "(Error) Missing temporary file: $tempFileName\n"; + $rc = 1; + goto MAIN_EXIT; + } + my @lines = split('\n',$out); + my %hash = (); + my %imagenames = (); + my $count = @lines; + if ($count < 1) { + print "(Error) $tempFileName does not have any data.\n"; + ( $out, $err, $returnvalue ) = eval { capture { `rm $tempFileName` } }; + chomp($out); + chomp($err); + chomp($returnvalue); + if (length($err) > 0) { + print "(Error) Cannot erase temporary file $tempFileName $err\n"; + } + $rc = 1; + goto MAIN_EXIT; + } + + # loop for any lines found + for (my $i=0; $i < $count; $i++) { + # skip comment lines, * or /* + if ( $lines[$i] =~ '^\s*[\*]') { + next; + } + if ( $lines[$i] =~ '^\s*/[*]') { + next; + } + # is this a blank line? if so skip it + if ($lines[$i] =~/^\s*$/) { + next; + } + my $semicolons = $lines[$i] =~ tr/\;//; + if ($semicolons < 3) { + print "(Error) Semicolons need to end each key=value on line ".($i+1)."\n"; + $rc = 1; + } + + %hash = ('IMAGE_NAME' => 0,'CLONE_FROM' => 0,'ECKD_POOL' => 0, 'FBA_POOL' => 0 ); + # IMAGE_NAME=imgBoth; CLONE_FROM=testFBA; ECKD_POOL=POOLECKD; FBA_POOL=POOLFBA + my @parms = split( ';', $lines[$i]); + my $parmcount = @parms; + # get the key and value for this item, store in hash + for (my $j=0; $j < $parmcount; $j++) { + # if this token is all blanks skip it. Could be reading blanks at the end of the line + if ($parms[$j] =~ /^\s*$/) { + next; + } + my $parmlength = length($parms[$j]); + my @keyvalue = split('=', $parms[$j]); + my $key = $keyvalue[0]; + $key =~ s/^\s+|\s+$//g; # get rid of leading and trailing blanks + + if ( length( $key ) == 0 ) { + print "(Error) Missing keyword on line ".($i+1)."\n"; + $rc = 1; + next; + } + my $value = $keyvalue[1]; + $value =~ s/^\s+|\s+$//g; + if ( length( $value ) == 0 ) { + print "(Error) Missing value for key $key on line ".($i+1)."\n"; + $rc = 1; + next + } + #uppercase both key and value; + my $UCkey = uc $key; + my $UCvalue = uc $value; + $hash{$UCkey} = $hash{$UCkey} + 1; + if ($UCkey eq "IMAGE_NAME") { + if (exists $imagenames{$UCvalue}) { + print "(Error) Duplicate IMAGE_NAME found on line ".($i+1)." with value: $value\n"; + $rc = 1; + } else { + $imagenames{$UCvalue} = 1; + } + } + if ($UCkey ne "IMAGE_NAME" && $UCkey ne "CLONE_FROM" && $UCkey ne "ECKD_POOL" && $UCkey ne "FBA_POOL") { + print "(Error) Unknown keyword $key found on line ".($i+1)."\n"; + $rc = 1; + } + } + # Check to make sure they have at least an image name, from and one pool + if ($hash{IMAGE_NAME} == 1 && $hash{CLONE_FROM} == 1 && ($hash{ECKD_POOL} ==1 || $hash{FBA_POOL} ==1 )) { + next; + } else { + if ($hash{IMAGE_NAME} == 0) { + print "(Error) Missing IMAGE_NAME key=value on line ".($i+1)."\n"; + $rc = 1; + } + if ($hash{IMAGE_NAME} > 1) { + print "(Error) Multiple IMAGE_NAME keys found on line ".($i+1)."\n"; + $rc = 1; + } + if ($hash{CLONE_FROM} == 0) { + print "(Error) Missing CLONE_FROM key=value on line ".($i+1)."\n"; + $rc = 1; + } + if ($hash{CLONE_FROM} > 1) { + print "(Error) Multiple CLONE_FROM keys found on line ".($i+1)."\n"; + $rc = 1; + } + if ($hash{ECKD_POOL} == 0 && $hash{FBA_POOL} == 0) { + print "(Error) Missing ECKD_POOL or FBA_POOL on line ".($i+1)."\n"; + $rc = 1; + } + if ($hash{ECKD_POOL} > 1) { + print "(Error) Multiple ECKD_POOL keys found on line ".($i+1)."\n"; + $rc = 1; + } + if ($hash{FBA_POOL} > 1) { + print "(Error) Multiple FBA_POOL keys found on line ".($i+1)."\n"; + $rc = 1; + } + } + } +} else { + print "(Error) Unable to access the $source_vdev disk.\n"; + $rc = 1; +} +# Main exit for this routine. Handles any necessary clean up. +MAIN_EXIT: +if (length($tempFileName) > 0 ) { + # If a good rc, Copy the temp file to the correct file + if ($rc == 0) { + ( $out, $err, $returnvalue ) = eval { capture { `/bin/cp -f $tempFileName $file_location$file_name` } }; + print $out; + print $err; + print $returnvalue; + chomp($out); + chomp($err); + chomp($returnvalue); + if (length($err) > 0) { + print "(Error) Cannot copy the temporary file $tempFileName to $file_location$file_name \n $err\n"; + $rc = 1; + } else { + print "Validation completed. Temporary file copied to $file_location$file_name.\nIt is ready to use\n"; + } + } + ( $out, $err, $returnvalue ) = eval { capture { `rm $tempFileName` } }; + chomp($out); + chomp($err); + chomp($returnvalue); + if (length($err) > 0) { + print "(Error) Cannot erase temporary file $tempFileName\n$err\n"; + $rc = 1; + } +} +exit $rc; diff --git a/xCAT-client/bin/zxcatIVP.pl b/xCAT-client/bin/zxcatIVP.pl new file mode 100644 index 000000000..5e1a79604 --- /dev/null +++ b/xCAT-client/bin/zxcatIVP.pl @@ -0,0 +1,2607 @@ +#!/usr/bin/perl +############################################################################### +# IBM (C) Copyright 2014, 2016 Eclipse Public License +# http://www.eclipse.org/org/documents/epl-v10.html +############################################################################### +# COMPONENT: zxcatIVP.pl +# +# This is an Installation Verification Program for z/VM's xCAT Management Node +# and zHCP agent. +############################################################################### +package xCAT::verifynode; +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +$XML::Simple::PREFERRED_PARSER='XML::Parser'; + +use strict; +#use warnings; +use Getopt::Long; +use Getopt::Long qw(GetOptionsFromString); +use MIME::Base64; +use Sys::Syslog qw( :DEFAULT setlogsock); +use Text::Wrap; +use LWP; +use JSON; +use lib "$::XCATROOT/lib/perl"; +use xCAT::zvmMsgs; +require HTTP::Request; + +# Global variables set based on input from the driver program. +my $glob_bypassMsg = 1; # Display bypass messages +my $glob_CMA = 0; # CMA appliance indicator, Assume not running in CMA +my $glob_CMARole = ''; # CMA role, CONTROLLER, COMPUTE or '' (unknown) +my $glob_cNAddress; # IP address or hostname of the compute node that + # is accessing this xCAT MN. (optional) +my $glob_defaultUserProfile; # Default profile used in creation of server instances. +my $glob_displayHelp = 0; # Display help instead of running the IVP +my @glob_diskpools; # Array of disk pools, e.g. ('POOLSCSI', 'POOL1'), + # that are expected to exist. (optional) +my $glob_expectedReposSpace; # Minimum amount of repository space. +my $glob_expUser; # User name used for importing and exporting images +my @glob_instFCPList; # Array of FCPs used by server instances +my $glob_hostNode; # Node of host being managed. If blank, + # IVP will search for the host node. (optional) +my $glob_macPrefix; # User prefix for MAC Addresses of Linux level 2 interfaces +my %glob_msgsToIgnore; # Hash of messages to be ignored +my $glob_mgtNetmask; +my $glob_mnNode; # Node name for xCAT MN (optional). +my $glob_moreCmdOps; # Additional command operands passed with an environment variable +my @glob_networks; # Array of networks and possible VLAN ranges + # eg. ( 'xcatvsw1', 'xcatvsw2:1:4999'). (optional) +my $obfuscatePw; # Obfuscate the PW in the driver file that is built +my $glob_signalTimeout; # Signal Shutdown mininum acceptable timeout +my $glob_syslogErrors; # Log errors in SYSLOG: 0 - no, 1 - yes +my %glob_vswitchOSAs; # Hash list of vswitches and their OSAs +my @glob_xcatDiskSpace; # Array information of directories in xCAT MN that should be validated for disk space +my $glob_xcatMNIp; # Expected IP address of this xCAT MN +my $glob_xcatMgtIp; # Expected IP address of this xCAT MN on the Mgt Network +my $glob_xcatUser; # User defined to communicate with xCAT MN +my $glob_xcatUserPw; # User's password defined to communicate with xCAT MN +my @glob_zhcpDiskSpace; # Array information of directories in zhcp that should be validated for disk space +my @glob_zhcpFCPList; # Array of FCPs used by zHCP +my $glob_zhcpNode; # Node name for xCAT zHCP server (optional) + +# Global IVP run time variables +my $glob_versionInfo; # Version info for xCAT MN and zHCP +my $glob_successfulTestCnt = 0; # Number of successful tests +my @glob_failedTests; # List of failed tests. +#my @glob_hostNodes; # Array of host node names +my %glob_hostNodes; # Hash of host node information +my %glob_ignored; # List of ignored messages +my $glob_ignoreCnt = 0; # Number of times we ignored a message +my %glob_localIPs; # Hash of local IP addresses for the xCAT MN's system +my $glob_localZHCP = ''; # Local ZHCP node name and hostname when ZHCP is on xCAT MN's system +my $glob_totalFailed = 0; # Number of failed tests +my $glob_testNum = 0; # Number of tests that were run + +my $glob_versionFileCMA = '/opt/ibm/cmo/version'; +my $glob_versionFileXCAT = '/opt/xcat/version'; +my $glob_applSystemRole = '/var/lib/sspmod/appliance_system_role'; + +# Tables for environment variable and command line operand processing. +my ( $cmdOp_bypassMsg, $cmdOp_cNAddress, $cmdOp_defaultUserProfile, $cmdOp_diskpools, + $cmdOp_expectedReposSpace, $cmdOp_expUser, $cmdOp_hostNode, $cmdOp_ignore, + $cmdOp_instFCPList, $cmdOp_instFCPList, $cmdOp_macPrefix, $cmdOp_mgtNetmask, + $cmdOp_mnNode, $cmdOp_moreCmdOps, $cmdOp_bypassMsg, $cmdOp_networks, $cmdOp_pw_obfuscated, + $cmdOp_syslogErrors, $cmdOp_signalTimeout, $cmdOp_vswitchOSAs, + $cmdOp_xcatDiskSpace, $cmdOp_xcatMgtIp, $cmdOp_xcatMNIp, + $cmdOp_xcatUser, $cmdOp_zhcpFCPList, $cmdOp_zhcpNode, $cmdOp_zhcpDiskSpace ); +my @cmdOps = ( + { + 'envVar' => 'zxcatIVP_bypassMsg', + 'opName' => 'bypassMsg', + 'inpVar' => 'cmdOp_bypassMsg', + 'var' => 'glob_bypassMsg', + 'type' => 'scalar', + 'desc' => "Controls whether bypass messages are produced:\n" . + "0: do not show bypass messages, or\n" . + "1: show bypass messages.\n" . + "Bypass messages indicate when a test is not run. " . + "This usually occurs when a required environment variable or command line operand is " . + "missing.", + }, + { + 'envVar' => 'zxcatIVP_cNAddress', + 'opName' => 'cNAddress', + 'inpVar' => 'cmdOp_cNAddress', + 'var' => 'glob_cNAddress', + 'type' => 'scalar', + 'desc' => 'Specifies the IP address or hostname of the OpenStack compute node that is ' . + 'accessing the xCAT MN.', + }, + { + 'envVar' => 'zxcatIVP_defaultUserProfile', + 'opName' => 'defaultUserProfile', + 'inpVar' => 'cmdOp_defaultUserProfile', + 'var' => 'glob_defaultUserProfile', + 'type' => 'scalar', + 'desc' => 'Specifies the default profile that is used in creation of server instances.', + }, + { + 'envVar' => 'zxcatIVP_diskpools', + 'opName' => 'diskpools', + 'inpVar' => 'cmdOp_diskpools', + 'var' => 'glob_diskpools', + 'type' => 'array', + 'case' => 'uc', + 'separator' => ';, ', + 'desc' => 'Specifies an array of disk pools that are expected to exist. ' . + 'The IVP will verify that space exists in those disk pools. ', + }, + { + 'envVar' => 'zxcatIVP_expectedReposSpace', + 'opName' => 'expectedReposSpace', + 'inpVar' => 'cmdOp_expectedReposSpace', + 'var' => 'glob_expectedReposSpace', + 'type' => 'scalar', + 'default' => '1G', + 'desc' => 'Specifies the expected space available in the xCAT MN image repository. ' . + 'The OpenStack compute node attempts to ensure that the space is available ' . + 'in the xCAT image repository by removing old images. This can cause ' . + 'images it be removed and added more often than desired if the value is too high.', + }, + { + 'envVar' => 'zxcatIVP_expUser', + 'opName' => 'expUser', + 'inpVar' => 'cmdOp_expUser', + 'var' => 'glob_expUser', + 'type' => 'scalar', + 'desc' => 'Specifies the name of the user under which the OpenStack Nova component runs ' . + 'on the compute node. The IVP uses this name when it attempts to verify access ' . + 'of the xCAT MN to the compute node.', + }, + { + 'envVar' => 'zxcatIVP_hostNode', + 'opName' => 'hostNode', + 'inpVar' => 'cmdOp_hostNode', + 'var' => 'glob_hostNode', + 'type' => 'scalar', + 'desc' => 'Specifies the node of host being managed by the compute node. ' . + 'The IVP will verify that the node name exists and use this to determine the ' . + 'ZHCP node that supports the host. If this value is missing or empty, the ' . + 'IVP will validate all host nodes that it detects on the xCAT MN.', + }, + { + 'envVar' => 'zxcatIVP_ignore', + 'opName' => 'ignore', + 'inpVar' => 'cmdOp_ignore', + 'var' => 'glob_msgsToIgnore', + 'type' => 'hash', + 'case' => 'uc', + 'separator' => ';, ', + 'desc' => 'Specifies a comma separated list of message Ids that should be ignored. ' . + 'Ignored messages do not generate a full message and are not counted as ' . + 'trigger to notify the monitoring userid (see XCAT_notify property in the ' . + 'DMSSICNF COPY file). Instead a line will be generated in the output indicating ' . + 'that the message was ignored.', + }, + { + 'envVar' => 'zxcatIVP_instFCPList', + 'opName' => 'instFCPList', + 'inpVar' => 'cmdOp_instFCPList', + 'var' => 'glob_instFCPList', + 'type' => 'array', + 'separator' => ';, ', + 'desc' => 'Specifies the list of FCPs used by instances.', + }, + { + 'envVar' => 'zxcatIVP_macPrefix', + 'opName' => 'macPrefix', + 'inpVar' => 'cmdOp_macPrefix', + 'var' => 'glob_macPrefix', + 'type' => 'scalar', + 'desc' => 'Specifies user prefix for MAC Addresses of Linux level 2 interfaces.', + }, + { + 'envVar' => 'zxcatIVP_mgtNetmask', + 'opName' => 'mgtNetmask', + 'inpVar' => 'cmdOp_mgtNetmask', + 'var' => 'glob_mgtNetmask', + 'type' => 'scalar', + 'desc' => 'Specifies xCat management interface netmask.', + }, + { + 'envVar' => 'zxcatIVP_mnNode', + 'opName' => 'mnNode', + 'inpVar' => 'cmdOp_mnNode', + 'var' => 'glob_mnNode', + 'type' => 'scalar', + 'desc' => 'Specifies the node name for xCAT MN.', + }, + { + 'envVar' => 'zxcatIVP_moreCmdOps', + 'opName' => 'moreCmdOps', + 'inpVar' => 'cmdOp_moreCmdOps', + 'var' => 'glob_moreCmdOps', + 'type' => 'scalar', + 'desc' => 'Specifies additional command operands to be passed to the zxcatIVP script. ' . + 'This is used interally by the IVP programs.', + }, + { + 'envVar' => 'zxcatIVP_networks', + 'opName' => 'networks', + 'inpVar' => 'cmdOp_networks', + 'var' => 'glob_networks', + 'type' => 'array', + 'separator' => ';, ', + 'desc' => "Specifies an array of networks and possible VLAN ranges. " . + "The array is a list composed of network names and optional " . + "vlan ranges in the form: vswitch:vlan_min:vlan_max where " . + "each network and vlan range components are separated by a colon.\n" . + "For example, 'vsw1,vsw2:1:4095' specifies two vswitches vsw1 without a " . + "vlan range and vsw2 with a vlan range of 1 to 4095.", + }, + { + 'envVar' => 'zxcatIVP_pw_obfuscated', + 'opName' => 'pw_obfuscated', + 'inpVar' => 'cmdOp_pw_obfuscated', + 'var' => 'obfuscatePw', + 'type' => 'scalar', + 'desc' => "Indicates whether the password zxcatIVP_xcatUserPw is obfuscated:\n" . + "1 - obfuscated,\n0 - in the clear.", + }, + { + 'envVar' => 'zxcatIVP_signalTimeout', + 'opName' => 'signalTimeout', + 'inpVar' => 'cmdOp_signalTimeout', + 'var' => 'glob_signalTimeout', + 'type' => 'scalar', + 'default' => '30', + 'desc' => "Specifies the minimum acceptable time value that should be specified ". + "in the z/VM using the SET SIGNAL SHUTDOWNTIME configuration statement. ". + "A value less than this value will generate a warning in the IVP.", + }, + { + 'envVar' => 'zxcatIVP_syslogErrors', + 'opName' => 'syslogErrors', + 'inpVar' => 'cmdOp_syslogErrors', + 'var' => 'glob_syslogErrors', + 'type' => 'scalar', + 'default' => '1', + 'desc' => "Specifies whether Warnings and Errors detected by the IVP are ". + "logged in the xCAT MN syslog:\n0: do not log,\n1: log to syslog.", + }, + { + 'envVar' => 'zxcatIVP_vswitchOSAs', + 'opName' => 'vswitchOSAs', + 'inpVar' => 'cmdOp_vswitchOSAs', + 'var' => 'glob_vswitchOSAs', + 'type' => 'hash', + 'separator' => ';, ', + 'desc' => 'Specifies vswitches and their related OSAs that are used by ' . + 'systems created by the OpenStack compute node.', + }, + { + 'envVar' => 'zxcatIVP_xcatDiskSpace', + 'opName' => 'xcatDiskSpace', + 'inpVar' => 'cmdOp_xcatDiskSpace', + 'var' => 'glob_xcatDiskSpace', + 'type' => 'array', + 'separator' => ';,', + 'default' => '/ 80 . 10M;/install 90 1g .', + 'desc' => 'Specifies a list of directories in the xCAT server that should be '. + 'verified for storage availability. Each directory consists of '. + 'a blank separated list of values:'. + "\n". + '* directory name,'. + "\n". + '* maximum in use percentage (a period indicates that the value should not be tested),'. + "\n". + '* minumum amount of available storage (period indicates that '. + 'available storage based on size should not be validated),'. + "\n". + '* minimum file size at which to generate a warning when available space tests '. + 'detect a size issue (a period indicates that the value should not be tested).'. + "\n\n". + "For example: '/ 80 5G 3M' will cause the IVP to check the space for the ". + 'root directory (/) to verify that it has at least 80% space available or '. + '5G worth of space available. If the space tests fail, a warning will be '. + 'generated for each file (that is not a jar or so file) which is 3M or larger. '. + 'Additional directories may be specified using a list separator.', + }, + { + 'envVar' => 'zxcatIVP_xcatMgtIp', + 'opName' => 'xcatMgtIp', + 'inpVar' => 'cmdOp_xcatMgtIp', + 'var' => 'glob_xcatMgtIp', + 'type' => 'scalar', + 'desc' => 'Specifies xCat MN\'s IP address on the xCAT management network.', + }, + { + 'envVar' => 'zxcatIVP_xcatMNIp', + 'opName' => 'xcatMNIp', + 'inpVar' => 'cmdOp_xcatMNIp', + 'var' => 'glob_xcatMNIp', + 'type' => 'scalar', + 'desc' => 'Specifies the expected IP address of the xcat management node.', + }, + { + 'envVar' => 'zxcatIVP_xcatUser', + 'opName' => 'xcatUser', + 'inpVar' => 'cmdOp_xcatUser', + 'var' => 'glob_xcatUser', + 'type' => 'scalar', + 'desc' => 'Specifies the user defined to communicate with xCAT management node.', + }, + { + 'envVar' => 'zxcatIVP_xcatUserPw', + 'opName' => 'xcatUserPw', + 'inpVar' => 'cmdOp_xcatUserPw', + 'var' => 'glob_xcatUserPw', + 'type' => 'scalar', + 'desc' => 'Specifies the user password defined to communicate with xCAT MN over the REST API.', + }, + { + 'envVar' => 'zxcatIVP_zhcpFCPList', + 'opName' => 'zhcpFCPList', + 'inpVar' => 'cmdOp_zhcpFCPList', + 'var' => 'glob_zhcpFCPList', + 'type' => 'array', + 'separator' => ';, ', + 'desc' => 'Specifies the list of FCPs used by zHCP.', + }, + { + 'envVar' => 'zxcatIVP_zhcpNode', + 'opName' => 'zhcpNode', + 'inpVar' => 'cmdOp_zhcpNode', + 'var' => 'glob_zhcpNode', + 'type' => 'scalar', + 'desc' => 'Specifies the expected ZHCP node name that the compute node ' . + 'expects to be used to manage the z/VM host.', + }, + { + 'envVar' => 'zxcatIVP_zhcpDiskSpace', + 'opName' => 'zhcpDiskSpace', + 'inpVar' => 'cmdOp_zhcpDiskSpace', + 'var' => 'glob_zhcpDiskSpace', + 'type' => 'array', + 'separator' => ';,', + 'default' => '/ 90 . 10M', + 'desc' => "Specifies a list of directories in the ZHCP server that should be ". + 'verified for storage availability. Each directory consists of '. + 'a blank separated list of values:'. + "\n". + '* directory name,'. + "\n". + '* maximum in use percentage (a period indicates that the value should not be tested),'. + "\n". + '* minumum amount of available storage (period indicates that '. + 'available storage based on size should not be validated),'. + "\n". + '* minimum file size at which to generate a warning when available space tests '. + 'detect a size issue (a period indicates that the value should not be tested).'. + "\n\n". + "For example: '/ 80 5G 3M' will cause the IVP to check the space for the ". + 'root directory (/) to verify that it has at least 80% space available or '. + '5G worth of space available. If the space tests fail, a warning will be '. + 'generated for each file (that is not a jar or so file) which is 3M or larger. '. + 'Additional directories may be specified using a list separator.', + }, + ); + +my $usage_string = "Usage:\n + zxcatIVP +or + zxcatIVP +or + zxcatIVP --help +or + zxcatIVP -h\n\n"; + + +#------------------------------------------------------- + +=head3 applyOverrides + + Description : Apply the overrides from either + the environment variables or command + line to the target global variables. + Arguments : None + Returns : None. + Example : applyOverrides(); + +=cut + +#------------------------------------------------------- +sub applyOverrides{ + # Handle the normal case variables and values. + foreach my $opHash ( @cmdOps ) { + my $opVarRef = eval('\$' . $opHash->{'inpVar'}); + if ( ! defined $$opVarRef) { + #print "Did not find \$$opHash->{'inpVar'}\n"; + next; + } else { + #print "key: $opHash->{'inpVar'}, value: $$opVarRef\n" + } + + # Modify the case of the value, if necessary. + if ( ! exists $opHash->{'case'} ) { + # Ignore case handling + } elsif ( $opHash->{'case'} eq 'uc' ) { + $$opVarRef = uc( $$opVarRef ); + } elsif ( $opHash->{'case'} eq 'lc' ) { + $$opVarRef = lc( $$opVarRef ); + } + + # Process the value to set the variable in this script. + if ( $opHash->{'type'} eq "scalar" ) { + my $globRef = eval('\$' . $opHash->{'var'}); + $$globRef = $$opVarRef; + } elsif ( $opHash->{'type'} eq "array" ) { + my $globRef = eval('\@' . $opHash->{'var'}); + my @array; + if ( $opHash->{'separator'} =~ /,/ and $$opVarRef =~ /,/ ) { + @array = split( ',', $$opVarRef ); + } elsif ( $opHash->{'separator'} =~ /;/ and $$opVarRef =~ /;/ ) { + @array = split( ';', $$opVarRef ); + } elsif ( $opHash->{'separator'} =~ /\s/ and $$opVarRef =~ /\s/ ) { + @array = split( '\s', $$opVarRef ); + } else { + push @array, $$opVarRef; + } + @$globRef = @array; + } elsif ( $opHash->{'type'} eq "hash" ) { + my $globRef = eval('\%' . $opHash->{'var'}); + my @array; + my %hash; + if ( $opHash->{'separator'} =~ /,/ and $$opVarRef =~ /,/ ) { + @array = split( ',', $$opVarRef ); + %hash = map { $_ => 1 } @array; + } elsif ( $opHash->{'separator'} =~ /;/ and $$opVarRef =~ /;/ ) { + @array = split( ';', $$opVarRef ); + %hash = map { $_ => 1 } @array; + } elsif ( $opHash->{'separator'} =~ /\s/ and $$opVarRef =~ /\s/ ) { + @array = split( '\s', $$opVarRef ); + %hash = map { $_ => 1 } @array; + } else { + $hash{$$opVarRef} = 1; + } + %$globRef = %hash; + } else { + print "Internal error: Unsupported \%cmdOpRef type '$opHash->{'type'}' for $opHash->{'inpVar'}\n"; + } + } +} + + +#------------------------------------------------------- + +=head3 calculateRoutingPrefix + + Description : Calculate the routing prefix for a given subnet mask. + Arguments : Subnet Mask + Returns : -1 - Unable to calculate routing prefix + zero or non-zero - Routing prefix number + Example : $rc = calculateRoutingPrefix( $subnetMask ); + +=cut + +#------------------------------------------------------- +sub calculateRoutingPrefix{ + my ( $subnetMask ) = @_; + my $routingPrefix = 0; + my @parts; + + # Determine the inet version based on the mask separator and + # calculate the routing prefix. + if ( $subnetMask =~ m/\./ ) { + # inet 4 mask + @parts = split( /\./, $subnetMask ); + foreach my $part ( @parts ) { + if (( $part =~ /\D/ ) || ( length($part) > 3 )) { + return -1; # subnet mask is not valid + } + foreach my $i ( 1, 2, 4, 8, + 16, 32, 64, 128 ) { + if ( $part & $i ) { + $routingPrefix++; + } + } + } + } elsif ( $subnetMask =~ m/\:/ ) { + # inet 6 mask + @parts = split( /:/, $subnetMask ); + foreach my $part ( @parts ) { + if (( $part =~ /[^0-9^a-f^A-F]/ ) || ( length($part) > 4 )) { + print "part failed: $part\n"; + return -1; # subnet mask is not valid + } + $part = hex $part; + foreach my $i ( 1, 2, 4, 8, + 16, 32, 64, 128, + 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768 ) { + if ( $part & $i ) { + $routingPrefix++; + } + } + } + } + + return $routingPrefix +} + + +#------------------------------------------------------- + +=head3 convertDiskSize + + Description : Reduce a size with a magnitude (eg. 25G or 25M) + to a common scalar value. + Arguments : Size to convert. + Returns : non-negative - No error + -1 - Error detected. + Example : my $size = convertDiskSize( $diskType, $diskSize ); + +=cut + +#------------------------------------------------------- +sub convertDiskSize{ + my ( $diskType, $diskSize ) = @_; + my $size; + + my $bytesPer3390Cylinder = 849960; + my $bytesPer3380Cylinder = 712140; + my $bytesPer9345Cylinder = 696840; + my $bytesPerFbaBlock = 512; + my $cylindersPer3390_03 = 3339; + my $kilobyte = 1024; + my $megabyte = 1024 ** 2; + my $gigabyte = 1024 ** 3; + + $diskType = uc( $diskType ); + if ( $diskType =~ '3390-' ) { + $size = $diskSize * $bytesPer3390Cylinder / $gigabyte; + $size = sprintf("%.2f", $size); + $size = "$diskSize(cyl) -> $size" . "Gig"; + } elsif ( $diskType =~ '3380-') { + $size = $diskSize * $bytesPer3380Cylinder / $gigabyte; + $size = sprintf("%.2f", $size); + $size = "$diskSize(cyl) -> $size" . "Gig"; + } elsif ( $diskType =~ '9345-') { + $size = $diskSize * $bytesPer9345Cylinder / $gigabyte; + $size = sprintf("%.2f", $size); + $size = "$diskSize(cyl) -> $size" . "Gig"; + } elsif ( $diskType =~ '9336-') { + $size = $diskSize * $bytesPerFbaBlock / $gigabyte; + $size = sprintf("%.2f", $size); + $size = "$diskSize(block) -> $size" . "Gig"; + } elsif ( $diskType =~ '9332') { + $size = $diskSize * $bytesPerFbaBlock / $gigabyte; + $size = "$diskSize(block)"; + } else { + $size = "$diskSize"; + } + + return $size; +} + + +#------------------------------------------------------- + +=head3 convertSize + + Description : Reduce a size with a magnitude (eg. 25G or 25M) + to a common scalar value. + Arguments : Size to convert. + Returns : non-negative - No error + -1 - Error detected. + Example : my $size = convertSize( "25G" ); + +=cut + +#------------------------------------------------------- +sub convertSize{ + my ( $magSize ) = @_; + my $size; + my $numeric; + my $kilobyte = 1024; + my $megabyte = 1024 ** 2; + my $gigabyte = 1024 ** 3; + my $terabyte = 1024 ** 4; + my $petabyte = 1024 ** 5; + my $exabyte = 1024 ** 6; + + $magSize = uc( $magSize ); + $numeric = substr( $magSize, 0, -1 ); + if ( length $magSize == 0 ) { + logTest( 'misc', "size is less than expected, value: $magSize." ); + } elsif ( $magSize =~ m/K$/ ) { + $size = $numeric * $kilobyte; + } elsif ( $magSize =~ m/M$/ ) { + $size = $numeric * $megabyte; + } elsif ( $magSize =~ m/G$/ ) { + $size = $numeric * $gigabyte; + } elsif ( $magSize =~ m/T$/ ) { + $size = $numeric * $terabyte; + } elsif ( $magSize =~ m/P$/ ) { + $size = $numeric * $petabyte; + } elsif ( $magSize =~ m/E$/ ) { + $size = $numeric * $exabyte; + } else { + logTest( 'misc', "magnitude of $magSize is unknown." ); + return -1; + } + + return $size; +} + + +#------------------------------------------------------- + +=head3 driveREST + + Description : Verify the REST interface is running. + Arguments : IP address + User + Password + Rest Object ( e.g. nodes/xcat ) + Method ( GET | PUT | POST | DELETE ) + Format + Returns : Response structure + Example : my $response = driveREST( $glob_xcatMNIp, $glob_xcatUser, + $glob_xcatUserPw, "nodes/$glob_mnNode", "GET", "json", \@restOps ); + +=cut + +#------------------------------------------------------- +sub driveREST{ + my ( $addr, $user, $pw, $obj, $method, $format, $restOps) = @_; + my $url = "https://$addr/xcatws/$obj" . "?userName=$user&password=$pw" . + "&format=$format"; + + my @updatearray; + my $fieldname; + my $fieldvalue; + my @args = (); + if ( scalar( @args ) > 0 ){ + foreach my $tempstr (@args) { + push @updatearray, $tempstr; + } + } + + my $request; + + my $ua = LWP::UserAgent->new(); + my $response; + if (( $method eq 'PUT' ) or ( $method eq 'POST' )) { + my $tempstr = encode_json \@updatearray; + $request = HTTP::Request->new( $method => $url ); + $request->header('content-type' => 'text/plain'); + $request->header( 'content-length' => length( $tempstr ) ); + $request->content( $tempstr ); + } elsif (( $method eq 'GET' ) or ( $method eq 'DELETE' )) { + $request = HTTP::Request->new( $method=> $url ); + } + + $response = $ua->request( $request ); + + #print $response->content . "\n"; + #print "code: " . $response->code . "\n"; + #print "message: " .$response->message . "\n"; + return $response; +} + + +#------------------------------------------------------- +=head3 findZhcpNode + + Description : Find the object name of the zHCP node. + Arguments : Target node whose ZHCP we want to find + Returns : zHCP node name, if found + undefined, if not found + Example : my $zhcpNode = findZhcpNode(); + +=cut + +#------------------------------------------------------- +sub findZhcpNode{ + my ( $targetNode) = @_; + my $rc = 0; + my $zhcpNode; + + # Get the HCP hostname from the node + my %targetInfo = getLsdefNodeInfo( $targetNode ); + my $hcpHostname = $targetInfo{'hcp'}; + + # Find the node that owns the zHCP hostname + my @nodes = getNodeNames(); + foreach my $node (@nodes){ + my %nodeInfo = getLsdefNodeInfo( $node ); + if ( $nodeInfo{'hostnames'} =~ $hcpHostname ) { + $zhcpNode = $node; + last; + } + } + + return $zhcpNode; +} + + +#------------------------------------------------------- + +=head3 getDiskPoolNames + + Description : Obtain the list of disk pools for a + z/VM host. + Arguments : Host Node + Returns : Array of disk pool names - No error + empty array - Error detected. + Example : my @pools = getDiskPoolNames($node); + +=cut + +#------------------------------------------------------- +sub getDiskPoolNames{ + my ( $hostNode) = @_; + my @pools; + + # Find the related zHCP node + my $zhcpNode = findZhcpNode( $hostNode ); + if ( !defined $zhcpNode ) { + return @pools; + } + + my $out = `/opt/xcat/bin/lsvm $zhcpNode --diskpoolnames | awk '{print \$NF}'`; + @pools = split /\n/, $out; + return @pools; +} + + +#------------------------------------------------------- + +=head3 getHostNodeNames + + Description : Get a list of the host nodes defined to this + xCAT MN. + Arguments : none + Returns : List of host nodes + undefined - Error detected. + Example : my @hostNodes = getHostNodeNames(); + +=cut + +#------------------------------------------------------- +sub getHostNodeNames{ + my @nodes = getNodeNames(); + my @hostNodes; + + foreach my $node (@nodes){ + my %nodeInfo = getLsdefNodeInfo( $node ); + if ( ! %nodeInfo ) { + next; + } + if (( exists $nodeInfo{'hosttype'} ) and ( $nodeInfo{'hosttype'} =~ 'zvm' )) { + push( @hostNodes, $node); + } + } + + return @hostNodes; +} + + +#------------------------------------------------------- + +=head3 getLocalIPs + + Description : Get the IP addresses from ifconfig. + Arguments : Node name or IP address + Returns : Hash of local IP addresses + Example : %localIPs = getLocalIPs(); + +=cut + +#------------------------------------------------------- +sub getLocalIPs { + my $ip; + my $junk; + my %localIPs; + my $rc = 0; + + my $out = `/sbin/ip addr | grep -e '^\\s*inet' -e '^\\s*inet6'`; + my @lines = split( '\n', $out ); + foreach my $line ( @lines ) { + my @parts = split( ' ', $line ); + ($ip) = split( '/', $parts[1], 2 ); + $localIPs{$ip} = 1; + } + +FINISH_getLocalIPs: + return %localIPs; +} + + +#------------------------------------------------------- + +=head3 getLsdefNodeInfo + + Description : Obtain node info from LSDEF. + Arguments : Name of node to retrieve + Returns : Hash of node properties. + Example : my %hash = getLsdefNodeInfo($node); + +=cut + +#------------------------------------------------------- +sub getLsdefNodeInfo{ + my ( $node) = @_; + my %hash; + + my $out = `/opt/xcat/bin/lsdef $node`; + + my @list1 = split /\n/, $out; + foreach my $item (@list1) { + if ( $item !~ "Object name:" ) { + my ($i,$j) = split(/=/, $item); + $i =~ s/^\s+|\s+$//g; # trim both ends of the string + $hash{$i} = $j; + } + } + + return %hash; +} + + +#------------------------------------------------------- + +=head3 getNodeNames + + Description : Get a list of the nodes defined to this + xCAT MN. + Arguments : none + Returns : Array of nodes + undefined - Error detected. + Example : my @nodes = getNodeNames(); + +=cut + +#------------------------------------------------------- +sub getNodeNames{ + my $out = `/opt/xcat/bin/lsdef | sed "s/ (node)//g"`; + my @nodes = split( /\n/, $out ); + return @nodes; +} + + +#------------------------------------------------------- + +=head3 getUseridFromLinux + + Description : Obtain the z/VM virtual machine userid from + /proc/sysinfo file. + Arguments : Variable to receive the output + Returns : 0 - No error + non-zero - Can't get the virtual machine id. + Example : my $rc = getUseridFromLinux( \$userid ); + +=cut + +#------------------------------------------------------- +sub getUseridFromLinux{ + my ( $userid) = @_; + my $rc = 0; + + $$userid = `cat /proc/sysinfo | grep 'VM00 Name:' | awk '{print \$NF}'`; + $$userid =~ s/^\s+|\s+$//g; # trim both ends of the string + + if ( $$userid ne '' ) { + $rc = 1; + } + + return $rc; +} + + +#------------------------------------------------------- + +=head3 getVswitchInfo + + Description : Query a vswitch and produce a hash of the data. + Arguments : zHCP node + Name of switch to be queried + Returns : hash of switch data, if found. + hash contains either: + $switchInfo{'Base'}{$property} = $value; + $switchInfo{'Authorized users'}{'User'} = $value; + $switchInfo{'Connections'}{$property} = $value; + $switchInfo{'Real device xxxx'}{$property} = $value; + Example : $rc = getVswitchInfo( $zhcpNode, $switch ); + +=cut + +#------------------------------------------------------- +sub getVswitchInfo{ + my ( $zhcpNode, $switch ) = @_; + my %switchInfo; + my @word; + my $device; + + my $out = `ssh $zhcpNode smcli Virtual_Network_Vswitch_Query -T xxxx -s $switch`; + if ( $out !~ /^Failed/ ) { + # Got some information. Process it. + my @lines = split( "\n", $out ); + pop( @lines ); + my $subsection = 'Base'; + foreach my $line ( @lines ) { + #print "line: $line\n"; + my $indent = $line =~ /\S/ ? $-[0] : length $line; # Get indentation level + $line =~ s/^\s+|\s+$//g; # trim both ends of the line; + if ( $line eq '' ) { + next; + } elsif ( $indent == 0 ) { + if ( $line =~ 'VSWITCH:' ) { + $line = substr( $line, 8 ); + $line =~ s/^\s+|\s+$//g; # trim both ends of the line; + @word = split( /:/, $line ); + $word[1] =~ s/^\s+|\s+$//g; # trim both ends of the line; + $switchInfo{'Base'}{$word[0]} = $word[1]; + } + } elsif ( $indent == 2 ) { + if ( $line =~ /Devices:/ ) { + $subsection = 'Real device'; + } elsif ( $line =~ /Authorized users:/ ) { + $subsection = 'Authorized users'; + } elsif ( $line =~ /Connections:/ ) { + $subsection = 'Connections'; + } else { + $subsection = 'Base'; + @word = split( /:/, $line ); + $switchInfo{$subsection}{$word[0]} = $word[1]; + } + } elsif ( $indent == 4 ) { + if ( $subsection eq 'Real device' ) { + @word = split( ':', $line ); + if ( $line =~ /Real device:/ ) { + $device = $word[1]; + $device =~ s/^\s+|\s+$//g; # trim both ends of the string + } else { + if ( !exists $word[1] ) { + $word[1] = ''; + } + my $key = "$subsection $device"; + $switchInfo{$key}{$word[0]} = $word[1]; + } + } elsif ( $subsection eq 'Authorized users' ) { + @word = split( ':', $line ); + if ( $word[1] eq '' ) { + next; + } + if ( exists $switchInfo{$subsection} ) { + $switchInfo{$subsection} = "$switchInfo{$subsection} $word[1]"; + } else { + $switchInfo{$subsection} = "$word[1]"; + } + } elsif ( $subsection eq 'Connections' ) { + @word = split( ' ', $line ); + if ( !exists $word[2] ) { + next; + } + $switchInfo{$subsection}{$word[2]} = $word[5]; + } + } + } + } + return %switchInfo; +} + + +#------------------------------------------------------- + +=head3 getVswitchInfoExtended + + Description : Query a vswitch and produce a hash of the data + using the extended (keyword) related API. + Arguments : zHCP node + Name of switch to be queried + Returns : hash of switch data, if found. + hash contains sections and hash/value pairs: + $switchInfo{'Base'}{$property} = $value; + $switchInfo{'Real device'}{$device}{$property} = $value; + $switchInfo{'Authorized users'}{$authUser}{$property} = $value; + $switchInfo{'Connections'}{$adapter_owner}{$property} = $value; + Example : $rc = getVswitchInfoExtended( $zhcpNode, $switch ); + +=cut + +#------------------------------------------------------- +sub getVswitchInfoExtended{ + my ( $zhcpNode, $switch ) = @_; + my %switchInfo; + my @word; + my $device; + my $authUser; + + my $out = `ssh $zhcpNode smcli Virtual_Network_Vswitch_Query_Extended -T xxxx -k switch_name=$switch`; + if ( $out !~ /^Failed/ ) { + # Got some information. Process it. + my @lines = split( "\n", $out ); + pop( @lines ); + my $subsection = 'Base'; + my $authPort; + my $adapter_owner; + + foreach my $line ( @lines ) { + #print "line: $line\n"; + my $indent = $line =~ /\S/ ? $-[0] : length $line; # Get indentation level + $line =~ s/^\s+|\s+$//g; # trim both ends of the line; + if ( $line eq '' ) { + next; + } + + $line =~ s/^\s+|\s+$//g; # trim both ends of the line; + @word = split( /:/, $line ); + $word[1] =~ s/^\s+|\s+$//g; # trim both ends of the line; + if ( !exists $word[1] ) { + $word[1] = ''; + } + + if ( $word[0] eq 'switch_name' ) { + $switchInfo{'Base'}{$word[0]} = $word[1]; + $subsection = 'Base'; + next; + } elsif ( $word[0] eq 'real_device_address' ) { + $subsection = 'Real device'; + $device = $word[1]; + if ( !exists $switchInfo{$subsection}{'RDEVs'} ) { + $switchInfo{$subsection}{'RDEVs'} = $device; + } else { + $switchInfo{$subsection}{'RDEVs'} = $switchInfo{$subsection}{'RDEVs'} . ' ' . $device; + } + next; + } elsif ( $word[0] eq 'port_num' ) { + $subsection = 'Authorized users'; + $authPort = $word[1]; + next; + } elsif ( $word[0] eq 'adapter_owner' ) { + $subsection = 'Connections'; + $adapter_owner = $word[1]; + if ( !exists $switchInfo{$subsection}{'ConnectedUsers'} ) { + $switchInfo{$subsection}{'ConnectedUsers'} = $adapter_owner; + } else { + $switchInfo{$subsection}{'ConnectedUsers'} = $switchInfo{$subsection}{'ConnectedUsers'} . ' ' . $adapter_owner; + } + next; + } + + # Fill in hash based upon the subsection we are handling. + my $key; + if ( $subsection eq 'Base' ) { + $switchInfo{$subsection}{$word[0]} = $word[1]; + } elsif ( $subsection eq 'Real device' ) { + $switchInfo{$subsection}{$device}{$word[0]} = $word[1]; + } elsif ( $subsection eq 'Authorized users' ) { + if ( $word[0] eq 'grant_userid' ) { + $authUser = $word[1]; + $switchInfo{$subsection}{$authUser}{'port_num'} = $authPort; + if ( !exists $switchInfo{$subsection}{'AuthorizedUsers'} ) { + $switchInfo{$subsection}{'AuthorizedUsers'} = $authUser; + } else { + $switchInfo{$subsection}{'AuthorizedUsers'} = $switchInfo{$subsection}{'AuthorizedUsers'} . ' ' . $authUser; + } + } else { + $switchInfo{$subsection}{$authUser}{$word[0]} = $word[1]; + } + } elsif ( $subsection eq 'Connections' ) { + $switchInfo{$subsection}{$adapter_owner}{$word[0]} = $word[1]; + } + } + } + return %switchInfo; +} + + +#------------------------------------------------------- + +=head3 hexDecode + + Description : Convert a string of printable hex + characters (4 hex characters per actual + character) into the actual string that + it represents. + Arguments : printable hex value + Returns : Perl string + Example : $rc = hexDecode(); + +=cut + +#------------------------------------------------------- +sub hexDecode { + my ( $hexVal ) = @_; + my $result = ''; + + if ( $hexVal =~ /^HexEncoded:/ ) { + ($hexVal) = $hexVal =~ m/HexEncoded:(.*)/; + my @hexes = unpack( "(a4)*", $hexVal); + for ( my $i = 0; $i < scalar(@hexes); $i++ ) { + $result .= chr( hex( $hexes[$i] ) ); + } + } else { + $result = $hexVal; + } + + return $result; +} + + +#------------------------------------------------------- + +=head3 logTest + + Description : Log the start and result of a test. + Failures are added to syslog and printed as script output. + Arguments : Status of the test: + bypassed: Bypassed a test (STDOUT, optionally SYSLOGged) + failed: Failed test (STDOUT, optionally SYSLOGged) + passed: Successful test (STDOUT only) + started: Start test (STDOUT only, increments test number) + misc: Miscellaneous output (STDOUT only) + miscNF: Miscellaneous non-formatted output (STDOUT only) + info: Information output similar to miscellaneous output + but has a message number (STDOUT only) + Message ID (used for "bypassed", "failed", or "warning" messages) or + message TEXT (used for "misc", "passed", or "started" messages). + Message id should begin with the initials of the subroutine + generating the message and begin at 1. For example, the + first error message from verifyNode subroutine would be 'VN01'. + Message substitution values (used for "bypassed", "failed", or + "warning" messages) + Returns : None + Example : logTest( 'failed', "VMNI01", $name, $tgtIp ); + logTest( 'started', "xCAT MN has a virtual storage size of at least $vstorMin." ); + +=cut + +#------------------------------------------------------- +sub logTest{ + my ( $testStatus, $msgInfo, @msgSubs ) = @_; + my $extraInfo; + my $rc; + my $sev; + my $msg; + + if ( $testStatus eq 'misc' ) { + # Miscellaneous output + $msgSubs[0] = "$msgInfo\n"; + ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'GENERIC_RESPONSE', \@msgSubs ); + print( "$msg\n" ); + } elsif ( $testStatus eq 'miscNF' ) { + # Miscellaneous output + print("$msgInfo\n"); + } elsif ( $testStatus eq 'passed' ) { + # Test was successful. Log it as ok. + if ( $msgInfo ne '' ) { + $msgSubs[0] = "$msgInfo\n"; + ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'GENERIC_RESPONSE', \@msgSubs ); + print( "$msg\n" ); + } + } elsif ( $testStatus eq 'started' ) { + # Start test + $glob_testNum++; + print( "\n" ); + $msgSubs[0] = "Test $glob_testNum: Verifying $msgInfo"; + ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', 'GENERIC_RESPONSE', \@msgSubs ); + print( "$msg\n" ); + } else { + ( $rc, $sev, $msg, $extraInfo ) = xCAT::zvmMsgs->buildMsg('ZXCATIVP', $msgInfo, \@msgSubs ); + + # Determine whether we need to ignore the message or produce it and count it. + if ( defined $glob_msgsToIgnore{$msgInfo} ) { + # Ignore this message id + $glob_ignored{$msgInfo} = 1; + $glob_ignoreCnt += 1; + print( "Message $msgInfo is being ignored but would have occurred here.\n" ); + } elsif ( defined $glob_msgsToIgnore{$sev} ) { + # Ignoring all messages of this severity. + $glob_ignored{$msgInfo} = 1; + $glob_ignoreCnt += 1; + print( "Message $msgInfo is being ignored but would have occurred here.\n" ); + } else { + # Handle the failed, warning and bypassed messages + if ( $testStatus eq 'failed' ) { + # Test failed. Log it as failure and produce necessary messages + $glob_totalFailed += 1; + if ( $glob_totalFailed == 1 || $glob_failedTests[-1] != $glob_testNum ) { + push( @glob_failedTests, $glob_testNum ); + } + print( "$msg" ); + if ( $extraInfo ne '' ) { + print( "$extraInfo" ); + } + } elsif ( $testStatus eq 'warning' ) { + # Warning unrelated to a test. + print("$msg"); + if ( $extraInfo ne '' ) { + print( "$extraInfo" ); + } + } elsif ( $testStatus eq 'info' ) { + # Information message + print("$msg"); + if ( $extraInfo ne '' ) { + print( "$extraInfo" ); + } + } elsif ( $testStatus eq 'bypassed' ) { + # Bypass message + if ( $glob_bypassMsg != 0 ) { + print("$msg"); + if ( $extraInfo ne '' ) { + print( "$extraInfo" ); + } + } + } + + # Write the message to syslog + if ( $testStatus ne 'info' ) { + my $logMsg = $msg; + $logMsg =~ s/\t//g; + $logMsg =~ s/\n/ /g; + syslog( 'err', $logMsg ); + } + } + } +} + + +#------------------------------------------------------- + +=head3 setOverrides + + Description : Set global variables based on input from + an external driver perl script, the + command line or the zxcatIVP_moreCmdOps + environment variable. This allows + the script to be run standalone or overriden + by a driver perl script. + Arguments : None + Returns : None. + Example : setOverrides(); + +=cut + +#------------------------------------------------------- +sub setOverrides{ + my $rc; + my $unrecognizedOps = ''; + my $val; + + # Read the environment variables. + foreach my $opHash ( @cmdOps ) { + my $inpRef = eval('\$' . $opHash->{'inpVar'}); + + # Update the local input variable with the value from the environment + # variable or set the default. + if ( defined $ENV{ $opHash->{'envVar'} } ) { + $$inpRef = $ENV{ $opHash->{'envVar'} }; + } else { + if ( exists $opHash->{'default'} ) { + $$inpRef = $opHash->{'default'}; + } else { + next; + } + } + } + + # Apply the environent variables as overrides to the global variables in this script. + applyOverrides(); + + # Clear the input variables so that we can use them for command line operands. + foreach my $opHash ( @cmdOps ) { + my $inpRef = eval('\$' . $opHash->{'inpVar'}); + $$inpRef = undef; + } + + # Handle options from the command line. + $Getopt::Long::ignorecase = 0; + Getopt::Long::Configure( "bundling" ); + if ( !GetOptions( + 'bypassMsg=s' => \$cmdOp_bypassMsg, + 'cNAddress=s' => \$cmdOp_cNAddress, + 'defaultUserProfile=s' => \$cmdOp_defaultUserProfile, + 'diskpools=s' => \$cmdOp_diskpools, + 'expectedReposSpace=s' => \$cmdOp_expectedReposSpace, + 'expUser=s' => \$cmdOp_expUser, + 'h|help' => \$glob_displayHelp, + 'hostNode=s' => \$cmdOp_hostNode, + 'ignore=s' => \$cmdOp_ignore, + 'instFCPList=s' => \$cmdOp_instFCPList, + 'macPrefix=s' => \$cmdOp_macPrefix, + 'mgtNetmask=s' => \$cmdOp_mgtNetmask, + 'mnNode=s' => \$cmdOp_mnNode, + 'moreCmdOps=s' => \$glob_moreCmdOps, + 'networks=s' => \$cmdOp_networks, + 'pw_obfuscated' => \$cmdOp_pw_obfuscated, + 'signalTimeout=s' => \$cmdOp_signalTimeout, + 'syslogErrors' => \$cmdOp_syslogErrors, + 'vswitchOSAs=s' => \$cmdOp_vswitchOSAs, + 'xcatDiskSpace=s' => \$cmdOp_xcatDiskSpace, + 'xcatMgtIp=s' => \$cmdOp_xcatMgtIp, + 'xcatMNIp=s' => \$cmdOp_xcatMNIp, + 'xcatUser=s' => \$cmdOp_xcatUser, + 'zhcpDiskSpace=s' => \$cmdOp_zhcpDiskSpace, + 'zhcpFCPList=s' => \$cmdOp_zhcpFCPList, + 'zhcpNode=s' => \$cmdOp_zhcpNode, + )) { + print $usage_string; + } + + # Handle options passed using the environment variable. + # This will override the same value that was passed in the command line. + # Don't specify the same option on both the command line and in the environment variable. + if ( defined $glob_moreCmdOps ) { + $glob_moreCmdOps =~ hexDecode( $glob_moreCmdOps ); + ($rc, $unrecognizedOps) = GetOptionsFromString( + $glob_moreCmdOps, + 'bypassMsg=s' => \$cmdOp_bypassMsg, + 'cNAddress=s' => \$cmdOp_cNAddress, + 'defaultUserProfile=s' => \$cmdOp_defaultUserProfile, + 'diskpools=s' => \$cmdOp_diskpools, + 'expectedReposSpace=s' => \$cmdOp_expectedReposSpace, + 'expUser=s' => \$cmdOp_expUser, + 'h|help' => \$glob_displayHelp, + 'hostNode=s' => \$cmdOp_hostNode, + 'ignore=s' => \$cmdOp_ignore, + 'instFCPList=s' => \$cmdOp_instFCPList, + 'macPrefix=s' => \$cmdOp_macPrefix, + 'mgtNetmask=s' => \$cmdOp_mgtNetmask, + 'mnNode=s' => \$cmdOp_mnNode, + 'networks=s' => \$cmdOp_networks, + 'pw_obfuscated' => \$cmdOp_pw_obfuscated, + 'signalTimeout=s' => \$cmdOp_signalTimeout, + 'syslogErrors' => \$cmdOp_syslogErrors, + 'vswitchOSAs=s' => \$cmdOp_vswitchOSAs, + 'xcatDiskSpace=s' => \$cmdOp_xcatDiskSpace, + 'xcatMgtIp=s' => \$cmdOp_xcatMgtIp, + 'xcatMNIp=s' => \$cmdOp_xcatMNIp, + 'xcatUser=s' => \$cmdOp_xcatUser, + 'zhcpDiskSpace=s' => \$cmdOp_zhcpDiskSpace, + 'zhcpFCPList=s' => \$cmdOp_zhcpFCPList, + 'zhcpNode=s' => \$cmdOp_zhcpNode, + ); + if ( $rc == 0 ) { + print $usage_string; + } + } + + # Apply the command line operands as overrides to the global variables in this script. + applyOverrides(); + + # Special handling for the deobfuscation of the user pw. + if ( defined $glob_xcatUserPw and $glob_xcatUserPw ne '' and $obfuscatePw ) { + # Unobfuscate the password so that we can use it. + $glob_xcatUserPw = decode_base64($val); + } + + # Special processing for ignore messages to convert general severity type + # operands to their numeric value. + if ( $glob_msgsToIgnore{'BYPASS'} ) { + delete $glob_msgsToIgnore{'BYPASS'}; + $glob_msgsToIgnore{'2'} = 1; + } + if ( $glob_msgsToIgnore{'INFO'} ) { + delete $glob_msgsToIgnore{'INFO'}; + $glob_msgsToIgnore{'3'} = 1; + } + if ( $glob_msgsToIgnore{'WARNING'} ) { + delete $glob_msgsToIgnore{'WARNING'}; + $glob_msgsToIgnore{'4'} = 1; + } + if ( $glob_msgsToIgnore{'ERROR'} ) { + delete $glob_msgsToIgnore{'ERROR'}; + $glob_msgsToIgnore{'5'} = 1; + } + +FINISH_setOverrides: + return; +} + + +#------------------------------------------------------- + +=head3 showHelp + + Description : Show the help inforamtion. + Arguments : None. + Returns : None. + Example : showHelp(); + +=cut + +#------------------------------------------------------- +sub showHelp{ + my ($rc, $sev, $extraInfo, @array); + my $msg; + + print "$0 run tests to verify the xCAT installation.\n\n"; + print $usage_string; + print "The following environment variables (indicated by env:) ". + "and command line\noperands (indicated by cmd:) are supported.\n\n"; + foreach my $opHash ( @cmdOps ) { + if ( exists $opHash->{'desc'} ) { + if ( exists $opHash->{'envVar'} ) { + print "env: $opHash->{'envVar'}\n"; + } + if ( exists $opHash->{'opName'} ) { + print "cmd: --$opHash->{'opName'} \n"; + } + if ( exists $opHash->{'separator'} ) { + print "List separator: '$opHash->{'separator'}'\n" + } + if ( exists $opHash->{'default'} ) { + print "Default: $opHash->{'default'}\n"; + } + print wrap( '', '', "Value: $opHash->{'desc'}" ). "\n"; + print "\n"; + } + } + print ( + "Usage notes:\n" . + "1. An input value can be specified in one of three ways, either as: \n" . + " * an environment variable, \n" . + " * an operand on the command line using the -- \n" . + " operand, or \n" . + " * a --moreCmdOps operand. \n" . + " The input value in the -- operand overrides the value \n" . + " specifed by the environment variable. The --moreCmdOps operand \n" . + " overrides both the -- operand and the environment \n" . + " variable. \n" . + "2. The value for an operand that has a list value may have the \n" . + " members of the list separated by one of the indicated \n" . + " 'List separator' operands. The same separator should be used to \n" . + " separator all members of the list. For example, do NOT separate \n" . + " the first and second element by a comma and separate the second . \n" . + " and third element by a semi-colon. \n" . + "3. If blank is an allowed list separator for the operand and is used \n" . + " then the list should be enclosed in quotes or double quotes so that\n" . + " the list is interpretted as a value associated with the operand. \n" . + " You may need to escape the quotes depending on how you are invoking\n" . + " the command. For this reason, it is often better to choose a \n" . + " separator other than blank. \n" + ); + + return; +} + + +#------------------------------------------------------- + +=head3 showPoolInfo + + Description : Show available space for each disk in the pool + Arguments : Disk pool name + Array of disk information for the pool + Returns : None. + Example : showPoolInfo($node, $args); + +=cut + +#------------------------------------------------------- +sub showPoolInfo{ + my ( $diskPool, $diskLines ) = @_; + my $lines; + + $lines = "$diskPool contains the following disks that have space available:"; + foreach my $disk ( @$diskLines ) { + my @diskInfo = split( / /, $disk ); + my $size = convertDiskSize( $diskInfo[2], $diskInfo[4] ); + $lines = $lines . "\nvolid: $diskInfo[1], type: $diskInfo[2], available: $size"; + } + logTest( 'misc', $lines ); +} + + +#------------------------------------------------------- + +=head3 verifyCMAProperties + + Description : Verify key CMA properties are specified + and set the global ROLE property for + user by other functions. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = verifyCMAProperties(); + +=cut + +#------------------------------------------------------- +sub verifyCMAProperties{ + logTest( "started", "some key CMA properties"); + + my $out = `cat $glob_versionFileCMA`; + chomp( $out ); + if ( $out ne '' ) { + $glob_versionInfo = "$out"; + # Determine CMA role: Controller or Compute + my $delim = "="; + open( FILE, $glob_applSystemRole ); + while ( ) { + my $line = $_; + if ( $line =~ "^role$delim" or $line =~ "^role $delim" ) { + my @array = split( /$delim/, $line, 2 ); + $array[1] =~ s/^\s+|\s+$//g; # trim both ends of the string + $glob_CMARole = uc( $array[1] ); + } + } + close(FILE); + if ( ' CONTROLLER COMPUTE COMPUTE_MN MN ' !~ / $glob_CMARole / ) { + logTest( "failed", "VCMAP01", $glob_applSystemRole ); + $glob_CMARole = ''; + } + } else { + $glob_versionInfo = "CMO Appliance version unknown"; + logTest( "failed", "VCMAP02", $glob_versionFileCMA ); + } +} + + +#------------------------------------------------------- + +=head3 verifyComputeNodeConnectivity + + Description : Verify the xCAT MN can SSH to the + Compute Node. + Arguments : Node address (IP or hostname) of the + Compute Node. + User underwhich remote exports will be performed. + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyComputeNodeConnectivity( $nodeAddress ); + +=cut + +#------------------------------------------------------- +sub verifyComputeNodeConnectivity{ + my ( $nodeAddress, $user ) = @_; + + logTest( 'started', "xCAT MN can ssh to $nodeAddress with user $user." ); + my $out = `ssh -o "NumberOfPasswordPrompts 0" $user\@$nodeAddress pwd`; + my $rc = $? >> 8; + if ( $rc != 0 ) { + logTest( 'failed', "VCNC01", $nodeAddress, $user ); + return 0; # Non-critical error detected + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyDirectorySpace + + Description : Verify disk directory space is sufficient. + Arguments : Array of directories containing: + directory name, + maximum percentage in use, + maximum file size (or empty if we should not check) + Printable node name or ZHCP host name (if remote = 1) + Remote processing flag (1 - use SSH to contact the node) + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : $rc = verifyDirectorySpace( \@dirInfo, $zhcpIP ); + +=cut + +#------------------------------------------------------- +sub verifyDirectorySpace{ + my ( $dirInfoRef, $system, $remote ) = @_; + my @dirInfo = @$dirInfoRef; + my $minAvailableSpace = '100M'; + my $largeFileSize = '30000k'; + my $out; + my $rc; + my @sizes; + + # If system is the ZHCP running on this xCAT MN's system then bypass the test + # because we would have already tested the directories when we ran the tests + # for the xCAT MN. + if ( $system eq $glob_localZHCP ) { + logTest( 'bypassed', "BPVDS01" ); + goto FINISH_verifyDirectorySpace; + } + + foreach my $line ( @dirInfo ) { + chomp( $line ); + $line =~ s/^\s+|\s+$//g; # trim both ends of the string + + my @info = split( ' ', $line ); + if ( ! defined $info[0] or $info[0] eq '' or ! defined $info[1] or $info[1] eq '' ) { + # Empty array item and/or maximum percentage is missing. + next; + } + if ( defined $info[2] and $info[2] ne '' and $info[2] ne '.' ) { + $minAvailableSpace = $info[2]; + } + if ( defined $info[3] and $info[3] ne '' and $info[3] ne '.' ) { + $largeFileSize = $info[3]; + } + + # Special case for old ZHCP servers with a memory backed / directory. + if ( $remote and $info[0] eq '/' ) { + $out = `ssh $system ls /persistent 1>/dev/null 2>/dev/null`; + $rc = $? >> 8; + if ( $rc == 255 ) { + logTest( 'failed', "STN01", $system ); + next; + } elsif ( $rc == 0 ) { + # Validate /persistent directory instead of / + $info[0] = '/persistent'; + } + } + + logTest( 'started', "the file system related to $info[0] on the $system system has sufficient space available." ); + my $sizeTestFailed = 0; + if( $remote ) { + $out = `ssh $system df -h $info[0] | sed '1d' | sed 'N;s/\\n/ /' | awk '{print \$4,\$5}'`; + $rc = $? >> 8; + if ( $rc == 255 ) { + logTest( 'failed', "STN01", $system ); + next; + } + } else { + $out = `df -h $info[0] | sed '1d' | sed 'N;s/\\n/ /' | awk '{print \$4,\$5}'`; + } + chomp( $out ); + if ( $out ) { + @sizes = split( ' ', $out, 2 ); + # Percentage In Use test + $sizes[1] =~ s/\%+$//g; # trim percent from end of the string + if ( $info[1] ne '.' and $sizes[1] > $info[1] ) { + logTest( 'failed', "VDS01", $info[0], $system, $sizes[1], $info[1] ); + $sizeTestFailed = 1; + } + # Minimum Available Size test + if ( $info[2] ne '.' and convertSize( $sizes[0] ) < convertSize( $minAvailableSpace ) ) { + logTest( 'failed', "VDS02", $info[0], $system, $sizes[0], $minAvailableSpace ); + $sizeTestFailed = 1; + } + + if ( $sizeTestFailed == 0 ) { + logTest( 'misc', "The file system related to $info[0] on the $system system is $sizes[1] percent in use with $sizes[0] available." ); + } + } else { + logTest( 'failed', "VDS03", $system, $info[0] ); + $sizeTestFailed = 1; + return 0; + } + + if ( $info[3] ne '.' and $sizeTestFailed == 1 ) { + # Show any large files in the directory space. + logTest( 'started', "the file system related to $info[0] directory on $system system has reasonable size files." ); + if( $remote ) { + $out = `ssh $system find $info[0] -mount -type f -size +$largeFileSize 2>/dev/null -exec ls -lh {} \\; | grep -v -e .so. -e .so -e .jar | awk \'{ print \$9 \": \" \$5 }\'`; + $rc = $? >> 8; + if ( $rc == 255 ) { + logTest( 'failed', "STN01", $system ); + next; + } + } else { + $out = `find $info[0] -mount -type f -size +$largeFileSize 2>/dev/null -exec ls -lh {} \\; | grep -v -e .so. -e .so -e .jar | awk \'{ print \$9 \": \" \$5 }\'`; + } + if ( $out ne '' ) { + $out =~ s/\n+$//g; # remove last newline + logTest( 'failed', "VDS04", $largeFileSize, $out ); + } else { + logTest( 'passed', "" ); + } + } + } + + if ( $system eq 'xCAT MN' and $glob_expectedReposSpace ne '' ) { + if ( convertSize( $sizes[0] ) < convertSize( $glob_expectedReposSpace ) ) { + logTest( 'failed', "VDS05", $sizes[0], $glob_expectedReposSpace ); + } else { + logTest( 'passed', "" ); + } + } + +FINISH_verifyDirectorySpace: + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyDiskPools + + Description : Verify disk pools are defined and have + at least a minimum amount of space. + Arguments : Array of expected disk pools. + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : $rc = verifyDiskPools( $hostNode, $diskpools ); + + lsvm zhcp --diskpoolnames + lsvm zhcp --diskpool pool1 free + lsvm zhcp --diskpool pool1 used + +=cut + +#------------------------------------------------------- +sub verifyDiskPools{ + my ( $hostNode, $diskPools ) = @_; + my $out; + my $zhcpNode; + + if ( exists $glob_hostNodes{$hostNode}{'zhcp'} ) { + $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'}; + } else { + return 0; + } + + logTest( 'started', "disk pools for host: $hostNode." ); + + $out = `/opt/xcat/bin/lsvm $zhcpNode --diskpoolnames | awk '{print \$NF}'`; + my @definedPools = split /\n/, $out; + + # Warn if no disk pools are defined. + if ( @definedPools == 0 ) { + logTest( 'failed', "VDP03", $hostNode ); + return 0; + } + + logTest( 'misc', "$hostNode has the following disk pools defined: " . join(', ', @$diskPools) . "." ); + + foreach my $diskPool ( @$diskPools ) { + $diskPool = uc( $diskPool ); + + # Verify pool is in the list of pools + if ( grep { $_ eq $diskPool } @definedPools ) { + } else { + logTest( 'failed', "VDP01", $diskPool ); + next; + } + + # Warn if we have very little disk space available + $out = `/opt/xcat/bin/lsvm $zhcpNode --diskpool $diskPool free | grep $zhcpNode | sed '1d'`; + my @disks = split /\n/, $out; + my $numberOfDisks = @disks; + if ( $numberOfDisks == 0 ) { + if (( $diskPool ne 'XCAT' ) and ( $diskPool ne 'XCAT1' )) { + logTest( 'failed', "VDP02", $diskPool ); + } + } else { + showPoolInfo( $diskPool, \@disks ); + } + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyHost + + Description : Verify the Host node is defined properly. + Arguments : Host node + ZHCP node + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyHost( $node ); + +=cut + +#------------------------------------------------------- +sub verifyHost{ + my ( $node) = @_; + my %hostInfo = getLsdefNodeInfo( $node ); + + # Verify node is defined + logTest( 'started', "that the host node ($node) is defined in xCAT." ); + my $count = keys %hostInfo; + if ( $count == 0 ) { + logTest( 'failed', "VHN01", $node ); + return 1; # Critical error detected. IVP should exit. + } + + # Verify the 'hcp' is defined for the node + logTest( 'started', "a zHCP is associated with the host node ($node)." ); + if ( $hostInfo{'hcp'} eq '' ) { + logTest( 'failed', "VHN02" ); + return 1; # Critical error detected. IVP should exit. + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyHostNode + + Description : Verify the Host node is defined properly. + Arguments : Host node + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyHostNode( $node ); + +=cut + +#------------------------------------------------------- +sub verifyHostNode{ + my ( $node ) = @_; + my %hostInfo = getLsdefNodeInfo( $node ); + + # Verify node is defined + logTest( 'started', "that the host node ($node) is defined in xCAT." ); + my $count = keys %hostInfo; + if ( $count == 0 ) { + logTest( 'failed', "VHN01", $node ); + return 1; # Critical error detected. IVP should exit. + } + + # Verify the 'hcp' is defined for the node + logTest( 'started', "a zHCP is associated with the host node ($node)." ); + if ( $hostInfo{'hcp'} eq '' ) { + logTest( 'failed', "VHN02" ); + return 1; # Critical error detected. IVP should exit. + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyMACUserPrefix + + Description : Verify that the specified MACADDR + user prefix matches the one on the host. + Arguments : Host node + MACADDR user prefix + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : verifyMACUserPrefix( $hostNode, $userPrefix ); + +=cut + +#------------------------------------------------------- +sub verifyMACUserPrefix{ + my ( $hostNode, $userPrefix ) = @_; + $userPrefix = uc( $userPrefix ); + logTest( 'started', "the z/VM system's MACID user prefix matches the one specified in the OpenStack configuration file." ); + + my %nodeInfo = getLsdefNodeInfo($hostNode); + + my $hostUserPrefix = `ssh $nodeInfo{'hcp'} vmcp QUERY VMLAN | sed '1,/VMLAN MAC address assignment:/d' | grep ' MACADDR Prefix:' | awk '{print \$6}'`; + chomp( $hostUserPrefix ); + + if ( $userPrefix ne $hostUserPrefix ) { + logTest( 'failed', "VMUP01", $hostNode, $hostUserPrefix, $userPrefix ); + } else { + logTest( 'passed', "" ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyMemorySize + + Description : Verify the virtual machine has + sufficient memory. + Arguments : None + Returns : 0 - No error + non-zero - Error detected. + Example : $rc = verifyMemorySize($node, $args); + +=cut + +#------------------------------------------------------- +sub verifyMemorySize{ + my $vstorMin = '8G'; + my ( $out, $tag, $storSize ); + + # Verify the virtual machine has the recommended virtual storage size. + logTest( 'started', "xCAT MN has a virtual storage size of at least $vstorMin." ); + + $out = `vmcp query virtual storage | grep STORAGE`; + if ( $out eq '' ) { + logTest( 'failed', "VMS01" ); + return 0; + } + + ($tag, $storSize) = split(/=/, $out, 2); + $storSize =~ s/^\s+|\s+$//g; # trim both ends of the string + my $convStorSize = convertSize( $storSize ); + my $convVStorMin = convertSize( $vstorMin ); + + if ( $convStorSize < $convVStorMin ) { + logTest( 'failed', "VMS02", $storSize, $vstorMin ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyMiscHostStuff + + Description : Verify miscellaneous items related to + the host. + Arguments : Host node + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyMiscHostStuff( $node ); + +=cut + +#------------------------------------------------------- +sub verifyMiscHostStuff{ + my ( $hostNode) = @_; + my $out; + my $rc; + my $zhcpNode; + + if ( exists $glob_hostNodes{$hostNode}{'zhcp'} ) { + $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'}; + } else { + return 0; + } + + # Verify the signal shutdown time is not too small + logTest( 'started', "the signal shutdown timeout on $hostNode is more than $glob_signalTimeout." ); + my $timeVal; + $out = `ssh $zhcpNode vmcp query signal shutdowntime`; + $rc = $? >> 8; + if ( $rc == 255 ) { + logTest( 'failed', 'STN01', $zhcpNode ); + } elsif ( $out !~ "System default shutdown signal timeout:" ) { + logTest( 'failed', 'VMHS01', $hostNode, $rc, $out ); + } else { + ($timeVal) = $out =~ m/System default shutdown signal timeout: (.*) seconds/; + if ( $timeVal < $glob_signalTimeout ) { + logTest( 'failed', 'VMHS02', $hostNode, $timeVal, $glob_signalTimeout ); + } + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyMnIp + + Description : Verify the xCAT MN Ip is the same one used + by this xCAT MN. + Arguments : xCAT MN IP address or host name + hostName flag, 1 - can be a hostname, 0 - must be an IP address + descriptive name string + subnet mask (optional) + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyMnIp( $ip, $possibleHostname, $name, $subnetMask ); + +=cut + +#------------------------------------------------------- +sub verifyMnIp{ + my ( $ip, $possibleHostname, $name, $subnetMask ) = @_; + my ( $out, $rest ); + my $tgtIp = $ip; + my $localRP; + my $addrType = 4; + + # Verify the IP address or hostname is defined for this machine + logTest( 'started', "xCAT MN has an interface for the $name defined as $ip." ); + + if ( $possibleHostname ) { + # Assume the input is a hostname, obtain the IP address associated with that name. + # Look for the name in /etc/hosts + $out = `grep " $ip " < /etc/hosts`; + if ( $out ne '' ) { + ($tgtIp, $rest) = split(/\s/, $out, 2); + } + } + + if ( $tgtIp =~ /:/ ) { + $addrType = 6; + } + + # Verify the IP address is defined + $out=`ip addr show to $tgtIp`; + if ( $out eq '' ) { + logTest( 'failed', "VMNI01", $name, $tgtIp ); + return 0; # Non-critical error detected + } else { + my $inetString; + if ( $addrType == 4 ) { + $inetString = " inet"; + } else { + $inetString = " inet$addrType"; + } + + my @lines= split( /\n/, $out ); + @lines= grep( /$inetString/, @lines ); + if ( @lines == 0 ) { + logTest( 'failed', "VMNI02", $name, $tgtIp ); + return 0; # Non-critical error detected + } + my @ipInfo = split( ' ', $lines[0] ); # split, ignoring leading spaces + my @parts = split( '/', $ipInfo[1] ); + $localRP = $parts[1]; + } + + # Verify the subnet mask matches what is set on the system + if ( defined $subnetMask ) { + logTest( 'started', "xCAT MN's subnet mask is $subnetMask." ); + my $rp = calculateRoutingPrefix( $subnetMask ); + if ( $rp == -1 ) { + logTest( 'failed', "VMNI03", $subnetMask ); + } + elsif ( $rp != $localRP ) { + logTest( 'failed', "VMNI04", $tgtIp, $localRP, $rp, $subnetMask, $name ); + } + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyMnNode + + Description : Verify the xCAT MN node is defined properly. + Arguments : xCAT MN node + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyMnNode( $node ); + +=cut + +#------------------------------------------------------- +sub verifyMnNode{ + my ( $node) = @_; + my %mnInfo = getLsdefNodeInfo( $node ); + + # Verify node is defined + logTest( 'started', "xCAT MN node ($node) is defined in xCAT." ); + my $count = keys %mnInfo; + if ( $count == 0 ) { + logTest( 'failed', "VMN01", $node ); + return 0; # Non-critical error detected + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyNodeExists + + Description : Verify that a named node exists in xCAT. + Arguments : Node name + Function of the node (e.g. "zHCP node") + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : verifyNodeExists($node, $function); + +=cut + +#------------------------------------------------------- +sub verifyNodeExists{ + my ( $node, $function ) = @_; + + logTest( 'started', "$function is defined and named $node." ); + my %hash = getLsdefNodeInfo($node); + if ( %hash ) { + logTest( 'passed', "" ); + } else { + logTest( 'failed', "VNE01", $node ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyNetworks + + Description : Verify the specified networks are defined + and have the expected VLAN settings. + Arguments : Host node name + Reference to array of networks to be verified + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyNetworks($hostNode, $network); + + lsvm zhcp --getnetworknames + lsvm zhcp --getnetwork xcatvsw2 + +=cut + +#------------------------------------------------------- +sub verifyNetworks{ + my ( $hostNode, $networks ) = @_; + my $out; + + my $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'}; + + foreach my $network ( @$networks ) { + $network = uc( $network ); + + # Split off any VLAN information from the input + my $match; + my @vlans = split( /:/, $network ); + if ( exists $vlans[1] ) { + $network = $vlans[0]; # Remove vlan info from the $network variable + $match = "VLAN Aware"; + } else { + $match = "VLAN Unaware"; + } + logTest( 'started', "$network is defined as a network to $hostNode." ); + + # Obtain the network info + my %switchInfo = getVswitchInfo( $zhcpNode, $network ); + if ( !%switchInfo ) { + logTest( 'failed', "VN01", $network ); + next; # Non-critical error detected, iterate to the next switch + } + + # Verify that the defined network matches the expectations. + logTest( 'started', "$network is $match" ); + if (( $match eq "VLAN Aware" && $switchInfo{'Base'}{'VLAN ID'} != 0 ) || + ( $match eq "VLAN Unaware" && $switchInfo{'Base'}{'VLAN ID'} == 0 )) { + logTest( 'passed', "" ); + } else { + logTest( 'failed', "VN02", $network, $match ); + } + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyProfile + + Description : Verify profile is defined in the z/VM directory. + Arguments : Host node name + Profile to be verified + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : $rc = verifyProfile($hostNode, $profile); + +=cut + +#------------------------------------------------------- +sub verifyProfile{ + my ( $hostNode, $profile ) = @_; + $profile = uc( $profile ); + + logTest( 'started', "$hostNode has the profile ($profile) in the z/VM directory." ); + my $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'}; + + my $out = `/opt/xcat/bin/chhypervisor $hostNode --smcli 'Image_Query_DM -T $profile'`; + if ( $out !~ "PROFILE $profile" ) { + logTest( 'failed', "VP01", $profile ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyREST + + Description : Verify the REST interface is running. + Arguments : None + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyREST(); + +=cut + +#------------------------------------------------------- +sub verifyREST{ + logTest( 'started', "REST API is accepting requests from user $glob_xcatUser." ); + my @restOps = (); + my $response = driveREST( $glob_xcatMNIp, $glob_xcatUser, $glob_xcatUserPw, "nodes/$glob_mnNode", "GET", "json", \@restOps ); + #print "Content: " . $response->content . "\n"; + #print "Code: " . $response->code . "\n"; + #print "Message: " .$response->message . "\n"; + + if ( $response->message ne "OK" or $response->code != 200 ) { + logTest( 'failed', "VR01", $response->code, $response->message, $response->content ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyUser + + Description : Verify a user is authorized for xCAT MN. + Arguments : User + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : $rc = verifyUser( $user ); + +=cut + +#------------------------------------------------------- +sub verifyUser{ + my ( $user ) = @_; + my $out; + + logTest( 'started', "user ($user) is in the xCAT policy table." ); + $out = `/opt/xcat/bin/gettab name=\'$user\' policy.rule`; + $out =~ s/^\s+|\s+$//g; # trim both ends of the string + if ( $out eq '' ) { + logTest( 'failed', "VU01", $user ); + } elsif ( $out ne 'allow' and $out ne 'accept' ) { + logTest( 'failed', "VU02", $user, $out ); + } else { + logTest( 'passed', "The test is successful. The user ($user) is in the policy table with the rule: \'$out\'." ); + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyVswitchOSAs + + Description : Verify the specified vswitches OSA exist. + Arguments : Hash of Vswitches and their OSAs + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyVswitchOSAs( \%vswitchOSAs ); + +=cut + +#------------------------------------------------------- +sub verifyVswitchOSAs{ + my ( $hostNode, $vswitchOSAs ) = @_; + my $out; + + logTest( 'started', "vswitches with related OSAs are valid." ); + + my $zhcpNode = $glob_hostNodes{$hostNode}{'zhcp'}; + + # For each vswitch, verify that it has the specified OSA associated + # with it and it is active or a backup. + foreach my $switch ( keys %$vswitchOSAs ) { + my %switchInfo = getVswitchInfoExtended( $zhcpNode, uc( $switch ) ); + if ( !%switchInfo ) { + logTest( 'failed', "VVO01", $switch ); + next; # Non-critical error detected, iterate to the next switch + } + + my @devices = split( ',', $$vswitchOSAs{$switch} ); + foreach my $device ( @devices ) { + $device = uc( $device ); + + my @osa = split( /\./, $device ); + + # Verify the RDEV + $device = substr( "000$osa[0]", -4 ); # pad with zeroes + if ( $switchInfo{'Real device'}{'RDEVs'} !~ $device ) { + logTest( 'failed', "VVO02", $switch, $device ); + } + } + } + + return 0; +} + + +#------------------------------------------------------- + +=head3 verifyZHCPNode + + Description : Verify the xCAT zHCP node is defined properly. + Arguments : Host node + Returns : 0 - OK, or only a non-critical error detected + non-zero - Critical error detected, IVP should exit. + Example : my $rc = verifyZHCPNode( $node ); + +=cut + +#------------------------------------------------------- +sub verifyZHCPNode{ + my ( $hostNode ) = @_; + my $out; + my $rc; + + logTest( 'started', "that a zHCP node is associated with the host: $hostNode." ); + my $zhcpNode = findZhcpNode( $hostNode ); + if ( ! defined $zhcpNode ) { + logTest( "failed", "VZN05", $hostNode ); + return 1; # Critical error detected. IVP should exit. + } + my %zhcpNodeInfo = getLsdefNodeInfo( $zhcpNode ); + + # Check if this ZHCP node is on the same system as the xCAT MN + if ( exists $zhcpNodeInfo{'ip'} and exists $glob_localIPs{$zhcpNodeInfo{'ip'}} ) { + $glob_localZHCP = $zhcpNode; + } + + # Verify that we can ping zHCP + logTest( 'started', "zHCP node ($zhcpNode) is running." ); + $out = `/opt/xcat/bin/pping $zhcpNode`; + if ( $out !~ "$zhcpNode: ping" ) { + logTest( 'failed', "VZN01", $zhcpNode ); + return 1; # Critical error detected. IVP should exit. + } + + # Obtain and zHCP version information. + $out = `ssh $zhcpNode "[ -e \"/opt/zhcp/version\" ] \&\& cat \"/opt/zhcp/version\""`; + $rc = $? >> 8; + if ( $rc == 255) { + logTest( 'failed', "STN01", $zhcpNode ); + return 1; # Critical error detected. IVP should exit. + } + if ( $out ne '' ) { + chomp( $out ); + $glob_versionInfo = "$glob_versionInfo\nOn $zhcpNode node: $out"; + } else { + $glob_versionInfo = "$glob_versionInfo\nOn $zhcpNode node: ZHCP version level is unknown."; + } + + # Drive a simple rpower request to zHCP which talks to CP + logTest( 'started', "zHCP ($zhcpNode) can handle a simple request to talk to CP." ); + $out = `/opt/xcat/bin/rpower $zhcpNode stat | grep '$zhcpNode:'`; + if ( $out !~ "$zhcpNode: on" ) { + logTest( 'failed', "VZN02", $zhcpNode ); + return 1; # Critical error detected. IVP should exit. + } + + # Drive a simple SMAPI request thru zHCP + logTest( 'started', "zHCP ($zhcpNode) can handle a simple request to SMAPI." ); + $out = `ssh $zhcpNode /opt/zhcp/bin/smcli Query_API_Functional_Level -T dummy 2>&1`; + $rc = $? >> 8; + if ( $rc == 255) { + logTest( 'failed', "STN01", $zhcpNode ); + return 1; # Critical error detected. IVP should exit. + } + if ( $out !~ "The API functional level is" ) { + chomp( $out ); + logTest( 'failed', "VZN04", $zhcpNode, $out ); + return 1; # Critical error detected. IVP should exit. + } + + # Yea, We can talk to SMAPI. Remember that we can use this ZHCP for other tests. + $glob_hostNodes{$hostNode}{'zhcp'} = $zhcpNode; + + # Drive a more complex request to zHCP, an LSVM command + logTest( 'started', "zHCP ($zhcpNode) can handle a more complex xCAT LSVM request." ); + $out = `/opt/xcat/bin/lsvm $zhcpNode | grep '$zhcpNode:'`; + $rc = $? >> 8; + if ( $rc == 255) { + logTest( 'failed', "STN01", $zhcpNode ); + return 1; # Critical error detected. IVP should exit. + } + my $zhcpUserid = uc( $zhcpNodeInfo{'userid'} ); + if ( $out !~ "USER $zhcpUserid" and $out !~ "IDENTITY $zhcpUserid" ) { + logTest( 'failed', "VZN03", $zhcpNode, $zhcpUserid ); + return 1; # Critical error detected. IVP should exit. + } + + return 0; +} + + + +#***************************************************************************** +# Main IVP routine +#***************************************************************************** + +my $rc; +my $out; +my $userid; +my $terminatingError = 0; + +# Update global variables based on overrides from an external perl script. +setOverrides(); + +# Handle help function. +if ( $glob_displayHelp == 1 ) { + showHelp(); + goto FINISH; +} + +# Establish SYSLOG logging for errors if function is desired. +if ( $glob_syslogErrors == 1 ) { + my $user = $ENV{ 'USER' }; + setlogsock( 'unix' ); + openlog( 'xcatIVP', '', 'user'); + syslog( 'info', "Began xcatIVP test" ); +} + +# Detect CMA and obtain the CMA's version information +if ( -e $glob_versionFileCMA ) { + $glob_CMA = 1; + verifyCMAProperties(); +} else { + $glob_CMA = 0; +} + +%glob_localIPs = getLocalIPs(); + +# Obtain the xCAT MN's version information +my $xcatVersion; +if ( -e $glob_versionFileXCAT ) { + $out = `cat $glob_versionFileXCAT`; + chomp( $out ); + $xcatVersion = "$out"; +} else { + $xcatVersion = "xCAT version: unknown"; +} +if ( $glob_versionInfo eq '' ) { + $glob_versionInfo = $xcatVersion; +} else { + $glob_versionInfo = "$glob_versionInfo\n$xcatVersion"; +} + +# Verify the memory size. +if ( $glob_CMA == 1 ) { + verifyMemorySize(); +} + +# Verify xCAT MN's IP address +if ( defined $glob_xcatMNIp) { + verifyMnIp( $glob_xcatMNIp, 1, "xCAT server address" ); +} + +# Verify xCAT MN mgt network IP address +if ( defined $glob_xcatMgtIp) { + verifyMnIp( $glob_xcatMgtIp, 0, "Mgt network IP address", $glob_mgtNetmask ); +} + +# Verify the management node is properly defined in xCAT +if ( defined $glob_mnNode ) { + verifyMnNode( $glob_mnNode ); +} else { + logTest( 'bypassed', "BPVMN01" ); +} + +# Create the list of host node information. +if ( defined $glob_hostNode ) { + $glob_hostNodes{$glob_hostNode}{'input'} = 1; +} else { + my @hostNodes = getHostNodeNames(); + foreach my $hostNode (@hostNodes) { + $glob_hostNodes{$hostNode}{'input'} = 0; + } + if ( keys %glob_hostNodes ) { + $glob_hostNode = split( ' ', keys( %glob_hostNodes ), 1 ); + } +} + +# Verify the host node is properly defined in xCAT and has a ZHCP agent. +foreach my $hostNode ( keys %glob_hostNodes ) {; + $terminatingError = verifyHostNode( $hostNode ); + if ( $terminatingError and scalar keys( %glob_hostNodes ) == 1 ) { + goto FINISH; + } + $terminatingError = 0; + + # Verify the zHCP node is properly defined in xCAT + $terminatingError = verifyZHCPNode( $hostNode ); + if ( $terminatingError and keys( %glob_hostNodes ) == 1 ) { + goto FINISH; + } + $terminatingError = 0; +} + +# Verify the zHCP node specified as input is accessible. +if ( defined $glob_zhcpNode ) { + # Verify the zHCP node is properly defined in xCAT + verifyNodeExists( $glob_zhcpNode, "zHCP node" ); +} + +# Verify the disk pools used to create virtual machines exist +# in the Directory Manager. +if ( @glob_diskpools ) { + verifyDiskPools( $glob_hostNode, \@glob_diskpools ); +} else { + logTest( 'bypassed', "BPVDP01" ); + foreach my $hostNode ( keys %glob_hostNodes ) { + my @pools = getDiskPoolNames( $hostNode ); + verifyDiskPools( $hostNode, \@pools ); + } +} + +# Verify the networks used by deployed virtual machines exist. +if ( @glob_networks ) { + verifyNetworks( $glob_hostNode, \@glob_networks ); +} else { + logTest( 'bypassed', "BPVN01" ); +} + +# Verify the MACADDR user prefix matches the one on the host. +if ( defined $glob_macPrefix ) { + verifyMACUserPrefix( $glob_hostNode, $glob_macPrefix ); +} + +# Verify vswitches with related OSAs are valid. +if ( %glob_vswitchOSAs ) { + verifyVswitchOSAs( $glob_hostNode, \%glob_vswitchOSAs ); +} + +# Verify file system space on the xCAT MN. +verifyDirectorySpace( \@glob_xcatDiskSpace, 'xCAT MN', 0 ); + +# Verify file system space on the zhcp server for each host. +foreach my $hostNode ( keys %glob_hostNodes ) { + verifyDirectorySpace( \@glob_zhcpDiskSpace, $glob_hostNodes{$hostNode}{'zhcp'}, 1 ); +} + +# Verify Host related items for each host in the hostNodes hash. +foreach my $hostNode ( keys %glob_hostNodes ) { + # Verify default user profile is defined to the Directory Manager. + if ( defined $glob_defaultUserProfile ) { + verifyProfile( $hostNode, $glob_defaultUserProfile ); + } + # Verify the signal shutdown interval is appropriate. + if ( $glob_signalTimeout != 0 ) { + verifyMiscHostStuff( $hostNode ); + } +} + +# Verify the xCAT user is defined in the xCAT policy table. +if ( defined $glob_xcatUser ) { + verifyUser( $glob_xcatUser ); +} + +# Verify the REST Interface is responsive. +if ( defined $glob_xcatUser and defined $glob_xcatUserPw and defined $glob_xcatMNIp and defined $glob_mnNode ) { + verifyREST(); +} + +# Verify that xCAT MN can access the compute node. +if ( defined $glob_cNAddress and defined $glob_expUser ) { + verifyComputeNodeConnectivity( $glob_cNAddress, $glob_expUser ); +} else { + logTest( "bypassed", "BPVCNC01" ); +} + +FINISH: +if ( $terminatingError ) { + logTest( "warning", "MAIN01" ); +} + +logTest( "misc", "" ); + +if ( $glob_versionInfo ) { + logTest( "miscNF", "The following versions of code were detected:\n" . $glob_versionInfo ); + logTest( "misc", "" ); +} + +if ( scalar(@glob_failedTests) != 0 ) { + logTest( "misc", "The following tests generated warning(s): " . join(", ", @glob_failedTests) . '.' ); +} +if ( $glob_displayHelp != 1 ) { + logTest( "misc", "$glob_testNum IVP tests ran, " . $glob_totalFailed . " tests generated warnings." ); +} + +if ( $glob_ignoreCnt != 0 ){ + logTest( "misc", "Ignored messages $glob_ignoreCnt times." ); + my @ignoreArray = sort keys %glob_ignored; + my $ignoreList = join ( ', ', @ignoreArray ); + logTest( "misc", "Message Ids of ignored messages: $ignoreList" ); +} + +# Close out our use of syslog +if ( $glob_syslogErrors == 1 ) { + syslog( 'info', "Ended zxcatIVP test" ); + closelog(); +} + +exit 0; diff --git a/xCAT-client/bin/zxcatexport.pl b/xCAT-client/bin/zxcatexport.pl new file mode 100644 index 000000000..1907aa313 --- /dev/null +++ b/xCAT-client/bin/zxcatexport.pl @@ -0,0 +1,333 @@ +#!/usr/bin/perl +############################################################################### +# IBM (C) Copyright 2015, 2016 Eclipse Public License +# http://www.eclipse.org/org/documents/epl-v10.html +############################################################################### +# COMPONENT: zxcatExport +# +# This is a program to save the xCAT tables to the /install/xcatmigrate directory; +# then close and export the /install LVM +# +# The reverse process on the other system can be done if the LVM disks are +# writeable and online. The zxcatmigrate script can be used for that. +# It will issue pvscan, vgimport, vgchange -ay, mkdir, then mount commands. +# +############################################################################### + +use strict; +use warnings; +use Capture::Tiny ':all'; +use Getopt::Long; +use lib '/opt/xcat/lib/perl/'; +use xCAT::TableUtils; +use xCAT::zvmUtils; +$| = 1; # turn off STDOUT buffering + +my $lvmPath = "/dev/xcat/repo"; +my $mountPoint = "/install"; +my $exportDir = "/install/xcatmigrate"; +my $exportTablesDir = "/install/xcatmigrate/xcattables"; +my $exportFcpConfigsDir = "/install/xcatmigrate/fcpconfigs"; +my $exportFcpOtherFilesDir = "/install/xcatmigrate/fcpotherfiles"; +my $exportDocloneFilesDir = "/install/xcatmigrate/doclone"; +my $lvmInfoFile = "lvminformation"; +my $lsdasdInfoFile = "lsdasdinformation"; +my $zvmVirtualDasdInfoFile = "zvmvirtualdasdinformation"; +my $vgName = "xcat"; + +# xCAT table information to be filled in +my $masterIP; +my $xcatNode; +my $hcp; +my $zhcpNode; + +my $version = "1.1"; +my $targetIP = ""; # IP address to get data from +my $skipInstallFiles = 0; # Skip copying any install files +my $skipTables = 0; # Skip copying and installing xcat tables +my $displayHelp = 0; # Display help information +my $versionOpt = 0; # Show version information flag + +my @entries; +my @propNames; +my $propVals; + +my $usage_string = "This script saves the xcat tables in /install/xcatmigrate and\n +then exports the LVM mounted at /install. This should only be used to migrate\n +the /install LVM to a new userid.\n\n + Usage:\n + $0 [ -v ]\n + $0 [ -h | --help ]\n + The following options are supported:\n + -h | --help Display help information\n + -v Display the version of this script.\n"; + + +#------------------------------------------------------- + +=head3 chompall + + Description : Issue chomp on all three input parms (pass by reference) + Arguments : arg1, arg2, arg3 + Returns : nothing + Example : chompall(\$out, \$err, \$returnvalue); + +=cut + +#------------------------------------------------------- +sub chompall{ + my ( $arg1, $arg2, $arg3 ) = @_; + chomp($$arg1); + chomp($$arg2); + chomp($$arg3); +} + +# *********************************************************** +# Mainline. Parse any arguments, usually no arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); + +GetOptions( + 'h|help' => \$displayHelp, + 'v' => \$versionOpt ); + +if ( $versionOpt ) { + print "Version: $version\n"; + exit; +} + +if ( $displayHelp ) { + print $usage_string; + exit; +} + +my $out = ''; +my $err = ''; +my $returnvalue = 0; + +# This looks in the passwd table for a key = sudoer +($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer(); + +# Scan the xCAT tables to get the zhcp node name +# Print out a message and stop if any errors found +@entries = xCAT::TableUtils->get_site_attribute("master"); +$masterIP = $entries[0]; +if ( !$masterIP ) { + print "xCAT site table is missing a master with ip address\n"; + exit; +} + +# Get xcat node name from 'hosts' table using IP as key +@propNames = ( 'node'); +$propVals = xCAT::zvmUtils->getTabPropsByKey('hosts', 'ip', $masterIP, @propNames); +$xcatNode = $propVals->{'node'}; +if ( !$xcatNode ) { + print "xCAT hosts table is missing a node with ip address of $masterIP\n"; + exit; +} + +# Get hcp for xcat from the zvm table using xcat node name +@propNames = ( 'hcp'); +$propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $xcatNode, @propNames ); +$hcp = $propVals->{'hcp'}; +if ( !$hcp ) { + print "xCAT zvm table is missing hcp value for $xcatNode\n"; + exit; +} + +# Get zhcp node name from 'hosts' table using hostname as key +@propNames = ( 'node'); +$propVals = xCAT::zvmUtils->getTabPropsByKey('hosts', 'hostnames', $hcp, @propNames); +$zhcpNode = $propVals->{'node'}; +if ( !$zhcpNode ) { + print "xCAT hosts table is missing a zhcp node with hostname of $hcp\n"; + exit; +} + +#Create the migrate directory and the xcat tables directory. This should not get error even if it exists +print "Creating directory $exportDir\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "mkdir -p -m 0755 $exportDir"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to create $exportDir:\n"; + print "$err\n"; + exit; +} + +print "Creating directory $exportTablesDir\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "mkdir -p -m 0755 $exportTablesDir"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to create $exportTablesDir:\n"; + print "$err\n"; + exit; +} + +print "Creating directory $exportFcpConfigsDir\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "mkdir -p -m 0755 $exportFcpConfigsDir"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to create $exportFcpConfigsDir:\n"; + print "$err\n"; + exit; +} + +print "Creating directory $exportFcpOtherFilesDir\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "mkdir -p -m 0755 $exportFcpOtherFilesDir"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to create $exportFcpOtherFilesDir:\n"; + print "$err\n"; + exit; +} + +print "Creating directory $exportDocloneFilesDir\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "mkdir -p -m 0755 $exportDocloneFilesDir"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to create $exportDocloneFilesDir:\n"; + print "$err\n"; + exit; +} + +#Save the current LVM information +print "Saving current LVM information at $exportDir/$lvmInfoFile \n"; +( $out, $err, $returnvalue ) = eval { capture { system( "vgdisplay '-v' 2>&1 > $exportDir/$lvmInfoFile"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to display LVM information:\n"; + print "$err\n"; + exit; +} + +#Save the current Linux DASD list information +print "Saving current Linux DASD list information at $exportDir/$lsdasdInfoFile \n"; +( $out, $err, $returnvalue ) = eval { capture { system( "lsdasd 2>&1 > $exportDir/$lsdasdInfoFile"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to display Linux DASD list information:\n"; + print "$err\n"; + exit; +} + +#Save the current zVM virtual DASD list information +print "Saving current zVM virtual DASD list information at $exportDir/$zvmVirtualDasdInfoFile \n"; +( $out, $err, $returnvalue ) = eval { capture { system( "vmcp q v dasd 2>&1 > $exportDir/$zvmVirtualDasdInfoFile"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to display zVM virtual DASD list information:\n"; + print "$err\n"; + exit; +} + +#save the xcat tables +print "Dumping xCAT tables to $exportTablesDir\n"; +( $out, $err, $returnvalue ) = eval { capture { system( ". /etc/profile.d/xcat.sh; /opt/xcat/sbin/dumpxCATdb -p $exportTablesDir"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to dump the xcat tables to $exportTablesDir:\n"; + print "$err\n"; + exit; +} + +#Check for and save any zhcp FCP configuration files +print "Checking zhcp for any FCP configuration files\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "ssh $zhcpNode ls /var/opt/zhcp/zfcp/*.conf"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue == 0) { + # Save any *.conf files + print "Copying /var/opt/zhcp/zfcp/*.conf files to $exportFcpConfigsDir\n"; + ( $out, $err, $returnvalue ) = eval { capture { system( "scp $::SUDOER\@$zhcpNode:/var/opt/zhcp/zfcp/*.conf $exportFcpConfigsDir"); } }; + chompall(\$out, \$err, \$returnvalue); + if ($returnvalue) { + print "Error rv:$returnvalue trying to use scp to copy files from $zhcpNode\n"; + print "$err\n"; + exit; + } +} else { + # If file not found, that is an OK error, if others then display error and exit + if (index($err, "No such file or directory")== -1) { + print "Error rv:$returnvalue trying to use ssh to list files on $zhcpNode\n"; + print "$err\n"; + exit; + } +} +# Check for any other zhcp FCP files +( $out, $err, $returnvalue ) = eval { capture { system( "ssh $zhcpNode ls /opt/zhcp/conf/*"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue == 0) { + # Save any files found + print "Copying /opt/zhcp/conf/*.conf files to $exportFcpOtherFilesDir\n"; + ( $out, $err, $returnvalue ) = eval { capture { system( "scp $::SUDOER\@$zhcpNode:/opt/zhcp/conf/* $exportFcpOtherFilesDir"); } }; + chompall(\$out, \$err, \$returnvalue); + if ($returnvalue) { + print "Error rv:$returnvalue trying to use scp to copy /opt/zhcp/conf/* files from $zhcpNode\n"; + print "$err\n"; + exit; + } +} else { + # If file not found, that is an OK error, if others then display error and exit + if (index($err, "No such file or directory")== -1) { + print "Error rv:$returnvalue trying to use ssh to list files on $zhcpNode\n"; + print "$err\n"; + exit; + } +} + +# Check for any doclone.txt file +( $out, $err, $returnvalue ) = eval { capture { system( "ls /var/opt/xcat/doclone.txt"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue == 0) { + # Save any file found + print "Copying /var/opt/xcat/doclone.txt file to $exportDocloneFilesDir\n"; + ( $out, $err, $returnvalue ) = eval { capture { system( "cp /var/opt/xcat/doclone.txt $exportDocloneFilesDir"); } }; + chompall(\$out, \$err, \$returnvalue); + if ($returnvalue) { + print "Error rv:$returnvalue trying to copy /var/opt/xcat/doclone.txt file\n"; + print "$err\n"; + exit; + } +} else { + # If file not found, that is an OK error, if others then display error and exit + if (index($err, "No such file or directory")== -1) { + print "Error rv:$returnvalue trying to copy /var/opt/xcat/doclone.txt file\n"; + print "$err\n"; + exit; + } +} + +#unmount the /install +print "Unmounting $lvmPath\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "umount $lvmPath"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to umount $lvmPath:\n"; + print "$err\n"; + exit; +} + +#mark the lvm inactive +print "Making the LVM $vgName inactive\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "vgchange '-an' $vgName"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to inactivate volume group $vgName:\n"; + print "$err\n"; + exit; +} + +#export the volume group +print "Exporting the volume group $vgName\n"; +( $out, $err, $returnvalue ) = eval { capture { system( "vgexport $vgName"); } }; +chompall(\$out, \$err, \$returnvalue); +if ($returnvalue) { + print "Error rv:$returnvalue trying to export volume group $vgName:\n"; + print "$err\n"; + exit; +} + +print "\nVolume group $vgName is exported, you can now signal shutdown xcat and\n"; +print "have the xcat lvm disks linked RW in the new appliance."; +exit 0; + + diff --git a/xCAT-client/bin/zxcatimport.pl b/xCAT-client/bin/zxcatimport.pl new file mode 100644 index 000000000..3bfc619fb --- /dev/null +++ b/xCAT-client/bin/zxcatimport.pl @@ -0,0 +1,758 @@ +#!/usr/bin/perl +############################################################################### +# IBM (C) Copyright 2015, 2016 Eclipse Public License +# http://www.eclipse.org/org/documents/epl-v10.html +############################################################################### +# COMPONENT: zxcatimport +# +# This is a program to copy the xCAT /install files and the xCAT tables from +# an old XCAT userid to the new appliance +# See usage string below for parameters. +# return code = 0 successful; else error. +# +############################################################################### + +use strict; +use warnings; +use Capture::Tiny ':all'; +use Getopt::Long; +use lib '/opt/xcat/lib/perl/'; +use xCAT::TableUtils; +use xCAT::Table; +use xCAT::zvmUtils; +use xCAT::MsgUtils; + +my $lvmPath = "/dev/xcat/repo"; +my $lvmMountPoint = "/install2"; +my $lvmImportDir = "/install2/xcatmigrate"; +my $lvmImportTablesDir = "/install2/xcatmigrate/xcattables"; +my $lvmImportFcpConfigsDir = "/install2/xcatmigrate/fcpconfigs"; +my $lvmImportFcpOtherFilesDir = "/install2/xcatmigrate/fcpotherfiles"; +my $lvmImportDocloneFilesDir = "/install2/xcatmigrate/doclone"; +my $lvmInfoFile = "lvminformation"; +my $lsdasdInfoFile = "lsdasdinformation"; +my $zvmVirtualDasdInfoFile = "zvmvirtualdasdinformation"; +my $vgName = "xcat"; +my $persistentMountPoint = "/persistent2"; +my @defaultTables = ("hosts", "hypervisor", "linuximage", "mac", "networks", "nodelist", + "nodetype", "policy", "zvm"); + +my $version = "1.0"; +my $out; +my $err; +my $returnValue; + +my $copyLvmFiles = 0; # Copy files under old /install to new /install +my $replaceAllXcatTables = 0; # Copy one or all xcat tables +my $addTableData = '*none*'; # Add node data to one table or default tables +my $copyFcpConfigs = 0; # copy old zhcp fcp *.conf files +my $copyDoclone = 0; # copy old doclone.txt file +my $replaceSshKeys = 0; # replace SSH keys with old xCAT SSH keys +my $displayHelp = 0; # Display help information +my $versionOpt = 0; # Show version information flag +my $rc = 0; + +my $usage_string = "This script will mount or copy the old xcat lvm and old persistent disk\n +to this appliance. The old xCAT LVM volumes and the persistent disk on XCAT userid must \n +be linked in write mode by this appliance. The LVM will be mounted at '/install2' and \n +the persistent disk will be mounted at '/persistent2'.\n +\n +The default is to just mount the persistent and LVM disks.\n +They will be mounted as /install2 and /persistent2\n\n + + Usage:\n + $0 [--addtabledata [tablename]] [--installfiles ] [--doclonefile]\n + [--fcppoolconfigs] [--replacealltables] [--replaceSSHkeys] \n + $0 [ -v ]\n + $0 [ -h | --help ]\n + The following options are supported:\n + --addtabledata [tablename] | ['tablename1 tablename2 ..']\n + Add old xCAT data to specific table(s) or\n + default to the following xCAT tables:\n + hosts, hypervisor, linuximage, mac, networks, nodelist,\n + nodetype, policy, zvm/n + --installfiles Copy any files from old /install to appliance /install\n + --doclonefile Copy any doclone.txt to appliance\n + --fcppoolconfigs Copy any zhcp FCP pool configuration files\n + --replacealltables Replace all xCAT tables with old xCAT tables\n + --replaceSSHkeys Replaces the current xCAT SSH keys with the old xCAT SSH keys\n + -h | --help Display help information\n + -v Display the version of this script.\n"; + +#------------------------------------------------------- + +=head3 chompall + + Description : Issue chomp on all three input parms (pass by reference) + Arguments : arg1, arg2, arg3 + Returns : nothing + Example : chompall(\$out, \$err, \$returnValue); + +=cut + +#------------------------------------------------------- +sub chompall{ + my ( $arg1, $arg2, $arg3 ) = @_; + chomp($$arg1); + chomp($$arg2); + chomp($$arg3); +} + +# =======unit test/debugging routine ========== +# print the return data from tiny capture return +# data for: out, err, return value +sub printreturndata{ + my ( $arg1, $arg2, $arg3 ) = @_; + print "=============================\n"; + print "Return value ($$arg3)\n"; + print "out ($$arg1)\n"; + print "err ($$arg2)\n\n"; +} + +## ---------------------------------------------- ## +## Subroutine to find device name from address + +sub get_disk($) +{ + my ($id_user) = @_; + my $id = hex $id_user; + my $hex_id = sprintf '%x', $id; + my $dev_path = sprintf '/sys/bus/ccw/drivers/dasd-eckd/0.0.%04x', $id; + unless (-d $dev_path) { + $dev_path = sprintf '/sys/bus/ccw/drivers/dasd-fba/0.0.%04x', $id; + } + -d $dev_path or return undef; + my $dev_block = "$dev_path/block"; + unless (-d $dev_block) { + # Try bringing the device online + for (1..5) { + system("echo 1 > $dev_path/online"); + last if -d $dev_block; + sleep(10); + } + } + opendir(my $dir, $dev_block) or return undef; + my $dev; + while ($dev = readdir $dir) { + last unless $dev eq '.' || $dev eq '..'; + } + closedir $dir; + defined $dev ? "/dev/$dev" : undef; +} + +#------------------------------------------------------- + +=head3 mountOldLVM + + Description : This routine will import the old LVM and mount + it at /install2 + Arguments : none + Returns : 0 - LVM mounted or already mounted + non-zero - Error detected. + Example : $rc = mountOldLVM; + +=cut + +#------------------------------------------------------- +sub mountOldLVM{ + + my $saveMsg; + my $saveErr; + my $saveReturnValue; + + #Check for /install2 If already mounted should get a return value(8192) and $err output + #Check $err for "is already mounted on", if found we are done. + print "Checking for $lvmMountPoint.\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "mount $lvmMountPoint"); } }; + chompall(\$out, \$err, \$returnValue); + if (index($err, "already mounted on $lvmMountPoint") > -1) { + print "Old xCAT LVM is already mounted at $lvmMountPoint\n"; + return 0; + } + + print "Importing $vgName\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "/sbin/vgimport $vgName"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + # There could be a case where the LVM has been imported already + # Save this error information and do the next step (vgchange) + $saveMsg = "Error rv:$returnValue trying to vgimport $vgName"; + $saveErr = "$err"; + $saveReturnValue = $returnValue; + } + + print "Activating LVM $vgName\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "/sbin/vgchange -a y $vgName"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + # If the import failed previously, put out that message instead. + if (!defined $saveMsg) { + print "$saveMsg\n"; + print "$saveErr\n"; + return $saveReturnValue; + } else { + print "Error rv:$returnValue trying to vgchange -a y $vgName\n"; + print "$err\n"; + retun $returnValue; + } + } + + print "Making $lvmMountPoint directory\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "mkdir -p $lvmMountPoint"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to mkdir -p $lvmMountPoint\n"; + print "$err\n"; + return $returnValue; + } + + print "Mounting LVM $lvmPath at $lvmMountPoint\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "mount -t ext3 $lvmPath $lvmMountPoint"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to mkdir -p $lvmMountPoint\n"; + print "$err\n"; + return $returnValue; + } + + print "Old xCAT LVM is now mounted at $lvmMountPoint\n"; + return 0; +} + +#------------------------------------------------------- + +=head3 mountOldPersistent + + Description : This routine will look for the old persistent disk and mount + it at /persistent2 + Arguments : none + Returns : 0 - /persistent2 mounted or already mounted + non-zero - Error detected. + Example : $rc = mountOldPersistent; + +=cut + +#------------------------------------------------------- +sub mountOldPersistent{ + + #Check for /persistent2 If already mounted should get a return value(8192) and $err output + #Check $err for "is already mounted on", if found we are done. + print "Checking for $persistentMountPoint.\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "mount $persistentMountPoint"); } }; + chompall(\$out, \$err, \$returnValue); + if (index($err, "already mounted on $persistentMountPoint") > -1) { + print "The old xCAT /persistent disk already mounted at $persistentMountPoint\n"; + return 0; + } + + # search the exported Linux lsdasd file to get the vdev for vdev 100 (dasda) + # should look like: 0.0.0100 active dasda 94:0 ECKD 4096 2341MB 599400 + my $dasda = `cat "$lvmImportDir/$lsdasdInfoFile" | egrep -i "dasda"`; + if (length($dasda) <= 50) { + print "Unable to find dasda information in $lvmImportDir/$lsdasdInfoFile\n"; + return 1; + } + my @tokens = split(/\s+/, $dasda); + my @vdevparts = split (/\./, $tokens[0]); + my $vdev = $vdevparts[2]; + if (!(length($vdev))) { + print "Unable to find a vdev value for dasda\n"; + return 1; + } + + # search the exported zVM virtual dasd list to get the volume id of the disk + # should look like: DASD 0100 3390 QVCD69 R/W 3330 CYL ON DASD CD69 SUBCHANNEL = 000B + my $voliddata = `cat "$lvmImportDir/$zvmVirtualDasdInfoFile" | egrep -i "DASD $vdev"`; + if (length($voliddata) <= 50) { + print "Unable to find volid information for $vdev in $lvmImportDir/$zvmVirtualDasdInfoFile\n"; + return 1; + } + @tokens = split(/\s+/, $voliddata); + my $volid = $tokens[3]; + if (!(length($volid))) { + print "Unable to find a volume id for vdev $vdev\n"; + return 1; + } + + # Now display the current zVM query v dasd to see if they have the volid listed + # and what vdev it is mounted on + ( $out, $err, $returnValue ) = eval { capture { system( "vmcp q v dasd 2>&1"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to vmcp q v dasd\n"; + print "$err\n"; + return $returnValue; + } + + # get the current VDEV the old volid is now using + # If not they they did not update the directory to link to the old classic disk + ( $out, $err, $returnValue ) = eval { capture { system( "echo \"$out\" | egrep -i $volid"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to echo $out\n"; + print "$err\n"; + return $returnValue; + } + if (!(length($out))) { + print "Unable to find a current vdev value for volume id $volid\n"; + return 1; + } + @tokens = split(/\s+/, $out); + my $currentvdev = $tokens[1]; + if (!(length($currentvdev))) { + print "Unable to find a current vdev value for volume id $volid\n"; + return 1; + } + + # Now get the Linux disk name that is being used for this vdev (/dev/dasdx) + my $devname = get_disk($currentvdev); + #print "Devname found: $devname\n"; + if (!(defined $devname)) { + print "Unable to find a Linux disk for address $currentvdev volume id $volid\n"; + return 1; + } + + # Create the directory for the mount of old persistent disk + ( $out, $err, $returnValue ) = eval { capture { system( "mkdir -p -m 0755 $persistentMountPoint"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to create $persistentMountPoint:\n"; + print "$err\n"; + return $returnValue; + } + + # Mount the old persistent disk, must be partition 1 + my $partition = 1; + ( $out, $err, $returnValue ) = eval { capture { system( "mount -t ext3 $devname$partition $persistentMountPoint"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to mount -t ext3 $devname$partition $persistentMountPoint\n"; + print "$err\n"; + return $returnValue; + } + print "The old xCAT /persistent disk is mounted at $persistentMountPoint\n"; + return 0; +} +# *********************************************************** +# Mainline. Parse any arguments +$Getopt::Long::ignorecase = 0; +Getopt::Long::Configure( "bundling" ); + +GetOptions( + 'installfiles' => \$copyLvmFiles, + 'replacealltables' => \$replaceAllXcatTables, + 'addtabledata:s' => \$addTableData, + 'fcppoolconfigs' => \$copyFcpConfigs, + 'doclonefile' => \$copyDoclone, + 'replaceSSHkeys' => \$replaceSshKeys, + 'h|help' => \$displayHelp, + 'v' => \$versionOpt ); + +if ( $versionOpt ) { + print "Version: $version\n"; + exit 0; +} + +if ( $displayHelp ) { + print $usage_string; + exit 0; +} + +# Use sudo or not +# This looks in the passwd table for a key = sudoer +($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer(); + +$rc = mountOldLVM(); +if ($rc != 0) { + exit 1; +} + +$rc = mountOldPersistent(); +if ($rc != 0) { + exit 1; +} + +# ***************************************************************************** +# **** Copy the LVM files from old xCAT LVM to current LVM +if ( $copyLvmFiles ) { + $rc = chdir("$lvmMountPoint"); + if (!$rc) { + print "Error rv:$rc trying to chdir $lvmMountPoint\n"; + exit 1; + } + + $out = `cp -a * /install 2>&1`; + if ($?) { + print "Error rv:$? trying to copy from $lvmMountPoint to /install. $out\n"; + exit $?; + } + print "Old LVM Files copied from $lvmMountPoint to /install\n" ; +} + +# ***************************************************************************** +# **** Replace all the current xCAT tables with the old xCAT tables +if ( $replaceAllXcatTables ) { + print "Restoring old xCAT tables from $lvmImportTablesDir\n"; + # restorexCATdb - restores the xCAT db tables from the directory -p path + ( $out, $err, $returnValue ) = eval { capture { system( ". /etc/profile.d/xcat.sh; /opt/xcat/sbin/restorexCATdb -p $lvmImportTablesDir"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to restore the xcat tables from $lvmImportTablesDir:\n"; + print "$err\n"; + exit 1; + } + # There is a chance the return value is 0, and the $out says "Restore of Database Complete."; + # Yet some of the tables had failures. That information is in $err + if (length($err)) { + print "Some tables did not restore. Error output:\n$err\n "; + exit 1; + } +} + +# ***************************************************************************** +# **** Copy the zhcp FCP config files +if ($copyFcpConfigs) { + # Check if there are any FCP config files to copy + ( $out, $err, $returnValue ) = eval { capture { system( "ls $lvmImportFcpConfigsDir/*.conf"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue == 0) { + # Save any *.conf files + print "Copying $lvmImportFcpConfigsDir/*.conf files to /var/opt/zhcp/zfcp\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "mkdir -p /var/opt/zhcp/zfcp && cp -R $lvmImportFcpConfigsDir/*.conf /var/opt/zhcp/zfcp/"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy files from $lvmImportFcpConfigsDir\n"; + print "$err\n"; + exit 1; + } + } else { + print "There were not any zhcp FCP *.conf files to copy\n"; + } + # Check if there are any other FCP files to copy + ( $out, $err, $returnValue ) = eval { capture { system( "ls $lvmImportFcpOtherFilesDir/*"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue == 0) { + # Save any files + print "Copying $lvmImportFcpOtherFilesDir/* files to /opt/zhcp/conf\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "mkdir -p /opt/zhcp/conf && cp -R $lvmImportFcpOtherFilesDir/* /opt/zhcp/conf/"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy files from $lvmImportFcpOtherFilesDir\n"; + print "$err\n"; + exit 1; + } + } else { + print "There were not any zhcp files from /opt/zhcp/conf to copy\n"; + } +} + +# ***************************************************************************** +# **** Copy the doclone.txt file if it exists +if ($copyDoclone) { + # Check if there is a doclone.txt to copy + ( $out, $err, $returnValue ) = eval { capture { system( "ls $lvmImportDocloneFilesDir/doclone.txt"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue == 0) { + # Save this file in correct location + print "Copying $lvmImportDocloneFilesDir/doclone.txt file to /var/opt/xcat/doclone.txt\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "cp -R $lvmImportDocloneFilesDir/doclone.txt /var/opt/xcat/"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy doclone.txt file from $lvmImportDocloneFilesDir\n"; + print "$err\n"; + exit 1; + } + } else { + print "There was not any doclone.txt file to copy\n"; + } +} + +# ***************************************************************************** +# **** Add old xCAT table data to a table +my $test = length($addTableData); +# Add old xCAT data to an existing table. Admin may need to delete out duplicates using the GUI +if ((length($addTableData)==0) || $addTableData ne "*none*") { + #defaultTables = ("hosts", "hypervisor", "linuximage", "mac", "networks", "nodelist", + # "nodetype", "policy", "zvm"); + my @tables = @defaultTables; + if (length($addTableData)>1 ) { + # use the table specified + @tables = (); + @tables = split(' ',$addTableData); + } + foreach my $atable (@tables) { + print "Adding data to table $atable\n"; + # the current xCAT code we have does not support the -a option + # use xCAT::Table functions + + my $tabledata = `cat "$lvmImportTablesDir\/$atable\.csv"`; + if (length($tabledata) <= 10) { + print "Unable to find table information for $atable in $lvmImportTablesDir\n"; + return 1; + } + # remove the hash tag from front + $tabledata =~ s/\#//; + my @rows = split('\n', $tabledata); + my @keys; + my @values; + my $tab; + # loop through all the csv rows, first are the header keys, rest is data + foreach my $i (0 .. $#rows) { + my %record; + #print "row $i data($rows[$i])\n"; + if ($i == 0) { + @keys = split(',', $rows[0]); + #print "Keys found:(@keys)\n"; + } else { + # now that we know we have data, lets create table + if (!defined $tab) { + $tab = xCAT::Table->new($atable, -create => 1, -autocommit => 0); + } + # put the data into the new table. + @values = split(',', $rows[$i]); + foreach my $v (0 .. $#values) { + # Strip off any leading and trailing double quotes + $values[$v] =~ s/"(.*?)"\z/$1/s; + $record{$keys[$v]} = $values[$v]; + #print "Row $i matches key $keys[$v] Value found:($values[$v])\n"; + } + } + # write out the row if any keys added to the hash + if (%record) { + my @dbrc = $tab->setAttribs(\%record, \%record); + if (!defined($dbrc[0])) { + print "Error ($dbrc[1]) setting database for table $atable"; + $tab->rollback(); + $tab->close; + exit 1; + } + } + } + # if we made it here and $tab is defined, commit it. + if (defined $tab) { + $tab->commit; + print "Data successfully added and committed to $atable.\n*****! Remember to check the table and remove any rows not needed\n"; + } + }#end for each table +} + +# ***************************************************************************** +# **** Replace the xCAT SSH key with the old xCAT SSH key + +# First copy the current keys and copy the old xCAT keys into unique file names +if ($replaceSshKeys) { + # Make temp file names to hold the current and old ssh public and private key + my $copySshKey= `/bin/mktemp -p /root/.ssh/ id_rsa.pub_XXXXXXXX`; + chomp($copySshKey); + my $copySshPrivateKey= `/bin/mktemp -p /root/.ssh/ id_rsa_XXXXXXXX`; + chomp($copySshPrivateKey); + + # Make temp files for the RSA backup keys in appliance + my $copyHostSshKey= `/bin/mktemp -p /etc/ssh/ ssh_host_rsa_key.pub_XXXXXXXX`; + chomp($copyHostSshKey); + my $copyHostSshPrivateKey= `/bin/mktemp -p /etc/ssh/ ssh_host_rsa_key_XXXXXXXX`; + chomp($copyHostSshPrivateKey); + + # Save old keys in unique names + my $oldSshKey= `/bin/mktemp -p /root/.ssh/ id_rsa.pub_OldMachineXXXXXXXX`; + chomp($oldSshKey); + my $oldSshPrivateKey= `/bin/mktemp -p /root/.ssh/ id_rsa_OldMachineXXXXXXXX`; + chomp($oldSshPrivateKey); + + print "Making backup copies of current xCAT SSH keys\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-p /root/.ssh/id_rsa.pub $copySshKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy /root/.ssh/id_rsa.pub to $copySshKey\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-p /root/.ssh/id_rsa $copySshPrivateKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy /root/.ssh/id_rsa to $copySshPrivateKey\n"; + print "$err\n"; + exit 1; + } + + # Save appliance backup keys + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-p /etc/ssh/ssh_host_rsa_key.pub $copyHostSshKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy /etc/ssh/ssh_host_rsa_key.pub to $copyHostSshKey\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-p /etc/ssh/ssh_host_rsa_key $copyHostSshPrivateKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy /etc/ssh/ssh_host_rsa_key to $copyHostSshPrivateKey\n"; + print "$err\n"; + exit 1; + } + + # Copy the old public key and make sure the permissions are 644 + print "Copying old xCAT SSH keys (renamed) from /persistent2 to /root/.ssh\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "cp /persistent2/root/.ssh/id_rsa.pub $oldSshKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy /persistent2/root/.ssh/id_rsa.pub to $oldSshKey\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "chmod 644 $oldSshKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to chmod 644 $oldSshKey\n"; + print "$err\n"; + exit 1; + } + + # Copy the private key and make sure the permissions are 600 + ( $out, $err, $returnValue ) = eval { capture { system( "cp /persistent2/root/.ssh/id_rsa $oldSshPrivateKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use cp to copy /persistent2/root/.ssh/id_rsa to $oldSshPrivateKey\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "chmod 600 $oldSshPrivateKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to chmod 600 $oldSshPrivateKey\n"; + print "$err\n"; + exit 1; + } + + # Now compare the IP of xCAT to zHCP . + # If they are the same, then only the private and public key in xCAT needs to be changed. + # If zhcp is on a different machine, then the first thing to be done is add the old xCAT key + # to the zhcp authorized_keys files, then do the switch on xcat of public and private keys + + my $xcatIp; + my $zhcpIp; + my $zhcpHostName; + my $zhcpNode; + my $xcatNodeName; + my @propNames; + my $propVals; + my @entries; + my $rc = 0; + + # Scan the xCAT tables to get the zhcp node name + # Print out a message and stop if any errors found + @entries = xCAT::TableUtils->get_site_attribute("master"); + $xcatIp = $entries[0]; + if ( !$xcatIp ) { + print "xCAT site table is missing a master with ip address\n"; + exit 1; + } + + # Get xcat node name from 'hosts' table using IP as key + @propNames = ( 'node'); + $propVals = xCAT::zvmUtils->getTabPropsByKey('hosts', 'ip', $xcatIp, @propNames); + $xcatNodeName = $propVals->{'node'}; + if ( !$xcatNodeName ) { + print "xCAT hosts table is missing a node with ip address of $xcatIp\n"; + exit 1; + } + + # Get hcp hostname for xcat from the zvm table using xcat node name + @propNames = ( 'hcp'); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $xcatNodeName, @propNames ); + $zhcpHostName = $propVals->{'hcp'}; + if ( !$zhcpHostName ) { + print "xCAT zvm table is missing hcp value for $xcatNodeName\n"; + exit 1; + } + + # Get zhcp IP and node from 'hosts' table using hostname as key + @propNames = ( 'ip', 'node'); + $propVals = xCAT::zvmUtils->getTabPropsByKey('hosts', 'hostnames', $zhcpHostName, @propNames); + $zhcpIp = $propVals->{'ip'}; + if ( !$zhcpIp ) { + print "xCAT hosts table is missing a zhcp node IP with hostname of $zhcpHostName\n"; + exit 1; + } + $zhcpNode = $propVals->{'node'}; + if ( !$zhcpNode ) { + print "xCAT hosts table is missing a zhcp node with hostname of $zhcpHostName\n"; + exit 1; + } + + if ($zhcpIp eq $xcatIp) { + print "xCAt and zhcp are on same IP, only need to update public and private keys\n"; + } else { + # Need to append the old SSH key to zhcp authorized_keys file + my $target = "$::SUDOER\@$zhcpHostName"; + print "Copying old SSH key to zhcp\n"; + ( $out, $err, $returnValue ) = eval { capture { system( "scp $oldSshKey $target:$oldSshKey"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to use scp to copy $oldSshKey to zhcp $oldSshKey\n"; + print "$err\n"; + exit 1; + } + # Adding the old SSH key to the authorized_keys file + # Make a copy of the old authorized_users file + my $suffix = '_' . substr($oldSshKey, -8); + ( $out, $err, $returnValue ) = eval { capture { system( "ssh $target cp \"/root/.ssh/authorized_keys /root/.ssh/authorized_keys$suffix\""); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to make a copy of the /root/.ssh/authorized_keys file\n"; + print "$err\n"; + exit 1; + } + # Add the key to zhcp authorized_keys file + ( $out, $err, $returnValue ) = eval { capture { system( "ssh $target cat \"$oldSshKey >> /root/.ssh/authorized_keys\""); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to append zhcp $oldSshKey to /root/.ssh/authorized_keys\n"; + print "$err\n"; + exit 1; + } + } + # We need to replace the xCAT public and private key with the old keys + # and add the old key to the authorized_keys on xCAT + ( $out, $err, $returnValue ) = eval { capture { system( "cat $oldSshKey >> /root/.ssh/authorized_keys"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to append xcat $oldSshKey to /root/.ssh/authorized_keys\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-f $oldSshKey /root/.ssh/id_rsa.pub"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to replace the /root/.ssh/id_rsa.pub with the $oldSshKey\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-f $oldSshPrivateKey /root/.ssh/id_rsa"); } }; + chompall(\$out, \$err, \$returnValue); + #printreturndata(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to replace the /root/.ssh/id_rsa with the $oldSshPrivateKey\n"; + print "$err\n"; + exit 1; + } + # Copy old keys into appliance saved key locations + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-f $oldSshKey /etc/ssh/ssh_host_rsa_key.pub"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to replace the /etc/ssh/ssh_host_rsa_key.pub with the $oldSshKey\n"; + print "$err\n"; + exit 1; + } + ( $out, $err, $returnValue ) = eval { capture { system( "cp \-f $oldSshPrivateKey /etc/ssh/ssh_host_rsa_key"); } }; + chompall(\$out, \$err, \$returnValue); + if ($returnValue) { + print "Error rv:$returnValue trying to replace the /etc/ssh/ssh_host_rsa_key with the $oldSshPrivateKey\n"; + print "$err\n"; + exit 1; + } + + print "Old xCAT SSH keys have replaced current SSH keys. Previous key data saved in unique names.\n"; +} + +exit 0; diff --git a/xCAT-client/openstack.versions b/xCAT-client/openstack.versions new file mode 100644 index 000000000..eedfd2571 --- /dev/null +++ b/xCAT-client/openstack.versions @@ -0,0 +1,9 @@ +# This file maps the OpenStack version numbers to their OpenStack release name. +15. OCATA +14. NEWTON +13. MIKATA +12. LIBERTY +2015. KILO +2014.2. JUNO +2014.1. ICEHOUSE +2013.2. HAVANA diff --git a/xCAT-client/xCAT-client.spec b/xCAT-client/xCAT-client.spec index e2146eee0..05dd07a37 100644 --- a/xCAT-client/xCAT-client.spec +++ b/xCAT-client/xCAT-client.spec @@ -16,6 +16,9 @@ BuildRoot: /var/tmp/%{name}-%{version}-%{release}-root %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) + # AIX will build with an arch of "ppc" %ifos linux BuildArch: noarch @@ -69,6 +72,10 @@ cp share/xcat/rvid/* $RPM_BUILD_ROOT/%{prefix}/share/xcat/rvid/ chmod 755 $RPM_BUILD_ROOT/%{prefix}/share/xcat/rvid/* %endif +%if %s390x +cp openstack.versions $RPM_BUILD_ROOT/%{prefix} +chmod 644 $RPM_BUILD_ROOT/%{prefix}/openstack.versions +%endif cp bin/* $RPM_BUILD_ROOT/%{prefix}/bin chmod 755 $RPM_BUILD_ROOT/%{prefix}/bin/* cp sbin/* $RPM_BUILD_ROOT/%{prefix}/sbin @@ -123,6 +130,17 @@ rm -f $RPM_BUILD_ROOT/%{prefix}/share/doc/man1/getxcatdocs.1.html rm -f $RPM_BUILD_ROOT/%{prefix}/share/man/man1/getxcatdocs.1 %endif +# Only zVM needs scripts below. These scripts also need Capture::Tiny +%if %nots390x +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/zxcatCopyCloneList.pl +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/zxcatexport.pl +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/zxcatimport.pl +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/mkdummyimage +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/verifynode +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/zvmMsg +rm -f $RPM_BUILD_ROOT/%{prefix}/bin/zxcatIVP.pl +%endif + # These links get made in the RPM_BUILD_ROOT/prefix area ln -sf xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/rpower ln -sf xcatclient $RPM_BUILD_ROOT/%{prefix}/bin/rscan diff --git a/xCAT-server/lib/perl/xCAT/xcatd.pm b/xCAT-server/lib/perl/xCAT/xcatd.pm index 69de2fd25..5e60e5ebb 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}){ @@ -224,10 +224,33 @@ sub validate { my $args = $request->{arg}; my $arglist; foreach my $argument (@$args) { - $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 +342,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 b8d0f23a9..5b302f018 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 [-i ] [-n ] [-V | --verbose] \n imgcapture [-h|--help] \n imgcapture [-v|--version]"; + my $usage = "Usage: imgcapture -t|--type {diskless|sysclone} [-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..74a969904 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 ($::VERBOSE) { + 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 413382266..7b8858156 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,7 +63,7 @@ sub findme { return; } unless ($SEQdiscover[0]) { - return; + return; } # Get the parameters for the sequential discovery @@ -444,20 +445,25 @@ 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] Sequential Discovery: 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]"; + 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, @@ -500,7 +506,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})) { @@ -510,6 +522,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; } } @@ -575,7 +588,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(); @@ -697,9 +710,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); @@ -729,9 +745,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]) { @@ -739,12 +756,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; @@ -787,7 +805,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 { @@ -805,12 +823,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|mtms|undef|all] [-l] + nodediscoverls [-t seq|profile|switch|blade|manual|mtms|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); @@ -819,14 +839,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)) { + 't=s' => \$type, + 'u=s' => \$uuid, + 'l' => \$long, + 'h|help' => \$help, + 'V|verbose' => \$::VERBOSE, + 'v|version' => \$ver, + 'z|zvmhost=s' => \$zvmHost )) { $usage->($callback); return; } @@ -841,9 +862,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|mtms|undef|all)$/) { + if ($type !~ /^(seq|profile|switch|blade|manual|mtms|undef|zvm|all)$/) { $usage->($callback, "The discovery type \'$type\' is not supported."); return; } @@ -852,24 +873,31 @@ Usage: # Check the running of sequential discovery @SEQDiscover = xCAT::TableUtils->get_site_attribute("__SEQDiscover"); + @ZVMDiscover = xCAT::TableUtils->get_site_attribute("__ZVMDiscover"); + my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); if ($SEQDiscover[0]) { $type = "seq"; - } else { - my @PCMDiscover = xCAT::TableUtils->get_site_attribute("__PCMDiscover"); - if ($PCMDiscover[0]) { - - #return directly if my type of discover is not running. + } elsif ($PCMDiscover[0]) { + #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 - $type = "all"; - } + } else { + # no type, no seq and no profile, then just display all + $type = "all"; } } + # 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; + } + my $DBname = xCAT::Utils->get_DBName; # support for DB2 - # Go thought discoverydata table and display the disocvery entries + # Go through discoverydata table and display the discovery entries my $distab = xCAT::Table->new('discoverydata'); unless ($distab) { my $rsp; @@ -922,7 +950,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) { @@ -957,8 +986,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); @@ -988,6 +1020,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."; @@ -997,10 +1030,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); } @@ -1016,6 +1052,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; @@ -1029,10 +1068,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); @@ -1050,7 +1092,8 @@ Usage: 'r' => \$remove, 'h|help' => \$help, 'V|verbose' => \$::VERBOSE, - 'v|version' => \$ver)) { + 'v|version' => \$ver, + 'z|zvmhost=s' => \$zvmHost )) { $usage->($callback); return; } @@ -1065,6 +1108,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) { @@ -1103,7 +1169,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; } @@ -1139,9 +1205,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..3378e512a 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 d04a98aae..bf8ed2ad6 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,180 @@ sub process_request { Description : Delete the user from user directory Arguments : Node to remove - Returns : Nothing + Upstream instance ID (Optional) + Upstream request ID (Optional) + Returns : Nothing, errors returned in $callback 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; + my $outmsg; - # 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); + if (xCAT::zvmUtils->checkOutput( $vswitch[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$vswitch[0]" ); + return; + } + 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", "" ); + + $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$_\"", $hcp, "removeVM", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | egrep -a -i $node`; + @luns = split("\n", $out); + 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 +927,66 @@ sub removeVM { Description : Change a virtual machine's configuration Arguments : Node - Option - Returns : Nothing + Option + Returns : Nothing, errors returned in $callback 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 = ""; + my $outmsg; + my $device = 0; + my $newlinkcall = 0; + my $rc = 0; + my $vdev; - # 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 +999,7 @@ sub changeVM { $mode = $args->[4]; } - my $readPw = "''"; + my $readPw = "''"; if ($args->[5]) { $readPw = $args->[5]; } @@ -812,6 +1014,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 +1026,200 @@ 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"); - - # 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"`; - xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); + $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 + # Check if zhcp has the new routine to link and online the disk + $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/linkdiskandbringonline $userId $addr $mode"`; + + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unable to communicate with zHCP agent" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unable to communicate with zHCP agent: $hcp" ); + $error = 1; + } elsif ( $rc > 0 && $rc != 127 ) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM()Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" ); + $error = 1; + } elsif ( $rc ==0 ) { + $newlinkcall = 1; + if ($out =~ m/Success:/i){ + # sample output=>linkdiskandbringonline maint start time: 2017-03-03-16:20:48.011 + # Success: Userid maint vdev 193 linked at ad35 device name dasdh + # linkdiskandbringonline exit time: 2017-03-03-16:20:52.150 + $out = `echo "$out" | egrep -a -i "Success:"`; + my @info = split( ' ', $out ); + $device = "/dev/" . $info[10]; + } else { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Error occurred in call to linkdiskandbringonline: $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred in call to linkdiskandbringonline: $out" ); + $error = 1; + } + } else { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Could not find zhcp linkdiskandbringonline, using old code path." ); + my $retry = 3; + $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 + 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"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat /proc/dasd/devices\"", $hcp, "changeVM", $select, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + $error = 1; + } else { + $select = `echo "$select" | egrep -a -i "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) { + xCAT::zvmUtils->printSyslog( "Error occurred during dasdfmt $device. Output: $out" ); + } + } + if ( !$error ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdasd -a $device 2>&1"`; + $error = (xCAT::zvmUtils->checkOutput( $out ) == -1) ? 1 : 0; + if ($error) { + xCAT::zvmUtils->printSyslog( "Error occurred during fdasd $device. Output: $out" ); + } + } + 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; + if ($error) { + xCAT::zvmUtils->printSyslog( "Error occurred during mkfs.xfs $device. Output: $out" ); + } + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs -t $fstype $device 2>&1"`; + $error = !($out =~ m/done/); + if ($error) { + xCAT::zvmUtils->printSyslog( "Error occurred during mkfs $fstype $device. Output: $out" ); + } + } + } + if ( $error ) { + xCAT::zvmUtils->printSyslog("$out"); + $out = "(Warning) Can not format disk with fstype $fstype" . "\n"; + } else { + $out = ""; + } + + # offline and detach disk using new call so that zhcp records are updated + if ( $newlinkcall == 1 ){ + $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/offlinediskanddetach $userId $addr"`; + $rc = $?; + + $rc = $? >> 8; + if ( $rc == 255 ) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unable to communicate with zHCP agent" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unable to communicate with zHCP agent: $hcp" ); + $error = 1; + } + if ($out =~ m/Success:/i){ + # sample output=>offlinediskanddetach maint start time: 2017-03-03-16:20:48.011 + # Success: Userid maint vdev 193 unlinked + # offlinediskanddetach exit time: 2017-03-03-16:20:52.150 + xCAT::zvmUtils->printSyslog("$out"); + } else { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Error occurred in call to offlinediskanddetach: $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred in call to offlinediskanddetach: $out" ); + $error = 1; + } + # Use old code to disable and detach + } else { + xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $vdev ); + `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null"`; + xCAT::zvmUtils->printSyslog("vmcp DETACH $vdev"); + } + } + + if ( !$error ) { + # Add to active configuration + $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 +1231,7 @@ sub changeVM { $mode = $args->[4]; } - my $readPw = "''"; + my $readPw = "''"; if ($args->[5]) { $readPw = $args->[5]; } @@ -886,6 +1246,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 +1258,401 @@ 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"); - - # 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"`; - xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); + $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; + } + + my $device = 0; + my $newlinkcall = 0; + my $rc = 0; + my $vdev; + + if ( $fstype && !$error ) { # Format the disk before making it active. + # link the disk + # Check if zhcp has the new routine to link and online the disk + $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/linkdiskandbringonline $userId $addr $mode"`; + $rc = $?; + + $rc = $? >> 8; + if ($rc == 255) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unable to communicate with zHCP agent" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unable to communicate with zHCP agent: $hcp" ); + $error = 1; + } elsif ( $rc > 0 && $rc != 127 ) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Unexpected error from SSH call to linkdiskandbringonline rc: $rc $out" ); + $error = 1; + } elsif ( $rc ==0 ) { + $newlinkcall = 1; + if ($out =~ m/Success:/i){ + # sample output=>linkdiskandbringonline maint start time: 2017-03-03-16:20:48.011 + # Success: Userid maint vdev 193 linked at ad35 device name dasdh + # linkdiskandbringonline exit time: 2017-03-03-16:20:52.150 + $out = `echo "$out" | egrep -a -i "Success:"`; + my @info = split( ' ', $out ); + $device = $info[10]; + } else { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Error occurred in call to linkdiskandbringonline: $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM()(Error occurred in call to linkdiskandbringonline: $out" ); + $error = 1; + } + } else { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Could not find zhcp linkdiskandbringonline, using old code path." ); + my $retry = 3; + $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 + 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"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat /proc/dasd/devices\"", $hcp, "changeVM", $select, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + $error = 1; + } else { + $out = `echo "$out" | egrep -a -i "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; + } + } + } + + #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; + if ($error) { + xCAT::zvmUtils->printSyslog( "Error occurred during fdisk $device. Output: $out" ); + } + } + + # 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) { + xCAT::zvmUtils->printSyslog( "Error occurred during fdisk to make a partition on $device. Output: $out" ); + } + } + 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; + if ($error) { + xCAT::zvmUtils->printSyslog( "Error occurred during mkfs.xfs $device. Output: $out" ); + } + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO mkfs -t $fstype $device 2>&1"`; + $error = !($out =~ m/done/); + if ($error) { + xCAT::zvmUtils->printSyslog( "Error occurred during mkfs $fstype $device. Output: $out" ); + } + } + } + if ( $error ) { + xCAT::zvmUtils->printSyslog("$out"); + $out = "(Warning) Can not format disk with fstype $fstype" . "\n"; + } else { + $out = ""; + } + + # offline and detach disk using new call so that zhcp records are updated + if ( $newlinkcall == 1 ){ + $out = `ssh -o ConnectTimeout=30 $::SUDOER\@$hcp "$::SUDO $::DIR/offlinediskanddetach $userId $addr"`; + $rc = $?; + + $rc = $? >> 8; + if ( $rc == 255 ) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unable to communicate with zHCP agent" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM()( is unable to communicate with zHCP agent: $hcp" ); + $error = 1; + } + if ( $rc > 0 ) { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Unexpected error from SSH call to offlinediskanddetach rc: $rc $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM()Unexpected error from SSH call to offlinediskanddetach rc: $rc $out" ); + $error = 1; + } + if ($out =~ m/Success:/i){ + # sample output=>Success: Userid maint vdev 193 unlinked + xCAT::zvmUtils->printSyslog("$out"); + } else { + xCAT::zvmUtils->printSyslog( "$node: changeVM() Error occurred in call to offlinediskanddetach: $out" ); + xCAT::zvmUtils->printLn( $callback, "$node: changeVM() Error occurred in call to offlinediskanddetach: $out" ); + $error = 1; + } + # Use old code to disable and detach + } else { + xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $hcp, "-d", $vdev ); + `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp DETACH $vdev &> /dev/null"`; + xCAT::zvmUtils->printSyslog("vmcp DETACH $vdev"); + } + } + if (!$error) { + # Add to active configuration + $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); + if (xCAT::zvmUtils->checkOutput( $deviceRef ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$deviceRef" ); + return; + } + %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 +1663,154 @@ 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'; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + $out = `echo "$out" | egrep -a -i 'ccw/0.0.' . $cur_device. ']online'`; + if (!(length $out)) { + # 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 +1821,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 +1832,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 +1888,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 +1914,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 +1949,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 +1960,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 +1996,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 +2014,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 +2029,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 +2038,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 +2056,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 +2070,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 +2089,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 +2117,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 +2126,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 +2136,200 @@ 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; + $out = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + $out = `echo "$out" | egrep -a -i "ID_SERIAL="`; + $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 +2342,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 +2379,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 +2392,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 +2479,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 +2644,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 +2655,6 @@ sub changeVM { $updateEntry = 1; next; } else { - # Write directory entry to file `echo "$_" >> $userEntryFile`; } @@ -1848,7 +2663,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 +2675,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) { @@ -1890,37 +2705,48 @@ sub changeVM { # Find the pool that contains the SCSI/FCP device my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun); + if (xCAT::zvmUtils->checkOutput( $pool ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$pool" ); + return; + } 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"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$pool.conf\" ", $hcp, "changeVM", $select, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $select = `echo "$select" | egrep -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 +2758,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 +2826,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 +2835,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 +2843,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 +2851,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 +2871,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 +2879,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 +2887,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 +2923,197 @@ 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) { + xCAT::zvmUtils->printSyslog("smcli Image_Definition_Query_DM -T $userId -k CPU"); + my $proc = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $userId -k CPU"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Definition_Query_DM -T $userId -k CPU\"", $hcp, "changeVM", $proc, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $proc = `echo "$proc" | egrep -a -i 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 +3125,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,40 +3244,60 @@ 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; @@ -2324,114 +3308,332 @@ sub powerVM { } # 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 ); + } + # 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; } @@ -2444,48 +3646,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; } @@ -2511,11 +3712,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; @@ -2527,104 +3728,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"; @@ -2636,7 +3839,7 @@ sub scanVM { $str .= " mgt=zvm\n\n"; } - xCAT::zvmUtils->printLn($callback, "$str"); + xCAT::zvmUtils->printLn( $callback, "$str" ); return; } @@ -2646,56 +3849,71 @@ sub scanVM { Description : Get hardware and software inventory of a given node Arguments : Node - Type of inventory (config|all) - Returns : Nothing + Type of inventory (all|config|console [logsize]|cpumem|cpumempowerstat|--freerepospace) + Returns : Nothing, errors returned in $callback Example : inventoryVM($callback, $node, $args); - + =cut #------------------------------------------------------- sub inventoryVM { # Get inputs - my ($callback, $node, $args) = @_; + my ( $callback, $node, $args ) = @_; # Output string my $str = ""; + my $outmsg; + my $rc; + # 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); @@ -2713,7 +3931,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); @@ -2727,7 +3945,90 @@ 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); + if (xCAT::zvmUtils->checkOutput( $cputime ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$cputime" ); + return; + } + + $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); @@ -2744,8 +4045,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); @@ -2763,7 +4064,11 @@ 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); + if (xCAT::zvmUtils->checkOutput( $cputime ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$cputime" ); + return; + } # Create output string $str .= "z/VM UserID: $userId\n"; @@ -2782,7 +4087,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); @@ -2793,16 +4100,83 @@ 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"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO /usr/sbin/vmur list\"", $hcp, "inventoryVM", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | 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; } @@ -2812,114 +4186,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; } @@ -2932,102 +4386,108 @@ 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}; # Create a new user in zVM without user directory entry file my $out; + my $outmsg; + my $rc; 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; } @@ -3036,11 +4496,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; } } @@ -3060,48 +4520,52 @@ 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 $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q vmlan\"", $hcp, "makeVM", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + my $prefix = `echo "$out" | 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 = `echo "$out" | 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; } @@ -3109,26 +4573,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; @@ -3136,12 +4599,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 @@ -3151,19 +4614,16 @@ sub makeVM { my $oldNicDef; my $nicDef; my $id; - 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 @@ -3171,14 +4631,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"); @@ -3187,39 +4647,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) @@ -3229,35 +4710,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; } @@ -3273,6 +4751,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"`; @@ -3282,11 +4761,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); @@ -3294,8 +4773,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 @@ -3305,7 +4784,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; @@ -3319,16 +4798,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; @@ -3345,35 +4825,43 @@ 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 (xCAT::zvmUtils->checkOutput( $dedicates[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$dedicates[0]" ); + return; + } + 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)) { @@ -3387,69 +4875,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; } @@ -3458,21 +4946,25 @@ 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 ); + if (xCAT::zvmUtils->checkOutput( $targetMac ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$targetMac" ); + return; + } # 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); @@ -3497,7 +4989,11 @@ 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 ); + if (xCAT::zvmUtils->checkOutput( $srcDisks[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$srcDisks[0]" ); + return; + } # Get details about source disks # Output is similar to: @@ -3505,13 +5001,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) { @@ -3523,52 +5020,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; @@ -3578,12 +5077,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 @@ -3591,8 +5090,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); @@ -3607,24 +5106,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 = $_; @@ -3632,7 +5127,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"); @@ -3644,22 +5139,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; @@ -3671,36 +5165,62 @@ 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 ); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } # 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 *** @@ -3711,11 +5231,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 @@ -3734,11 +5254,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 @@ -3749,7 +5269,7 @@ sub cloneVM { # Handle the remaining nodes # Wait for all processes to end foreach (@children) { - waitpid($_, 0); + waitpid( $_, 0 ); } # Remove source user entry @@ -3757,21 +5277,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; @@ -3796,11 +5316,11 @@ sub cloneVM { Root parition device address 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, + Returns : Nothing, errors returned in $callback + Example : clone($callback, $_, $args, \@srcDisks, \%srcLinkAddr, \%srcDiskSize, + $srcNicAddr, $hcpNetName, \@srcVswitch, $srcOs, $srcMac, $netEntries, $sourceIp, $srcNetwork, $srcMask); - + =cut #------------------------------------------------------- @@ -3815,15 +5335,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/; @@ -3838,30 +5357,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; } @@ -3869,25 +5396,26 @@ 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"); my $out; + my $outmsg; my @lines; my @words; # 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]; @@ -3896,8 +5424,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"); @@ -3929,21 +5457,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 @@ -3952,17 +5480,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 @@ -3970,9 +5498,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`; @@ -3980,15 +5508,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"); @@ -3998,9 +5526,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); @@ -4016,9 +5544,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; } @@ -4027,18 +5555,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 *** @@ -4050,11 +5587,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"; } @@ -4065,27 +5602,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); @@ -4100,35 +5638,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); @@ -4143,8 +5682,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' ) @@ -4154,16 +5693,22 @@ 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"); + xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId\"", $hcp, "clone", $out, $tgtNode ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | sed '\$d' | grep -a -i "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 @@ -4174,236 +5719,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); @@ -4428,15 +6042,26 @@ 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\]"`; + xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp \"$::SUDO /usr/bin/file -s /dev/$tgtDevNode*\""); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO /usr/bin/file -s /dev/$tgtDevNode*\"", $hcp, "clone", $out, $tgtDevNode ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } } else { - $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode* | grep -v swap | grep -o $tgtDevNode\[1-9\]"`; + xCAT::zvmUtils->printSyslog("ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*\""); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*\"", $hcp, "clone", $out, $tgtDevNode ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } } + $out = `echo "$out" | egrep -a -i -v swap | egrep -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("$out"); # Wait before trying again @@ -4444,15 +6069,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"); @@ -4469,26 +6095,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 @@ -4497,32 +6128,38 @@ 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 ) { + xCAT::zvmUtils->printSyslog("grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO grep -a -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts\"", $hcp, "clone", $out, $tgtDevNode ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | egrep -a -i 'ifcfg-eth'`; 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]; } @@ -4536,7 +6173,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)) { @@ -4547,10 +6184,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) { @@ -4561,18 +6199,23 @@ EOM"`; # Set MAC address my $networkFile = $tgtNode . "NetworkConfig"; my $config; - if ($srcOs =~ m/rhel/i) { + $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $ifcfgPath\"", $hcp, "clone", $config, $tgtDevNode ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + if ( $srcOs =~ m/rhel/i ) { # Red Hat only - $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath" | grep -v "MACADDR"`; + $config = `echo "$config" | egrep -a -i -v "MACADDR"`; $config .= "MACADDR='" . $targetMac . "'\n"; } else { - # SUSE only - $config = `ssh $::SUDOER\@$hcp "$::SUDO cat $ifcfgPath" | grep -v "LLADDR" | grep -v "UNIQUE"`; + $config = `echo "$config" | egrep -a -i -v "LLADDR" | egrep -a -i -v "UNIQUE"`; # Set to MAC address (only for layer 2) - if ($layer == 2) { + if ( $layer == 2 ) { $config .= "LLADDR='" . $targetMac . "'\n"; $config .= "UNIQUE=''\n"; } @@ -4589,16 +6232,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 @@ -4606,9 +6247,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`; @@ -4629,7 +6270,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"`; @@ -4644,14 +6285,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; } } @@ -4664,35 +6305,39 @@ EOM"`; * Punch initrd, kernel, and parmfile to node reader * Layer 2 and 3 VSwitch/Lan supported Arguments : Node - Returns : Nothing + Returns : Nothing, errors returned in $callback 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/; @@ -4704,8 +6349,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=//; @@ -4719,7 +6365,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; } @@ -4736,30 +6381,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]; @@ -4769,8 +6413,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'}; @@ -4780,41 +6424,42 @@ 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") { + my $outmsg; + 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; } @@ -4822,62 +6467,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; } @@ -4889,12 +6529,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) { @@ -4902,18 +6542,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)); @@ -4921,52 +6561,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)); } @@ -4979,8 +6616,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 @@ -5000,7 +6637,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)) { @@ -5013,18 +6650,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; @@ -5034,21 +6671,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; } @@ -5059,11 +6695,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 @@ -5083,7 +6719,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"; @@ -5092,16 +6728,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; } @@ -5112,31 +6748,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; @@ -5152,7 +6788,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`; @@ -5172,17 +6808,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 @@ -5192,7 +6883,14 @@ 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/$_"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$_\"", $hcp, "nodeSet", $entry, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $entry = `echo "$entry" | egrep -a -i ",$node,"`; chomp($entry); if (!$entry) { next; @@ -5201,13 +6899,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) { @@ -5222,7 +6920,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) { @@ -5231,8 +6929,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`; @@ -5250,9 +6948,8 @@ END } if ($hasZfcp) { - # Insert device list - my $find = 'replace_zfcp'; + my $find = 'replace_zfcp'; my $replace = <\\ END @@ -5260,7 +6957,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"); @@ -5268,13 +6965,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($_); } } @@ -5301,11 +6998,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"; @@ -5317,9 +7014,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); @@ -5329,42 +7027,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; } @@ -5372,41 +7079,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`; @@ -5426,9 +7133,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 @@ -5438,7 +7173,14 @@ 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/$_"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$_\"", $hcp, "nodeSet", $entry, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $entry = `echo "$entry" | egrep -a -i ",$node,"`; chomp($entry); if (!$entry) { next; @@ -5447,13 +7189,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) { @@ -5468,7 +7210,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) { @@ -5477,14 +7219,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; } } @@ -5495,17 +7237,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; } } @@ -5515,17 +7257,22 @@ END close(SAMPLEPARM); # Get mdisk virtual address - my @mdisks = xCAT::zvmUtils->getMdisks($callback, $::SUDOER, $node); + my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $::SUDOER, $node ); + if (xCAT::zvmUtils->checkOutput( $mdisks[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$mdisks[0]" ); + return; + } @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],"; @@ -5534,20 +7281,24 @@ 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 ); + if (xCAT::zvmUtils->checkOutput( $dedicates[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$dedicates[0]" ); + return; + } @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],"; @@ -5556,8 +7307,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 @@ -5587,15 +7338,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"; @@ -5603,9 +7354,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); @@ -5616,42 +7368,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; } @@ -5659,25 +7420,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; } } @@ -5689,42 +7450,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($_); } } @@ -5742,7 +7499,7 @@ END $parms = $parms . "STATEMNT=$stateMnt XCAT=$master:$xcatdPort\n"; # Write to parmfile - open(PARMFILE, ">$parmFile"); + open( PARMFILE, ">$parmFile" ); print PARMFILE "$parms"; close(PARMFILE); } @@ -5752,117 +7509,249 @@ 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." ); + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO ls /tmp"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $::SUDOER\@$hcp \"$::SUDO ls /tmp\"", $hcp, "nodeSet", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + if (`echo "$out" | egrep -a -i "$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 (`echo "$out" | egrep -a -i "$"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 (`echo "$out" | egrep -a -i "$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 ); + if (xCAT::zvmUtils->checkOutput( $srcDisks[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$srcDisks[0]" ); + return; + } + + # 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); } @@ -5870,94 +7759,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; } @@ -5972,54 +7864,57 @@ END * Requires the node be online * Saves MAC address in 'mac' table Arguments : Node - Returns : Nothing + Returns : Nothing, errors returned in $callback 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 ); } + my $out; + my $outmsg; + my $rc; + # 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; } @@ -6027,51 +7922,63 @@ 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); + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q v nic"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q v nic\"", $hcp, "getMacs", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | 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; } @@ -6085,42 +7992,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; } @@ -6131,10 +8037,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; } @@ -6145,60 +8051,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 @@ -6212,7 +8117,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; @@ -6222,32 +8127,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; } @@ -6256,14 +8161,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 @@ -6272,58 +8177,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" ); } } } @@ -6333,7 +8292,7 @@ sub updateNode { $out = "$node: (Error) Option not supported"; } - xCAT::zvmUtils->printLn($callback, "$out"); + xCAT::zvmUtils->printLn( $callback, "$out" ); return; } @@ -6345,14 +8304,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 @@ -6374,11 +8333,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'}; @@ -6388,9 +8347,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 @@ -6400,22 +8359,25 @@ 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 $ssi{$node} = xCAT::zvmUtils->querySSI($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $ssi{$node} ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$ssi{$node}" ); + return; + } # 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; @@ -6423,30 +8385,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 @@ -6457,11 +8418,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; @@ -6469,68 +8429,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; } @@ -6541,25 +8499,25 @@ sub listTree { Description : Configure the virtualization hosts Arguments : Node Arguments - Returns : Nothing + Returns : Nothing, errors returned in $callback 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; } @@ -6567,7 +8525,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 @@ -6578,42 +8536,44 @@ sub changeHypervisor { # Output string my $out = ""; + my $outmsg; + my $rc; # 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"); @@ -6623,11 +8583,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; } @@ -6639,24 +8631,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; } @@ -6666,23 +8657,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 @@ -6691,15 +8682,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; } @@ -6708,20 +8699,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] "; } @@ -6732,25 +8722,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)."); @@ -6765,29 +8754,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 @@ -6801,19 +8799,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"`; } @@ -6822,33 +8822,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; } @@ -6858,28 +8857,36 @@ 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"); + if (xCAT::zvmUtils->checkOutput( $pool ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$pool" ); + return; + } + 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); + if (xCAT::zvmUtils->checkOutput( $srcDiskRef ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$srcDiskRef" ); + return; + } 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'}; @@ -6888,9 +8895,13 @@ sub changeHypervisor { my $tgtSize; if ($useWwpnLun) { my $tgtDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $tgtWwpn, $tgtLun); + if (xCAT::zvmUtils->checkOutput( $tgtDiskRef ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$tgtDiskRef" ); + return; + } 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'}; @@ -6906,7 +8917,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; } } @@ -6919,20 +8930,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/) { @@ -6943,51 +8953,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); @@ -7001,44 +9009,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(); @@ -7046,21 +9099,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; @@ -7072,11 +9124,19 @@ sub changeHypervisor { xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool"); return; } else { + if (xCAT::zvmUtils->checkOutput( $pool ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$pool" ); + return; + } xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool"); } # Get source device's attributes my $srcDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun); + if (xCAT::zvmUtils->checkOutput( $srcDiskRef ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$srcDiskRef" ); + return; + } my %srcDisk = %$srcDiskRef; if (!defined($srcDisk{'lun'}) && !$srcDisk{'lun'}) { xCAT::zvmUtils->printLn($callback, "$node: (Error) Source zFCP device $wwpn/$lun does not exists"); @@ -7085,21 +9145,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"; @@ -7109,41 +9168,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"); @@ -7157,11 +9215,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) { @@ -7170,24 +9228,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"); @@ -7195,20 +9253,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(); @@ -7219,10 +9277,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"; @@ -7236,14 +9294,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,16 +9311,20 @@ sub changeHypervisor { xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool"); return; } else { + if (xCAT::zvmUtils->checkOutput( $pool ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$pool" ); + return; + } xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool"); } # 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; @@ -7271,10 +9332,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; @@ -7289,9 +9349,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) { @@ -7300,7 +9360,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"); @@ -7313,26 +9373,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"); } @@ -7348,14 +9409,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 @@ -7364,8 +9425,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 @@ -7374,7 +9435,7 @@ sub changeHypervisor { } # removevswitch [name] - elsif ($args->[0] eq "--removevswitch") { + elsif ( $args->[0] eq "--removevswitch" ) { my $name = $args->[1]; # Delete a VSWITCH @@ -7382,85 +9443,118 @@ 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"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$pool.conf\"", $hcp, "changeHypervisor", $entry, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $entry = `echo "$entry" | 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) { @@ -7473,17 +9567,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); @@ -7498,25 +9593,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 = ""; @@ -7525,22 +9632,33 @@ 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); + if (xCAT::zvmUtils->checkOutput( $deviceRef ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$deviceRef" ); + return; + } + 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); } @@ -7549,20 +9667,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. @@ -7570,16 +9688,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"`; } @@ -7591,8 +9707,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; @@ -7604,14 +9719,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; @@ -7624,16 +9739,16 @@ sub changeHypervisor { Description : Get hardware and software inventory of a given hypervisor Arguments : Node Type of inventory (config|all) - Returns : Nothing + Returns : Nothing, errors returned in $callback 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'; @@ -7641,14 +9756,18 @@ sub inventoryHypervisor { # Output string my $str = ""; + my $rc; + my $out; + my $outmsg; + # 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; } @@ -7658,49 +9777,94 @@ sub inventoryHypervisor { my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); # Load VMCP module - my $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/modprobe vmcp"`; + $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); + if (xCAT::zvmUtils->checkOutput( $lparCpuTotal ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparCpuTotal" ); + return; + } # Get used physical CPU in this LPAR my $lparCpuUsed = xCAT::zvmUtils->getLparCpuUsed($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparCpuUsed ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparCpuUsed" ); + return; + } # Get LPAR memory total my $lparMemTotal = xCAT::zvmUtils->getLparMemoryTotal($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparMemTotal ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparMemTotal" ); + return; + } # Get LPAR memory Offline my $lparMemOffline = xCAT::zvmUtils->getLparMemoryOffline($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparMemOffline ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparMemOffline" ); + return; + } # Get LPAR memory Used my $lparMemUsed = xCAT::zvmUtils->getLparMemoryUsed($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparMemUsed ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparMemUsed" ); + return; + } - $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); + if (xCAT::zvmUtils->checkOutput( $lparCpuTotal ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparCpuTotal" ); + return; + } # Get used physical CPU in this LPAR my $lparCpuUsed = xCAT::zvmUtils->getLparCpuUsed($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparCpuUsed ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparCpuUsed" ); + return; + } # Get CEC model my $cecModel = xCAT::zvmUtils->getCecModel($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $cecModel ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$cecModel" ); + return; + } # Get vendor of CEC my $cecVendor = xCAT::zvmUtils->getCecVendor($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $cecVendor ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$cecVendor" ); + return; + } # Get hypervisor type and version my $hvInfo = xCAT::zvmUtils->getHypervisorInfo($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $hvInfo ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$hvInfo" ); + return; + } # Get processor architecture my $arch = xCAT::zvmUtils->getArch($::SUDOER, $hcp); @@ -7710,15 +9874,31 @@ sub inventoryHypervisor { # Get LPAR memory total my $lparMemTotal = xCAT::zvmUtils->getLparMemoryTotal($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparMemTotal ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparMemTotal" ); + return; + } # Get LPAR memory Offline my $lparMemOffline = xCAT::zvmUtils->getLparMemoryOffline($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparMemOffline ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparMemOffline" ); + return; + } # Get LPAR memory Used my $lparMemUsed = xCAT::zvmUtils->getLparMemoryUsed($::SUDOER, $hcp); + if (xCAT::zvmUtils->checkOutput( $lparMemUsed ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$lparMemUsed" ); + return; + } + + # 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"; @@ -7730,17 +9910,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); @@ -7749,17 +9929,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; @@ -7771,8 +9953,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"; @@ -7781,8 +9963,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]; @@ -7798,36 +9979,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"`; } @@ -7837,10 +10018,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; } @@ -7855,16 +10036,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+$//; @@ -7880,12 +10060,17 @@ 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++) { + xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId\"", $hcp, "inventoryHypervisor", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | 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/; @@ -7905,32 +10090,37 @@ 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:"); + xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp\"", $hcp, "inventoryHypervisor", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | 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/; @@ -7960,14 +10150,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,,,,"); } } } @@ -7976,19 +10165,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"`; @@ -7996,10 +10185,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; } @@ -8010,10 +10199,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; } @@ -8024,22 +10213,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; } @@ -8050,10 +10239,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; } @@ -8064,10 +10253,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; } @@ -8075,7 +10264,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]*):*/; @@ -8087,19 +10276,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) { @@ -8111,16 +10299,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" @@ -8131,21 +10319,27 @@ 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:"); + xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp\"", $hcp, "inventorHypervisor", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | 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/; @@ -8158,14 +10352,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]; @@ -8174,32 +10367,55 @@ 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`; + $out = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO cat $::ZFCPPOOL/$pool.conf\"", $hcp, "inventoryHypervisor", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | egrep -a -i "$space"`; + $str .= $out; } } # 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; } @@ -8217,132 +10433,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; } @@ -8353,9 +10563,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" ); } } @@ -8370,39 +10580,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; } @@ -8410,26 +10620,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; @@ -8437,7 +10646,7 @@ sub evacuate { my $smcliArgs; my $nodes = ""; foreach (@entries) { - $iNode = $_->{'node'}; + $iNode = $_->{'node'}; $iUserId = $_->{'userid'}; # Skip zHCP entry @@ -8445,7 +10654,7 @@ sub evacuate { next; } - $nodes .= $iNode . ","; + $nodes .= $iNode . ","; } # Strip last comma @@ -8453,14 +10662,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; } @@ -8475,18 +10684,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; @@ -8494,34 +10703,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; } @@ -8531,10 +10739,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; } @@ -8544,48 +10758,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" ); } } @@ -8597,31 +10849,56 @@ sub eventLog { Arguments : Node OS Archictecture + Specified provisioning method type Profile Device information - Returns : Nothing - Example : imageCapture( $callback, $node, $os, $arch, $profile, $osimg, $device ); - + Compression level: 0 - none, 1 thru 9 - gzip compression level + Returns : Nothing, errors returned in $callback + 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 $outmsg; 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'; } @@ -8636,145 +10913,407 @@ 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"'; + $out = xCAT::zvmUtils->execcmdonVM($sudoer, $node, $cmd, $callback); + if (xCAT::zvmUtils->checkOutput( $out ) == -1) { + return; + } + $out = `echo "$out" | egrep -a -i "^root=" | cut '-c6-'`; + + 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"); + $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 { + # provmethod is 'sysclone' + + # Get the list of mdisks + my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sudoer, $node ); + if (xCAT::zvmUtils->checkOutput( $srcDisks[0] ) == -1) { + xCAT::zvmUtils->printLn( $callback, "$srcDisks[0]" ); return; } - } else { - $devName = substr $devName, 5; + 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"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $sudoer\@$hcp \"$sudo /sbin/vmcp q user $targetUserId 2>/dev/null\"", $hcp, "imageCapture", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | egrep -a -i "HCPCQU045E"`; + while ( !$out && $max < 60 ) { + sleep(15); # Wait 15 seconds + $max++; + $out=`ssh $sudoer\@$hcp "$sudo /sbin/vmcp q user $targetUserId 2>/dev/null"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $sudoer\@$hcp \"$sudo /sbin/vmcp q user $targetUserId 2>/dev/null\"", $hcp, "imageCapture", $out, $node ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | egrep -a -i "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; } } @@ -8782,41 +11321,934 @@ 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, errors returned in $callback + 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 $outmsg; + 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 + xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $tgtUserId"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId"`; + ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $::SUDOER\@$hcp \"$::SUDO $::DIR/smcli Image_Query_DM -T $tgtUserId\"", $hcp, "specialClone", $out, $tgtNode ); + if ($rc != 0) { + xCAT::zvmUtils->printLn( $callback, "$outmsg" ); + return; + } + $out = `echo "$out" | sed '\$d' | grep -a -i "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..a390dce39 --- /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]*$/i ); + $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..66d5e6808 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/zvmdiscovery.pm @@ -0,0 +1,2313 @@ +#!/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', 'maint640', + 'migmaint', 'monwrite', 'mproute', + 'operator', 'operatns', 'opersymp', 'opncloud', + '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', 'vsmwork4', + 'vsmwork5', 'vsmwork6', 'vsmwork7', 'vsmwork8', 'vsmwork9', + '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 = scalar( keys %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 117f483a9..9d8d09b6b 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 7c6fb8a78..0094eb924 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 68d5934f6..605ad9a7e 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 c59eb1df5..f5cf474ed 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..f51e1327f --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_JUNO.pl @@ -0,0 +1,1556 @@ +#!/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 ); + if ( ! defined $rc or $rc ne '1' ) { + print "Unable to open $driver for output: $!\n"; + return 200; + } + + # 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 $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + print "Warning: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } else { + print "Info: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } + return 603; + } + 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`; + my $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + print "Warning: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } else { + print "Info: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } + return 604; + } + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Remove sequence numbers and weed out blank lines + $line = substr( $line, 0, 71 ); + $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 ); + if ( exists $settings{'role'} ) { + $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..17f16a7d5 --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_KILO.pl @@ -0,0 +1,1557 @@ +#!/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 ); + if ( ! defined $rc or $rc ne '1' ) { + print "Unable to open $driver for output: $!\n"; + return 200; + } + + # 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 $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + print "Warning: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } else { + print "Info: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } + return 603; + } + 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`; + my $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + print "Warning: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } else { + print "Info: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } + return 604; + } + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Remove sequence numbers and weed out blank lines + $line = substr( $line, 0, 71 ); + $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 ); + if ( exists $settings{'role'} ) { + $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..ee269271f --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_LIBERTY.pl @@ -0,0 +1,1763 @@ +#!/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 ); + if ( ! defined $rc or $rc ne '1' ) { + print "Unable to open $driver for output: $!\n"; + return 200; + } + + # 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 $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + print "Warning: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } else { + print "Info: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } + return 603; + } + 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 + $out = `grep -v ^\$ $file`; + my $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + print "Warning: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } else { + print "Info: $file cannot be read.\n". + " Verification continues but without properties from the indicated file.\n"; + } + return 604; + } + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Remove sequence numbers and weed out blank lines + $line = substr( $line, 0, 71 ); + $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 ); + if ( exists $settings{'role'} ) { + $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..46bb8ff98 --- /dev/null +++ b/xCAT-server/share/xcat/tools/zvm/prep_zxcatIVP_NEWTON.pl @@ -0,0 +1,2442 @@ +#!/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.', + }, + 'FILE05' => + { 'severity' => 2, + 'recAction' => 0, + 'text' => '%s cannot be read.', + 'explain' => 'The indicated file is an OpenStack configuration file which was '. + 'attempted to be scanned to obtain configuration properties. '. + 'This file cannot be read.', + 'sysAct' => 'Verification continues but without properties from the indicated file.', + 'userResp' => 'Determine why the file cannot be read. If the file is not needed then you can '. + 'ignore this message. Otherwise, you should correct the issue.'. + "\n". + 'If this script is being run remotely by the xCAT IVP rather than '. + 'by your invocation of the script, you may need to specify a '. + 'different OpenStack user to the orchestrator script (verifynode). '. + 'This will require that you have set up the certificates on the '. + 'compute node to allow the xCAT MN to access the system during the IVP. '. + 'You can find information on setting up SSH keys in the Enabling z/VM for OpenStack '. + 'manual in the OpenStack Configuration chapter.', + }, + 'FILE06' => + { 'severity' => 4, + 'recAction' => 0, + 'text' => '%s cannot be read.', + 'explain' => 'The indicated file is an OpenStack configuration file which was '. + 'attempted to be scanned to obtain configuration properties. '. + 'This file cannot be read.', + 'sysAct' => 'Verification continues but without properties from the indicated file.', + 'userResp' => 'Determine why the file cannot be read. If the file is not needed then you can '. + 'ignore this message. Otherwise, you should correct the issue.'. + "\n". + 'If this script is being run remotely by the xCAT IVP rather than '. + 'by your invocation of the script, you may need to specify a '. + 'different OpenStack user to the orchestrator script (verifynode). '. + 'This will require that you have set up the certificates on the '. + 'compute node to allow the xCAT MN to access the system during the IVP. '. + 'You can find information on setting up SSH keys in the Enabling z/VM for OpenStack '. + 'manual in the OpenStack Configuration chapter.', + }, + '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 ); + if ( ! defined $rc or $rc ne '1' ) { + logResponse( 'DRIV02', $driver, $! ); + return 200; + } + + # 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 $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + logResponse( 'FILE06', $file ); + } else { + logResponse( 'FILE05', $file ); + } + return 603; + } + 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`; + my $rc = $?; + if ( $rc != 0 ) { + if ( $required ) { + logResponse( 'FILE06', $file ); + } else { + logResponse( 'FILE05', $file ); + } + return 604; + } + $out =~ s{/\*.*?\*/}{}gs; + + my @lines = split( "\n", $out ); + foreach my $line ( @lines ) { + # Remove sequence numbers and weed out blank lines + $line = substr( $line, 0, 71 ); + $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 ); + if ( exists $settings{'role'} ) { + $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..4f9c35be3 100644 --- a/xCAT-server/xCAT-server.spec +++ b/xCAT-server/xCAT-server.spec @@ -18,23 +18,37 @@ 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 +# 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) +%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 +%if %nots390x Requires: grub2-xcat perl-Net-HTTPS-NB perl-HTTP-Async -#%endif +%endif %endif %endif @@ -313,7 +327,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 +338,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 @@ -334,20 +348,32 @@ cp xCAT-wsapi/* $RPM_BUILD_ROOT/%{prefix}/ws rm -f $RPM_BUILD_ROOT/%{prefix}/ws/xcatws.cgi %endif -%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 -%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 +%if %nots390x +rm -f $RPM_BUILD_ROOT/%{prefix}/ws/zvmxcatws.cgi %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 +%if %fsm +%else +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 +%if %nots390x +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 +%else +# Add in old version 1 REST-API and version 2 REST-API to z/VM, default to version 1 +echo "ScriptAlias /xcatwsv2 %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 +echo "ScriptAlias /xcatwsv2 %{prefix}/ws/xcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 +echo "ScriptAlias /xcatws %{prefix}/ws/zvmxcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache22 +echo "ScriptAlias /xcatws %{prefix}/ws/zvmxcatws.cgi" >> $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat-ws.conf.apache24 +%endif +%endif + +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 +388,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 +460,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.apache22 b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 index 8e7326c59..21ce5b943 100644 --- a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 +++ b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache22 @@ -1,10 +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 %{REQUEST_URI} xcatws -RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R,L] +RewriteCond %{HTTPS} !=on +RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L] +RewriteRule ^/?xcatwsv2/(.*) https://%{SERVER_NAME}/xcatwsv2/$1 [R,L] - + Order allow,deny Allow from all diff --git a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 index b10ccf224..7fc48853c 100644 --- a/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 +++ b/xCAT-server/xCAT-wsapi/xcat-ws.conf.apache24 @@ -1,10 +1,11 @@ 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] +RewriteRule ^/?xcatwsv2/(.*) https://%{SERVER_NAME}/xcatwsv2/$1 [R,L] - + Require all granted diff --git a/xCAT-server/xCAT-wsapi/xcatws.cgi b/xCAT-server/xCAT-wsapi/xcatws.cgi deleted file mode 100755 index a5e8c39dd..000000000 --- a/xCAT-server/xCAT-wsapi/xcatws.cgi +++ /dev/null @@ -1,3455 +0,0 @@ -#!/usr/bin/perl -# IBM(c) 2014 EPL license http://www.eclipse.org/legal/epl-v10.html -use strict; -use CGI qw/:standard/; #todo: remove :standard when the code only uses object oriented interface -use Data::Dumper; - -#talk to the server -use Socket; -use IO::Socket::INET; -use IO::Socket::SSL; - -# The hash %URIdef defines all the xCAT resources which can be access from Web Service. -# This script will be called when a https request with the URI started with /xcatws/ is sent to xCAT Web Service -# Inside this script: -# 1. The main body parses the URI and parameters -# 2. Base on the URI, go through the %URIdef to find the matched resource by the 'matcher' which is defined for each resource -# 3. Call the 'fhandler' which is defined in the resource to communicate with xcatd and get the xml response -# 3.1 The 'fhandler' generates the xml request base on the resource, parameters and http method 'GET|PUT|POST|DELETE', sends to xcatd and then get the xml response -# 4. Call the 'outhdler' which is defined in the resource to parse the xml response and translate it to JSON format -# 5. Output the http response to STDOUT -# -# Refer to the $URIdef{node}->{allnode} and $URIdef{node}->{nodeallattr} for your new created resource definition. -# -# |--node - Resource Group -# | `--allnode - Resource Name -# | `--desc - Description for the Resource -# | `--desc[1..10] - Additional description for the Resource -# | `--matcher - The matcher which is used to match the URI to the Resource -# | `--GET - The info is used to handle the GET request -# | `--desc - Description for the GET operation -# | `--desc[1..10] - Additional description for the GET operation -# | `--usage - Usage message. The format must be '|Parameters for the GET request|Returns for the GET request|'. The message in the '|' can be black, but the delimiter '|' must be kept. -# | `--example - Example message. The format must be '|Description|GET|URI PUT/POST_data|Return Msg|'. The messages in the four sections must be completed. -# | `--cmd - The xCAT command line coammnd which will be used to complete the request. It's not a must have attribute. -# | `--fhandler - The call back subroutine which is used to handle the GET request. Generally, it parses the parameters from request and then call xCAT command. This subroutine can be exclusive or shared. -# | `--outhdler - The call back subroutine which is used to handle the GET request. Generally, it parses the xml output from the 'fhandler' and then format the output to JSON. This subroutine can be exclusive or shared. -# | `--PUT - The info is used to handle the PUT request -# | `--POST - The info is used to handle the POST request -# | `--DELETE - The info is used to handle the DELETE request - -# The common messages which can be used in the %URIdef -my %usagemsg = ( - objreturn => "Json format: An object which includes multiple \' : {att:value, attr:value ...}\' pairs.", - objchparam => "Json format: An object which includes multiple \'att:value\' pairs.", - non_getreturn => "No output when execution is successfull. Otherwise output the error information in the Standard Error Format: {error:[msg1,msg2...],errocode:errornum}." -); - -my %URIdef = ( - #### definition for node resources - nodes => { - allnode => { - desc => "[URI:/nodes] - The node list resource.", - desc1 => "This resource can be used to display all the nodes which have been defined in the xCAT database.", - matcher => '^/nodes$', - GET => { - desc => "Get all the nodes in xCAT.", - desc1 => "The attributes details for the node will not be displayed.", - usage => "||Json format: An array of node names.|", - example => "|Get all the node names from xCAT database.|GET|/nodes|[\n \"node1\",\n \"node2\",\n \"node3\",\n]|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout_remove_appended_type, - } - }, - nodeallattr => { - desc => "[URI:/nodes/{noderange}] - The node resource", - matcher => '^/nodes/[^/]*$', - GET => { - desc => "Get all the attibutes for the node {noderange}.", - desc1 => "The keyword ALLRESOURCES can be used as {noderange} which means to get node attributes for all the nodes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the attibutes for node \'node1\'.|GET|/nodes/node1|{\n \"node1\":{\n \"profile\":\"compute\",\n \"netboot\":\"xnba\",\n \"arch\":\"x86_64\",\n \"mgt\":\"ipmi\",\n \"groups\":\"all\",\n ...\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - PUT => { - desc => "Change the attibutes for the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Change the attributes mgt=dfm and netboot=yaboot.|PUT|/nodes/node1 {\"mgt\":\"dfm\",\"netboot\":\"yaboot\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - POST => { - desc => "Create the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: {options:{opt1:v1,opt2:v2},attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Create a node with attributes groups=all, mgt=dfm and netboot=yaboot|POST|/nodes/node1 {\"options\":{\"--template\":\"x86_64kvmguest-template\"}, \"groups\":\"all\",\"mgt\":\"dfm\",\"netboot\":\"yaboot\"}||", - cmd => "mkdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the node {noderange}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete the node node1|DELETE|/nodes/node1||", - cmd => "rmdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - }, - nodeattr => { - desc => "[URI:/nodes/{noderange}/attrs/{attr1,attr2,attr3 ...}] - The attributes resource for the node {noderange}", - matcher => '^/nodes/[^/]*/attrs/\S+$', - GET => { - desc => "Get the specific attributes for the node {noderange}.", - desc1 => "The keyword ALLRESOURCES can be used as {noderange} which means to get node attributes for all the nodes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the attributes {groups,mgt,netboot} for node node1|GET|/nodes/node1/attrs/groups,mgt,netboot|{\n \"node1\":{\n \"netboot\":\"xnba\",\n \"mgt\":\"ipmi\",\n \"groups\":\"all\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - PUT_backup => { - desc => "Change attributes for the node {noderange}. DataBody: {attr1:v1,att2:v2,att3:v3 ...}.", - usage => "||An array of node objects.|", - example => "|Get the attributes {groups,mgt,netboot} for node node1|GET|/nodes/node1/attrs/groups;mgt;netboot||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - } - }, - nodestat => { - desc => "[URI:/nodes/{noderange}/nodestat}] - The attributes resource for the node {noderange}", - matcher => '^/nodes/[^/]*/nodestat$', - GET => { - desc => "Get the running status for the node {noderange}.", - usage => "||An object which includes multiple entries like: : { nodestat : }|", - example => "|Get the running status for node node1|GET|/nodes/node1/nodestat|{\n \"node1\":{\n \"nodestat\":\"noping\"\n }\n}|", - cmd => "nodestat", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - nodehost => { - desc => "[URI:/nodes/{noderange}/host] - The mapping of ip and hostname for the node {noderange}", - matcher => '^/nodes/[^/]*/host$', - POST => { - desc => "Create the mapping of ip and hostname record for the node {noderange}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Create the mapping of ip and hostname record for node \'node1\'.|POST|/nodes/node1/host||", - cmd => "makehosts", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - noderename => { - desc => "[URI:/nodes/{noderange}/rename] - Change old_nodename into new_nodename", - matcher => '^/nodes/[^/]*/rename$', - PUT => { - desc => "Change node name.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Change nodename for node \'node1\'.|PUT|/nodes/node1/rename||", - cmd => "chdef", - fhandler => \&actionhdl, - outhdler => \&defout_remove_appended_info, - }, - }, - nodedns => { - desc => "[URI:/nodes/{noderange}/dns] - The dns record resource for the node {noderange}", - matcher => '^/nodes/[^/]*/dns$', - POST => { - desc => "Create the dns record for the node {noderange}.", - desc1 => "The prerequisite of the POST operation is the mapping of ip and noderange for the node has been added in the /etc/hosts.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Create the dns record for node \'node1\'.|POST|/nodes/node1/dns||", - cmd => "makedns", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the dns record for the node {noderange}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete the dns record for node node1|DELETE|/nodes/node1/dns||", - cmd => "makedns", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - nodedhcp => { - desc => "[URI:/nodes/{noderange}/dhcp] - The dhcp record resource for the node {noderange}", - matcher => '^/nodes/[^/]*/dhcp$', - POST => { - desc => "Create the dhcp record for the node {noderange}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Create the dhcp record for node \'node1\'.|POST|/nodes/node1/dhcp||", - cmd => "makedhcp", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the dhcp record for the node {noderange}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete the dhcp record for node node1|DELETE|/nodes/node1/dhcp||", - cmd => "makedhcp", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - power => { - desc => "[URI:/nodes/{noderange}/power] - The power resource for the node {noderange}", - matcher => '^/nodes/[^/]*/power$', - GET => { - desc => "Get the power status for the node {noderange}.", - usage => "||An object which includes multiple entries like: : { power : }|", - example => "|Get the power status.|GET|/nodes/node1/power|{\n \"node1\":{\n \"power\":\"on\"\n }\n}|", - cmd => "rpower", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Change power status for the node {noderange}.", - usage => "|Json Formatted DataBody: {action:on/off/reset ...}.|$usagemsg{non_getreturn}|", - example => "|Change the power status to on|PUT|/nodes/node1/power {\"action\":\"on\"}||", - cmd => "rpower", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - provision => { - desc => "[URI:/nodes/{noderange}/provision] - The deployment resource for the node {noderange}", - matcher => '^/nodes/[^/]*/provision$', - PUT => { - desc => "OS provision for the node {noderange}.", - usage => "|Json Formatted DataBody: {osimage: ubuntu16.04.1-x86_64-install-compute, action: boot}.|$usagemsg{non_getreturn}|", - example => "|Provision the node|PUT|/nodes/node1/provision {\"osimage\":\"ubuntu16.04.1-x86_64-install-compute\"}||", - cmd => "rinstall", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - energy => { - desc => "[URI:/nodes/{noderange}/energy] - The energy resource for the node {noderange}", - matcher => '^/nodes/[^/]*/energy$', - GET => { - desc => "Get all the energy status for the node {noderange}.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the energy attributes.|GET|/nodes/node1/energy|{\n \"node1\":{\n \"cappingmin\":\"272.3 W\",\n \"cappingmax\":\"354.0 W\"\n ...\n }\n}|", - cmd => "renergy", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Change energy attributes for the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: {powerattr:value}.|$usagemsg{non_getreturn}|", - example => "|Turn on the cappingstatus to [on]|PUT|/nodes/node1/energy {\"cappingstatus\":\"on\"}||", - cmd => "renergy", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - energyattr => { - disable => 1, - desc => "[URI:/nodes/{noderange}/energy/{cappingmaxmin,cappingstatus,cappingvalue ...}] - The specific energy attributes resource for the node {noderange}", - matcher => '^/nodes/[^/]*/energy/\S+$', - GET => { - desc => "Get the specific energy attributes cappingmaxmin,cappingstatus,cappingvalue ... for the node {noderange}.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the energy attributes which are specified in the URI.|GET|/nodes/node1/energy/cappingmaxmin,cappingstatus|{\n \"node1\":{\n \"cappingmin\":\"272.3 W\",\n \"cappingmax\":\"354.0 W\"\n }\n}|", - cmd => "renergy", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT_backup => { - desc => "Change energy attributes for the node {noderange}. ", - usage => "|$usagemsg{objchparam} DataBody: {powerattr:value}.|$usagemsg{non_getreturn}|", - example => "|Turn on the cappingstatus to [on]|PUT|/nodes/node1/energy {\"cappingstatus\":\"on\"}||", - cmd => "renergy", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - serviceprocessor => { - disable => 1, - desc => "[URI:/nodes/{noderange}/sp/{community|ip|netmask|...}] - The attribute resource of service processor for the node {noderange}", - matcher => '^/nodes/[^/]*/sp/\S+$', - GET => { - desc => "Get the specific attributes for service processor resource.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the snmp community for the service processor of node1.|GET|/nodes/node1/sp/community|{\n \"node1\":{\n \"SP SNMP Community\":\"public\"\n }\n}|", - cmd => "rspconfig", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Change the specific attributes for the service processor resource. ", - usage => "|$usagemsg{objchparam} DataBody: {community:public}.|$usagemsg{non_getreturn}|", - example => "|Set the snmp community to [mycommunity].|PUT|/nodes/node1/sp/community {\"value\":\"mycommunity\"}||", - cmd => "rspconfig", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - macaddress => { - disable => 1, - desc => "[URI:/nodes/{noderange}/mac] - The mac address resource for the node {noderange}", - matcher => '^/nodes/[^/]*/mac$', - GET => { - desc => "Get the mac address for the node {noderange}. Generally, it also updates the mac attribute of the node.", - cmd => "getmacs", - fhandler => \&common, - }, - }, - nextboot => { - desc => "[URI:/nodes/{noderange}/nextboot] - The temporary bootorder resource in next boot for the node {noderange}", - matcher => '^/nodes/[^/]*/nextboot$', - GET => { - desc => "Get the next bootorder.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the bootorder for the next boot. (It's only valid after setting.)|GET|/nodes/node1/nextboot|{\n \"node1\":{\n \"nextboot\":\"Network\"\n }\n}|", - cmd => "rsetboot", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Change the next boot order. ", - usage => "|$usagemsg{objchparam} DataBody: {order:net/hd}.|$usagemsg{non_getreturn}|", - example => "|Set the bootorder for the next boot.|PUT|/nodes/node1/nextboot {\"order\":\"net\"}||", - cmd => "rsetboot", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - bootorder => { - desc => "[URI:/nodes/{noderange}/bootorder] - The permanent bootorder resource for the node {noderange}", - matcher => '^/nodes/[^/]*/bootorder$', - GET => { - desc => "Get the permanent boot order.", - usage => "|?|?|", - example => "|Get the permanent bootorder for the node1.|GET|/nodes/node1/bootorder|?|", - cmd => "rbootseq", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Change the boot order. DataBody: {\"order\":\"net,hd\"}.", - usage => "|Put data: Json formatted order:value pair.|?|", - example => "|Set the permanent bootorder for the node1.|PUT|/nodes/node1/bootorder|?|", - cmd => "rbootseq", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - vitals => { - desc => "[URI:/nodes/{noderange}/vitals] - The vitals resources for the node {noderange}", - matcher => '^/nodes/[^/]*/vitals$', - GET => { - desc => "Get all the vitals attibutes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the vitails attributes for the node1.|GET|/nodes/node1/vitals|{\n \"node1\":{\n \"SysBrd Fault\":\"0\",\n \"CPUs\":\"0\",\n \"Fan 4A Tach\":\"3330 RPM\",\n \"Drive 15\":\"0\",\n \"SysBrd Vol Fault\":\"0\",\n \"nvDIMM Flash\":\"0\",\n \"Progress\":\"0\"\n ...\n }\n}|", - cmd => "rvitals", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - vitalsattr => { - disable => 1, - desc => "[URI:/nodes/{noderange}/vitals/{temp|voltage|wattage|fanspeed|power|leds...}] - The specific vital attributes for the node {noderange}", - matcher => '^/nodes/[^/]*/vitals/\S+$', - GET => { - desc => "Get the specific vitals attibutes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the \'fanspeed\' vitals attribute.|GET|/nodes/node1/vitals/fanspeed|{\n \"node1\":{\n \"Fan 1A Tach\":\"3219 RPM\",\n \"Fan 4B Tach\":\"2688 RPM\",\n \"Fan 3B Tach\":\"2560 RPM\",\n \"Fan 4A Tach\":\"3330 RPM\",\n \"Fan 2A Tach\":\"3293 RPM\",\n \"Fan 1B Tach\":\"2592 RPM\",\n \"Fan 3A Tach\":\"3182 RPM\",\n \"Fan 2B Tach\":\"2592 RPM\"\n }\n}|", - cmd => "rvitals", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - inventory => { - desc => "[URI:/nodes/{noderange}/inventory] - The inventory attributes for the node {noderange}", - matcher => '^/nodes/[^/]*/inventory$', - GET => { - desc => "Get all the inventory attibutes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the inventory attributes for node1.|GET|/nodes/node1/inventory|{\n \"node1\":{\n \"DIMM 21 \":\"8GB PC3-12800 (1600 MT/s) ECC RDIMM\",\n \"DIMM 1 Manufacturer\":\"Hyundai Electronics\",\n \"Power Supply 2 Board FRU Number\":\"94Y8105\",\n \"DIMM 9 Model\":\"HMT31GR7EFR4C-PB\",\n \"DIMM 8 Manufacture Location\":\"01\",\n \"DIMM 13 Manufacturer\":\"Hyundai Electronics\",\n \"DASD Backplane 4\":\"Not Present\",\n ...\n }\n}|", - cmd => "rinv", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - inventoryattr => { - desc => "[URI:/nodes/{noderange}/inventory/{pci|model...}] - The specific inventory attributes for the node {noderange}", - matcher => '^/nodes/[^/]*/inventory/\S+$', - GET => { - desc => "Get the specific inventory attibutes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the \'model\' inventory attribute for node1.|GET|/nodes/node1/inventory/model|{\n \"node1\":{\n \"System Description\":\"System x3650 M4\",\n \"System Model/MTM\":\"7915C2A\"\n }\n}|", - cmd => "rinv", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - eventlog => { - desc => "[URI:/nodes/{noderange}/eventlog] - The eventlog resource for the node {noderange}", - matcher => '^/nodes/[^/]*/eventlog$', - GET => { - desc => "Get all the eventlog for the node {noderange}.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the eventlog for node1.|GET|/nodes/node1/eventlog|{\n \"node1\":{\n \"eventlog\":[\n \"03/19/2014 15:17:58 Event Logging Disabled, Log Area Reset/Cleared (SEL Fullness)\"\n ]\n }\n}|", - cmd => "reventlog", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - DELETE => { - desc => "Clean up the event log for the node {noderange}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete all the event log for node1.|DELETE|/nodes/node1/eventlog|[\n {\n \"eventlog\":[\n \"SEL cleared\"\n ],\n \"name\":\"node1\"\n }\n]|", - cmd => "reventlog", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - beacon => { - desc => "[URI:/nodes/{noderange}/beacon] - The beacon resource for the node {noderange}", - matcher => '^/nodes/[^/]*/beacon$', - GET_backup => { - desc => "Get the beacon status for the node {noderange}.", - cmd => "rbeacon", - fhandler => \&common, - }, - PUT => { - desc => "Change the beacon status for the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: {action:on/off/blink}.|$usagemsg{non_getreturn}|", - example => "|Turn on the beacon.|PUT|/nodes/node1/beacon {\"action\":\"on\"}|[\n {\n \"name\":\"node1\",\n \"beacon\":\"on\"\n }\n]|", - cmd => "rbeacon", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - vm => { - desc => "[URI:/nodes/{noderange}/vm] - The virtualization node {noderange}.", - desc1 => "The node should be a virtual machine of type kvm, esxi ...", - matcher => '^/nodes/[^/]*/vm$', - GET_backup => { - desc => "Get the vm status for the node {noderange}.", - cmd => "lsvm", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Change the configuration for the virtual machine {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: \n Set memory size - {\"memorysize\":\"sizeofmemory(MB)\"}\n Add new disk - {\"adddisk\":\"sizeofdisk1(GB),sizeofdisk2(GB)\"}\n Purge disk - {\"purgedisk\":\"scsi_id1,scsi_id2\"}|$usagemsg{non_getreturn}|", - example => "|Set memory to 3000MB.|PUT|/nodes/node1/vm {\"memorysize\":\"3000\"}||", - example1 => "|Add a new 20G disk.|PUT|/nodes/node1/vm {\"adddisk\":\"20G\"}||", - example2 => "|Purge the disk \'hdb\'.|PUT|/nodes/node1/vm {\"purgedisk\":\"hdb\"}||", - cmd => "chvm", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - POST => { - desc => "Create the vm node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: \n Set CPU count - {\"cpucount\":\"numberofcpu\"}\n Set memory size - {\"memorysize\":\"sizeofmemory(MB)\"}\n Set disk size - {\"disksize\":\"sizeofdisk\"}\n Do it by force - {\"force\":\"yes\"}|$usagemsg{non_getreturn}|", - example => "|Create the vm node1 with a 30G disk, 2048M memory and 2 cpus.|POST|/nodes/node1/vm {\"disksize\":\"30G\",\"memorysize\":\"2048\",\"cpucount\":\"2\"}||", - cmd => "mkvm", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the vm node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: \n Purge disk - {\"purge\":\"yes\"}\n Do it by force - {\"force\":\"yes\"}|$usagemsg{non_getreturn}|", - example => "|Remove the vm node1 by force and purge the disk.|DELETE|/nodes/node1/vm {\"force\":\"yes\",\"purge\":\"yes\"}||", - cmd => "rmvm", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - vmclone => { - desc => "[URI:/nodes/{noderange}/vmclone] - The clone resource for the virtual node {noderange}.", - desc1 => "The node should be a virtual machine of kvm, esxi ...", - matcher => '^/nodes/[^/]*/vmclone$', - POST => { - desc => "Create a clone master from node {noderange}. Or clone the node {noderange} from a clone master.", - usage => "|$usagemsg{objchparam} DataBody: \n Clone a master named \"mastername\" - {\"tomaster\":\"mastername\"}\n Clone a node from master \"mastername\" - {\"frommaster\":\"mastername\"}\n Use Detach mode - {\"detach\":\"yes\"}\n Do it by force - {\"force\":\"yes\"}|The messages of creating Clone target.|", - example1 => "|Create a clone master named \"vmmaster\" from the node1.|POST|/nodes/node1/vmclone {\"tomaster\":\"vmmaster\",\"detach\":\"yes\"}|{\n \"node1\":{\n \"vmclone\":\"Cloning of node1.hda.qcow2 complete (clone uses 9633.19921875 for a disk size of 30720MB)\"\n }\n}|", - example2 => "|Clone the node1 from the clone master named \"vmmaster\".|POST|/nodes/node1/vmclone {\"frommaster\":\"vmmaster\"}||", - cmd => "clonevm", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - vmmigrate => { - desc => "[URI:/nodes/{noderange}/vmmigrate] - The virtualization resource for migration.", - desc1 => "The node should be a virtual machine of kvm, esxi ...", - matcher => '^/nodes/[^/]*/vmmigrate$', - POST => { - desc => "Migrate a node to targe node.", - usage => "|$usagemsg{objchparam} DataBody: {\"target\":\"targethost\"}.", - example => "|Migrate node1 to target host host2.|POST|/nodes/node1/vmmigrate {\"target\":\"host2\"}||", - cmd => "rmigrate", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - }, - updating => { - desc => "[URI:/nodes/{noderange}/updating] - The updating resource for the node {noderange}", - matcher => '^/nodes/[^/]*/updating$', - POST => { - desc => "Update the node with file syncing, software maintenance and rerun postscripts.", - usage => "||An array of messages for performing the node updating.|", - example => "|Initiate an updatenode process.|POST|/nodes/node2/updating|[\n \"There were no syncfiles defined to process. File synchronization has completed.\",\n \"Performing software maintenance operations. This could take a while, if there are packages to install.\n\",\n \"node2: Wed Mar 20 15:01:43 CST 2013 Running postscript: ospkgs\",\n \"node2: Running of postscripts has completed.\"\n]|", - cmd => "updatenode", - fhandler => \&actionhdl, - outhdler => \&infoout, - }, - }, - filesyncing => { - desc => "[URI:/nodes/{noderange}/filesyncing] - The filesyncing resource for the node {noderange}", - matcher => '^/nodes/[^/]*/filesyncing$', - POST => { - desc => "Sync files for the node {noderange}.", - usage => "||An array of messages for performing the file syncing for the node.|", - example => "|Initiate an file syncing process.|POST|/nodes/node2/filesyncing|[\n \"There were no syncfiles defined to process. File synchronization has completed.\"\n]|", - cmd => "updatenode", - fhandler => \&actionhdl, - outhdler => \&infoout, - }, - }, - software_maintenance => { - desc => "[URI:/nodes/{noderange}/sw] - The software maintenance for the node {noderange}", - matcher => '^/nodes/[^/]*/sw$', - POST => { - desc => "Perform the software maintenance process for the node {noderange}.", - usage => "||$usagemsg{objreturn}|", - example => "|Initiate an software maintenance process.|POST|/nodes/node2/sw|{\n \"node2\":[\n \" Wed Apr 3 09:05:42 CST 2013 Running postscript: ospkgs\",\n \" Unable to read consumer identity\",\n \" Postscript: ospkgs exited with code 0\",\n \" Wed Apr 3 09:05:44 CST 2013 Running postscript: otherpkgs\",\n \" ./otherpkgs: no extra rpms to install\",\n \" Postscript: otherpkgs exited with code 0\",\n \" Running of Software Maintenance has completed.\"\n ]\n}|", - cmd => "updatenode", - fhandler => \&actionhdl, - outhdler => \&infoout, - }, - }, - postscript => { - desc => "[URI:/nodes/{noderange}/postscript] - The postscript resource for the node {noderange}", - matcher => '^/nodes/[^/]*/postscript$', - POST => { - desc => "Run the postscripts for the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: {scripts:[p1,p2,p3,...]}.|$usagemsg{objreturn}|", - example => "|Initiate an updatenode process.|POST|/nodes/node2/postscript {\"scripts\":[\"syslog\",\"remoteshell\"]}|{\n \"node2\":[\n \" Wed Apr 3 09:01:33 CST 2013 Running postscript: syslog\",\n \" Shutting down system logger: [ OK ]\",\n \" Starting system logger: [ OK ]\",\n \" Postscript: syslog exited with code 0\",\n \" Wed Apr 3 09:01:33 CST 2013 Running postscript: remoteshell\",\n \" Stopping sshd: [ OK ]\",\n \" Starting sshd: [ OK ]\",\n \" Postscript: remoteshell exited with code 0\",\n \" Running of postscripts has completed.\"\n ]\n}|", - cmd => "updatenode", - fhandler => \&actionhdl, - outhdler => \&infoout, - }, - }, - nodeshell => { - desc => "[URI:/nodes/{noderange}/nodeshell] - The nodeshell resource for the node {noderange}", - matcher => '^/nodes/[^/]*/nodeshell$', - POST => { - desc => "Run the command in the shell of the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: set environment {ENV:{en1:v1,en2:v2}}, raw command {raw:[op1,op2]}, direct command {command:[cmd1,cmd2]}.|$usagemsg{objreturn}|", - example => "|Run the \'date\' command on the node2.|POST|/nodes/node2/nodeshell {\"command\":[\"date\",\"ls\"]}|{\n \"node2\":[\n \" Wed Apr 3 08:30:26 CST 2013\",\n \" testline1\",\n \" testline2\"\n ]\n}|Use ENV and raw command on the node2.|POST|/nodes/node2/nodeshell {\"ENV\":{\"DSH_REMOTE_PASSWORD\":\"cluster\",\"DSH_FROM_USERID\":\"root\",\"DSH_TO_USERID\":\"root\"},\"raw\":[\"-K\"]}|[\n \"/usr/bin/ssh setup is complete.\",\n \"return code = 0\"\n]|", - cmd => "xdsh", - fhandler => \&actionhdl, - outhdler => \&infoout, - }, - }, - nodecopy => { - desc => "[URI:/nodes/{noderange}/nodecopy] - The nodecopy resource for the node {noderange}", - matcher => '^/nodes/[^/]*/nodecopy$', - POST => { - desc => "Copy files to the node {noderange}.", - usage => "|$usagemsg{objchparam} DataBody: {src:[file1,file2],target:dir}.|$usagemsg{non_getreturn}|", - example => "|Copy files /tmp/f1 and /tmp/f2 from xCAT MN to the node2:/tmp.|POST|/nodes/node2/nodecopy {\"src\":[\"/tmp/f1\",\"/tmp/f2\"],\"target\":\"/tmp\"}|no output for succeeded copy.|", - cmd => "xdcp", - fhandler => \&actionhdl, - outhdler => \&infoout, - }, - }, - subnodes => { - desc => "[URI:/nodes/{noderange}/subnodes] - The sub-nodes resources for the node {noderange}", - matcher => '^/nodes/[^/]*/subnodes$', - GET => { - desc => "Return the Children nodes for the node {noderange}.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the children nodes for node \'node1\'.|GET|/nodes/node1/subnodes|{\n \"cmm01node09\":{\n \"mpa\":\"ngpcmm01\",\n \"parent\":\"ngpcmm01\",\n \"serial\":\"1035CDB\",\n \"mtm\":\"789523X\",\n \"cons\":\"fsp\",\n \"hwtype\":\"blade\",\n \"objtype\":\"node\",\n \"groups\":\"blade,all,p260\",\n \"mgt\":\"fsp\",\n \"nodetype\":\"ppc,osi\",\n \"slotid\":\"9\",\n \"hcp\":\"10.1.9.9\",\n \"id\":\"1\"\n },\n ...\n}|", - cmd => "rscan", - fhandler => \&actionhdl, - outhdler => \&defout, - }, - - # the put should be implemented by customer that using GET to get all the resources and define it with PUT /nodes/ - PUT_bak => { - desc => "Update the Children node for the node {noderange}.", - cmd => "rscan", - fhandler => \&common, - }, - }, - bootstate => { - desc => "[URI:/nodes/{noderange}/bootstate] - The boot state resource for node {noderange}.", - matcher => '^/nodes/[^/]*/bootstate$', - GET => { - desc => "Get boot state.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the next boot state for the node1.|GET|/nodes/node1/bootstate|{\n \"node1\":{\n \"bootstat\":\"boot\"\n }\n}|", - cmd => "nodeset", - fhandler => \&actionhdl, - outhdler => \&actionout, - }, - PUT => { - desc => "Set the boot state.", - usage => "|$usagemsg{objchparam} DataBody: {osimage:xxx}/{state:offline}.|$usagemsg{non_getreturn}|", - example => "|Set the next boot state for the node1.|PUT|/nodes/node1/bootstate {\"osimage\":\"rhels6.4-x86_64-install-compute\"}||", - cmd => "nodeset", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - - # TODO: rflash - }, - - #### definition for group resources - groups => { - all_groups => { - desc => "[URI:/groups] - The group list resource.", - desc1 => "This resource can be used to display all the groups which have been defined in the xCAT database.", - matcher => '^/groups$', - GET => { - desc => "Get all the groups in xCAT.", - desc1 => "The attributes details for the group will not be displayed.", - usage => "||Json format: An array of group names.|", - example => "|Get all the group names from xCAT database.|GET|/groups|[\n \"__mgmtnode\",\n \"all\",\n \"compute\",\n \"ipmi\",\n \"kvm\",\n]|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout_remove_appended_type, - } - }, - group_allattr => { - desc => "[URI:/groups/{groupname}] - The group resource", - matcher => '^/groups/[^/]*$', - GET => { - desc => "Get all the attibutes for the group {groupname}.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the attibutes for group \'all\'.|GET|/groups/all|{\n \"all\":{\n \"members\":\"zxnode2,nodexxx,node1,node4\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - PUT => { - desc => "Change the attibutes for the group {groupname}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Change the attributes mgt=dfm and netboot=yaboot.|PUT|/groups/all {\"mgt\":\"dfm\",\"netboot\":\"yaboot\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - }, - group_attr => { - desc => "[URI:/groups/{groupname}/attrs/{attr1,attr2,attr3 ...}] - The attributes resource for the group {groupname}", - matcher => '^/groups/[^/]*/attrs/\S+$', - GET => { - desc => "Get the specific attributes for the group {groupname}.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the attributes {mgt,netboot} for group all|GET|/groups/all/attrs/mgt,netboot|{\n \"all\":{\n \"netboot\":\"yaboot\",\n \"mgt\":\"dfm\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - }, - }, - - #### definition for services resources: dns, dhcp, hostname - services => { - host => { - desc => "[URI:/services/host] - The hostname resource.", - matcher => '^/services/host$', - POST => { - desc => "Create the ip/hostname records for all the nodes to /etc/hosts.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Create the ip/hostname records for all the nodes to /etc/hosts.|POST|/services/host||", - cmd => "makehosts", - fhandler => \&nonobjhdl, - outhdler => \&noout, - } - }, - dns => { - desc => "[URI:/services/dns] - The dns service resource.", - matcher => '^/services/dns$', - POST => { - desc => "Initialize the dns service.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Initialize the dns service.|POST|/services/dns||", - cmd => "makedns", - fhandler => \&nonobjhdl, - outhdler => \&noout, - } - }, - bmcdiscover => { - desc => "[URI:/services/bmcdiscover] - The bmc which does support nmap in the xCAT cluster.", - matcher => '^/services/bmcdiscover/[^/]+$', - GET => { - desc => "Get all the bmc alive.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the bmc which do not support slp in the network.|", - cmd => "bmcdiscover", - fhandler => \&bmclisthdl, - outhdler => \&defout_remove_appended_info, - - } - }, - checkbmcauth => { - desc => "[URI:/services/checkbmcauth] - Check if bmc user or password is correct.", - matcher => '^/services/checkbmcauth/[^/]*/[^/]+$', - GET => { - desc => "Check if bmc user or password is correct.", - usage => "||$usagemsg{objreturn}|", - example => "|Check bmc user or password.|GET|/services/checkbmcauth||", - cmd => "bmcdiscover", - fhandler => \&bmccheckhdl, - outhdler => \&defout_remove_appended_info, - - } - }, - getbmcipsource => { - desc => "[URI:/services/getbmcipsource] - Get BMC IP Address source.", - matcher => '^/services/getbmcipsource/[^/]*/[^/]+$', - GET => { - desc => "Get BMC IP Address source.", - usage => "||$usagemsg{objreturn}|", - example => "|Get BMC IP Address source.|GET|/services/getbmcipsource||", - cmd => "bmcdiscover", - fhandler => \&bmccheckhdl, - outhdler => \&defout_remove_appended_info, - - } - }, - - dhcp => { - desc => "[URI:/services/dhcp] - The dhcp service resource.", - matcher => '^/services/dhcp$', - POST => { - desc => "Create the dhcpd.conf for all the networks which are defined in the xCAT Management Node.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Create the dhcpd.conf and restart the dhcpd.|POST|/services/dhcp||", - cmd => "makedhcp", - fhandler => \&nonobjhdl, - outhdler => \&noout, - } - }, - - # todo: for slpnode, we need use the query attribute to specify the network parameter for lsslp command - slpnodes => { - desc => "[URI:/services/slpnodes] - The nodes which support SLP in the xCAT cluster", - matcher => '^/services/slpnodes', - GET => { - desc => "Get all the nodes which support slp protocol in the network.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the nodes which support slp in the network.|GET|/services/slpnodes|{\n \"ngpcmm01\":{\n \"mpa\":\"ngpcmm01\",\n \"otherinterfaces\":\"10.1.9.101\",\n \"serial\":\"100037A\",\n \"mtm\":\"789392X\",\n \"hwtype\":\"cmm\",\n \"side\":\"2\",\n \"objtype\":\"node\",\n \"nodetype\":\"mp\",\n \"groups\":\"cmm,all,cmm-zet\",\n \"mgt\":\"blade\",\n \"hidden\":\"0\",\n \"mac\":\"5c:f3:fc:25:da:99\"\n },\n ...\n}|", - cmd => "lsslp", - fhandler => \&nonobjhdl, - outhdler => \&defout, - }, - PUT_bakcup => { - desc => "Update the discovered nodes to database.", - cmd => "lsslp", - fhandler => \&common, - }, - }, - specific_slpnodes => { - desc => "[URI:/services/slpnodes/{CEC|FRAME|MM|IVM|RSA|HMC|CMM|IMM2|FSP...}] - The slp nodes with specific service type in the xCAT cluster", - matcher => '^/services/slpnodes/[^/]*$', - GET => { - desc => "Get all the nodes with specific slp service type in the network.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the CMM nodes which support slp in the network.|GET|/services/slpnodes/CMM|{\n \"ngpcmm01\":{\n \"mpa\":\"ngpcmm01\",\n \"otherinterfaces\":\"10.1.9.101\",\n \"serial\":\"100037A\",\n \"mtm\":\"789392X\",\n \"hwtype\":\"cmm\",\n \"side\":\"2\",\n \"objtype\":\"node\",\n \"nodetype\":\"mp\",\n \"groups\":\"cmm,all,cmm-zet\",\n \"mgt\":\"blade\",\n \"hidden\":\"0\",\n \"mac\":\"5c:f3:fc:25:da:99\"\n },\n \"Server--SNY014BG27A01K\":{\n \"mpa\":\"Server--SNY014BG27A01K\",\n \"otherinterfaces\":\"10.1.9.106\",\n \"serial\":\"100CF0A\",\n \"mtm\":\"789392X\",\n \"hwtype\":\"cmm\",\n \"side\":\"1\",\n \"objtype\":\"node\",\n \"nodetype\":\"mp\",\n \"groups\":\"cmm,all,cmm-zet\",\n \"mgt\":\"blade\",\n \"hidden\":\"0\",\n \"mac\":\"34:40:b5:df:0a:be\"\n }\n}|", - cmd => "lsslp", - fhandler => \&nonobjhdl, - outhdler => \&defout, - }, - PUT_backup => { - desc => "Update the discovered nodes to database.", - cmd => "lsslp", - fhandler => \&common, - }, - }, - #### definition for mknb [-c] - nbimage => { - desc => "[URI:/services/nbimage] - Create netboot root image for specified arch.", - matcher => '^/services/nbimage/arch/[ppc64|x86_64]', - POST => { - desc => "creates a network boot root image", - usage => "|$usagemsg{objchparam} DataBody: {\"onlyconfigfile\":\"[true|yes|Y|1]|[false|no|N|0]\"}.|$usagemsg{non_getreturn}|", - example => "|Create a network boot root iamge for the specified arch|", - cmd => "mknb", - fhandler => \&actionhdl, - }, - }, - console => { - desc => "[URI:/services/console] - Conserver configuration on management node.", - matcher => '^/services/console$', - PUT => { - desc => "Update conserver configuration", - usage => "|Json Formatted DataBody: {nodes: [node1, node2], action: on/off trust_host: }.|$usagemsg{non_getreturn}|", - example => "|Enable the console capability for node1|PUT|/services/console {\"nodes\":\n[\"node1\", \"node2\"]\"\n, \"action\": \"on\", \n\"trust_host\": \"host\"}||", - cmd => "makeconservercf", - fhandler => \&actionhdl, - outhdler => \&noout, - } - }, - }, - - #### definition for network resources - networks => { - allnetwork => { - desc => "[URI:/networks] - The network list resource.", - desc1 => "This resource can be used to display all the networks which have been defined in the xCAT database.", - matcher => '^\/networks$', - GET => { - desc => "Get all the networks in xCAT.", - desc1 => "The attributes details for the networks will not be displayed.", - usage => "||Json format: An array of networks names.|", - example => "|Get all the networks names from xCAT database.|GET|/networks|[\n \"network1\",\n \"network2\",\n \"network3\",\n]|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout_remove_appended_type, - }, - POST => { - desc => "Create the networks resources base on the network configuration on xCAT MN.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Create the networks resources base on the network configuration on xCAT MN.|POST|/networks||", - cmd => "makenetworks", - fhandler => \&actionhdl, - outhdler => \&noout, - }, - }, - network_allattr => { - desc => "[URI:/networks/{netname}] - The network resource", - matcher => '^\/networks\/[^\/]*$', - GET => { - desc => "Get all the attibutes for the network {netname}.", - desc1 => "The keyword ALLRESOURCES can be used as {netname} which means to get network attributes for all the networks.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the attibutes for network \'network1\'.|GET|/networks/network1|{\n \"network1\":{\n \"gateway\":\"\",\n \"mask\":\"255.255.255.0\",\n \"mgtifname\":\"eth2\",\n \"net\":\"10.0.0.0\",\n \"tftpserver\":\"10.0.0.119\",\n ...\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - PUT => { - desc => "Change the attibutes for the network {netname}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Change the attributes mgtifname=eth0 and net=10.1.0.0.|PUT|/networks/network1 {\"mgtifname\":\"eth0\",\"net\":\"10.1.0.0\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - POST => { - desc => "Create the network {netname}. DataBody: {attr1:v1,att2:v2...}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Create a network with attributes gateway=10.1.0.1, mask=255.255.0.0 |POST|/networks/network1 {\"gateway\":\"10.1.0.1\",\"mask\":\"255.255.0.0\"}||", - cmd => "mkdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the network {netname}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete the network network1|DELETE|/networks/network1||", - cmd => "rmdef", - fhandler => \&defhdl, - outhdler => \&noout - }, - }, - network_attr => { - desc => "[URI:/networks/{netname}/attrs/attr1,attr2,...] - The attributes resource for the network {netname}", - matcher => '^\/networks\/[^\/]*/attrs/\S+$', - GET => { - desc => "Get the specific attributes for the network {netname}.", - desc1 => "The keyword ALLRESOURCES can be used as {netname} which means to get network attributes for all the networks.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the attributes {groups,mgt,netboot} for network network1|GET|/networks/network1/attrs/gateway,mask,mgtifname,net,tftpserver|{\n \"network1\":{\n \"gateway\":\"9.114.34.254\",\n \"mask\":\"255.255.255.0\",\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - PUT__backup => { - desc => "Change attributes for the network {netname}. DataBody: {attr1:v1,att2:v2,att3:v3 ...}.", - usage => "||An array of network objects.|", - example => "|Get the attributes {gateway,mask,mgtifname,net,tftpserver} for networks network1|GET|/networks/network1/attrs/gateway;mask;net||", - cmd => "chdef", - fhandler => \&noout, - } - }, - - }, - - #### definition for osimage resources - osimages => { - osimage => { - desc => "[URI:/osimages] - The osimage resource.", - matcher => '^\/osimages$', - GET => { - desc => "Get all the osimage in xCAT.", - usage => "||Json format: An array of osimage names.|", - example => "|Get all the osimage names.|GET|/osimages|[\n \"sles11.2-x86_64-install-compute\",\n \"sles11.2-x86_64-install-iscsi\",\n \"sles11.2-x86_64-install-iscsiibft\",\n \"sles11.2-x86_64-install-service\"\n]|", - - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout_remove_appended_type, - }, - POST => { - desc => "Create the osimage resources base on the parameters specified in the Data body.", - - #usage => "|$usagemsg{objchparam} DataBody: {iso:isoname\\file:filename\\node:noderange,params:[{attr1:value1,attr2:value2}]}|$usagemsg{non_getreturn}|", - usage => "|$usagemsg{objchparam} DataBody: {iso:isoname\\file:filename,params:[{attr1:value1,attr2:value2}]}|$usagemsg{non_getreturn}|", - example1 => "|Create osimage resources based on the ISO specified|POST|/osimages {\"iso\":\"/iso/RHEL6.4-20130130.0-Server-ppc64-DVD1.iso\"}||", - example2 => "|Create osimage resources based on an xCAT image or configuration file|POST|/osimages {\"file\":\"/tmp/sles11.2-x86_64-install-compute.tgz\"}||", - - # TD: the imgcapture need to be moved to nodes/.*/osimages - # example3 => "|Create a image based on the specified Linux diskful node|POST|/osimages {\"node\":\"rhcn1\"}||", - cmd => "copycds", - fhandler => \&imgophdl, - outhdler => \&noout, - }, - }, - osimage_allattr => { - desc => "[URI:/osimages/{imgname}] - The osimage resource", - matcher => '^\/osimages\/[^\/]*$', - GET => { - desc => "Get all the attibutes for the osimage {imgname}.", - desc1 => "The keyword ALLRESOURCES can be used as {imgname} which means to get image attributes for all the osimages.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the attributes for the specified osimage.|GET|/osimages/sles11.2-x86_64-install-compute|{\n \"sles11.2-x86_64-install-compute\":{\n \"provmethod\":\"install\",\n \"profile\":\"compute\",\n \"template\":\"/opt/xcat/share/xcat/install/sles/compute.sles11.tmpl\",\n \"pkglist\":\"/opt/xcat/share/xcat/install/sles/compute.sles11.pkglist\",\n \"osvers\":\"sles11.2\",\n \"osarch\":\"x86_64\",\n \"osname\":\"Linux\",\n \"imagetype\":\"linux\",\n \"otherpkgdir\":\"/install/post/otherpkgs/sles11.2/x86_64\",\n \"osdistroname\":\"sles11.2-x86_64\",\n \"pkgdir\":\"/install/sles11.2/x86_64\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - - POST => { - desc => "Create the osimage {imgname}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,attr2:v2]|$usagemsg{non_getreturn}|", - example => "|Create a osimage obj with the specified parameters.|POST|/osimages/sles11.3-ppc64-install-compute {\"osvers\":\"sles11.3\",\"osarch\":\"ppc64\",\"osname\":\"Linux\",\"provmethod\":\"install\",\"profile\":\"compute\"}||", - cmd => "mkdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - PUT => { - desc => "Change the attibutes for the osimage {imgname}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,attr2:v2...}|$usagemsg{non_getreturn}|", - example => "|Change the 'osvers' and 'osarch' attributes for the osiamge.|PUT|/osimages/sles11.2-ppc64-install-compute/ {\"osvers\":\"sles11.3\",\"osarch\":\"x86_64\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the osimage {imgname}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete the specified osimage.|DELETE|/osimages/sles11.3-ppc64-install-compute||", - cmd => "rmdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - }, - osimage_attr => { - desc => "[URI:/osimages/{imgname}/attrs/attr1,attr2,attr3 ...] - The attributes resource for the osimage {imgname}", - matcher => '^\/osimages\/[^\/]*/attrs/\S+$', - GET => { - desc => "Get the specific attributes for the osimage {imgname}.", - desc1 => "The keyword ALLRESOURCES can be used as {imgname} which means to get image attributes for all the osimages.", - usage => "||Json format: An array of attr:value pairs for the specified osimage.|", - example => "|Get the specified attributes.|GET|/osimages/sles11.2-ppc64-install-compute/attrs/imagetype,osarch,osname,provmethod|{\n \"sles11.2-ppc64-install-compute\":{\n \"provmethod\":\"install\",\n \"osname\":\"Linux\",\n \"osarch\":\"ppc64\",\n \"imagetype\":\"linux\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - - # TD, the implementation may need to be change. - PUT_backup => { - desc => "Change the attibutes for the osimage {imgname}.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,attr2:v2...}|$usagemsg{non_getreturn}|", - example => "|Change the 'osvers' and 'osarch' attributes for the osiamge.|PUT|/osimages/sles11.2-ppc64-install-compute/attrs/osvers;osarch {\"osvers\":\"sles11.3\",\"osarch\":\"x86_64\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - - }, - osimage_op => { - desc => "[URI:/osimages/{imgname}/instance] - The instance for the osimage {imgname}", - matcher => '^\/osimages\/[^\/]*/instance$', - POST => { - desc => "Operate the instance of the osimage {imgname}.", - usage => "|$usagemsg{objchparam} DataBody: {action:gen\\pack\\export,params:[{attr1:value1,attr2:value2...}]}|$usagemsg{non_getreturn}|", - example1 => "|Generates a stateless image based on the specified osimage|POST|/osimages/sles11.2-x86_64-install-compute/instance {\"action\":\"gen\"}||", - example2 => "|Packs the stateless image from the chroot file system based on the specified osimage|POST|/osimages/sles11.2-x86_64-install-compute/instance {\"action\":\"pack\"}||", - example3 => "|Exports an xCAT image based on the specified osimage|POST|/osimages/sles11.2-x86_64-install-compute/instance {\"action\":\"export\"}||", - cmd => "", - fhandler => \&imgophdl, - }, - DELETE => { - desc => "Delete the stateless or statelite image instance for the osimage {imgname} from the file system", - usage => "||$usagemsg{non_getreturn}", - example => "|Delete the stateless image for the specified osimage|DELETE|/osimages/sles11.2-x86_64-install-compute/instance||", - cmd => "rmimage", - fhandler => \&imgophdl, - }, - }, - - # todo: genimage, packimage, imagecapture, imgexport, imgimport - }, - - #### definition for policy resources - policy => { - policy => { - desc => "[URI:/policy] - The policy resource.", - matcher => '^\/policy$', - GET => { - desc => "Get all the policies in xCAT.", - desc1 => "It will dislplay all the policy resource.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the policy objects.|GET|/policy|[\n \"1\",\n \"1.2\",\n \"2\",\n \"4.8\"\n]|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout_remove_appended_type, - }, - }, - policy_allattr => { - desc => "[URI:/policy/{policy_priority}] - The policy resource", - matcher => '^\/policy\/[^\/]*$', - GET => { - desc => "Get all the attibutes for a policy {policy_priority}.", - desc1 => "It will display all the policy attributes for one policy resource.", - desc2 => "The keyword ALLRESOURCES can be used as {policy_priority} which means to get policy attributes for all the policies.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the attribute for policy 1.|GET|/policy/1|{\n \"1\":{\n \"name\":\"root\",\n \"rule\":\"allow\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - PUT => { - desc => "Change the attibutes for the policy {policy_priority}.", - desc1 => "It will change one or more attributes for a policy.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Set the name attribute for policy 3.|PUT|/policy/3 {\"name\":\"root\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - POST => { - desc => "Create the policy {policyname}. DataBody: {attr1:v1,att2:v2...}.", - desc1 => "It will creat a new policy resource.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Create a new policy 10.|POST|/policy/10 {\"name\":\"root\",\"commands\":\"rpower\"}||", - cmd => "chdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the policy {policy_priority}.", - desc1 => "Remove one or more policy resource.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Delete the policy 10.|DELETE|/policy/10||", - cmd => "rmdef", - fhandler => \&defhdl, - outhdler => \&noout, - }, - }, - policy_attr => { - desc => "[URI:/policy/{policyname}/attrs/{attr1,attr2,attr3,...}] - The attributes resource for the policy {policy_priority}", - matcher => '^\/policy\/[^\/]*/attrs/\S+$', - GET => { - desc => "Get the specific attributes for the policy {policy_priority}.", - desc1 => "It will get one or more attributes of a policy.", - desc2 => "The keyword ALLRESOURCES can be used as {policy_priority} which means to get policy attributes for all the policies.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the name and rule attributes for policy 1.|GET|/policy/1/attrs/name,rule|{\n \"1\":{\n \"name\":\"root\",\n \"rule\":\"allow\"\n }\n}|", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - }, - }, - #### definition for global setting resources - globalconf => { - all_site => { - desc => "[URI:/globalconf] - The global configuration resource.", - desc1 => "This resource can be used to display all the global configuration which have been defined in the xCAT database.", - matcher => '^\/globalconf$', - GET => { - desc => "Get all the xCAT global configuration.", - desc1 => "It will display all the global attributes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get all the global configuration|GET|/globalconf|{\n \"clustersite\":{\n \"xcatconfdir\":\"/etc/xcat\",\n \"tftpdir\":\"/tftpboot\",\n ...\n }\n}|", - cmd => "lsdef", - fhandler => \&sitehdl, - outhdler => \&defout, - }, - POST_backup => { - desc => "Add the site attributes. DataBody: {attr1:v1,att2:v2...}.", - desc1 => "One or more global attributes could be added/modified.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Add one or more attributes to xCAT database|POST|/globalconf {\"domain\":\"cluster.com\",\"mydomain\":\"mycluster.com\"}||", - cmd => "chdef", - fhandler => \&sitehdl, - }, - }, - site => { - desc => "[URI:/globalconf/attrs/{attr1,attr2 ...}] - The specific global configuration resource.", - matcher => '^\/globalconf/attrs/\S+$', - GET => { - desc => "Get the specific configuration in global.", - desc1 => "It will display one or more global attributes.", - usage => "||$usagemsg{objreturn}|", - example => "|Get the \'master\' and \'domain\' configuration.|GET|/globalconf/attrs/master,domain|{\n \"clustersite\":{\n \"domain\":\"cluster.com\",\n \"master\":\"192.168.1.15\"\n }\n}|", - cmd => "lsdef", - fhandler => \&sitehdl, - outhdler => \&defout, - }, - PUT => { - desc => "Change the global attributes.", - desc1 => "It can be used for changing/adding global attributes.", - usage => "|$usagemsg{objchparam} DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => "|Change/Add the domain attribute.|PUT|/globalconf/attrs/domain {\"domain\":\"cluster.com\"}||", - cmd => "chdef", - fhandler => \&sitehdl, - outhdler => \&noout, - }, - POST_backup => { - desc => "Create the global configuration entry. DataBody: {name:value}.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Create the domain attribute|POST|/globalconf/attrs/domain {\"domain\":\"cluster.com\"}|?|", - cmd => "chdef", - fhandler => \&sitehdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Remove the site attributes.", - desc1 => "Used for femove one or more global attributes.", - usage => "||$usagemsg{non_getreturn}|", - example => "|Remove the domain configure.|DELETE|/globalconf/attrs/domain||", - cmd => "chdef", - fhandler => \&sitehdl, - outhdler => \&noout, - }, - }, - }, - - #### definition for database/table resources - tables => { - table_nodes => { - desc => "[URI:/tables/{tablelist}/nodes/{noderange}] - The node table resource", - desc1 => "For a large number of nodes, this API call can be faster than using the corresponding nodes resource. The disadvantage is that you need to know the table names the attributes are stored in.", - matcher => '^/tables/[^/]+/nodes/[^/]+$', - GET => { - desc => "Get attibutes of tables for a noderange.", - usage => "||An object containing each table. Within each table object is an array of node objects containing the attributes.|", - example1 => qq(|Get all the columns from table nodetype for node1 and node2.|GET|/tables/nodetype/nodes/node1,node2|{\n \"nodetype\":[\n {\n \"provmethod\":\"rhels6.4-x86_64-install-compute\",\n \"profile\":\"compute\",\n \"arch\":\"x86_64\",\n \"name\":\"node1\",\n \"os\":\"rhels6.4\"\n },\n {\n \"provmethod\":\"rhels6.3-x86_64-install-compute\",\n \"profile\":\"compute\",\n \"arch\":\"x86_64\",\n \"name\":\"node2\",\n \"os\":\"rhels6.3\"\n }\n ]\n}|), - example2 => qq(|Get all the columns from tables nodetype and noderes for node1 and node2.|GET|/tables/nodetype,noderes/nodes/node1,node2|{\n \"noderes\":[\n {\n \"installnic\":\"mac\",\n \"netboot\":\"xnba\",\n \"name\":\"node1\",\n \"nfsserver\":\"192.168.1.15\"\n },\n {\n \"installnic\":\"mac\",\n \"netboot\":\"pxe\",\n \"name\":\"node2\",\n \"proxydhcp\":\"no\"\n }\n ],\n \"nodetype\":[\n {\n \"provmethod\":\"rhels6.4-x86_64-install-compute\",\n \"profile\":\"compute\",\n \"arch\":\"x86_64\",\n \"name\":\"node1\",\n \"os\":\"rhels6.4\"\n },\n {\n \"provmethod\":\"rhels6.3-x86_64-install-compute\",\n \"profile\":\"compute\",\n \"arch\":\"x86_64\",\n \"name\":\"node2\",\n \"os\":\"rhels6.3\"\n }\n ]\n}|), - fhandler => \&tablenodehdl, - outhdler => \&tableout, - }, - PUT => { - desc => "Change the node table attibutes for {noderange}.", - usage => "|A hash of table names and attribute objects. DataBody: {table1:{attr1:v1,att2:v2,...}}.|$usagemsg{non_getreturn}|", - example => '|Change the nodetype.arch and noderes.netboot attributes for nodes node1,node2.|PUT|/tables/nodetype,noderes/nodes/node1,node2 {"nodetype":{"arch":"x86_64"},"noderes":{"netboot":"xnba"}}||', - fhandler => \&tablenodeputhdl, - outhdler => \&noout, - }, - }, - table_nodes_attrs => { - desc => "[URI:/tables/{tablelist}/nodes/nodes/{noderange}/{attrlist}] - The node table attributes resource", - desc1 => "For a large number of nodes, this API call can be faster than using the corresponding nodes resource. The disadvantage is that you need to know the table names the attributes are stored in.", - matcher => '^/tables/[^/]+/nodes/[^/]+/[^/]+$', - GET => { - desc => "Get table attibutes for a noderange.", - usage => "||An object containing each table. Within each table object is an array of node objects containing the attributes.|", - example => qq(|Get OS and ARCH attributes from nodetype table for node1 and node2.|GET|/tables/nodetype/nodes/node1,node2/os,arch|{\n \"nodetype\":[\n {\n \"arch\":\"x86_64\",\n \"name\":\"node1\",\n \"os\":\"rhels6.4\"\n },\n {\n \"arch\":\"x86_64\",\n \"name\":\"node2\",\n \"os\":\"rhels6.3\"\n }\n ]\n}|), - fhandler => \&tablenodehdl, - outhdler => \&tableout, - }, - PUT_backup => { - desc => "[URI:/tables/nodes/{noderange}] - Change the node table attibutes for the {noderange}.", - usage => "|A hash of table names and attribute objects. DataBody: {table1:{attr1:v1,att2:v2,...}}.|$usagemsg{non_getreturn}|", - example => '|Change the nodehm.mgmt and noderes.netboot attributes for nodes node1-node5.|PUT|/tables/nodes/node1-node5 {"nodehm":{"mgmt":"ipmi"},"noderes":{"netboot":"xnba"}}||', - fhandler => \&tablenodeputhdl, - outhdler => \&noout, - }, - }, - table_all_rows => { - desc => "[URI:/tables/{tablelist}/rows] - The non-node table resource", - desc1 => "Use this for tables that don't have node name as the key of the table, for example: passwd, site, networks, polciy, etc.", - matcher => '^/tables/[^/]+/rows$', - GET => { - desc => "Get all rows from non-node tables.", - usage => "||An object containing each table. Within each table object is an array of row objects containing the attributes.|", - example => qq(|Get all rows from networks table.|GET|/tables/networks/rows|{\n \"networks\":[\n {\n \"netname\":\"192_168_13_0-255_255_255_0\",\n \"gateway\":\"192.168.13.254\",\n \"staticrangeincrement\":\"1\",\n \"net\":\"192.168.13.0\",\n \"mask\":\"255.255.255.0\"\n },\n {\n \"netname\":\"192_168_12_0-255_255_255_0\",\n \"gateway\":\"192.168.12.254\",\n \"staticrangeincrement\":\"1\",\n \"net\":\"192.168.12.0\",\n \"mask\":\"255.255.255.0\"\n },\n ]\n}|), - fhandler => \&tablerowhdl, - outhdler => \&tableout, - }, - }, - table_rows => { - desc => "[URI:/tables/{tablelist}/rows/{keys}] - The non-node table rows resource", - desc1 => "Use this for tables that don't have node name as the key of the table, for example: passwd, site, networks, polciy, etc.", - desc2 => "{keys} should be the name=value pairs which are used to search table. e.g. {keys} should be [net=192.168.1.0,mask=255.255.255.0] for networks table query since the net and mask are the keys of networks table.", - matcher => '^/tables/[^/]+/rows/[^/]+$', - GET => { - desc => "Get attibutes for rows from non-node tables.", - usage => "||An object containing each table. Within each table object is an array of row objects containing the attributes.|", - example => qq(|Get row which net=192.168.1.0,mask=255.255.255.0 from networks table.|GET|/tables/networks/rows/net=192.168.1.0,mask=255.255.255.0|{\n \"networks\":[\n {\n \"mgtifname\":\"eth0\",\n \"netname\":\"192_168_1_0-255_255_255_0\",\n \"tftpserver\":\"192.168.1.15\",\n \"gateway\":\"192.168.1.100\",\n \"staticrangeincrement\":\"1\",\n \"net\":\"192.168.1.0\",\n \"mask\":\"255.255.255.0\"\n }\n ]\n}|), - fhandler => \&tablerowhdl, - outhdler => \&tableout, - }, - PUT => { - desc => "Change the non-node table attibutes for the row that matches the {keys}.", - usage => "|A hash of attribute names and values. DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - example => '|Create a route row in the routes table.|PUT|/tables/routes/rows/routename=privnet {"net":"10.0.1.0","mask":"255.255.255.0","gateway":"10.0.1.254","ifname":"eth1"}||', - fhandler => \&tablerowputhdl, - outhdler => \&noout, - }, - DELETE => { - desc => "Delete rows from a non-node table that have the attribute values specified in {keys}.", - usage => "||$usagemsg{non_getreturn}|", - example => '|Delete a route row which routename=privnet in the routes table.|DELETE|/tables/routes/rows/routename=privnet||', - fhandler => \&tablerowdelhdl, - outhdler => \&noout, - }, - }, - table_rows_attrs => { - desc => "[URI:/tables/{tablelist}/rows/{keys}/{attrlist}] - The non-node table attributes resource", - desc1 => "Use this for tables that don't have node name as the key of the table, for example: passwd, site, networks, polciy, etc.", - matcher => '^/tables/[^/]+/rows/[^/]+/[^/]+$', - GET => { - desc => "Get specific attibutes for rows from non-node tables.", - usage => "||An object containing each table. Within each table object is an array of row objects containing the attributes.|", - example => qq(|Get attributes mgtifname and tftpserver which net=192.168.1.0,mask=255.255.255.0 from networks table.|GET|/tables/networks/rows/net=192.168.1.0,mask=255.255.255.0/mgtifname,tftpserver|{\n \"networks\":[\n {\n \"mgtifname\":\"eth0\",\n \"tftpserver\":\"192.168.1.15\"\n }\n ]\n}|), - fhandler => \&tablerowhdl, - outhdler => \&tableout, - }, - }, - }, - - #### definition for tokens resources - tokens => { - tokens => { - desc => "[URI:/tokens] - The authentication token resource.", - matcher => '^\/tokens', - POST => { - desc => "Create a token.", - usage => "||An array of all the global configuration list.|", - example => "|Aquire a token for user \'root\'.|POST|/tokens {\"userName\":\"root\",\"userPW\":\"cluster\"}|{\n \"token\":{\n \"id\":\"a6e89b59-2b23-429a-b3fe-d16807dd19eb\",\n \"expire\":\"2014-3-8 14:55:0\"\n }\n}|", - fhandler => \&nonobjhdl, - outhdler => \&tokenout, - }, - POST_backup => { - desc => "Add the site attributes. DataBody: {attr1:v1,att2:v2...}.", - usage => "|?|?|", - example => "|?|?|?|?|", - cmd => "chdef", - fhandler => \&sitehdl, - }, - }, - }, - - ### interface to access local system resource which is not managed by xcat directly - ### localres can be looked as a top level non-xcat resource pool - ### rest operation will trasfer the target resource to xcatd plugin to handle the non-xcat resource - localres => { - localres => { - desc => "[URI:/localres/*] - The local non-xcat resource.", - matcher => '^/localres(/[^/]*)+$', - GET => { - desc => "List information for the target system resource.", - usage => "||For target resource can match any resource type which can be proccssed by the resthelper plugin.|", - example => qq(|List adapters on MN machine|GET|/localres/interface/|{\n \"interfaces\":[\n\"eth0\",\n \"eth1\",\n]"|), - cmd => "localrest", - fhandler => \&localreshdl, - outhdler => \&localresout, - }, - } - }, - - templates => { - node => { - desc => - "[URI:/templates/node] - The template information of nodes.", - matcher => '^/templates/node$', - GET => { - desc => "Show attributes of a node template.", - usage => "||$usagemsg{objreturn}|", - example => "|GET all the attibutes of node template \'x86_64kvmguest-template\'.|GET|/templates/node {\"options\":{\"--template\":\"x86_64kvmguest-template\"}} |{\n \"arch\":{\n \"x86_64\":\"compute\",\n \"bmc\":\"MANDATORY:The hostname or ip address of the BMC adapater\",\n \bmcpassword\":\"MANDATORY:the password of the BMC\",\n \"mgt\":\"ipmi\",\n \"groups\":\"all\",\n ...\n }\n}", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout, - }, - }, - # Generally, the 'oprions' json filed could be used to support different argument of the command. - # As limited by the different outhdler, a new resource name 'all' has to be added here. - all => { - desc => "[URI:/templates/node/all] - The template information of node.", - matcher => '^/templates/node/all$', - GET => { - desc => "List template items of node.", - usage => "||$usagemsg{objreturn}|", - example => "|List all the templates. |GET /templates/node/all {\"options\":{\"--template\":\"\"}}| [cec-template,hmc-template,ppc64le-template,x86_64-template,x86_64kvmguest-template]", - cmd => "lsdef", - fhandler => \&defhdl, - outhdler => \&defout_remove_appended_type, - }, - }, - }, -); - -# supported formats -my %formatters = ( - 'json' => \&wrapJson, - - #'html' => \&wrapHtml, - #'xml' => \&wrapXml -); - -# error status codes -my $STATUS_BAD_REQUEST = "400 Bad Request"; -my $STATUS_UNAUTH = "401 Unauthorized"; -my $STATUS_FORBIDDEN = "403 Forbidden"; -my $STATUS_NOT_FOUND = "404 Not Found"; -my $STATUS_NOT_ALLOWED = "405 Method Not Allowed"; -my $STATUS_NOT_ACCEPTABLE = "406 Not Acceptable"; -my $STATUS_TIMEOUT = "408 Request Timeout"; -my $STATUS_EXPECT_FAILED = "417 Expectation Failed"; -my $STATUS_TEAPOT = "418 I'm a teapot"; -my $STATUS_SERVICE_UNAVAILABLE = "503 Service Unavailable"; - -# good status codes -my $STATUS_OK = "200 OK"; -my $STATUS_CREATED = "201 Created"; - -# Development notes: -# - added this line to /etc/httpd/conf/httpd.conf to hide the cgi-bin and .cgi extension in the uri: -# ScriptAlias /xcatws /var/www/cgi-bin/xcatws.cgi -# - also upgraded CGI to 3.52 -# - If "Internal Server Error" is returned, look at /var/log/httpd/ssl_error_log -# - can run your cgi script from the cli: http://perldoc.perl.org/CGI.html#DEBUGGING - -# This is how the parameters come in: -# GET: url parameters come $q->url_param. There is no put/post data. -# PUT: url parameters come $q->url_param. Put data comes in q->param(PUTDATA). -# POST: url parameters come $q->url_param. Post data comes in q->param(POSTDATA). -# DELETE: ?? - -# Notes from http://perldoc.perl.org/CGI.html: -# %params = $q->Vars; # same as $q->param() except put it in a hash -# @foo = split("\0",$params{'foo'}); -# my $error = $q->cgi_error; #todo: check for errors that occurred while processing user input -# print $q->end_html; #todo: add the tags -# $q->url_param() # gets url options, even when there is put/post data (unlike q->param) - - - -#### Main procedure to handle the REST request - -# To get the HTTP elements through perl CGI module -my $q = CGI->new; -my $pathInfo = $q->path_info; # the resource specification, i.e. everything in the url after xcatws -my $requestType = $q->request_method(); # GET, PUT, POST, PATCH, DELETE -my $userAgent = $q->user_agent(); # the client program: curl, etc. -my @path = split(/\//, $pathInfo); # The uri path like /nodes/node1/... - -# Define the golbal variables which will be used through the handling process -my $pageContent = ''; # Global var containing the ouptut back to the rest client -my %header_info; #Global var containing the extra info to the http header -my $request = { clienttype => 'ws' }; # Global var that holds the request to send to xcatd -my $format = 'json'; # The output format for a request invoke -my $xmlinstalled; # Global var to speicfy whether the xml modules have been loaded - -# To easy the perl debug, this script can be run directly with 'perl -d' -# This script also support to generate the rest api doc automatically. -# Following part of code will not be run when this script is called by http server -my $dbgdata; -sub dbgusage { print "Usage:\n $0 -h\n $0 -g rst > ../../docs/source/advanced/restapi/restapi_resource/restapi_reference.rst (generate document)\n $0 {GET|PUT|POST|DELETE} URI user:password \'{data}\'\n"; } - -if ($ARGV[0] eq "-h") { - dbgusage(); - exit 0; -} elsif ($ARGV[0] eq "-g") { - - # generate the document - require genrestapidoc; - if (defined($ARGV[1])) { - genrestapidoc::gendoc(\%URIdef, $ARGV[1]); - } else { - genrestapidoc::gendoc(\%URIdef); - } - exit 0; -} elsif ($ARGV[0] eq "-d") { - displayUsage(); - exit 0; -} elsif ($ARGV[0] =~ /(GET|PUT|POST|DELETE)/) { - - # parse the parameters when run this script locally - $requestType = $ARGV[0]; - $pathInfo = $ARGV[1]; - - unless ($pathInfo) { dbgusage(); exit 1; } - - if ($ARGV[2] =~ /(.*):(.*)/) { - $ENV{userName} = $1; - $ENV{password} = $2; - } else { - dbgusage(); - exit 0; - } - $dbgdata = $ARGV[3] if defined($ARGV[3]); -} elsif (defined($ARGV[0])) { - dbgusage(); - exit 1; -} - -my $JSON; # global ptr to the json object. Its set by loadJSON() - -# Since the json is the only supported format, load it at beginning -# need to do this early, so we can fetch the PUT/POST params -loadJSON(); - -# the input parameters from both the url and put/post data will be combined and then -# separated into the general params (not specific to the api call) and params specific to the call -# Note: some of the values of the params in the hash can be arrays -# $generalparams - the general parameters like 'debug=1', 'pretty=1' -# $paramhash - all parameters that come from the url or put/post data except the ones that are put in $generalparams -my ($generalparams, $paramhash) = fetchParameters(); - -my $DEBUGGING = $generalparams->{debug}; # turn on or off the debugging output by setting debug=1 (or 2) in the url string -if ($DEBUGGING) { - displaydebugmsg(); -} - -# The filter flag is used to group the nodes which have the same output -my $XCOLL = $generalparams->{xcoll}; - -# Process the format requested -$format = $generalparams->{format} if (defined($generalparams->{format})); - -# Remove the last '/' in the pathInfo -$pathInfo =~ s/\/$//; - -# Get the payload format from the end of URI -#if ($pathInfo =~ /\.json$/) { -# $format = "json"; -# $pathInfo =~ s/\.json$//; -#} elsif ($pathInfo =~ /\.json.pretty$/) { -# $format = "json"; -# $pretty = 1; -# $pathInfo =~ s/\.json.pretty$//; -#} elsif ($pathInfo =~ /\.xml$/) { -# $format = "xml"; -# $pathInfo =~ s/\.xml$//; -#} elsif ($pathInfo =~ /\.html$/) { -# $format = "html"; -# $pathInfo =~ s/\.html$//; -#} - -#if (!exists $formatters{$format}) { -# error("The format '$format' is not supported",$STATUS_BAD_REQUEST); -#} - -if ($format eq 'json') { - - # make the output to be readable if 'pretty=1' is specified - if ($generalparams->{pretty}) { $JSON->indent(1); } -} - -# we need XML all the time to send request to xcat, even if thats not the return format requested by the user -loadXML(); - -# The first layer of resource URI. It should be 'nodes' for URI '/nodes/node1' -my $uriLayer1; - -# Get all the layers in the URI -my @layers = split('\/', $pathInfo); -shift(@layers); - -if ($#layers < 0) { - - # If no resource was specified, list all the resource groups which have been defined in the %URIdef - my $json; - foreach (sort keys %URIdef) { - push @{$json}, $_; - } - if ($json) { - addPageContent($JSON->encode($json)); - } - sendResponseMsg($STATUS_OK); # this will also exit -} else { - $uriLayer1 = $layers[0]; -} - -# set the user and password to access xcatd -$request->{becomeuser}->[0]->{username}->[0] = $ENV{userName} if (defined($ENV{userName})); -$request->{becomeuser}->[0]->{username}->[0] = $generalparams->{userName} if (defined($generalparams->{userName})); -$request->{becomeuser}->[0]->{password}->[0] = $ENV{password} if (defined($ENV{password})); -$request->{becomeuser}->[0]->{password}->[0] = $generalparams->{userPW} if (defined($generalparams->{userPW})); - -# use the token if it is specified with X_AUTH_TOKEN head -$request->{tokens}->[0]->{tokenid}->[0] = $ENV{'HTTP_X_AUTH_TOKEN'} if (defined($ENV{'HTTP_X_AUTH_TOKEN'})); - -# find and invoke the correct handler and output handler functions -my $outputdata; -my $handled; -if (defined($URIdef{$uriLayer1})) { - - # Make sure the resource has been defined - foreach my $res (keys %{ $URIdef{$uriLayer1} }) { - my $matcher = $URIdef{$uriLayer1}->{$res}->{matcher}; - if ($pathInfo =~ m|$matcher|) { - - # matched to a resource - unless (defined($URIdef{$uriLayer1}->{$res}->{$requestType})) { - error("request method '$requestType' is not supported on resource '$pathInfo'", $STATUS_NOT_ALLOWED); - } - if (defined($URIdef{$uriLayer1}->{$res}->{$requestType}->{fhandler})) { - my $params; - - $params->{'cmd'} = $URIdef{$uriLayer1}->{$res}->{$requestType}->{cmd} if (defined($URIdef{$uriLayer1}->{$res}->{$requestType}->{cmd})); - $params->{'outputhdler'} = $URIdef{$uriLayer1}->{$res}->{$requestType}->{outhdler} if (defined($URIdef{$uriLayer1}->{$res}->{$requestType}->{outhdler})); - $params->{'layers'} = \@layers; - $params->{'resourcegroup'} = $uriLayer1; - $params->{'resourcename'} = $res; - - # Call the handler subroutine which specified in 'fhandler' to send request to xcatd and get the response - $outputdata = $URIdef{$uriLayer1}->{$res}->{$requestType}->{fhandler}->($params); - - # Filter the output data from the response - $outputdata = filterData($outputdata); - - # Restructure the output data with the subroutine which is specified in 'outhdler' - if (defined($URIdef{$uriLayer1}->{$res}->{$requestType}->{outhdler})) { - $outputdata = $URIdef{$uriLayer1}->{$res}->{$requestType}->{outhdler}->($outputdata, $params); - } else { - - # Call the appropriate formatting function stored in the formatters hash as default output handler - if (exists $formatters{$format}) { - $formatters{$format}->($outputdata); - } - } - - $handled = 1; - last; - } - } - } -} else { - - # not matches to any resource group. Check the 'resource group' to improve the performance - error("Unspported resource.", $STATUS_NOT_FOUND); -} - -# the URI cannot match to any resources which are defined in %URIdef -unless ($handled) { - error("Unspported resource.", $STATUS_NOT_FOUND); -} - - -# all output has been added into the global varibale pageContent, call the response funcion to generate HTTP reply and exit - -if (isPost()) { - sendResponseMsg($STATUS_CREATED); -} -else { - sendResponseMsg($STATUS_OK); -} - -#### End of the Main Program - - - - - -#=========================================================== -# Subrutines -sub isGET { return uc($requestType) eq "GET"; } -sub isPost { return uc($requestType) eq "POST"; } -sub isPut { return uc($requestType) eq "PUT"; } -sub isPatch { return uc($requestType) eq "PATCH"; } -sub isDelete { return uc($requestType) eq "DELETE"; } - - -#handle the output for def command and rscan -#handle the input like -# ===raw xml input -# $d->{info}->[msg list] - each msg could be mulitple msg which split with '\n' -# $d->{data}->[msg list] -# -# ===msg format -# Object name: -# attr=value -#OR -# : -# attr=value -# --- -#TO -# --- -# { : { -# attr : value -# ... -# } ... } -sub defout { - my $data = shift; - - my $json; - foreach my $d (@$data) { - my $nodename; - my $lines; - my @alldata; - if (defined($d->{info})) { - foreach (@{ $d->{info} }) { - push @alldata, split('\n', $_); - } - $lines = \@alldata; - } elsif (defined($d->{data})) { - foreach (@{ $d->{data} }) { - push @alldata, split('\n', $_); - } - $lines = \@alldata; - } - foreach my $l (@$lines) { - if ($l =~ /No responses/) { # handle the case that no output from lsslp command - return; - } elsif ($l =~ /Could not find any object definitions/) { - $json->{info} = $l; - last; - } - if ($l =~ /^Object name: / || $l =~ /^\S+:$/) { # start new node - if ($l =~ /^Object name:\s+(\S+)/) { # handle the output of lsdef -t - $nodename = $1; - } - if ($l =~ /^(\S+):$/) { # handle the output for stanza format '-z' - $nodename = $1; - } - } - else { # just an attribute of the current node - if (!$nodename) { error('improperly formatted lsdef output from xcatd', $STATUS_TEAPOT); } - my ($attr, $val) = $l =~ /^\s*(\S+?)=(.*)$/; - if (!defined($attr)) { error('improperly formatted lsdef output from xcatd', $STATUS_TEAPOT); } - $json->{$nodename}->{$attr} = $val; - } - } - } - if ($json) { - addPageContent($JSON->encode($json), 1); - } -} - -#handle the output for lsdef -t command -#handle the input like -# ===raw xml input -# $d->{info}->[msg list] - each msg could be mulitple msg which split with '\n' -# -# ===msg format -# node1 (node) -# node2 (node) -# node3 (node) -# --- -#TO -# --- -# node1 -# node2 -# node3 -sub defout_remove_appended_type { - my $data = shift; - - my $json; - foreach my $d (@$data) { - my $jsonnode; - my $lines = $d->{info}; - foreach my $l (@$lines) { - if ($l =~ /^(\S*)\s+\(.*\)$/) { # start new node - push @{$json}, $1; - } elsif ($l =~ /Could not find any object definitions/) { - push @{$json}, $l; - last; - } - } - } - if ($json) { - addPageContent($JSON->encode($json), 1); - } -} - -#handle the output for bmcdiscover command -#handle the input like -# -#$VAR1 = [ -# { -# 'info' => [ -# 'bmc_1' -# ] -# }, -# { -# 'info' => [ -# 'bmc_2' -# ] -# }, -# { -# 'info' => [ -# 'bmc_3' -# ] -# } -# ]; -# -# ===msg format -# bmc_1 -# bmc_2 -# bmc_3 -# --- -# -#TO -# --- -# [ -# "bmc_1", -# "bmc_2", -# "bmc_3" -# ] - -# -sub defout_remove_appended_info { - my $data = shift; - - my $json; - foreach my $d (@$data) { - my $jsonnode; - my $lines = $d->{info}; - foreach my $l (@$lines) { - - # if ($l =~ /^(\S*)\s+\(.*\)$/) { # start new node - push(@{$json}, $l); - - # } - } - } - if ($json) { - addPageContent($JSON->encode($json), 1); - } -} - - -sub localresout { - my $data = shift; - my $json; - if ($data->[0]->{info}->[0] eq 'stream') { - $format = 'stream'; - $header_info{'attachment'} = $data->[0]->{info}->[1]; - addPageContent($data->[0]->{info}->[2]); - } elsif ($data->[0]->{info}->[0] eq 'json') { - addPageContent($data->[0]->{info}->[1]); - } -} - -# hanlde the output which has the node irrelevant message (e.g. the output for updatenode command) -# handle the input like -# ===raw xml input -# $d->{info}->[msg list] - each msg could be mulitple msg which split with '\n' -# $d->{data}->[msg list] -# $d->{data}->{contents}->[msg list] -# -# ===msg format -# "There were no syncfiles defined to process. File synchronization has completed.", -# "Performing software maintenance operations. This could take a while, if there are packages to install.", -# "node2: Tue Apr 2 15:55:57 CST 2013 Running postscript: ospkgs", -# --- -#TO -# --- -# [ -# "There were no syncfiles defined to process. File synchronization has completed.", -# "Performing software maintenance operations. This could take a while, if there are packages to install.", -# "node2: Tue Apr 2 15:55:57 CST 2013 Running postscript: ospkgs", -# ] -# -# An exception is to handle the output of 'xdsh'(nodeshell). Since each msg has a : head, split the head out and group -# the msg with the name in the head. - -sub infoout { - my $data = shift; - my $param = shift; - - my $json; - foreach my $d (@$data) { - if (defined($d->{info})) { - foreach (@{ $d->{info} }) { - push @{$json}, split('\n', $_); - } - } - if (defined($d->{data})) { - if (ref($d->{data}->[0]) ne "HASH") { - foreach (@{ $d->{data} }) { - push @{$json}, split('\n', $_); - } - } else { - if (defined($d->{data}->[0]->{contents})) { - push @{$json}, @{ $d->{data}->[0]->{contents} }; - } - } - } - if (defined($d->{error})) { - push @{$json}, @{ $d->{error} }; - } - } - - # for nodeshell (xdsh), group msg with node name - if ($param->{'resourcename'} =~ /(nodeshell|postscript|software_maintenance)/) { - my $jsonnode; - foreach (@{$json}) { - if (/^(\S+):(.*)$/) { - push @{ $jsonnode->{$1} }, $2 if ($2 !~ /^\s*$/); - } - } - if (!$jsonnode && $json) - { - push(@{$jsonnode}, @{$json}); - } - addPageContent($JSON->encode($jsonnode), 1); - return; - } - if ($json) { - addPageContent($JSON->encode($json), 1); - } -} - -# hanlde the output which is node relevant (rpower, rinv, rvitals ...) -# the output must be grouped with 'node' as key -# handle the input like -# ===raw xml input -# $d->{node}->{name}->[name] # this is must have, otherwise ignore the msg -# $d->{node}->{data}->[msg] -#OR -# $d->{node}->{name}->[name] # this is must have, otherwise ignore the msg -# $d->{node}->{data}->{contents}->[msg] -#OR -# $d->{node}->{name}->[name] # this is must have, otherwise ignore the msg -# $d->{node}->{data}->{contents}->[msg] -# $d->{node}->{data}->{desc}->[msg] -# -# Note: if does not have '$d->{node}->{data}->{desc}', use the resource name as the name of attribute. -# e.g. Get /node/node1/power, the record is '"power":"off"' -# -# ===msg format -# -# -# 1.41 (VVE128GUS 2013/07/22) -# UEFI Version -# -# node1 -# -# --- -#TO -# --- -# { -# "node1":{ -# "UEFI Version":"1.41 (VVE128GUS 2013/07/22)", -# } -# } -sub actionout { - my $data = shift; - my $param = shift; - - my $jsonnode; - foreach my $d (@$data) { - unless (defined($d->{node}->[0]->{name})) { - next; - } - if (defined($d->{node}->[0]->{data}) && (ref($d->{node}->[0]->{data}->[0]) ne "HASH" || !defined($d->{node}->[0]->{data}->[0]->{contents}))) { - - # no $d->{node}->{data}->{contents} or $d->{node}->[0]->{data} is not hash - $jsonnode->{ $d->{node}->[0]->{name}->[0] }->{ $param->{'resourcename'} } = $d->{node}->[0]->{data}->[0]; - } elsif (defined($d->{node}->[0]->{data}->[0]->{contents})) { - if (defined($d->{node}->[0]->{data}->[0]->{desc})) { - - # has $d->{node}->{data}->{desc} - $jsonnode->{ $d->{node}->[0]->{name}->[0] }->{ $d->{node}->[0]->{data}->[0]->{desc}->[0] } = $d->{node}->[0]->{data}->[0]->{contents}->[0]; - } else { - - # use resourcename as the record name - if ($param->{'resourcename'} eq "eventlog") { - push @{ $jsonnode->{ $d->{node}->[0]->{name}->[0] }->{ $param->{'resourcename'} } }, $d->{node}->[0]->{data}->[0]->{contents}->[0]; - } elsif ($param->{'resourcename'} =~ /(vitals|inventory)/) { - - # handle output of rvital and rinv for ppc node - #push @{$jsonnode->{$d->{node}->[0]->{name}->[0]}}, $d->{node}->[0]->{data}->[0]->{contents}->[0]; - push @{ $jsonnode->{ $d->{node}->[0]->{name}->[0] }->{Message} }, $d->{node}->[0]->{data}->[0]->{contents}->[0]; - } else { - $jsonnode->{ $d->{node}->[0]->{name}->[0] }->{ $param->{'resourcename'} } = $d->{node}->[0]->{data}->[0]->{contents}->[0]; - } - } - } - } - - addPageContent($JSON->encode($jsonnode), 1) if ($jsonnode); -} - -# hanlde the output which has the token id -# handle the input like -# ===raw xml input -# $d->{data}->{token}->{id} -# $d->{data}->{token}->{expire} -sub tokenout { - my $data = shift; - - my $json; - foreach my $d (@$data) { - if (defined($d->{data}) && defined($d->{data}->[0]->{token})) { - $json->{token}->{id} = $d->{data}->[0]->{token}->[0]->{id}->[0]; - $json->{token}->{expire} = $d->{data}->[0]->{token}->[0]->{expire}->[0]; - } - } - - if ($json) { - addPageContent($JSON->encode($json)); - } -} - -# This is the general callback subroutine for PUT/POST/DELETE methods -# when this subroutine is called, that means the operation has been done successfully -# The correct output is 'null' -sub noout { - return; -### for debugging - my $data = shift; - - addPageContent(qq(\n\n\n=======================================================\nDebug: Following message is just for debugging. It will be removed in the GAed version.\n)); - - my $json; - if ($data) { - addPageContent($JSON->encode($data)); - } - - addPageContent(qq(["Debug: the operation has been done successfully"])); -### finish the debugging -} - -# The operation callback subroutine for def related resource (lsdef, chdef ...) -# assembe the xcat request, send it to xcatd and get response -sub defhdl { - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # set the command name - $request->{command} = $params->{'cmd'}; - - # push the -t args for *def command - my $resrctype = $params->{'resourcegroup'}; - $resrctype =~ s/s$//; # remove the last 's' as the type of object - push @args, ('-t', $resrctype) if $resrctype ne 'template'; - - # push the object name - node/noderange - if (defined($urilayers[1]) && $resrctype ne 'template') { - if ($urilayers[1] eq "ALLRESOURCES") { - unless (isGET()) { - error("Keyword ALLRESOURCES is only supported for GET Action.", $STATUS_NOT_FOUND); - } - push @args, '-l'; - } else { - push @args, ('-o', $urilayers[1]); - } - } - - # For template only. - if ($resrctype eq 'template') { - if ($params->{'resourcename'} eq "all") { - $paramhash->{'options'}->{'-a'} = ""; - } - } - # For the put/post which specifies attributes like mgt=ipmi groups=all - foreach my $k (keys(%$paramhash)) { - next if (!$k); - # NOTE: The json field 'options' may be confilict with the attibute - # name called 'options', but it does not happen currently. A better - # solution is to add a new json field called attributes like this - # {options:{}, attributes:{}} to avoid of the ambiguity. - if ($k eq 'options') { - my $options = $paramhash->{$k}; - my ($opt_key, $opt_val); - while(($opt_key, $opt_val) = each(%{$options})) { - next if (!$opt_key || grep (/^$opt_key$/, ('-o','-l','-t'))); - push @args, $opt_key; - push @args, $opt_val if $opt_val; - } - next; - } - push @args, "$k=$paramhash->{$k}" if $paramhash->{$k}; - } - - if ($params->{'resourcename'} eq "allnode") { - push @args, '-s'; - } elsif ($params->{'resourcename'} =~ /(nodeattr|osimage_attr|group_attr|policy_attr|network_attr)/) { - - # if /nodes/node1/attrs/attr1,att2 is specified, for get request, - # use 'lsdef -i' to specify the attribute list - my $attrs = $urilayers[3]; - $attrs =~ s/;/,/g; - - if (isGET()) { - push @args, ('-i', $attrs); - } - } - - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - - return $responses; -} - -# The operation callback subroutine for any node related resource (power, energy ...) -# assembe the xcat request, send it to xcatd and get response -sub actionhdl { - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # set the command name - $request->{command} = $params->{'cmd'}; - - # push the object name - node/noderange - if (defined($urilayers[1])) { - $request->{noderange} = $urilayers[1]; - } - - if ($params->{'resourcename'} eq "power") { - if (isGET()) { - push @args, 'stat'; - } elsif ($paramhash->{'action'}) { - - #my @v = keys(%$paramhash); - push @args, $paramhash->{'action'}; - } else { - error("Missed Action.", $STATUS_NOT_FOUND); - } - } elsif ($params->{'resourcename'} =~ /(energy|energyattr)/) { - if (isGET()) { - if ($params->{'resourcename'} eq "energy") { - push @args, 'all'; - } elsif ($params->{'resourcename'} eq "energyattr") { - my @attrs = split(',', $urilayers[3]); - push @args, @attrs; - } - } elsif ($paramhash) { - my @params = keys(%$paramhash); - push @args, "$params[0]=$paramhash->{$params[0]}"; - } else { - error("Missed Action.", $STATUS_NOT_FOUND); - } - } elsif ($params->{'resourcename'} eq "bootstate") { - if (isGET()) { - push @args, 'stat'; - } elsif ($paramhash->{'action'}) { - push @args, $paramhash->{'action'}; - } elsif ($paramhash) { - my @params = keys(%$paramhash); - if ($params[0] eq "state") { - - # hanlde the {state:offline} - push @args, $paramhash->{ $params[0] }; - } else { - - # handle the {osimage:imagename} - push @args, "$params[0]=$paramhash->{$params[0]}"; - } - } else { - error("Missed Action.", $STATUS_NOT_FOUND); - } - } elsif ($params->{'resourcename'} eq "nextboot") { - if (isGET()) { - push @args, 'stat'; - } elsif ($paramhash->{'order'}) { - push @args, $paramhash->{'order'}; - } else { - error("Missed Action.", $STATUS_NOT_FOUND); - } - } elsif ($params->{'resourcename'} =~ /(vitals|vitalsattr|inventory|inventoryattr)/) { - if (defined($urilayers[3])) { - my @attrs = split(';', $urilayers[3]); - push @args, @attrs; - } else { # default, get all attrs - push @args, "all"; - } - } elsif ($params->{'resourcename'} eq "serviceprocessor") { - if (isGET()) { - push @args, $urilayers[3]; - } elsif (isPut() or isPost()) { - if ($paramhash->{'value'} and defined($urilayers[3])) { - push @args, $urilayers[3] . "=" . $paramhash->{'value'}; - } else { - foreach my $key (keys %$paramhash) { - if (($key ne '') and (exists($paramhash->{$key}))) { - push @args, $key . "=" . $paramhash->{$key}; - } - } - } - } - } elsif ($params->{'resourcename'} eq "eventlog") { - if (isGET()) { - push @args, 'all'; - } elsif (isDelete()) { - push @args, 'clear'; - } - } elsif ($params->{'resourcename'} eq "beacon") { - if (isPut()) { - push @args, $paramhash->{'action'}; - } - } elsif ($params->{'resourcename'} eq "filesyncing") { - push @args, '-F'; - } elsif ($params->{'resourcename'} eq "software_maintenance") { - push @args, '-S'; - } elsif ($params->{'resourcename'} eq "postscript") { - push @args, '-P'; - if (defined($paramhash->{'scripts'})) { - push @args, join(',', @{ $paramhash->{'scripts'} }); - } - } elsif ($params->{'resourcename'} eq "nodeshell") { - if (%$paramhash) - { - foreach my $key1 (keys %$paramhash) { - if ($key1 eq "ENV" && defined($paramhash->{'ENV'})) { - foreach my $key (keys %{ $paramhash->{'ENV'} }) { - if (($key ne '') and (exists($paramhash->{'ENV'}->{$key}))) { - push(@{ $request->{env} }, "$key=$paramhash->{'ENV'}->{$key}"); - } - } - next; - } - elsif ($key1 eq "raw" && defined($paramhash->{'raw'})) { - if (ref($paramhash->{'raw'}) eq "ARRAY") { - push @args, join(';', @{ $paramhash->{'raw'} }); - } else { - push @args, $paramhash->{'raw'}; - } - next; - } - elsif ($key1 eq "command" && defined($paramhash->{'command'})) { - if (ref($paramhash->{'command'}) eq "ARRAY") { - push @args, join(';', @{ $paramhash->{'command'} }); - } else { - push @args, $paramhash->{'command'}; - } - next; - } - } - } - else { - error("Lack of operation data.", $STATUS_BAD_REQUEST, 3); - } - - } elsif ($params->{'resourcename'} eq "nodecopy") { - if (defined($paramhash->{'src'})) { - push @args, @{ $paramhash->{'src'} }; - } - if (defined($paramhash->{'target'})) { - push @args, $paramhash->{'target'}; - } - } elsif ($params->{'resourcename'} =~ /(dns|dhcp)/) { - if (isDelete()) { - push @args, '-d'; - } - } elsif ($params->{'resourcename'} eq "subnodes") { - if (isGET()) { - push @args, '-z'; - } - } elsif ($params->{'resourcename'} eq "vm") { - - # handle the virtual machine - if (isGET()) { - - # do nothing for kvm and esxi - } elsif (isPut()) { # change the configuration of vm - if (defined($paramhash->{'adddisk'})) { #add new disk - push @args, ('-a', $paramhash->{'adddisk'}); - } - - #if (defined ($paramhash->{'rmdisk'})) { #remove disk - # push @args, ('-d', $paramhash->{'rmdisk'}); - #} - if (defined($paramhash->{'purgedisk'})) { #purge disk - push @args, ('-p', $paramhash->{'purgedisk'}); - } - if (defined($paramhash->{'resizedisk'})) { #change the disk size - $paramhash->{'resizedisk'} =~ s/\:/=/; # replace : to be = in the param - push @args, ('--resize', $paramhash->{'resizedisk'}); - } - if (defined($paramhash->{'memorysize'})) { #change the memory size - push @args, ('--mem', $paramhash->{'memorysize'}); - } - if (defined($paramhash->{'cpucount'})) { #change the cpu size - push @args, ('--cpus', $paramhash->{'cpucount'}); - } - } elsif (isPost()) { # create virtual machine - if (defined($paramhash->{'master'})) { # specify the master node for clone - push @args, ('-m', $paramhash->{'master'}); - } - if (defined($paramhash->{'disksize'})) { # specify disk size - push @args, ('-s', $paramhash->{'disksize'}); - } - if (defined($paramhash->{'memorysize'})) { #specify the memory size - push @args, ('--mem', $paramhash->{'memorysize'}); - } - if (defined($paramhash->{'cpucount'})) { #specify the cpu size - push @args, ('--cpus', $paramhash->{'cpucount'}); - } - if (defined($paramhash->{'force'}) && $paramhash->{'force'} eq "yes") { # force the recreate - push @args, "-f"; - } - } elsif (isDelete()) { - if (defined($paramhash->{'force'}) && $paramhash->{'force'} eq "yes") { # force the recreate - push @args, "-f"; - } - if (defined($paramhash->{'purge'}) && $paramhash->{'purge'} eq "yes") { # purge disk when remove the vm - push @args, "-p"; - } - } - } elsif ($params->{'resourcename'} eq "vmclone") { - - # handle the clone of virtual machine - if (isPost()) { - if (defined($paramhash->{'tomaster'})) { - push @args, ("-t", $paramhash->{'tomaster'}); - } elsif (defined($paramhash->{'frommaster'})) { - push @args, ("-b", $paramhash->{'frommaster'}); - } else { - error("Lack of operation data.", $STATUS_BAD_REQUEST, 3); - } - - if (defined($paramhash->{'detach'}) && $paramhash->{'detach'} eq "yes") { - push @args, "-d"; - } - if (defined($paramhash->{'force'}) && $paramhash->{'force'} eq "yes") { # force the recreate - push @args, "-f"; - } - } - } elsif (($params->{'resourcename'} eq "vmmigrate")) { - - # handle the migration of virtual machine - if (isPost()) { - if (defined($paramhash->{'target'})) { - push @args, $paramhash->{'target'}; - } else { - error("Lack of operation data.", $STATUS_BAD_REQUEST, 3); - } - } - } elsif ($params->{'resourcename'} eq "noderename") { - - if (isPut()) { - if (defined($paramhash->{'newNode'})) { #specify the new name for node - push @args, ('-t', "node"); - push @args, ('-o', $urilayers[1]); - push @args, ('-n', $paramhash->{'newNode'}); - } - } - } elsif ($params->{'resourcename'} eq "nbimage") { - delete $request->{noderange}; - push @args, $urilayers[3]; - if (isPost()) { - if (defined($paramhash->{'onlyconfigfile'})) { - my $tmp_value = $paramhash->{'onlyconfigfile'}; - if ($tmp_value =~ /true|yes|Y|1/i) { - push @args, "-c"; - } elsif ($tmp_value !~ /false|no|N|0/i) { - error("Option value \"$tmp_value\" invalid.", $STATUS_BAD_REQUEST, 3); - } - } - } - - } elsif ($params->{'resourcename'} eq "console") { - delete $request->{noderange}; - if ($paramhash->{'action'}) { - my %action = ('on' => '', 'off' => '-d'); - push @args, $action{$paramhash->{'action'}}; - if ($paramhash->{'nodes'}) { - $request->{noderange} = join(',', @{$paramhash->{'nodes'}}); - } else { - error("Missed node.") - } - } - if ($paramhash->{'trust_host'}) { - push @args, '-t'; - push @args, $paramhash->{'trust_host'}; - } - } elsif ($params->{'resourcename'} eq "provision") { - if ($paramhash->{'osimage'}) { - push @args, 'osimage='.$paramhash->{'osimage'}; - } elsif ($paramhash->{'action'}) { - push @args, $paramhash->{'action'}; - } - } - - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - - return $responses; -} - -sub localreshdl { - my $params = shift; - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # set the command name - $request->{command} = $params->{'cmd'}; - - if (isGET() && scalar(@urilayers) > 1 && $urilayers[-1] eq "detail") { - push @args, "show"; - } elsif (isGET() && scalar(@urilayers) > 1 && $urilayers[-1] eq "file") { - push @args, "download"; - } elsif (isGET()) { - push @args, "list"; - } elsif (isPost()) { - push @args, "create"; - } elsif (isPut()) { - push @args, "update"; - } elsif (isDelete()) { - push @args, "delete"; - } - shift @urilayers; - foreach my $item (@urilayers) { - push @args, $item if $item ne 'detail' && $item ne 'file'; - } - push @{ $request->{arg} }, @args; - - # localrest is single plugin handler, use sequntial to avoid of multi-level processes - $request->{'sequential'}->[0] = 1; - my $req = genRequest(); - my $responses = sendRequest($req); - return $responses; -} - -# The operation callback subroutine for node irrelevant commands like makedns -n and makedhcp -n -# assembe the xcat request, send it to xcatd and get response -sub nonobjhdl { - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # set the command name - $request->{command} = $params->{'cmd'}; - if ($params->{'resourcename'} =~ /(dns|dhcp)/) { - push @args, '-n'; - } elsif ($params->{'resourcename'} eq "slpnodes") { - if (isGET()) { - push @args, '-z'; - } - } elsif ($params->{'resourcename'} eq "specific_slpnodes") { - if (isGET()) { - push @args, "-z"; - push @args, "-s"; - push @args, $urilayers[2]; - } - } elsif ($params->{'resourcename'} eq "tokens") { - $request->{gettoken}->[0]->{username}->[0] = $generalparams->{userName} if (defined($generalparams->{userName})); - $request->{gettoken}->[0]->{password}->[0] = $generalparams->{userPW} if (defined($generalparams->{userPW})); - } - - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - - return $responses; -} - - -# operate image instance for a osimage -sub imgophdl { - my $params = shift; - my @args = (); - if (isPost()) { - if ($params->{'resourcename'} eq "osimage_op") { - my $action = $paramhash->{'action'}; - unless ($action) { - error("Missed Action.", $STATUS_NOT_FOUND); - } elsif ($action eq "gen") { - $params->{'cmd'} = "genimage"; - } elsif ($action eq "pack") { - $params->{'cmd'} = "packimage"; - } elsif ($action eq "export") { - $params->{'cmd'} = "imgexport"; - } else { - error("Incorrect action:$action.", $STATUS_BAD_REQUEST); - } - } elsif ($params->{'resourcename'} eq "osimage") { - if (exists($paramhash->{'iso'})) { - $params->{'cmd'} = "copycds"; - push @{ $params->{layers} }, $paramhash->{'iso'}; - } elsif (exists($paramhash->{'file'})) { - $params->{'cmd'} = "imgimport"; - push @{ $params->{layers} }, $paramhash->{'file'}; - } elsif (exists($paramhash->{'node'})) { - $params->{'cmd'} = "imgcapture"; - - #push @{$params->{layers}}, $paramhash->{'node'}; - push @{ $request->{noderange} }, $paramhash->{'node'}; - } else { - error("Invalid source.", $STATUS_NOT_FOUND); - } - } - } - $request->{command} = $params->{'cmd'}; - push @args, $params->{layers}->[1]; - if (exists($paramhash->{'params'})) { - foreach (keys %{ $paramhash->{'params'}->[0] }) { - push @args, ($_, $paramhash->{'params'}->[0]->{$_}); - } - } - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - return $responses; -} - -sub sitehdl { - my $params = shift; - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # set the command name - $request->{command} = $params->{'cmd'}; - - # push the -t args - push @args, '-t'; - push @args, 'site'; - if (isGET()) { - push @args, 'clustersite'; - } - if (defined($urilayers[2])) { - if (isGET()) { - push @args, ('-i', $urilayers[2]); - } - } - if (isDelete()) { - if (defined($urilayers[2])) { - push @args, "$urilayers[2]="; - } - } - foreach my $k (keys(%$paramhash)) { - push @args, "$k=$paramhash->{$k}" if ($k); - } - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - - return $responses; -} - - -# get attrs of tables for a noderange -sub tablenodehdl { - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # the array elements for @urilayers are: - # 0 - 'table' - # 1 - - # 2 - 'nodes' - # 3 - (optional) - # 4 - (optional) - - # set the command name - my @tables = split(/,/, $urilayers[1]); - - if (!defined($urilayers[3]) || $urilayers[3] eq 'ALLNODES') { - $request->{command} = 'getTablesAllNodeAttribs'; - } else { - $request->{command} = 'getTablesNodesAttribs'; - $request->{noderange} = $urilayers[3]; - } - - # For both getTablesAllNodeAttribs and getTablesNodesAttribs, the rest of the request strucutre looks like this: - # table => [ - # { - # tablename => nodehm, - # attr => [ - # mgmt, - # cons - # ] - # }, - # { - # tablename => ipmi, - # attr => [ - # ALL - # ] - # } - # ] - - # if they specified attrs, sort/group them by table - my $attrlist = $urilayers[4]; - if (!defined($attrlist)) { $attrlist = 'ALL'; } # attr=ALL means get all non-blank attributes - my @attrs = split(/,/, $attrlist); - my %attrhash; - foreach my $a (@attrs) { - if ($a =~ /\./) { - my ($table, $attr) = split(/\./, $a); - push @{ $attrhash{$table} }, $attr; - } - else { # the attr doesn't have a table qualifier so apply to all tables - foreach my $t (@tables) { push @{ $attrhash{$t} }, $a; } - } - } - - # deal with all of the tables and the attrs for each table - foreach my $tname (@tables) { - my $table = { tablename => $tname }; - if (defined($attrhash{$tname})) { $table->{attr} = $attrhash{$tname}; } - else { $table->{attr} = 'ALL'; } - push @{ $request->{table} }, $table; - } - - - my $req = genRequest(); - - # disabling the KeyAttr option is important in this case, so xmlin doesn't pull the name attribute - # out of the node hash and make it the key - my $responses = sendRequest($req, { SuppressEmpty => undef, ForceArray => 0, KeyAttr => [] }); - - return $responses; -} - -#get bmc ip address source -#check if bmc user or password is correct -sub bmccheckhdl { - - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - my $bmc_ip; - my $bmc_user; - my $bmc_pw; - - # set the command name - $request->{command} = $params->{'cmd'}; - - # get bmc ip - if (defined($urilayers[2])) - { - $bmc_ip = $urilayers[2]; - } - - # get bmc user and password - if (defined($urilayers[3])) - { - my @keyvals = split(/,/, $urilayers[3]); - foreach my $kv (@keyvals) - { - my ($key, $value) = split(/\s*=\s*/, $kv, 2); - if ($key eq "bmcuser") - { - $bmc_user = $value; - } - elsif ($key eq "bmcpw") - { - $bmc_pw = $value; - } - } - } - - if ($params->{'resourcename'} eq "checkbmcauth") { - if (isGET()) { - - push @args, "-i"; - push @args, $bmc_ip; - if (defined($bmc_user) && $bmc_user ne "none") - { - push @args, "-u"; - push @args, $bmc_user; - - } - push @args, "-p"; - push @args, $bmc_pw; - push @args, "-c"; - } - } - - if ($params->{'resourcename'} eq "getbmcipsource") { - if (isGET()) { - push @args, "-i"; - push @args, $bmc_ip; - if (defined($bmc_user) && $bmc_user ne "none") - { - push @args, "-u"; - push @args, $bmc_user; - } - push @args, "-p"; - push @args, $bmc_pw; - push @args, "--ipsource"; - } - } - - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - - return $responses; - - -} - - -#get bmc list for bmcdiscover -sub bmclisthdl { - - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - my $m_value; - my $ip_range; - - # the array elements for @urilayers are: - # 0 - 'bmcdiscover' - # 1 - (optional) - - # set the command name - $request->{command} = $params->{'cmd'}; - - # get method and ip_range - if (defined($urilayers[2])) - { - my @keyvals = split(/,/, $urilayers[2]); - foreach my $kv (@keyvals) - { - my ($key, $value) = split(/\s*=\s*/, $kv, 2); - if ($key eq "method") - { - $m_value = $value; - } - elsif ($key eq "iprange") - { - $ip_range = $value; - } - } - } - - - if ($params->{'resourcename'} eq "bmcdiscover") { - if (isGET()) { - if (defined($m_value)) - { - push @args, "-s"; - push @args, $m_value; - } - push @args, "--range"; - push @args, $ip_range; - } - - } - - push @{ $request->{arg} }, @args; - my $req = genRequest(); - my $responses = sendRequest($req); - - return $responses; - -} - -# get attrs of tables for keys -sub tablerowhdl { - my $params = shift; - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # the array elements for @urilayers are: - # 0 - 'table' - # 1 - - # 2 - 'rows' - # 3 - (optional) - # 4 - (optional) - - # do stuff that is common between getAttribs and getTablesAllRowAttribs - my @tables = split(/,/, $urilayers[1]); - my $attrlist = $urilayers[4]; - if (!defined($attrlist)) { $attrlist = 'ALL'; } # attr=ALL means get all non-blank attributes - my @attrs = split(/,/, $attrlist); - - # get all rows for potentially multiple tables - if (!defined($urilayers[3]) || $urilayers[3] eq 'ALLROWS') { - $request->{command} = 'getTablesAllRowAttribs'; - - # For getTablesAllRowAttribs, the rest of the request strucutre needs to look like this: - # table => [ - # { - # tablename => nodehm, - # attr => [ - # mgmt, - # cons - # ] - # }, - # { - # tablename => ipmi, - # attr => [ - # ALL - # ] - # } - # ] - - # if they specified attrs, sort/group them by table - my %attrhash; - foreach my $a (@attrs) { - if ($a =~ /\./) { - my ($table, $attr) = split(/\./, $a); - push @{ $attrhash{$table} }, $attr; - } - else { # the attr doesn't have a table qualifier so apply to all tables - foreach my $t (@tables) { push @{ $attrhash{$t} }, $a; } - } - } - - # deal with all of the tables and the attrs for each table - foreach my $tname (@tables) { - my $table = { tablename => $tname }; - if (defined($attrhash{$tname})) { $table->{attr} = $attrhash{$tname}; } - else { $table->{attr} = 'ALL'; } - push @{ $request->{table} }, $table; - } - } - - # for 1 table, get just one row based on the keys given - else { - if (scalar(@tables) > 1) { error('currently you can only specify keys for a single table.', $STATUS_BAD_REQUEST); } - $request->{command} = 'getAttribs'; - - # For getAttribs, the rest of the request strucutre needs to look like this: - # { - # table => networks, - # keys => { - # net => 11.35.0.0, - # mask => 255.255.0.0 - # } - # attr => [ - # netname, - # dhcpserver - # ] - # }, - $request->{table} = $tables[0]; - if (defined($urilayers[3])) { - my @keyvals = split(/,/, $urilayers[3]); - foreach my $kv (@keyvals) { - my ($key, $value) = split(/\s*=\s*/, $kv, 2); - $request->{keys}->{$key} = $value; - } - } - foreach my $a (@attrs) { push @{ $request->{attr} }, $a; } - } - - my $req = genRequest(); - - # disabling the KeyAttr option is important in this case, so xmlin doesn't pull the name attribute - # out of the node hash and make it the key - my $responses = sendRequest($req, { SuppressEmpty => undef, ForceArray => 0, KeyAttr => [] }); - - return $responses; -} - -# parse the output of all attrs of tables for the GET calls. This is used for both node-oriented tables -# and non-node-oriented tables. -#todo: investigate a converter straight from xml to json -sub tableout { - my $data = shift; - my $json = {}; - - # For the table get calls, we turned off ForceArray and KeyAttr for XMLin(), so the output is a little - # different than usual. Each element is a hash with key "table" that is either a hash or array of hashes. - # Each element of that is a hash with 2 keys called "tablename" and "node". The latter has either: an array of node hashes, - # or (if there is only 1 node returned) the node hash directly. - # We are producing json that is a hash of table name keys that each have an array of node objects. - foreach my $d (@$data) { - my $table = $d->{table}; - if (!defined($table)) { # special case for the getAttribs cmd - $json->{ $request->{table} }->[0] = $d; - last; - } - - #debug(Dumper($d)); debug (Dumper($jsonnode)); - if (ref($table) eq 'HASH') { $table = [$table]; } # if a single table, make it a 1 element array of tables - foreach my $t (@$table) { - my $jsonnodes = []; # start an array of node objects for this table - my $tabname = $t->{tablename}; - if (!defined($tabname)) { $tabname = 'unknown' . $::i++; } #todo: have lissa fix this bug - $json->{$tabname} = $jsonnodes; # add it into the top level hash - my $node = $t->{node}; - if (!defined($node)) { $node = $t->{row}; } - - #debug(Dumper($d)); debug (Dumper($jsonnode)); - if (ref($node) eq 'HASH') { $node = [$node]; } # if a single node, make it a 1 element array of nodes - foreach my $n (@$node) { push @$jsonnodes, $n; } - } - } - addPageContent($JSON->encode($json)); -} - -# set attrs of nodes in tables -sub tablenodeputhdl { - my $params = shift; - - # from the %URIdef: - # desc => "[URI:/tables/nodes/{noderange}] - Change the table attibutes for the {noderange}.", - # usage => "|An array of table objects. Each table object contains the table name and an object of attribute values. DataBody: {table1:{attr1:v1,att2:v2,...}}.|$usagemsg{non_getreturn}|", - # example => '|Change the nodehm.mgmt and noderes.netboot attributes for nodes node1-node5.|PUT|/tables/nodes/node1-node5 {"nodehm":{"mgmt":"ipmi"},"noderes":{"netboot":"xnba"}}||', - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # the array elements for @urilayers are: - # 0 - 'table' - # 1 - - # 2 - 'nodes' - # 3 - - - # set the command name - $request->{command} = 'setNodesAttribs'; - $request->{noderange} = $urilayers[3]; - - # For setNodesAttribs, the rest of the request strucutre looks like this: - # arg => { - # table => [ - # { - # name => nodehm, - # attr => { - # mgmt => ipmi - # } - # }, - # { - # name => noderes, - # attr => { - # netboot => xnba - # } - # } - # ] - # } - - # Get table list in the URI - my @uritbs = split(/,/, $urilayers[1]); - - # Go thru the list of tables (which are the top level keys in paramhash) - my $tables = []; - $request->{arg}->{table} = $tables; - foreach my $k (keys(%$paramhash)) { - my $intable = $k; - - # Check the validity of tables - if (!grep(/^$intable$/, @uritbs)) { - error("The table $intable is NOT in the URI.", $STATUS_BAD_REQUEST); - } - my $attrhash = $paramhash->{$k}; - my $outtable = { name => $intable, attr => $attrhash }; - push @$tables, $outtable; - } - - my $req = genRequest(); - - # disabling the KeyAttr option is important in this case, so xmlin doesn't pull the name attribute - # out of the node hash and make it the key - my $responses = sendRequest($req, { SuppressEmpty => undef, ForceArray => 1, KeyAttr => [] }); - - return $responses; -} - -# set attrs of a row in a non-node table -sub tablerowputhdl { - my $params = shift; - - # from %URIdef: - # desc => "[URI:/tables/{table}/rows/{keys}] - Change the non-node table attibutes for the row that matches the {keys}.", - # usage => "|A hash of attribute names and values. DataBody: {attr1:v1,att2:v2,...}.|$usagemsg{non_getreturn}|", - # example => '|Creat a route row in the routes table.|PUT|/tables/routes/rows/routename=privnet {"net":"10.0.1.0","mask":"255.255.255.0","gateway":"10.0.1.254","ifname":"eth1"}||', - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # the array elements for @urilayers are: - # 0 - 'table' - # 1 - - # 2 - 'rows' - # 3 - - - # set the command name - $request->{command} = 'setAttribs'; - - # For setAttribs, the rest of the xml request strucutre looks like this: - # routes
                                                  - # - # foo - # - # - # 10.0.1.0 - # This is a test - # - - # set the table name and keys - $request->{table} = $urilayers[1]; - my @keyvals = split(/,/, $urilayers[3]); - foreach my $kv (@keyvals) { - my ($key, $value) = split(/\s*=\s*/, $kv, 2); - $request->{keys}->{$key} = $value; - } - - # the attribute/value hash is already in paramhash - $request->{attr} = $paramhash; - - my $req = genRequest(); - - # disabling the KeyAttr option is important in this case, so xmlin doesn't pull the name attribute - # out of the node hash and make it the key - my $responses = sendRequest($req, { SuppressEmpty => undef, ForceArray => 1, KeyAttr => [] }); - - return $responses; -} - -# delete rows in a non-node table -sub tablerowdelhdl { - my $params = shift; - - # from %URIdef: - # desc => "[URI:/tables/{table}/rows/{attrvals}] - Delete rows from a non-node table that have the attribute values specified in {attrvals}.", - # usage => "||$usagemsg{non_getreturn}|", - # example => '|Delete a route row in the routes table.|PUT|/tables/routes/rows/routename=privnet||', - - my @args; - my @urilayers = @{ $params->{'layers'} }; - - # the array elements for @urilayers are: - # 0 - 'table' - # 1 - - # 2 - 'rows' - # 3 - - - # set the command name - $request->{command} = 'delEntries'; - - # For delEntries, the rest of the xml request strucutre looks like this: - # - # nodelist - # - # compute1,lissa - # down - # - #
                                                  - - # set the table name and attr/vals - my $table = {}; # will hold the name and attr/vals - $request->{table}->[0] = $table; #todo: the xcat delEntries cmd supports multiple tables in 1 request. We could support this if the attr names were table.attr - $table->{name} = $urilayers[1]; - my @attrvals = split(/,/, $urilayers[3]); - foreach my $av (@attrvals) { - my ($attr, $value) = split(/\s*=\s*/, $av, 2); - $table->{attr}->{$attr} = $value; - } - - my $req = genRequest(); - - # disabling the KeyAttr option is important in this case, so xmlin doesn't pull the name attribute - # out of the node hash and make it the key - my $responses = sendRequest($req, { SuppressEmpty => undef, ForceArray => 1, KeyAttr => [] }); - - return $responses; -} - - -# display the resource list when run 'xcatws.cgi -d' -sub displayUsage { - foreach my $group (keys %URIdef) { - print "Resource Group: $group\n"; - foreach my $res (keys %{ $URIdef{$group} }) { - print " Resource: $res\n"; - print " $URIdef{$group}->{$res}->{desc}\n"; - if (defined($URIdef{$group}->{$res}->{GET})) { - print " GET: $URIdef{$group}->{$res}->{GET}->{desc}\n"; - } - if (defined($URIdef{$group}->{$res}->{PUT})) { - print " PUT: $URIdef{$group}->{$res}->{PUT}->{desc}\n"; - } - if (defined($URIdef{$group}->{$res}->{POST})) { - print " POST: $URIdef{$group}->{$res}->{POST}->{desc}\n"; - } - if (defined($URIdef{$group}->{$res}->{DELETE})) { - print " DELETE: $URIdef{$group}->{$res}->{DELETE}->{desc}\n"; - } - } - } -} - - -# This handles and removes serverdone and error tags in the perl data structure that is from the xml that xcatd returned -#bmp: is there a way to avoid make a copy of the whole response? For big output that could be time consuming. -# For the error tag, you don't have to bother copying the response, because you are going to exit anyway. -# Maybe this function could just verify there is a serverdone and handle any error, and then -# let each specific output handler ignore the serverdone tag? - -# Filter out the error message -# If has 'error' message in the output data, push them all to one list -# If has 'errorcode' in the output data, set it to be the errorcode of response. Otherwise, the errorcode to '1' -# When get the 'serverdone' identifer -# If 'errorcode' has been set, return the error message and error code like {error:[msg1,msg2...], errorcode:num} -# Otherwise, pass the output to the outputhandler for the specific resource - -sub filterData { - my $data = shift; - - #debugandexit(Dumper($data)); - - my $outputdata; - my $outputerror; - my @errmsgindata; # put the out->{data} for error message output - - #trim the serverdone message off - foreach (@{$data}) { - if (defined($_->{error})) { - if (ref($_->{error}) eq 'ARRAY') { - foreach my $msg (@{ $_->{error} }) { - if ($msg =~ /(Permission denied|Authentication failure)/) { - - # return 401 Unauthorized - error("Authentication failure", $STATUS_UNAUTH); - } else { - push @{ $outputerror->{error} }, $msg; - } - } - } else { - push @{ $outputerror->{error} }, $_->{error}; - } - - if (defined($_->{errorcode})) { - if (ref($_->{errorcode}) eq 'ARRAY') { - $outputerror->{errorcode} = $_->{errorcode}->[0]; - } else { - $outputerror->{errorcode} = $_->{errorcode}; - } - } else { - - # set the default errorcode to '1' - $outputerror->{errorcode} = '1'; - } - } elsif (defined($_->{errorcode}) && $_->{errorcode}->[0] ne "0") { # defined errorcode, but not define the error msg - $outputerror->{errorcode} = $_->{errorcode}->[0]; - if (defined($_->{data}) && ref($_->{data}->[0]) ne "HASH") { - - # to get the message in data for the case that errorcode is set but no 'error' attr - push @errmsgindata, $_->{data}->[0]; - } - if (@errmsgindata) { - push @{ $outputerror->{error} }, @errmsgindata; - } else { - push @{ $outputerror->{error} }, "Failed with unknown reason."; - } - } - - # handle the output like - # - # node1 - # Unable to identify plugin for this command, check relevant tables: nodehm.power,mgt;nodehm.mgt - # 1 - # - if (defined($_->{node}) && defined($_->{node}->[0]->{error})) { - if (defined($_->{node}->[0]->{name})) { - push @{ $outputerror->{error} }, "$_->{node}->[0]->{name}->[0]: " . $_->{node}->[0]->{error}->[0]; - } - if (defined($_->{node}->[0]->{errorcode})) { - $outputerror->{errorcode} = $_->{node}->[0]->{errorcode}->[0]; - } else { - - # set the default errorcode to '1' - $outputerror->{errorcode} = '1'; - } - } - - - if (exists($_->{serverdone})) { - if (defined($outputerror->{error}) || defined($outputerror->{error})) { - addPageContent($JSON->encode($outputerror)); - - #return the default http error code to be 403 forbidden - sendResponseMsg($STATUS_FORBIDDEN); - - #} else { - #otherwise, ignore the 'servicedone' data - # next; - } else { - delete($_->{serverdone}); - if (scalar(keys %{$_}) > 0) { - push @{$outputdata}, $_; - } - } - } else { - if (defined($_->{data}) && ref($_->{data}->[0]) ne "HASH") { - - # to get the message in data for the case that errorcode is set but no 'error' attr - push @errmsgindata, $_->{data}->[0]; - } - push @{$outputdata}, $_; - } - } - - return $outputdata; -} - -# Structure the response perl data structure into well-formed json. Since the structure of the -# xml output that comes from xcatd is inconsistent and not very structured, we have a lot of work to do. -sub wrapJson { - - # this is an array of responses from xcatd. Often all the output comes back in 1 response, but not always. - my $data = shift; - - addPageContent($JSON->encode($data)); - return; - - - # put, delete, and patch usually just give a short msg, if anything - if (isPut() || isDelete() || isPatch()) { - addPageContent($JSON->encode($data)); - return; - } -} - -# Append content to the global var holding the output to go back to the rest client -# 1st param - The output message -# 2nd param - A flag to specify the format of 1st param: 1 - json formatted standard xcat output data -sub addPageContent { - my $newcontent = shift; - my $userdata = shift; - - if ($userdata && $XCOLL) { - my $group; - my $hash = $JSON->decode($newcontent); - if (ref($hash) eq "HASH") { - foreach my $node (keys %{$hash}) { - if (ref($hash->{$node}) eq "HASH") { - my $value; - foreach (sort (keys %{ $hash->{$node} })) { - $value .= "$_$hash->{$node}->{$_}"; - } - push @{ $group->{$value}->{node} }, $node; - $group->{$value}->{orig} = $hash->{$node}; - } elsif (ref($hash->{$node}) eq "ARRAY") { - my $value; - foreach (sort (@{ $hash->{$node} })) { - $value .= "$_"; - } - push @{ $group->{$value}->{node} }, $node; - $group->{$value}->{orig} = $hash->{$node}; - } - } - } - my $groupout; - foreach my $value (keys %{$group}) { - if (defined $group->{$value}->{node}) { - my $nodes = join(',', @{ $group->{$value}->{node} }); - if (defined($group->{$value}->{orig})) { - $groupout->{$nodes} = $group->{$value}->{orig}; - } - } - } - $newcontent = $JSON->encode($groupout) if ($groupout); - } - - $pageContent .= $newcontent; -} - -# send the response to client side, then exit -# with http there is only one return for each request, so all content should be in pageContent global variable when you call this -# create the response header by status code and format -sub sendResponseMsg { - my $code = shift; - my $tempFormat = ''; - if ('json' eq $format) { - $tempFormat = 'application/json'; - } - elsif ('xml' eq $format) { - $tempFormat = 'text/xml'; - } - elsif ('stream' eq $format) { - $tempFormat = 'application/octet-stream'; - } - else { - $tempFormat = 'text/html'; - } - - if ($header_info{attachment}) { - print $q->header(-status => $code, - -type => $tempFormat, - -attachment => $header_info{attachment}, - -Content_length => length($pageContent)); - } else { - print $q->header(-status => $code, -type => $tempFormat); - if ($pageContent) { $pageContent .= "\n"; } # if there is any content, append a newline - } - print $pageContent; - exit(0); -} - -# Convert xcat request to xml for sending to xcatd -sub genRequest { - my $xml = XML::Simple::XMLout($request, RootName => 'xcatrequest', NoAttr => 1, KeyAttr => []); -} - -# Send the request to xcatd and read the response. The request passed in has already been converted to xml. -# The response returned to the caller of this function has already been converted from xml to perl structure. -sub sendRequest { - my $request = shift; - my $xmlinoptions = shift; # optional arg to not set ForceArray on the XMLin() call - my $sitetab; - my $retries = 0; - - if ($DEBUGGING == 2) { - my $preXml = $request; - $preXml =~ s/< /g; - $preXml =~ s/>/>
                                                  /g; - addPageContent($q->p("DEBUG: request XML: " . $request . "\n")); - } - - #hardcoded port for now - my $port = 3001; - my $xcatHost = "localhost:$port"; - - #temporary, will be using username and password - my $homedir = "/root"; - my $keyfile = $homedir . "/.xcat/client-cred.pem"; - my $certfile = $homedir . "/.xcat/client-cred.pem"; - my $cafile = $homedir . "/.xcat/ca.pem"; - - my $client; - if (-r $keyfile and -r $certfile and -r $cafile) { - $client = IO::Socket::SSL->new( - PeerAddr => $xcatHost, - SSL_key_file => $keyfile, - SSL_cert_file => $certfile, - SSL_ca_file => $cafile, - SSL_verify_mode => SSL_VERIFY_PEER, - SSL_verifycn_scheme => "none", - SSL_use_cert => 1, - Timeout => 15,); - } - else { - $client = IO::Socket::SSL->new( - PeerAddr => $xcatHost, - SSL_verify_mode => 0, - Timeout => 15,); - } - unless ($client) { - if ($@ =~ /SSL Timeout/) { - error("Connection failure: SSL Timeout or incorrect certificates in ~/.xcat", $STATUS_TIMEOUT); - } - else { - error("Connection failurexx: $@", $STATUS_SERVICE_UNAVAILABLE); - } - } - - debug("request xml=$request"); - print $client $request; - - my $response; - my $rsp; - my $fullResponse = []; - my $cleanexit = 0; - while (<$client>) { - $response .= $_; - if (m/<\/xcatresponse>/) { - - #replace ESC with xxxxESCxxx because XMLin cannot handle it - if ($DEBUGGING) { - - #addPageContent("DEBUG: response from xcatd: " . $response . "\n"); - } - $response =~ s/\e/xxxxESCxxxx/g; - debug("response xml=$response"); - - #bmp: i added the $xmlinoptions var because for the table output it saved me processing if everything - # wasn't forced into arrays. Consider if that could save you processing on other api calls too. - if (!$xmlinoptions) { $xmlinoptions = { SuppressEmpty => undef, ForceArray => 1 }; } - $rsp = XML::Simple::XMLin($response, %$xmlinoptions); - - #debug(Dumper($rsp)); - - #add ESC back - foreach my $key (keys %$rsp) { - if (ref($rsp->{$key}) eq 'ARRAY') { - foreach my $text (@{ $rsp->{$key} }) { - next unless defined $text; - $text =~ s/xxxxESCxxxx/\e/g; - } - } - else { - $rsp->{$key} =~ s/xxxxESCxxxx/\e/g; - } - } - - $response = ''; - push(@$fullResponse, $rsp); - if (exists($rsp->{serverdone})) { - $cleanexit = 1; - last; - } - } - } - unless ($cleanexit) { - error("communication with the xCAT server seems to have been ended prematurely", $STATUS_SERVICE_UNAVAILABLE); - } - - if ($DEBUGGING == 2) { - addPageContent($q->p("DEBUG: full response from xcatd: " . Dumper($fullResponse))); - } - return $fullResponse; -} - -# Put input parameters from both $q->url_param and put/post data (if it exists) into generalparams and paramhash for all to use -# 1st output param - The params which are listed in @generalparamlis as a general parameters like 'debug=1, pretty=1' -# 2nd output param - All the params from url params and 'PUTDATA'/'POSTDATA' except the ones in @generalparamlis -sub fetchParameters { - my @generalparamlist = qw(userName userPW pretty debug xcoll); - - # 1st check for put/post data and put that in the hash - my $pdata; - if (isPut()) { - $pdata = $q->param('PUTDATA'); - - # in the sles 11.x, the 'PUTDATA' param is not supported for PUT method - # so we have to work around it by getting it by myself - unless ($pdata) { - if (-f "/etc/SuSE-release") { # SUSE os - if ($ENV{'CONTENT_TYPE'} =~ /json/) { - $q->read_from_client(\$pdata, $ENV{'CONTENT_LENGTH'}); - } - } - } - } elsif (isPost()) { - $pdata = $q->param('POSTDATA'); - } elsif (isDelete() || isGET()) { - if ($ENV{'CONTENT_TYPE'} =~ /json/) { - $q->read_from_client(\$pdata, $ENV{'CONTENT_LENGTH'}); - } - } - - if ($dbgdata) { - $pdata = $dbgdata; - } - - my $genparms = {}; - my $phash; - if ($pdata) { - $phash = eval { $JSON->decode($pdata); }; - if ($@) { - - # remove the code location information to make the output looks better - if ($@ =~ m/ at \//) { - $@ =~ s/ at \/.*$//; - } - error("$@", $STATUS_BAD_REQUEST); - } - - #debug("phash=" . Dumper($phash)); - if (ref($phash) ne 'HASH') { error("put or post data must be a json object (hash/dict).", $STATUS_BAD_REQUEST); } - - # if any general parms are in the put/post data, move them to genparms - foreach my $k (keys %$phash) { - if (grep(/^$k$/, @generalparamlist)) { - $genparms->{$k} = $phash->{$k}; - delete($phash->{$k}); - } - } - } - else { $phash = {}; } - - # now get params from the url (if any of the keys overlap, the url value will overwrite the put/post value) - foreach my $p ($q->url_param) { - my @a = $q->url_param($p); # this could be a single value or an array, have to figure it out - my $value; - if (scalar(@a) > 1) { $value = [@a]; } # convert it to a reference to an array - else { $value = $a[0]; } - if (grep(/^$p$/, @generalparamlist)) { $genparms->{$p} = $value; } - else { $phash->{$p} = $value; } - } - - return ($genparms, $phash); -} - -# Load the XML::Simple module -sub loadXML { - if ($xmlinstalled) { return; } - - $xmlinstalled = eval { require XML::Simple; }; - unless ($xmlinstalled) { - error('The XML::Simple perl module is missing. Install perl-XML-Simple before using the xCAT REST web services API with this format."}', $STATUS_SERVICE_UNAVAILABLE); - } - $XML::Simple::PREFERRED_PARSER = 'XML::Parser'; -} - -# Load the JSON perl module, if not already loaded. Sets the $JSON global var. -sub loadJSON { - if ($JSON) { return; } # already loaded - # require JSON dynamically and let them know if it is not installed - my $jsoninstalled = eval { require JSON; }; - unless ($jsoninstalled) { - error("JSON perl module missing. Install perl-JSON before using the xCAT REST web services API.", $STATUS_SERVICE_UNAVAILABLE); - } - $JSON = JSON->new(); -} - -# add a error msg to the output in the correct format and end this request -sub error { - my ($errmsg, $httpcode, $errorcode) = @_; - my $json; - $json->{error} = $errmsg; - $json->{errorcode} = '2'; - if ($errorcode) { - $json->{errorcode} = $errorcode; - } - - addPageContent($JSON->encode($json)); - sendResponseMsg($httpcode); -} - - -# if debugging, output the given string -sub debug { - if (!$DEBUGGING) { return; } - addPageContent($q->p("DEBUG: $_[0]\n")); -} - -# when having bugs that cause this cgi to not produce any output, output something and then exit. -sub debugandexit { - debug("$_[0]\n"); - sendResponseMsg($STATUS_OK); -} - -sub displaydebugmsg { - addPageContent($q->p("DEBUG: generalparams:" . Dumper($generalparams))); - addPageContent($q->p("DEBUG: paramhash:" . Dumper($paramhash))); - addPageContent($q->p("DEBUG: q->request_method: $requestType\n")); - - #addPageContent($q->p("DEBUG: q->user_agent: $userAgent\n")); - addPageContent($q->p("DEBUG: pathInfo: $pathInfo\n")); - - #addPageContent($q->p("DEBUG: path " . Dumper(@path) . "\n")); - #foreach (keys(%ENV)) { addPageContent($q->p("DEBUG: ENV{$_}: $ENV{$_}\n")); } - #addPageContent($q->p("DEBUG: userName=".$paramhash->{userName}.", password=".$paramhash->{password}."\n")); - #addPageContent($q->p("DEBUG: http() values:\n" . http() . "\n")); - #if ($pdata) { addPageContent($q->p("DEBUG: pdata: $pdata\n")); } - addPageContent("\n"); - if ($DEBUGGING == 3) { - sendResponseMsg($STATUS_OK); # this will also exit - } -} - - -# push flags (options) onto the xcatd request. Arguments: request args array, flags array. -# Format of flags array: -# Use this function for cmds with a lot of flags like xdcp and xdsh -sub pushFlags { - my ($args, $flags) = @_; - foreach my $f (@$flags) { - my ($key, $flag, $arg) = @$f; - if (defined($paramhash->{$key})) { - push @$args, $flag; - if ($arg) { push @$args, $paramhash->{$key}; } - } - } -} - - diff --git a/xCAT-server/xCAT-wsapi/zvmxcatws.cgi b/xCAT-server/xCAT-wsapi/zvmxcatws.cgi new file mode 100644 index 000000000..84701929a --- /dev/null +++ b/xCAT-server/xCAT-wsapi/zvmxcatws.cgi @@ -0,0 +1,2521 @@ +#!/usr/bin/perl +use strict; +use CGI qw/:standard/; +#use JSON; # require this dynamically later on so that installations that do not use xcatws.cgi do not need perl-JSON +use Data::Dumper; + +#added the line: +#ScriptAlias /xcatws /var/www/cgi-bin/xcatws.cgi +#to /etc/httpd/conf/httpd.conf to hid the cgi-bin and .cgi extension in the uri +# +# also upgraded CGI to 3.52 + +#take the JSON or XML and put it into a data structure +#all data input will be done from the common structure + +#turn on or off the debugging output +my $DEBUGGING = 0; +my $VERSION = "2.8"; + +my $q = CGI->new; +my $url = $q->url; +my $pathInfo = $q->path_info; +my $requestType = $ENV{'REQUEST_METHOD'}; +my $queryString = $ENV{'QUERY_STRING'}; +my %queryhash; +my @path = split(/\//, $pathInfo); +shift(@path); +my $resource = $path[0]; +my $pageContent = ''; +my $request = {clienttype => 'ws'}; + +#error status codes +my $STATUS_BAD_REQUEST = "400 Bad Request"; +my $STATUS_UNAUTH = "401 Unauthorized"; +my $STATUS_FORBIDDEN = "403 Forbidden"; +my $STATUS_NOT_FOUND = "404 Not Found"; +my $STATUS_NOT_ALLOWED = "405 Method Not Allowed"; +my $STATUS_NOT_ACCEPTABLE = "406 Not Acceptable"; +my $STATUS_TIMEOUT = "408 Request Timeout"; +my $STATUS_EXPECT_FAILED = "417 Expectation Failed"; +my $STATUS_TEAPOT = "418 I'm a teapot"; +my $STATUS_SERVICE_UNAVAILABLE = "503 Service Unavailable"; + +#good status codes +my $STATUS_OK = "200 OK"; +my $STATUS_CREATED = "201 Created"; + +#default format +my $format = 'html'; + +sub addPageContent { + my $newcontent = shift; + $pageContent .= $newcontent; +} + +#send the response to client side +#the http only return once in each request, so all content shoudl save in a global variable, +#create the response header by status +sub sendResponseMsg { + my $code = shift; + my $tempFormat = ''; + if ('json' eq $format) { + $tempFormat = 'application/json'; + } + elsif ('xml' eq $format) { + $tempFormat = 'text/xml'; + } + else { + $tempFormat = 'text/html'; + } + print $q->header(-status => $code, -type => $tempFormat); + print $pageContent; + exit(0); +} + +sub unsupportedRequestType { + addPageContent("request method '$requestType' is not supported on resource '$resource'"); + sendResponseMsg($STATUS_NOT_ALLOWED); +} + +use XML::Simple; +$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; + +sub genRequest { + if ($DEBUGGING) { + addPageContent($q->p("request " . Dumper($request))); + } + my $xml = XMLout($request, RootName => 'xcatrequest', NoAttr => 1, KeyAttr => []); +} + +#data formatters. To add one simple copy the format of an existing one +# and add it to this hash +my %formatters = ( + 'html' => \&wrapHtml, + 'json' => \&wrapJson, + 'xml' => \&wrapXml,); + +fetchParameter($queryString); + +if ($queryhash{'format'}) { + $format = $queryhash{'format'}->[0]; + if (!exists $formatters{$format}) { + addPageContent("The format '$format' is not valid"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if ($format eq 'json') { + # require JSON dynamically and let them know if it is not installed + my $jsoninstalled = eval { require JSON; }; + unless ($jsoninstalled) { + addPageContent('{"data":"JSON perl module missing. Install perl-JSON before using the xCAT REST web services API."}'); + sendResponseMsg($STATUS_SERVICE_UNAVAILABLE); + } + } +} + +my $XCAT_PATH = '/opt/xcat/bin'; + +#resource handlers +my %resources = ( + groups => \&groupsHandler, + images => \&imagesHandler, + logs => \&logsHandler, + monitors => \&monitorsHandler, + networks => \&networksHandler, + nodes => \&nodesHandler, + notifications => \¬ificationsHandler, + policies => \&policiesHandler, + site => \&siteHandler, + tables => \&tablesHandler, + accounts => \&accountsHandler, + objects => \&objectsHandler, + vms => \&vmsHandler, + debug => \&debugHandler, + hypervisor => \&hypervisorHandler, + version => \&versionHandler); + +#if no resource was specified +if ($pathInfo =~ /^\/$/ || $pathInfo =~ /^$/) { + addPageContent($q->p("This is the root page for the xCAT Rest Web Service. Available resources are:")); + foreach (sort keys %resources) { + addPageContent($q->p($_)); + } + sendResponseMsg($STATUS_OK); +} + +sub doesResourceExist { + my $res = shift; + return exists $resources{$res}; +} + +if ($DEBUGGING) { + if (defined $q->param('PUTDATA')) { + addPageContent("put data " . $q->p($q->param('PUTDATA') . "\n")); + } elsif (isPut()) { + my $entries = JSON::decode_json($q->param('PUTDATA')); + if (scalar(@$entries) >= 1) { + addPageContent("put data \n"); + foreach (@$entries) { + addPageContent("$_\n"); + } + } + } + + if (defined $q->param('POSTDATA')) { + addPageContent("post data " . $q->p($q->param('POSTDATA') . "\n")); + } elsif (isPost()) { + my $entries = JSON::decode_json($q->param('POSTDATA')); + if (scalar(@$entries) >= 1) { + addPageContent("post data \n"); + foreach (@$entries) { + addPageContent("$_\n"); + } + } + } + + addPageContent($q->p("Parameters ")); + my @params = $q->param; + foreach (@params) { + addPageContent("$_ = " . join(',', $q->param($_)) . "\n"); + } + addPageContent($q->p("Query String $queryString" . "\n")); + addPageContent($q->p("Query parameters from the Query String" . Dumper(\%queryhash) . "\n")); + addPageContent($q->p("HTTP Method $requestType" . "\n")); + addPageContent($q->p("URI $url" . "\n")); + addPageContent($q->p("path " . Dumper(@path) . "\n")); +} + +#when use put and post, can not fetch the url-parameter, so add this sub to support all kinks of method +sub fetchParameter { + my $parstr = shift; + unless ($parstr) { + return; + } + + my @pairs = split(/&/, $parstr); + foreach my $pair (@pairs) { + my ($key, $value) = split(/=/, $pair, 2); + $value =~ tr/+/ /; + $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/chr(hex($1))/eg; + push @{$queryhash{$key}}, $value; + } +} + +#extract the put data or post data into perl hash, easy for retrieve +sub extractData { + my $temphash = shift; + my $parArray = shift; + my $key; + my $value; + my $position; + + #traversal all element in the array + foreach (@$parArray) { + $position = index($_, '='); + if ($position < 0) { + $key = $_; + $value = 1; + } + else { + $key = substr $_, 0, $position; + $value = substr $_, $position + 1; + } + $temphash->{$key} = $value; + + if ($DEBUGGING) { + addPageContent($q->p("The parameter extract from put/post data:
                                                  " . Dumper($temphash))); + } + } +} + +my $userName=http('userName'); +my $password=http('password'); + +sub handleRequest { + if (defined $queryhash{'userName'}) { + $userName = $queryhash{'userName'}->[0]; + } + if (defined $queryhash{'password'}) { + $password = $queryhash{'password'}->[0]; + } + if ($userName && $password) { + $request->{becomeuser}->[0]->{username}->[0] = $userName; + $request->{becomeuser}->[0]->{password}->[0] = $password; + } + my @data = $resources{$resource}->(); + wrapData(\@data); +} + +my @groupFields = ('groupname', 'grouptype', 'members', 'wherevals', 'comments', 'disable'); + +#get is done +#post and delete are done but not tested +#groupfiles4dsh is done but not tested +sub groupsHandler { + my @responses; + my @args; + my $groupName; + + #is the group name in the URI? + if (defined $path[1]) { + $groupName = $path[1]; + } + + #in the query string? + else { + $groupName = $q->param('groupName'); + } + + if (isGet()) { + if (defined $groupName) { + $request->{command} = 'gettab'; + push @args, "groupname=$groupName"; + if (defined $q->param('field')) { + foreach ($q->param('field')) { + push @args, "nodegroup.$_"; + } + } + else { + foreach (@groupFields) { + push @args, "nodegroup.$_"; + } + } + } + else { + $request->{command} = 'tabdump'; + push @args, 'nodegroup'; + } + } + + #does it make sense to even have this? + elsif (isPost()) { + my $nodeRange = $q->param('nodeRange'); + if ((defined $groupName) && (defined $nodeRange)) { + $request->{command} = 'mkdef'; + push @args, '-t'; + push @args, 'group'; + push @args, '-o'; + push @args, $groupName; + push @args, "members=$nodeRange"; + } + else { + addPageContent("A node range and group name must be specified for creating a group"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif (isPut()) { + + #handle groupfiles4dsh -p /tmp/nodegroupfiles + if ($q->param('command') eq "4dsh") { + if ($q->param('path')) { + $request->{command} = 'groupfiles4dsh'; + push @args, "p=$q->param('path')"; + } + else { + addPageContent("The path must be specified for creating directories for dsh"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + if (defined $groupName && defined $q->param('fields')) { + $request->{command} = 'nodegrpch'; + push @args, $groupName; + push @args, $q->param('field'); + } + else { + addPageContent("The group and fields must be specified to update groups"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + } + elsif (isDelete()) { + if (defined $groupName) { + $request->{command} = 'rmdef'; + push @args, '-t'; + push @args, 'group'; + push @args, '-o'; + push @args, $groupName; + } + else { + addPageContent("The group must be specified to delete a group"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +my @imageFields = ( + 'imagename', 'profile', 'imagetype', 'provmethod', 'osname', 'osvers', + 'osdistro', 'osarch', 'synclists', 'comments', 'disable'); + +#get is done, nothing else +sub imagesHandler { + my @responses; + my @args; + my $image; + my $subResource; + + if (defined($path[1])) { + $image = $path[1]; + } + + if (isGet()) { + $request->{command} = 'lsdef'; + push @args, '-t', 'osimage'; + if (defined $image) { + push @args, '-o', $image; + } + if (defined($q->param('field'))) { + push @args, '-i'; + push @args, join(',', $q->param('field')); + } + if (defined($q->param('criteria'))) { + foreach ($q->param('criteria')) { + push @args, '-w', "$_"; + } + } + } + elsif (isPost()) { + my $operationname = $image; + my $entries; + my %entryhash; + + #check the post data + unless (defined($q->param('POSTDATA'))) { + addPageContent("Invalid Parameters"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + $entries = JSON::decode_json($q->param('POSTDATA')); + if (scalar(@$entries) < 1) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + extractData(\%entryhash, $entries); + + #for image capture + if ($operationname eq 'capture') { + $request->{command} = 'imgcapture'; + if (defined($entryhash{'nodename'})) { + $request->{noderange} = $entryhash{'nodename'}; + } + else { + addPageContent('No node range.'); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (defined($entryhash{'profile'})) { + push @args, '-p'; + push @args, $entryhash{'profile'}; + } + if (defined($entryhash{'osimage'})) { + push @args, '-o'; + push @args, $entryhash{'osimage'}; + } + if (defined($entryhash{'bootinterface'})) { + push @args, '-i'; + push @args, $entryhash{'bootinterface'}; + } + if (defined($entryhash{'netdriver'})) { + push @args, '-n'; + push @args, $entryhash{'netdriver'}; + } + if (defined($entryhash{'device'})) { + push @args, '-d'; + push @args, $entryhash{'device'}; + } + if (defined($entryhash{'compress'})) { + push @args, '-c'; + push @args, $entryhash{'compress'}; + } + } + elsif ($operationname eq 'export') { + $request->{command} = 'imgexport'; + if (defined($entryhash{'osimage'})) { + push @args, $entryhash{'osimage'}; + } + else { + addPageContent('No image specified'); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (defined($entryhash{'destination'})) { + push @args, $entryhash{'destination'}; + } + if (defined($entryhash{'postscripts'})) { + push @args, '-p'; + push @args, $entryhash{'postscripts'}; + } + if (defined($entryhash{'extra'})) { + push @args, '-e'; + push @args, $entryhash{'extra'}; + } + if (defined($entryhash{'remotehost'})) { + push @args, '-R'; + push @args, $entryhash{'remotehost'}; + } + if (defined($entryhash{'verbose'})) { + push @args, '-v'; + } + } + elsif ($operationname eq 'import') { + $request->{command} = 'imgimport'; + if (defined($entryhash{'osimage'})) { + push @args, $entryhash{'osimage'}; + } + else { + addPageContent('No image specified'); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (defined($entryhash{'profile'})) { + push @args, '-f'; + push @args, $entryhash{'profile'}; + } + if (defined($entryhash{'remotehost'})) { + push @args, '-R'; + push @args, $entryhash{'remotehost'}; + } + if (defined($entryhash{'postscripts'})) { + push @args, '-p'; + push @args, $entryhash{'postscripts'}; + } + if (defined($entryhash{'nozip'})) { + push @args, '-n'; + } + + if (defined($entryhash{'verbose'})) { + push @args, '-v'; + } + } + } + elsif (isPut()) { + + #check the operation type + unless (defined $path[2]) { + addPageContent("The subResource $subResource does not exist"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + $subResource = $path[2]; + + #check the image name + unless (defined $image) { + addPageContent("The image name is required to clean an os image"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if ($subResource eq 'check') { + $request->{command} = 'chkosimage'; + if (defined($q->param('PUTDATA'))) { + push @args, '-c'; + } + push @args, $image; + } + else { + addPageContent("The subResource $subResource does not exist"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif (isDelete()) { + if (defined $image) { + $request->{command} = 'rmimage'; + if (defined $q->param('verbose')) { + push @args, '-v'; + } + push @args, $image; + } + elsif (defined $q->param('os') && defined $q->param('arch') && defined $q->param('profile')) { + push @args, '-o'; + push @args, $q->param('os'); + push @args, '-a'; + push @args, $q->param('arch'); + push @args, '-p'; + push @args, $q->param('profile'); + } + else { + addPageContent( + "Either the image name or the os, architecture and profile must be specified to remove an image"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +#complete +sub logsHandler { + my @responses; + my @args; + my $logType; + + if (defined $path[1]) { + $logType = $path[1]; + } + + #in the query string? + else { + $logType = $q->param('logType'); + } + my $nodeRange = $q->param('nodeRange'); + + #no real output unless the log type is defined + if (!defined $logType) { + addPageContent("Current logs available are auditlog, eventlog, and diagnostics"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (isGet()) { + if ($logType eq "reventLog") { + if (defined $nodeRange) { + $request->{command} = 'reventlog'; + push @args, $nodeRange; + if (defined $q->param('count')) { + push @args, $q->param('count'); + } + } + else { + addPageContent("nodeRange must be specified to GET remote event logs"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif ($logType eq "diagnostics") { + addPageContent(uc($requestType) . " remote diagnostic logs is not supported"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + $request->{command} = 'tabdump'; + push @args, $logType; + } + } + + #this clears the log + elsif (isPut()) { + if ($logType eq "reventlog") { + if (defined $nodeRange) { + $request->{command} = 'reventlog'; + push @args, $nodeRange; + push @args, 'clear'; + } + else { + addPageContent("nodeRange must be specified to clean remote event logs"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif ($logType eq "diagnostics") { + addPageContent(uc($requestType) . " remote diagnostic logs is not supported"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + + #should it return the removed entries? + if (defined $q->param('showRemoved')) { + push @args, '-V'; + } + if (defined $q->param('count') || defined $q->param('percent') || defined $q->param('lastRecord')) { + + #remove some of the entries + $request->{command} = 'tabprune'; + + #remove a certain number of records + if (defined $q->param('count')) { + push @args, ('-n', $q->param('count')); + } + + #remove a percentage of the records + if (defined $q->param('percent')) { + push @args, ('-p', $q->param('percent')); + } + + #remove all records before this record + if (defined $q->param('lastRecord')) { + push @args, ('-i', $q->param('lastRecord')); + } + } + else { + $request->{command} = 'tabprune'; + + #-a removes all + push @args, '-a'; + } + } + } + # Currently, only diagnostic logs can be created + elsif (isPost()) { + if ($logType eq "diagnostics") { + + # Potential base bug on the reventlog and auditlog paths: + # $q->param('nodeRange') != $queryhash{'nodeRange'} + # The base code uses the first, which is undef even when a URL + # query parameter named nodeRange was passed by the caller (e.g. is + # found in the nova-compute.log traces). + # Re-assigning nodeRange on this new path since it does return + # a value. + + $nodeRange = $queryhash{'nodeRange'}; + if (defined $nodeRange) { + $request->{command} = 'diagnostics'; + $request->{noderange} = $nodeRange; + + my $parameter; + # Parse the optional upstream request ID, e.g. an OpenStack request UUID + $parameter = 'requestid'; + if (defined $queryhash{$parameter}) { + push @args, '--'.$parameter; + push @args, $queryhash{$parameter}->[0]; + } + + # Parse the optional upstream object ID, e.g. an OpenStack nova instance UUID + $parameter = 'objectid'; + if (defined $queryhash{$parameter}) { + push @args, '--'.$parameter; + push @args, $queryhash{$parameter}->[0]; + } + + unless ($q->param('POSTDATA')) { + addPageContent("A request body containing parameters is required"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + # Collect all parameters from the postdata + my %entryhash; + my $entries = JSON::decode_json($q->param('POSTDATA')); + if (scalar(@$entries) < 1) { + addPageContent("No request parameters were supplied in the message body."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + extractData(\%entryhash, $entries); + # TODO: add parameter parsing back in to pass through all body parameters. + # How to handle duplicate property names? Perhaps prefix them with upstream_ , and pass the prefix too ;-) + + # Parse the optional body parameters from the caller (NOT URL query parameters) + # TODO: this is early code that hard-codes a single key passed by the + # z/VM OpenStack nova plugin. It will be replaced later with more + # general code that behaves similarly. + $parameter = 'reason'; + if (defined $entryhash{$parameter}) { + push @args, '--upstream-'.$parameter; + push @args, $entryhash{$parameter}; + } + + } + else { + addPageContent("nodeRange must be specified to collect diagnostics"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + addPageContent("POST is only valid for logType(s): diagnostics. It is not valid with the supplied logType: $logType"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +#complete +sub monitorsHandler { + my @responses; + my @args; + my $monitor; + + if (defined $path[1]) { + $monitor = $path[1]; + } + + #in the query string? + elsif (defined $q->param('monitor')) { + push @args, $q->param('monitor'); + } + if (defined $monitor) { + push @args, $monitor; + } + + if (isGet()) { + $request->{command} = 'monls'; + } + elsif (isPost()) { + $request->{command} = 'monadd'; + if ($q->param('nodeStatMon')) { + push @args, '-n'; + } + + #get the plug-in specific settings array + foreach ($q->param('pluginSetting')) { + push @args, '-s'; + push @args, $_; + } + } + elsif (isDelete()) { + $request->{command} = 'monrm'; + } + elsif (isPut() || isPatch()) { + my $action = $q->param('action'); + if ($action eq "start") { + $request->{command} = 'monstart'; + } + elsif ($action eq "stop") { + $request->{command} = 'monstop'; + } + elsif ($action eq "config") { + $request->{command} = 'moncfg'; + } + elsif ($action eq "deconfig") { + $request->{command} = 'mondeconfig'; + } + else { + unsupportedRequestType(); + } + if (!defined $q->param('nodeRange')) { + + #error + } + else { + push @args, $q->param('nodeRange'); + } + if (defined $q->param('remote')) { + push @args, '-r'; + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +sub networksHandler { + my @responses; + my @args; + my $netname = ''; + + if (isGet()) { + $request->{command} = 'lsdef'; + push @{$request->{arg}}, '-t', 'network'; + if (defined($path[1])) { + push @{$request->{arg}}, '-o', $path[1]; + } + my @temparray = $q->param('field'); + + #add the field name to get + if (scalar(@temparray) > 0) { + push @{$request->{arg}}, '-i'; + push @{$request->{arg}}, join(',', @temparray),; + } + } + elsif (isPut() || isPost()) { + my $entries; + my $iscommand = 0; + if (isPut()) { + $request->{command} = 'chdef'; + if (defined($path[1])) { + if ($path[1] eq "makehosts" || $path[1] eq "makedns") { + # Issue makehost/makedns directly + $request->{command} = $path[1]; + $iscommand = 1; + } + } + } + else { + $request->{command} = 'mkdef'; + } + + if (!$iscommand) { + if (defined $path[1]) { + $netname = $path[1]; + } + + if ($netname eq '') { + addPageContent('A network name must be specified.'); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + push @{$request->{arg}}, '-t', 'network', '-o', $netname; + + if (defined($q->param('PUTDATA'))) { + $entries = JSON::decode_json($q->param('PUTDATA')); + } + elsif (defined($q->param('POSTDATA'))) { + $entries = JSON::decode_json($q->param('POSTDATA')); + } + else { + addPageContent("No Field and Value map was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (scalar($entries) < 1) { + addPageContent("No Field and Value map was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + foreach (@$entries) { + push @{$request->{arg}}, $_; + } + } + } + elsif (isDelete()) { + $request->{command} = 'rmdef'; + + if (defined $path[1]) { + $netname = $path[1]; + } + if ($netname eq '') { + addPageContent('A network name must be specified.'); + sendResponseMsg($STATUS_BAD_REQUEST); + } + push @{$request->{arg}}, '-t', 'network', '-o', $netname; + } + else { + unsupportedRequestType(); + exit(0); + } + @responses = sendRequest(genRequest()); + + return @responses; +} + +sub nodesHandler { + my @responses; + my @args; + my $noderange; + my @envs; + + if (defined $path[1]) { + $noderange = $path[1]; + } + + if (isGet()) { + my $subResource; + if (defined $path[2]) { + $subResource = $path[2]; + unless (defined($noderange)) { + addPageContent("Invalid nodes and/or groups in noderange"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + $request->{noderange} = $noderange; + + #use the corresponding command by the subresource name + if ($subResource eq "power") { + $request->{command} = 'rpower'; + + #no fields will default to 'stat' + if (defined $q->param('field')) { + push @args, $q->param('field'); + } + else { + push @args, 'stat'; + } + } + elsif ($subResource eq "energy") { + $request->{command} = 'renergy'; + + #no fields will default to 'all' + if (defined $q->param('field')) { + push @args, $q->param('field'); + } + else { + push @args, 'all'; + } + } + elsif ($subResource eq "status") { + $request->{command} = 'nodestat'; + } + elsif ($subResource eq "inventory") { + $request->{command} = 'rinv'; + if (defined $q->param('field')) { + push @args, $q->param('field'); + } + else { + push @args, 'all'; + } + } + elsif ($subResource eq "vitals") { + $request->{command} = 'rvitals'; + if (defined $q->param('field')) { + push @args, $q->param('field'); + } + else { + push @args, 'all'; + } + } + elsif ($subResource eq "scan") { + $request->{command} = 'rscan'; + if (defined $q->param('field')) { + push @args, $q->param('field'); + } + } + else { + addPageContent("Unspported operation on nodes object."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + $request->{command} = 'lsdef'; + push @args, "-t", "node"; + + #add the nodegroup into args + if (defined($noderange)) { + push @args, "-o", $noderange; + } + + #maybe it's specified in the parameters + my @temparray = $q->param('field'); + if (scalar(@temparray) > 0) { + push @args, "-i"; + push @args, join(',', @temparray); + } + } + } + elsif (isPut()) { + my $subResource; + my @entries; + my $entrydata; + + unless (defined($noderange)) { + addPageContent("Invalid nodes and/or groups in noderange"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + $request->{noderange} = $noderange; + + unless ($q->param('PUTDATA')) { + #temporary allowance for the put data to be contained in the queryString + unless ($queryhash{'putData'}) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + foreach my $put (@{$queryhash{'putData'}}) { + my ($key, $value) = split(/=/, $put, 2); + if ($key eq 'field' && $value) { + push @entries, $value; + } + } + } + } + else { + @entries = JSON::decode_json($q->param('PUTDATA')); + if (scalar(@entries) < 1) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + + if (defined $path[2]) { + $subResource = $path[2]; + + if (($subResource ne "dsh") && ($subResource ne "dcp")) { + # For any function other than "dsh" or "dcp", + # move all operands to the argument list. + foreach (@entries) { + if (ref($_) eq 'ARRAY') { + foreach (@$_) { + push @args, $_; + } + } else { + push @args, $_; + } + } + } + if ($subResource eq "power") { + $request->{command} = "rpower"; + my %elements; + extractData(\%elements, @entries); + + unless (scalar(%elements)) { + addPageContent("No power operands were supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif ($subResource eq "energy") { + $request->{command} = "renergy"; + } + elsif ($subResource eq "bootstat" or $subResource eq "bootstate") { + $request->{command} = "nodeset"; + } + elsif ($subResource eq "bootseq") { + $request->{command} = "rbootseq"; + } + elsif ($subResource eq "setboot") { + $request->{command} = "rsetboot"; + } + elsif ($subResource eq "migrate") { + $request->{command} = "rmigrate"; + } + elsif ($subResource eq "execcmdonvm") { + $request->{command} = "execcmdonvm"; + } + elsif ($subResource eq "dsh") { + $request->{command} = "xdsh"; + my %elements; + extractData(\%elements, @entries); + if (defined($elements{'devicetype'})) { + push @args, '--devicetype'; + push @args, $elements{'devicetype'}; + } + if (defined($elements{'execute'})) { + push @args, '-e'; + } + if (defined($elements{'environment'})) { + push @args, '-E'; + push @args, $elements{'environment'}; + } + if (defined($elements{'fanout'})) { + push @args, '-f'; + push @args, $elements{'fanout'}; + } + if (defined($elements{'nolocale'})) { + push @args, '-L'; + } + if (defined($elements{'userid'})) { + push @args, '-l'; + push @args, $elements{'userid'}; + } + if (defined($elements{'monitor'})) { + push @args, '-m'; + } + if (defined($elements{'options'})) { + push @args, '-o'; + push @args, $elements{'options'}; + } + if (defined($elements{'showconfig'})) { + push @args, '-q'; + } + if (defined($elements{'silent'})) { + push @args, '-Q'; + } + if (defined($elements{'remoteshell'})) { + push @args, '-r'; + push @args, $elements{'remoteshell'}; + } + if (defined($elements{'syntax'})) { + push @args, '-S'; + push @args, $elements{'syntax'}; + } + if (defined($elements{'timeout'})) { + push @args, '-t'; + push @args, $elements{'timeout'}; + } + if (defined($elements{'envlist'})) { + push @args, '-X'; + push @args, $elements{'envlist'}; + } + if (defined($elements{'sshsetup'})) { + push @args, '-K'; + push @args, $elements{'sshsetup'}; + } + if (defined($elements{'rootimg'})) { + push @args, '-i'; + push @args, $elements{'rootimg'}; + } + if (defined($elements{'command'})) { + push @args, $elements{'command'}; + } + if (defined($elements{'remotepasswd'})) { + push @envs, 'DSH_REMOTE_PASSWORD=' . $elements{'remotepasswd'}; + push @envs, 'DSH_FROM_USERID=root'; + push @envs, 'DSH_TO_USERID=root'; + } + } + elsif ($subResource eq "dcp") { + $request->{command} = "xdcp"; + my %elements; + extractData(\%elements, @entries); + if (defined($elements{'fanout'})) { + push @args, '-f'; + push @args, $elements{'fanout'}; + } + if (defined($elements{'rootimg'})) { + push @args, '-i'; + push @args, $elements{'rootimg'}; + } + if (defined($elements{'options'})) { + push @args, '-o'; + push @args, $elements{'options'}; + } + if (defined($elements{'rsyncfile'})) { + push @args, '-F'; + push @args, $elements{'rsyncfile'}; + } + if (defined($elements{'preserve'})) { + push @args, '-p'; + } + if (defined($elements{'pull'})) { + push @args, '-P'; + } + if (defined($elements{'showconfig'})) { + push @args, '-q'; + } + if (defined($elements{'remotecopy'})) { + push @args, '-r'; + push @args, $elements{'remotecopy'}; + } + if (defined($elements{'recursive'})) { + push @args, '-R'; + } + if (defined($elements{'timeout'})) { + push @args, '-t'; + push @args, $elements{'timeout'}; + } + if (defined($elements{'source'})) { + push @args, $elements{'source'}; + } + if (defined($elements{'target'})) { + push @args, $elements{'target'}; + } + } + } + else { + my %elements; + my $name; + my $val; + + $request->{command} = "tabch"; + push @args, "node=" . $request->{noderange}; + + extractData(\%elements, @entries); + while (($name, $val) = each (%elements)) { + push @args, $name . "=" . $val; + } + } + } + elsif (isPost()) { + $request->{command} = 'mkdef'; + push @args, "-t", "node"; + + unless (defined($noderange)) { + addPageContent("No nodename was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + push @args, "-o", $noderange; + + if ($q->param('POSTDATA')) { + my $entries = JSON::decode_json($q->param('POSTDATA')); + if (scalar($entries) < 1) { + addPageContent("No Field and Value map was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + foreach (@$entries) { + push @args, $_; + } + } + } + elsif (isDelete()) { + + #FYI: the nodeRange for delete has to be specified in the URI + $request->{command} = 'rmdef'; + push @args, "-t", "node"; + unless (defined($noderange)) { + addPageContent("No nodename was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + push @args, "-o", $noderange; + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + if (@envs) { + push @{$request->{env}}, @envs; + } + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +my @notificationFields = ('filename', 'tables', 'tableops', 'comments', 'disable'); + +#complete, unless there is some way to alter existing notifications +sub notificationsHandler { + my @responses; + my @args; + + #does not support using the notification fileName in the URI + + if (isGet()) { + if (defined $q->param('fileName')) { + $request->{command} = 'gettab'; + push @args, "filename" . $q->param('fileName'); + + #if they specified the fields, just get those + if (defined $q->param('field')) { + foreach ($q->param('field')) { + push @args, $_; + } + } + + #else show all of the fields + else { + foreach (@notificationFields) { + push @args, "notification.$_"; + } + } + } + else { + $request->{command} = 'tabdump'; + push @args, "notification"; + } + } + elsif (isPost()) { + $request->{command} = 'regnotif'; + if (!defined $q->param('fileName') || !defined $q->param('table') || !defined $q->param('operation')) { + addPageContent("fileName, table and operation must be specified for a POST on /notifications"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + push @args, $q->param('fileName'); + my $tables; + foreach ($q->param('table')) { + $tables .= "$_,"; + } + + #get rid of the extra comma + chop($tables); + push @args, $tables; + push @args, '-o'; + my $operations; + foreach ($q->param('operation')) { + $operations .= "$_,"; + } + + #get rid of the extra comma + chop($operations); + push @args, $q->param('operation'); + } + } + elsif (isDelete()) { + $request->{command} = 'unregnotif'; + if (defined $q->param('fileName')) { + push @args, $q->param('fileName'); + } + else { + addPageContent("fileName must be specified for a DELETE on /notifications"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + addPageContent("request is " . Dumper($request)); + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +my @policyFields = + ('priority', 'name', 'host', 'commands', 'noderange', 'parameters', 'time', 'rule', 'comments', 'disable'); + +#complete +sub policiesHandler { + my @responses; + my @args; + my $priority; + + #does it specify the prioirty in the URI? + if (defined $path[1]) { + $priority = $path[1]; + } + + #in the query string? + elsif (defined $q->param('priority')) { + $priority = $q->param('priority'); + } + + if (isGet()) { + if (defined $priority) { + $request->{command} = 'gettab'; + push @args, "priority=$priority"; + my @fields = $q->param('field'); + + #if they specified fields to retrieve + if (@fields) { + push @args, @fields; + } + + #give them everything if nothing is specified + else { + foreach (@policyFields) { + push @args, "policy.$_"; + } + } + } + else { + $request->{command} = 'tabdump'; + push @args, 'policy'; + } + } + elsif (isPost()) { + if (defined $priority) { + $request->{command} = 'tabch'; + push @args, "priority=$priority"; + for ($q->param) { + if ($_ ne /priority/) { + push @args, "policy.$_=" . $q->param($_); + } + } + } + + #some response about the priority being required + else { + addPageContent("The priority must be specified when creating a policy"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif (isDelete()) { + + #just allowing a delete by priority at the moment, could expand this to anything + if (defined $priority) { + $request->{command} = 'tabch'; + push @args, '-d'; + push @args, "priority=$priority"; + push @args, "policy"; + } + } + elsif (isPut() || isPatch()) { + if (defined $priority) { + $request->{command} = 'tabch'; + push @args, "priority=$priority"; + for ($q->param) { + if ($_ ne /priority/) { + push @args, "policy.$_=" . $q->param($_); + } + } + } + + #some response about the priority being required + else { + addPageContent("The priority must be specified when updating a policy"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + addPageContent("request is " . Dumper($request)); + my $req = genRequest(); + @responses = sendRequest($req); + + return @responses; +} + +#complete +sub siteHandler { + my @data; + my @responses; + my @args; + + if (isGet()) { + $request->{command} = 'lsdef'; + push @{$request->{arg}}, '-t', 'site', '-o', 'clustersite'; + my @temparray = $q->param('field'); + + #add the field name to get + if (scalar(@temparray) > 0) { + push @{$request->{arg}}, '-i'; + push @{$request->{arg}}, join(',', @temparray); + } + } + elsif (isPut()) { + $request->{command} = 'chdef'; + push @{$request->{arg}}, '-t', 'site', '-o', 'clustersite'; + unless ($q->param('PUTDATA')) { + #temporary allowance for the put data to be contained in the queryString + unless ($queryhash{'putData'}) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + foreach my $put (@{$queryhash{'putData'}}) { + my ($key, $value) = split(/=/, $put, 2); + if ($key eq 'field' && $value) { + push @{$request->{arg}}, $value; + } + } + } + } else { + if ($q->param('PUTDATA')) { + my $entries = JSON::decode_json($q->param('PUTDATA')); + foreach (@$entries) { + push @{$request->{arg}}, $_; + } + } + else { + addPageContent("No Field and Value map was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + } + else { + unsupportedRequestType(); + } + + my $req = genRequest(); + @responses = sendRequest($req); + return @responses; +} + +my $formatType; + +#provide direct table access +#complete and tested on the site table +#use of the actual DELETE doesn't seem to fit here, since a resource would not be deleted +#using PUT or PATCH instead, though it doesn't feel all that correct either +sub tablesHandler { + my @responses; + my $table; + my @args; + + #is the table name specified in the URI? + if (defined $path[1]) { + $table = $path[1]; + } + + #handle all gets + if (isGet()) { + + #table was specified + if (defined $table) { + if (defined($q->param('col'))) { + $request->{command} = 'gettab'; + push @args, $q->param('col') . '=' . $q->param('value'); + my @temparray = $q->param('attribute'); + foreach (@temparray) { + push @args, $table . '.' . $_; + } + } + else { + $request->{command} = 'tabdump'; + push @args, $table; + if (!defined $q->param('desc')) { + $formatType = 'splitCommas'; + } + } + } + else { + $request->{command} = 'tabdump'; + } + } + elsif (isPut() || isPatch()) { + my $condition = $q->param('condition'); + my @vals; + my $entries; + if (!defined $condition) { + unless ($q->param('PUTDATA')) { + foreach my $put (@{$queryhash{'putData'}}) { + my ($key, $value) = split(/=/, $put, 2); + if ($key eq 'condition' && $value) { + $condition = $value; + } + } + foreach my $put (@{$queryhash{'putData'}}) { + my ($key, $value) = split(/=/, $put, 2); + if ($key eq 'value') { + push(@vals, $value); + } + } + } + else { + $entries = JSON::decode_json($q->param('PUTDATA')); + if (scalar(@$entries) < 1) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + } + + if (!defined $table || !defined $condition) { + if (scalar(@$entries) < 1) { + addPageContent("The table and condition must be specified when adding, changing or deleting an entry"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + $request->{command} = 'tabch'; + my $del; + if (!defined $q->param('delete')) { + foreach my $put (@{$queryhash{'putData'}}) { + my ($key, $value) = split(/=/, $put, 2); + if ($key eq 'delete') { + $del = 1; + } + } + } + + if (defined $q->param('delete') || defined $del) { + push @args, '-d'; + push @args, $condition; + push @args, $table; + } + elsif (defined $condition) { + push @args, $condition; + if ($q->param('value')) { + for ($q->param('value')) { + push @args, "$table.$_"; + } + } + else { + @args = (@args, @vals); + } + } + else { + foreach (@$entries) { + push @args, split(/ /,$_); + } + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + return @responses; +} + +my @accountFields = ('key', 'username', 'password', 'cryptmethod', 'comments', 'disable'); + +#done aside from being able to change cluster users, which xcat can't do yet +sub accountsHandler { + my @responses; + my @args; + my $key = $q->param('key'); + + if (isGet()) { + + #passwd table + if (!defined $q->param('clusterUser')) { + if (defined $key) { + $request->{command} = 'gettab'; + push @args, "key=$key"; + if (defined $q->param('field')) { + foreach ($q->param('field')) { + push @args, "passwd.$_"; + } + } + else { + foreach (@accountFields) { + push @args, "passwd.$_"; + } + } + } + else { + $request->{command} = 'tabdump'; + push @args, 'passwd'; + } + } + + #cluster user list + else { + $request->{command} = 'xcatclientnnr'; + push @args, 'clusteruserlist'; + push @args, '-p'; + } + } + elsif (isPost()) { + if (!defined $q->param('clusterUser')) { + if (defined $key) { + $request->{command} = 'tabch'; + push @args, "key=$key"; + for ($q->param) { + if ($_ !~ /key/) { + push @args, "passwd.$_=" . $q->param($_); + } + } + } + else { + addPageContent("The key must be specified when creating a non-cluster user"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + + #active directory user + else { + if (defined $q->param('userName') && defined $q->param('userPass')) { + $request->{command} = 'xcatclientnnr'; + push @args, 'clusteruseradd'; + push @args, $q->param('userName'); + push @{$request->{arg}}, @args; + $request->{environment} = {XCAT_USERPASS => $q->param('userPass')}; + } + else { + addPageContent("The key must be specified when creating a cluster user"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + } + elsif (isDelete()) { + if (!defined $q->param('clusterUser')) { + + #just allowing a delete by key at the moment, could expand this to anything + if (defined $key) { + $request->{command} = 'tabch'; + push @args, '-d'; + push @args, "key=$key"; + push @args, "passwd"; + } + else { + addPageContent("The key must be specified when deleting a non-cluster user"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + else { + if (defined $q->param('userName')) { + $request->{command} = 'xcatclientnnr'; + push @args, 'clusteruserdel'; + push @args, $q->param('userName'); + } + else { + addPageContent("The userName must be specified when deleting a cluster user"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + } + elsif (isPut() || isPatch()) { + if (!defined $q->param('clusterUser')) { + if (defined $key) { + $request->{command} = 'tabch'; + push @args, "key=$key"; + for ($q->param) { + if ($_ !~ /key/) { + push @args, "passwd.$_=" . $q->param($_); + } + } + } + else { + addPageContent("The key must be specified when updating a non-cluster user"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + + #TODO: there isn't currently a way to update cluster users + else { + + } + } + else { + unsupportedRequestType(); + exit(0); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + return @responses; +} + +sub objectsHandler { + my @responses; + my @args; + my @objectTypeList = ( + "auditlog", "boottarget", "eventlog", "firmware", "group", "monitoring", + "network", "node", "notification", "osimage", "policy", "route", + "site"); + + #my %objectTypes; + #foreach my $item (@objectTypeList) { $objectTypes{$item} = 1 } + my @objectTypes; + my @objects; + if (defined $path[1]) { + $objectTypes[0] = $path[1]; + if (defined $path[2]) { + $objects[0] = $path[2]; + } + } + if (defined $q->param('objectType')) { + @objectTypes = $q->param('objectType'); + } + if (defined $q->param('object')) { + @objects = $q->param('object'); + } + + if ($q->param('verbose')) { + push @args, '-v'; + } + + if (isGet()) { + if (defined $objectTypes[0]) { + $request->{command} = 'lsdef'; + push @args, '-l'; + push @args, '-t'; + push @args, join(',', @objectTypes); + if (defined $objects[0]) { + push @args, '-o'; + push @args, join(',', @objects); + } + if ($q->param('info')) { + push @args, '-h'; + } + } + else { + if ($q->param('info')) { + push @args, '-h'; + } + else { + + #couldn't find a way to do this through xcatd, so shortcutting the request + my %resp = (data => \@objectTypeList); + return (\%resp); + } + } + } + elsif (isPut()) { + $request->{command} = 'chdef'; + if ($q->param('verbose')) { + push @args, '-v'; + } + if (!defined $q->param('objectType')) { + addPageContent("The object must be specified."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + push @args, '-t'; + push @args, join(',', $q->param('objectType')); + } + if ($q->param('objectName')) { + push @args, join(',', $q->param('objectName')); + } + if ($q->param('dynamic')) { + push @args, '-d'; + } + if ($q->param('minus')) { + push @args, '-m'; + } + if ($q->param('plus')) { + push @args, '-p'; + } + if (defined $q->param('field')) { + foreach ($q->param('field')) { + + #if it has ==, !=. =~ or !~ operators in the field, use the -w option + if (/==|!=|=~|!~/) { + push @args, '-w'; + } + push @args, $_; + } + } + if ($q->param('nodeRange')) { + push @args, $q->param('nodeRange'); + } + + } + elsif (isPost()) { + $request->{command} = 'mkdef'; + if ($q->param('verbose')) { + push @args, '-v'; + } + if (!defined $q->param('objectType')) { + addPageContent("The object must be specified."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + else { + push @args, '-t'; + push @args, join(',', $q->param('objectType')); + } + if ($q->param('objectName')) { + push @args, join(',', $q->param('objectName')); + } + if ($q->param('dynamic')) { + push @args, '-d'; + } + if ($q->param('force')) { + push @args, '-f'; + } + if (defined $q->param('field')) { + foreach ($q->param('field')) { + + #if it has ==, !=. =~ or !~ operators in the field, use the -w option + if (/==|!=|=~|!~/) { + push @args, '-w'; + } + push @args, $_; + } + } + if ($q->param('nodeRange')) { + push @args, $q->param('nodeRange'); + } + + } + elsif (isDelete()) { + $request->{command} = 'rmdef'; + if (defined $q->param('info')) { + push @args, '-h'; + } + elsif (defined $q->param('all')) { + push @args, '-a'; + } + elsif (defined $objectTypes[0]) { + push @args, '-t'; + push @args, join(',', @objectTypes); + if (defined $objects[0]) { + push @args, '-o'; + push @args, join(',', @objects); + } + } + else { + addPageContent( +"Either the help info must be requested or the object must be specified or the flag that indicates everything should be removed." + ); + sendResponseMsg($STATUS_BAD_REQUEST); + } + if (defined $q->param('nodeRange')) { + push @args, $q->param('nodeRange'); + } + } + else { + unsupportedRequestType(); + exit(); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + return @responses; +} + +#complete i think, tho chvm could handle args better +sub vmsHandler { + my @args; + my $noderange; + my $subResource; + if (defined $path[1]) { + $noderange = $path[1]; + $request->{noderange} = $noderange; + } + else { + addPageContent("Invalid nodes and/or groups in noderange"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (isGet()) { + $request->{command} = 'lsvm'; + if (defined $q->param('all')) { + push @args, '-a'; + } + + # for z/VM + if (defined $q->param('networknames')) { + push @args, '--getnetworknames'; + } + + if (defined $q->param('network')) { + push @args, '--getnetwork'; + push @args, $q->param('getnetwork'); + } + + if (defined $q->param('diskpoolnames')) { + push @args, '--diskpoolnames'; + } + + if (defined $q->param('diskpool')) { + push @args, '--diskpool'; + push @args, $q->param('diskpool'); + } + + if (defined $q->param('checknics')) { + push @args, '--checknics'; + push @args, $q->param('checknics'); + } + } + elsif (isPost()) { + my $entries; + my %entryhash; + my $position; + $request->{command} = 'mkvm'; + unless ($q->param('POSTDATA')) { + addPageContent("Invalid Parameters"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + #collect all parameters from the postdata + $entries = JSON::decode_json($q->param('POSTDATA')); + if (scalar(@$entries) < 1) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + extractData(\%entryhash, $entries); + + # For zVM; clonefrom must be first so that the mkvm call + # has the clone from node in correct spot in makeVM args + if (defined $entryhash{'clonefrom'}) { + push @args, $entryhash{'clonefrom'}; + } + + #for system p + if (defined $entryhash{'cec'}) { + push @args, '-c'; + push @args, $entryhash{'cec'}; + } + + if (defined $entryhash{'startId'}) { + push @args, '-i'; + push @args, $entryhash{'startId'}; + } + + if (defined $entryhash{'source'}) { + push @args, '-l'; + push @args, $entryhash{'source'}; + } + + if (defined $entryhash{'profile'}) { + push @args, '-p'; + push @args, $entryhash{'profile'}; + } + + if (defined $entryhash{'full'}) { + push @args, '--full'; + } + + #for KVM & Vmware + if (defined $entryhash{'master'}) { + push @args, '-m'; + push @args, $entryhash{'master'}; + } + + if (defined $entryhash{'disksize'}) { + push @args, '-s'; + push @args, $entryhash{'disksize'}; + } + + if (defined $entryhash{'memory'}) { + push @args, '--mem'; + push @args, $entryhash{'memory'}; + } + + if (defined $entryhash{'cpu'}) { + push @args, '--cpus'; + push @args, $entryhash{'cpu'}; + } + + if (defined $entryhash{'force'}) { + push @args, '-f'; + } + + # for z/VM + if (defined $entryhash{'userid'}) { + push @args, '--userid'; + push @args, $entryhash{'userid'}; + } + + if (defined $entryhash{'size'}) { + push @args, '--size'; + push @args, $entryhash{'size'}; + } + + if (defined $entryhash{'password'}) { + push @args, '--password'; + push @args, $entryhash{'password'}; + } + + if (defined $entryhash{'privilege'}) { + push @args, '--privilege'; + push @args, $entryhash{'privilege'}; + } + + if (defined $entryhash{'diskpool'}) { + push @args, '--diskpool'; + push @args, $entryhash{'diskpool'}; + } + + if (defined $entryhash{'diskvdev'}) { + push @args, '--diskVdev'; + push @args, $entryhash{'diskvdev'}; + } + if (defined $entryhash{'imagename'}) { + push @args, '--imagename'; + push @args, $entryhash{'imagename'}; + } + if (defined $entryhash{'osimage'}) { + push @args, '--osimage'; + push @args, $entryhash{'osimage'}; + } + if (defined $entryhash{'ipl'}) { + push @args, '--ipl'; + push @args, $entryhash{'ipl'}; + } + # For the mkvm call the zvm.pm code is looking for key=value + # for pool and pw; rather than a "--key value" + if ( defined $entryhash{'pool'} ) { + push @args, "pool=$entryhash{'pool'}"; + } + if ( defined $entryhash{'pw'} ) { + push @args, "pw=$entryhash{'pw'}"; + } + } + elsif (isPut()) { + $request->{command} = 'chvm'; + if ($q->param('PUTDATA')) { + my $entries = JSON::decode_json($q->param('PUTDATA')); + if (scalar(@$entries) < 1) { + addPageContent("No Field and Value map was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + foreach (@$entries) { + # Handle blank delimited parameters + push @args, split(/ /,$_); + } + } + else { + addPageContent("No Field and Value map was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + } + elsif (isDelete()) { + $request->{command} = 'rmvm'; + if (defined $q->param('retain')) { + push @args, '-r'; + } + if (defined $q->param('service')) { + push @args, '--service'; + } + } + else { + unsupportedRequestType(); + exit(); + } + + # Note: MUST parse these parameters after all others if we want to avoid + # duplicating this code on each if branch, since + # lsvm depends on its "subcommand" being the first parameter. + # TODO if we add these parameters to other paths, could we use a subroutine instead? only 2 inputs. + + # Parse the optional upstream object ID, e.g. an OpenStack nova instance UUID + if (defined $queryhash{'objectid'}) { + push @args, '--objectid'; + push @args, $queryhash{'objectid'}->[0]; + } + + # Parse the optional upstream request ID, e.g. an OpenStack request UUID + if (defined $queryhash{'requestid'}) { + push @args, '--requestid'; + push @args, $queryhash{'requestid'}->[0]; + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + my @responses = sendRequest($req); + return @responses; +} + +sub versionHandler { + $request->{command} = "lsxcatd"; + push @{$request->{arg}}, "-v"; + my $req = genRequest(); + my @responses = sendRequest($req); + return @responses; +} + +#for operations that take a 'long' time to finish, this will provide the interface to check their status +sub jobsHandler { + +} + +sub hypervisorHandler { + my @responses; + my @args; + if (isPut()) { + my %entryhash; + if (defined $path[1]) { + $request->{noderange} = $path[1]; + } + else { + addPageContent("Invalid nodes and/or groups in node in noderange"); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + if (defined $path[2]) { + $request->{command} = $path[2]; + } + else { + $request->{command} = 'chhypervisor'; + } + my $entries = JSON::decode_json( $q->param('PUTDATA') ); + if (scalar(@$entries) < 1) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + foreach (@$entries) { + push @args, split(/ /,$_); + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + return @responses; + } +} + +sub debugHandler { + my @responses; + my @args; + if (isPut()) { + my %entryhash; + $request->{command} = 'xcatclientnnr xcatdebug'; + + #push @args, 'xcatdebug'; + my $entries = JSON::decode_json( $q->param('PUTDATA') ); + if (scalar(@$entries) < 1) { + addPageContent("No set attribute was supplied."); + sendResponseMsg($STATUS_BAD_REQUEST); + } + + foreach (@$entries) { + push @{$request->{arg}}, $_; + } + + push @{$request->{arg}}, @args; + my $req = genRequest(); + @responses = sendRequest($req); + return @responses; + } +} + +#all data wrapping and writing is funneled through here +sub wrapData { + my $data = shift; + my $errorInformation = ''; + + #trim the serverdone message off + if (exists $data->[0]->{serverdone} && exists $data->[0]->{error}) { + $errorInformation = $data->[0]->{error}->[0]; + addPageContent($q->p($errorInformation)); + if (($errorInformation =~ /Permission denied/) || ($errorInformation =~ /Authentication failure/)) { + sendResponseMsg($STATUS_UNAUTH); + } + else { + sendResponseMsg($STATUS_FORBIDDEN); + } + exit 1; + } + else { + pop @{$data}; + } + if (exists $formatters{$format}) { + $formatters{$format}->($data); + } + + #all information were add into the global varibale, call the response funcion + if (exists $data->[0]->{info} && $data->[0]->{info}->[0] =~ /Could not find an object/) { + sendResponseMsg($STATUS_NOT_FOUND); + } + elsif (isPost()) { + sendResponseMsg($STATUS_CREATED); + } + else { + sendResponseMsg($STATUS_OK); + } +} + +sub wrapJson { + my $data = shift; + my $json; + $json->{'data'} = $data; + addPageContent(JSON::to_json($json)); +} + +sub wrapHtml { + my $item; + my $response = shift; + my $baseUri = $url . $pathInfo; + if ($baseUri !~ /\/^/) { + $baseUri .= "/"; + } + + foreach my $element (@$response) { + + #foreach my $element (@$data){ + #if($element->{error}){ + if ($element->{node}) { + addPageContent(""); + foreach $item (@{$element->{node}}) { + + #my $url = $baseUri.$item->{name}[0]; + addPageContent(""); + if (exists $item->{data} && exists $item->{data}[0]) { + if (ref($item->{data}[0]) eq 'HASH') { + if (exists $item->{data}[0]->{desc} && exists $item->{data}[0]->{desc}[0]) { + addPageContent(""); + } + if (ref($item->{data}[0]) eq 'HASH' && exists $item->{data}[0]->{contents}[0]) { + addPageContent(""); + } + } + else { + addPageContent(""); + } + } + elsif (exists $item->{error}) { + addPageContent(""); + } + addPageContent(""); + } + addPageContent("
                                                  $item->{name}[0]$item->{data}[0]->{desc}[0]$item->{data}[0]->{contents}[0]$item->{data}[0]$item->{error}[0]
                                                  "); + } + if ($element->{data}) { + addPageContent(""); + foreach $item (@{$element->{data}}) { + my @values = split(/:/, $item, 2); + addPageContent(""); + foreach (@values) { + if ($formatType =~ /splitCommas/) { + my @fields = split(/,/, $_, -1); + foreach (@fields) { + addPageContent(""); + } + } + else { + addPageContent(""); + } + } + addPageContent("\n"); + } + addPageContent("
                                                  $_$_
                                                  "); + } + if ($element->{info}) { + addPageContent(""); + foreach $item (@{$element->{info}}) { + addPageContent(""); + my $fieldname = ''; + my $fieldvalue = ''; + + #strip whitespace in the string + $item =~ s/^\s+//; + $item =~ s/\s+$//; + if ($item =~ /Object/) { + ($fieldname, $fieldvalue) = split(/:/, $item); + } + elsif ($item =~ /.*=.*/) { + my $position = index $item, '='; + $fieldname = substr $item, 0, $position; + $fieldvalue = substr $item, $position + 1; + } + else { + $fieldname = $item; + } + addPageContent(""); + if ($fieldvalue ne '') { + addPageContent(""); + } + addPageContent("\n"); + } + addPageContent("
                                                  " . $fieldname . "" . $fieldvalue . "
                                                  "); + } + if ($element->{error}) { + addPageContent(""); + foreach $item (@{$element->{error}}) { + addPageContent(""); + } + addPageContent("
                                                  " . $item . "
                                                  "); + } + } +} + +sub wrapXml { + my @data = shift; + foreach (@data) { + foreach (@$_) { + addPageContent(XMLout($_, RootName => '', NoAttr => 1, KeyAttr => [])); + } + } +} + +#general tests for valid requests and responses with HTTP codes here +if (!doesResourceExist($resource)) { + addPageContent("Resource '$resource' does not exist"); + sendResponseMsg($STATUS_NOT_FOUND); +} +else { + if ($DEBUGGING) { + addPageContent($q->p("resource is $resource")); + } + handleRequest(); +} + +#talk to the server +use Socket; +use IO::Socket::INET; +use IO::Socket::SSL; + +# The database initialization may take some time in the system boot scenario +# wait for a while for the database initialization +#do we really need to do this for the web service? +sub sendRequest { + my $request = shift; + my $sitetab; + my $retries = 0; + + if ($DEBUGGING) { + my $preXml = $request; + + #$preXml =~ s/< /g; + #$preXml =~ s/>/>
                                                  /g; + addPageContent($q->p("request XML
                                                  " . $preXml)); + } + + #hardcoded port for now + my $port = 3001; + my $xcatHost = "localhost:$port"; + + #temporary, will be using username and password + my $homedir = "/root"; + my $keyfile = $homedir . "/.xcat/client-cred.pem"; + my $certfile = $homedir . "/.xcat/client-cred.pem"; + my $cafile = $homedir . "/.xcat/ca.pem"; + + my $client; + if (-r $keyfile and -r $certfile and -r $cafile) { + $client = IO::Socket::SSL->new( + PeerAddr => $xcatHost, + SSL_key_file => $keyfile, + SSL_cert_file => $certfile, + SSL_ca_file => $cafile, + SSL_use_cert => 1, + Timeout => 15,); + } + else { + $client = IO::Socket::SSL->new( + PeerAddr => $xcatHost, + SSL_verify_mode => 'SSL_VERIFY_NONE', + Timeout => 15,); + } + unless ($client) { + if ($@ =~ /SSL Timeout/) { + addPageContent("Connection failure: SSL Timeout or incorrect certificates in ~/.xcat"); + sendResponseMsg($STATUS_TIMEOUT); + } + else { + addPageContent("Connection failurexx: $@"); + sendResponseMsg($STATUS_SERVICE_UNAVAILABLE); + } + } + + print $client $request; + + my $response; + my $rsp; + my @fullResponse; + my $cleanexit = 0; + while (<$client>) { + $response .= $_; + if (m/<\/xcatresponse>/) { + + #replace ESC with xxxxESCxxx because XMLin cannot handle it + if ($DEBUGGING) { + addPageContent($response . "\n"); + } + $response =~ s/\e/xxxxESCxxxx/g; + + #print "responseXML is ".$response; + $rsp = XMLin($response, SuppressEmpty => undef, ForceArray => 1); + + #add ESC back + foreach my $key (keys %$rsp) { + if (ref($rsp->{$key}) eq 'ARRAY') { + foreach my $text (@{$rsp->{$key}}) { + next unless defined $text; + $text =~ s/xxxxESCxxxx/\e/g; + } + } + else { + $rsp->{$key} =~ s/xxxxESCxxxx/\e/g; + } + } + + $response = ''; + push(@fullResponse, $rsp); + if ($rsp->{serverdone}) { + $cleanexit = 1; + last; + } + } + } + unless ($cleanexit) { + addPageContent("ERROR/WARNING: communication with the xCAT server seems to have been ended prematurely"); + sendResponseMsg($STATUS_SERVICE_UNAVAILABLE); + exit(0); + } + + if ($DEBUGGING) { + addPageContent($q->p("response " . Dumper(@fullResponse))); + } + return @fullResponse; +} + +sub isGet { + return uc($requestType) eq "GET"; +} + +sub isPut { + return uc($requestType) eq "PUT"; +} + +sub isPost { + return uc($requestType) eq "POST"; +} + +sub isPatch { + return uc($requestType) eq "PATCH"; +} + +sub isDelete { + return uc($requestType) eq "DELETE"; +} + +#check to see if this is a valid user. userName and password are already set +sub isAuthenticUser { + $request->{command} = 'authcheck'; + my $req = genRequest(); + my @responses = sendRequest($req); + if ($responses[0]->{data}[0] eq "Authenticated") { + + #user is authenticated + return 1; + } + + #authentication failure + addPageContent($responses[0]->{error}[0]); + sendResponseMsg($STATUS_UNAUTH); +} diff --git a/xCAT/xCAT.spec b/xCAT/xCAT.spec index d293339d2..340f20006 100644 --- a/xCAT/xCAT.spec +++ b/xCAT/xCAT.spec @@ -28,14 +28,24 @@ Conflicts: xCATsn Requires: perl-DBD-SQLite Requires: xCAT-client = 4:%{version}-%{release} Requires: xCAT-server = 4:%{version}-%{release} -Requires: xCAT-probe = 4:%{version}-%{release} -Requires: xCAT-genesis-scripts-x86_64 = 1:%{version}-%{release} -Requires: xCAT-genesis-scripts-ppc64 = 1:%{version}-%{release} -Requires: rsync %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) + +%if %nots390x +Requires: xCAT-probe = 4:%{version}-%{release} +Requires: xCAT-genesis-scripts-x86_64 = 1:%{version}-%{release} +Requires: xCAT-genesis-scripts-ppc64 = 1:%{version}-%{release} +%endif + +Requires: rsync + %ifos linux Requires: httpd nfs-utils nmap bind perl(CGI) # on RHEL7, need to specify it explicitly @@ -45,7 +55,7 @@ Requires: /usr/bin/killall Requires: /usr/sbin/dhcpd # On RHEL this pulls in openssh-server, on SLES it pulls in openssh Requires: /usr/bin/ssh -%ifnarch s390x +%if %nots390x Requires: /etc/xinetd.d/tftp Requires: xCAT-buildkit # Stty is only needed for rcons on ppc64 nodes, but for mixed clusters require it on both x and p @@ -56,7 +66,7 @@ Requires: perl-IO-Stty # The aix rpm cmd forces us to do this outside of ifos type stmts %if %notpcm %ifos linux -%ifnarch s390x +%if %nots390x # PCM does not use or ship conserver Requires: conserver-xcat %endif @@ -64,7 +74,9 @@ Requires: conserver-xcat %endif #support mixed cluster +%if %nots390x Requires: elilo-xcat xnba-undi +%endif %ifarch i386 i586 i686 x86 x86_64 Requires: syslinux @@ -79,8 +91,10 @@ Requires: ipmitool-xcat >= 1.8.17-1 %if %notpcm # PCM does not need or ship syslinux-xcat +%if %nots390x Requires: syslinux-xcat %endif +%endif %description xCAT is a server management package intended for at-scale management, including @@ -128,7 +142,7 @@ fi %install -mkdir -p $RPM_BUILD_ROOT/etc/xcat/conf.orig +mkdir -p $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig mkdir -p $RPM_BUILD_ROOT/etc/apache2/conf.d mkdir -p $RPM_BUILD_ROOT/etc/httpd/conf.d mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d @@ -176,8 +190,8 @@ mkdir -p postscripts/hostkeys cd - cp %{SOURCE1} $RPM_BUILD_ROOT/etc/httpd/conf.d/xcat.conf cp %{SOURCE1} $RPM_BUILD_ROOT/etc/apache2/conf.d/xcat.conf -cp %{SOURCE7} $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat.conf.apach24 -cp %{SOURCE1} $RPM_BUILD_ROOT/etc/xcat/conf.orig/xcat.conf.apach22 +cp %{SOURCE7} $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat.conf.apach24 +cp %{SOURCE1} $RPM_BUILD_ROOT/etc/%httpconfigdir/conf.orig/xcat.conf.apach22 cp %{SOURCE5} $RPM_BUILD_ROOT/etc/xCATMN mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT @@ -190,19 +204,19 @@ cp LICENSE.html $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT if [ -n "$(httpd -v 2>&1 |grep -e '^Server version\s*:.*\/2.4')" ] then rm -rf /etc/httpd/conf.d/xcat.conf - cp /etc/xcat/conf.orig/xcat.conf.apach24 /etc/httpd/conf.d/xcat.conf + cp /etc/%httpconfigdir/conf.orig/xcat.conf.apach24 /etc/httpd/conf.d/xcat.conf fi if [ -n "$(apachectl -v 2>&1 |grep -e '^Server version\s*:.*\/2.4')" ] then rm -rf /etc/apache2/conf.d/xcat.conf - cp /etc/xcat/conf.orig/xcat.conf.apach24 /etc/apache2/conf.d/xcat.conf + cp /etc/%httpconfigdir/conf.orig/xcat.conf.apach24 /etc/apache2/conf.d/xcat.conf fi if [ -n "$(apache2ctl -v 2>&1 |grep -e '^Server version\s*:.*\/2.4')" ] then rm -rf /etc/apache2/conf.d/xcat.conf - cp /etc/xcat/conf.orig/xcat.conf.apach24 /etc/apache2/conf.d/xcat.conf + cp /etc/%httpconfigdir/conf.orig/xcat.conf.apach24 /etc/apache2/conf.d/xcat.conf fi # Let rsyslogd perform close of any open files @@ -244,8 +258,8 @@ exit 0 %files %{prefix} # one for sles, one for rhel. yes, it's ugly... -/etc/xcat/conf.orig/xcat.conf.apach24 -/etc/xcat/conf.orig/xcat.conf.apach22 +/etc/%httpconfigdir/conf.orig/xcat.conf.apach24 +/etc/%httpconfigdir/conf.orig/xcat.conf.apach22 /etc/httpd/conf.d/xcat.conf /etc/apache2/conf.d/xcat.conf /etc/xCATMN diff --git a/xCAT/xcat.conf.apach24 b/xCAT/xcat.conf.apach24 index d4e83370c..89e28e345 100644 --- a/xCAT/xcat.conf.apach24 +++ b/xCAT/xcat.conf.apach24 @@ -24,4 +24,3 @@ Alias /xcat-doc "/opt/xcat/share/doc" AllowOverride None Require all granted - diff --git a/xCATsn/xCATsn.spec b/xCATsn/xCATsn.spec index f87fc923a..8020b6b0f 100644 --- a/xCATsn/xCATsn.spec +++ b/xCATsn/xCATsn.spec @@ -57,7 +57,9 @@ Requires: conserver-xcat %endif #support mixed cluster +%ifnarch s390x Requires: elilo-xcat xnba-undi +%endif %ifarch i386 i586 i686 x86 x86_64 Requires: syslinux