From 11f6324d79d13dbf15d96e454131cc0bb0fd65c2 Mon Sep 17 00:00:00 2001 From: phamt Date: Mon, 12 Apr 2010 14:44:14 +0000 Subject: [PATCH] Updated z/VM plugin to support update release git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@5743 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd --- perl-xCAT/xCAT/zvmCPUtils.pm | 124 +- perl-xCAT/xCAT/zvmUtils.pm | 246 +- xCAT-server/lib/xcat/plugins/zvm.pm | 5727 +++++++++++++++------------ 3 files changed, 3376 insertions(+), 2721 deletions(-) diff --git a/perl-xCAT/xCAT/zvmCPUtils.pm b/perl-xCAT/xCAT/zvmCPUtils.pm index 8ef2bf72a..c1f703564 100644 --- a/perl-xCAT/xCAT/zvmCPUtils.pm +++ b/perl-xCAT/xCAT/zvmCPUtils.pm @@ -1,4 +1,4 @@ -# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 @@ -18,7 +18,7 @@ use warnings; =head3 getUserId - Description : Get userID of given node + Description : Get the userID of a given node Arguments : Node Returns : UserID Example : my $userID = xCAT::zvmCPUtils->getUserId($node); @@ -32,7 +32,7 @@ sub getUserId { my ( $class, $node ) = @_; # Get userId using VMCP - my $out = `ssh -o ConnectTimeout=5 $node "vmcp q userid"`; + my $out = `ssh -o ConnectTimeout=5 $node "vmcp q userid"`; my @results = split( ' ', $out ); return ( $results[0] ); @@ -42,7 +42,7 @@ sub getUserId { =head3 getHost - Description : Get z/VM host for given node + Description : Get the z/VM host for a given node Arguments : Node Returns : z/VM host Example : my $host = xCAT::zvmCPUtils->getHost($node); @@ -67,7 +67,7 @@ sub getHost { =head3 getPrivileges - Description : Get privilege class of given node + Description : Get the privilege class of a given node Arguments : Node Returns : Privilege class Example : my $memory = xCAT::zvmCPUtils->getPrivileges($node); @@ -94,7 +94,7 @@ sub getPrivileges { =head3 getMemory - Description : Get memory of given node + Description : Get the memory of a given node Arguments : Node Returns : Memory Example : my $memory = xCAT::zvmCPUtils->getMemory($node); @@ -118,7 +118,7 @@ sub getMemory { =head3 getCpu - Description : Get processor(s) of given node + Description : Get the processor(s) of a given node Arguments : Node Returns : Processor(s) Example : my $proc = xCAT::zvmCPUtils->getCpu($node); @@ -142,7 +142,7 @@ sub getCpu { =head3 getNic - Description : Get network interface card (NIC) of given node + Description : Get the network interface card (NIC) of a given node Arguments : Node Returns : NIC(s) Example : my $nic = xCAT::zvmCPUtils->getNic($node); @@ -164,9 +164,76 @@ sub getNic { #------------------------------------------------------- +=head3 getNetworkNames + + Description : Get a list of network names + Arguments : Node + Returns : Network names + Example : my $lans = xCAT::zvmCPUtils->getNetworkNames($node); + +=cut + +#------------------------------------------------------- +sub getNetworkNames { + + # Get inputs + my ( $class, $node ) = @_; + + # Get network names + my $out = `ssh -o ConnectTimeout=5 $node "vmcp q lan | egrep 'LAN|VSWITCH'"`; + my @lines = split( '\n', $out ); + my @parms; + my $names; + foreach (@lines) { + + # Trim output + $_ = xCAT::zvmUtils->trimStr($_); + @parms = split( ' ', $_ ); + + # Get the network name + if ( $parms[0] eq "LAN" || $parms[0] eq "VSWITCH" ) { + $names .= $parms[0] . " " . $parms[2] . "\n"; + } + } + + return ($names); +} + +#------------------------------------------------------- + +=head3 getNetwork + + Description : Get the network configuration + Arguments : Node + Network name + Returns : Network configuration + Example : my $config = xCAT::zvmCPUtils->getNetwork($node, $netName); + +=cut + +#------------------------------------------------------- +sub getNetwork { + + # Get inputs + my ( $class, $node, $netName ) = @_; + + # Get network configuration + my $out; + if ( $netName eq "all" ) { + $out = `ssh -o ConnectTimeout=5 $node "vmcp q lan"`; + } + else { + $out = `ssh -o ConnectTimeout=5 $node "vmcp q lan $netName"`; + } + + return ($out); +} + +#------------------------------------------------------- + =head3 getDisks - Description : Get disk(s) of given node + Description : Get the disk(s) of given node Arguments : Node Returns : Disk(s) Example : my $storage = xCAT::zvmCPUtils->getDisks($node); @@ -190,7 +257,7 @@ sub getDisks { =head3 loadVmcp - Description : Load VMCP module on specified node + Description : Load Linux VMCP module on a given node Arguments : Node Returns : Nothing Example : xCAT::zvmCPUtils->loadVmcp($node); @@ -212,7 +279,7 @@ sub loadVmcp { =head3 getVswitchId - Description : Get VSWITCH ID of given node + Description : Get the VSWITCH ID(s) of given node Arguments : Node Returns : VSwitch IDs Example : my @vswitch = xCAT::zvmCPUtils->getVswitchId($node); @@ -242,7 +309,7 @@ sub getVswitchId { =head3 grantVSwitch - Description : Grant access to virtual switch (VSWITCH) for given userID + Description : Grant access to a virtual switch (VSWITCH) for given userID Arguments : HCP node User ID VSWITCH ID @@ -369,7 +436,7 @@ sub purgeReader { =head3 sendCPCmd - Description : Send CP command to given userID (Class C users only) + Description : Send CP command to a given userID (Class C users only) Arguments : HCP node UserID to send CP command Returns : Nothing @@ -407,7 +474,7 @@ sub getNetworkLayer { # 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'}; @@ -416,7 +483,7 @@ sub getNetworkLayer { } # Get network name - my $out = `ssh -o ConnectTimeout=5 $node "vmcp q v nic" | egrep -i "VSWITCH|LAN"`; + my $out = `ssh -o ConnectTimeout=5 $node "vmcp q v nic" | egrep -i "VSWITCH|LAN"`; my @lines = split( '\n', $out ); # Go through each line and extract VSwitch and Lan names @@ -484,7 +551,8 @@ sub getNetworkType { my ( $class, $hcp, $netName ) = @_; # Get network details - my $out = `ssh -o ConnectTimeout=5 $hcp "vmcp q lan $netName" | grep "Type"`; + my $out = + `ssh -o ConnectTimeout=5 $hcp "vmcp q lan $netName" | grep "Type"`; # Go through each line and determine network type my @lines = split( '\n', $out ); @@ -509,3 +577,27 @@ sub getNetworkType { return $netType; } + +#------------------------------------------------------- + +=head3 defineCpu + + Description : Add processor(s) to given node + Arguments : Node + Returns : Nothing + Example : my $out = xCAT::zvmCPUtils->defineCpu($node, $addr, $type); + +=cut + +#------------------------------------------------------- +sub defineCpu { + + # Get inputs + my ( $class, $node, $addr, $type ) = @_; + + # Define processor(s) + my $out = + `ssh -o ConnectTimeout=5 $node "vmcp define cpu $addr type $type"`; + + return ($out); +} diff --git a/perl-xCAT/xCAT/zvmUtils.pm b/perl-xCAT/xCAT/zvmUtils.pm index e54016b97..c769b1e85 100644 --- a/perl-xCAT/xCAT/zvmUtils.pm +++ b/perl-xCAT/xCAT/zvmUtils.pm @@ -1,4 +1,4 @@ -# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 @@ -239,7 +239,7 @@ sub replaceStr { =head3 printLn - Description : Print string + Description : Print string to stdout Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printLn($callback, $str); @@ -301,45 +301,10 @@ sub isZvmNode { #------------------------------------------------------- -=head3 getIp - - Description : Get the IP address of a given node - Arguments : Node - Returns : IP address of given node - Example : my $ip = xCAT::zvmUtils->getIp($node); - -=cut - -#------------------------------------------------------- -sub getIp { - - # Get inputs - my ( $class, $node ) = @_; - - # Get IP address - my $out = `ssh -o ConnectTimeout=10 $node "ifconfig" | grep "inet addr:" | grep -v "127.0.0.1"`; - my @lines = split( '\n', $out ); - - # Get the first IP that comes back - my @parms = split( ' ', $lines[0] ); - foreach (@parms) { - - # Get inet addr parameter - if ( $_ =~ m/addr:/i ) { - my @ip = split( ':', $_ ); - return ( $ip[1] ); - } - } - - return; -} - -#------------------------------------------------------- - =head3 getHwcfg - Description : Get hardware configuration file path of given node - SUSE -- /etc/sysconfig/hardware/hwcfg-qeth + Description : Get the hardware configuration file path + e.g. /etc/sysconfig/hardwarehwcfg-qeth-bus-ccw-0.0.0600 Arguments : Node Returns : Hardware configuration file path Example : my $hwcfg = xCAT::zvmUtils->getHwcfg($node); @@ -372,9 +337,35 @@ sub getHwcfg { #------------------------------------------------------- +=head3 getIp + + Description : Get the IP address of a given node + Arguments : Node + Returns : IP address of given node + Example : my $ip = xCAT::zvmUtils->getIp($node); + +=cut + +#------------------------------------------------------- +sub getIp { + + # Get inputs + 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 | grep "$node "`; + my @parms = split( ' ', $out ); + + return $parms[0]; +} + +#------------------------------------------------------- + =head3 getIfcfg - Description : Get network configuration file path of given node + Description : Get the network configuration file path of a given node Red Hat -- /etc/sysconfig/network-scripts/ifcfg-eth SUSE -- /etc/sysconfig/network/ifcfg-qeth Arguments : Node @@ -418,10 +409,10 @@ sub getIfcfg { =head3 getIfcfgByNic - Description : Get /etc/sysconfig/network/ifcfg-qeth file name of given NIC + Description : Get the network configuration file path of a given node by its NIC address Arguments : Node NIC address - Returns : /etc/sysconfig/network/ifcfg-qeth file name + Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfgByNic($node, $nic); =cut @@ -441,9 +432,19 @@ sub getIfcfgByNic { # 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 $node "ls /etc/sysconfig/network-scripts/ifcfg-eth*" | grep "$nic"`; + $out = `ssh -o ConnectTimeout=5 $node "ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; @parms = split( '\n', $out ); - return ( $parms[0] ); + + # Go through each line + foreach( @parms ) { + + # If the network file contains the NIC address + $out = `ssh -o ConnectTimeout=5 $node "cat $_" | grep "$nic"`; + if ( $out ) { + # Return network file path + return ( $_ ); + } + } } # If it is SUSE -- ifcfg-qeth file is in /etc/sysconfig/network @@ -459,77 +460,6 @@ sub getIfcfgByNic { #------------------------------------------------------- -=head3 getBroadcastIP - - Description : Get IP broadcast of given node - Arguments : Node - Returns : IP broadcast - Example : my $broadcast = xCAT::zvmUtils->getBroadcastIP($node); - -=cut - -#------------------------------------------------------- -sub getBroadcastIP { - - # Get inputs - my ( $class, $node ) = @_; - - # Get IP address - my $out = `ssh -o ConnectTimeout=5 $node "ifconfig" | grep "Bcast:" | cut -d: -f3`; - my @parms = split( ' ', $out ); - - return ( $parms[0] ); -} - -#------------------------------------------------------- - -=head3 getDns - - Description : Get DNS server of given node - Arguments : Node - Returns : DNS server - Example : my $dns = xCAT::zvmUtils->getDns($node); - -=cut - -#------------------------------------------------------- -sub getDns { - - # Get inputs - my ( $class, $node ) = @_; - - # Get IP address - my $out = `ssh -o ConnectTimeout=5 $node "cat /etc/resolv.conf" | grep "nameserver"`; - my @parms = split( ' ', $out ); - - return ( $parms[1] ); -} - -#------------------------------------------------------- - -=head3 getGateway - - Description : Get default gateway of given node - Arguments : Node - Returns : Default gateway - Example : my $gw = xCAT::zvmUtils->getGateway($node); - -=cut - -#------------------------------------------------------- -sub getGateway { - - # Get inputs - my ( $class, $node ) = @_; - - # Get IP address - my $out = `ssh -o ConnectTimeout=5 $node "cat /etc/sysconfig/network/routes"`; - my @parms = split( ' ', $out ); - return ( $parms[1] ); -} - -#------------------------------------------------------- - =head3 sendFile Description : Send a file to a given node using SCP @@ -580,8 +510,7 @@ sub getRootDiskAddr { my $devNode = $parms[0]; # Get disk address - $out = - `ssh $node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; + $out = `ssh $node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; @parms = split( " ", $out ); return ( $parms[0] ); } @@ -590,9 +519,9 @@ sub getRootDiskAddr { =head3 disableEnableDisk - Description : Disable/enable a disk for given node + Description : Disable/enable a disk for a given node Arguments : Device address - Option (-d|-e) + Option [-d|-e] Returns : Nothing Example : my $out = xCAT::zvmUtils->disableEnableDisk($callback, $node, $option, $devAddr); @@ -616,7 +545,7 @@ sub disableEnableDisk { =head3 getMdisks - Description : Get MDisk statements in user directory entry + Description : Get the MDisk statements in the user entry of a given node Arguments : Node Returns : MDisk statements Example : my @mdisks = xCAT::zvmUtils->getMdisks($callback, $node); @@ -659,7 +588,7 @@ sub getMdisks { =head3 getUserEntryWODisk - Description : Get user directory entry for given node + Description : Get the user entry of a given node without MDISK statments, and save it to a file Arguments : Node File name to save user entry under @@ -719,7 +648,7 @@ sub getUserEntryWODisk { =head3 appendHostname - Description : Append specified hostname in front of a given string + Description : Append a specified hostname in front of a given string Arguments : Hostname String Returns : String with hostname in front @@ -774,7 +703,7 @@ sub checkOutput { =head3 isAddressUsed - Description : Check if given address is used + Description : Check if given an address is used Arguments : Node Disk address Returns : 0 Address used @@ -800,7 +729,7 @@ sub isAddressUsed { =head3 getMacID - Description : Get MACID from /opt/zhcp/conf/next_macid on HCP + Description : Get the MACID from /opt/zhcp/conf/next_macid on the HCP Arguments : HCP node Returns : MACID Example : my $macId = xCAT::zvmUtils->getMacID($hcp); @@ -848,7 +777,7 @@ sub getMacID { Description : Generate a MACID Arguments : HCP node - Returns : MACID + Returns : Nothing Example : my $macId = xCAT::zvmUtils->generateMacId($hcp); =cut @@ -902,7 +831,7 @@ sub generateMacId { $out = `ssh -o ConnectTimeout=5 $hcp "echo $macId > /opt/zhcp/conf/next_macid"`; } - return $macId; + return; } #------------------------------------------------------- @@ -934,7 +863,7 @@ sub createMacAddr { # Get HCP MAC address # Get the first MAC address found - my $out = `ssh -o ConnectTimeout=5 $hcp "vmcp q nic" | grep "MAC"`; + my $out = `ssh -o ConnectTimeout=5 $hcp "vmcp q nic" | grep "MAC:"`; my @lines = split( "\n", $out ); my @vars = split( " ", $lines[0] ); @@ -964,68 +893,9 @@ sub createMacAddr { #------------------------------------------------------- -=head3 getSn - - Description : Get serial number for given node - Arguments : Node - Returns : Serial number - Example : my $sn = xCAT::zvmUtils->getSn($node); - -=cut - -#------------------------------------------------------- -sub getSn { - - # Get inputs - my ( $class, $node ) = @_; - - # Get node properties from 'zvm' table - my @propNames = ('hcp'); - my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); - - # Get HCP - my $hcp = $propVals->{'hcp'}; - - # Look in /proc/sysinfo to get serial number - my $out = `ssh $hcp "cat /proc/sysinfo" | egrep -i "manufacturer|type|model|sequence code|plant"`; - my @props = split( '\n', $out ); - my $man = $props[0]; - my $type = $props[1]; - my $model = $props[2]; - my $sn = $props[3]; - my $plant = $props[4]; - - # Trim and get property value - # Get manufacturer - @props = split( ':', $man ); - $man = xCAT::zvmUtils->trimStr( $props[1] ); - - # Get machine type - @props = split( ':', $type ); - $type = xCAT::zvmUtils->trimStr( $props[1] ); - - # Get model - @props = split( ': ', $model ); - $model = xCAT::zvmUtils->trimStr( $props[1] ); - @props = split( ' ', $model ); - $model = xCAT::zvmUtils->trimStr( $props[0] ); - - # Get sequence number - @props = split( ':', $sn ); - $sn = xCAT::zvmUtils->trimStr( $props[1] ); - - # Get plant - @props = split( ':', $plant ); - $plant = xCAT::zvmUtils->trimStr( $props[1] ); - - return ("$man-$type-$model-$plant-$sn"); -} - -#------------------------------------------------------- - =head3 getOs - Description : Get operating system name of given node + Description : Get the operating system name of a given node Arguments : Node Returns : Operating system name Example : my $osName = xCAT::zvmUtils->getOs($node); @@ -1048,7 +918,7 @@ sub getOs { =head3 getArch - Description : Get architecture of given node + Description : Get the architecture of a given node Arguments : Node Returns : Architecture of node Example : my $arch = xCAT::zvmUtils->getArch($node); diff --git a/xCAT-server/lib/xcat/plugins/zvm.pm b/xCAT-server/lib/xcat/plugins/zvm.pm index ba38e0f0d..fb1794a4c 100644 --- a/xCAT-server/lib/xcat/plugins/zvm.pm +++ b/xCAT-server/lib/xcat/plugins/zvm.pm @@ -1,2517 +1,3210 @@ -# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html -#------------------------------------------------------- - -=head1 - - xCAT plugin to support z/VM - -=cut - -#------------------------------------------------------- -package xCAT_plugin::zvm; -use xCAT::zvmUtils; -use xCAT::zvmCPUtils; -use xCAT::MsgUtils; -use Sys::Hostname; -use xCAT::Table; -use xCAT::Utils; -use Getopt::Long; -use strict; - -# use warnings; - -# If the following line is not included, you get: -# /opt/xcat/lib/perl/xCAT_plugin/zvm.pm did not return a true value -1; - -#------------------------------------------------------- - -=head3 handled_commands - - Return list of commands handled by this plugin - -=cut - -#------------------------------------------------------- - -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', - }; -} - -#------------------------------------------------------- - -=head3 preprocess_request - - Check and setup for hierarchy - -=cut - -#------------------------------------------------------- -sub preprocess_request { - my $req = shift; - my $callback = shift; - - # Hash array - my %sn; - - # Scalar variable - my $sn; - - # Array - my @requests; - - # 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::Utils->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. Useage: zvm \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 $request = shift; - my $callback = shift; - my $nodes = $request->{node}; - my $command = $request->{command}->[0]; - my $args = $request->{arg}; - my $envs = $request->{env}; - my %rsp; - my $rsp; - my @nodes = @$nodes; - my $host = hostname(); - - # Directory where executables are - $::DIR = "/opt/zhcp/bin"; - - # Process ID for xfork() - my $pid; - - # Child process IDs - my @children; - - # Power on or off a node - if ( $command eq "rpower" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - powerVM( $callback, $_, $args ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Hardware and software inventory - elsif ( $command eq "rinv" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - inventoryVM( $callback, $_, $args ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Create or clone a virtual server - elsif ( $command eq "mkvm" ) { - foreach (@nodes) { - - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - - # Determine if the argument is a node - my $ans = 'FALSE'; - if ( $args->[0] ) { - $ans = xCAT::zvmUtils->isZvmNode( $args->[0] ); - } - - # If it is a node -- then clone given node - if ( $ans eq 'TRUE' ) { - cloneVM( $callback, $_, $args ); - } - - # If it is not a node -- then create node based on directory entry - # Or create a NOLOG if no entry is provided - else { - makeVM( $callback, $_, $args ); - } - - # Exit process - exit(0); - } # End of elsif - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Remove a virtual server - elsif ( $command eq "rmvm" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - removeVM( $callback, $_ ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Print the user directory entry - elsif ( $command eq "lsvm" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - listVM( $callback, $_ ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Change the user directory entry - elsif ( $command eq "chvm" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - changeVM( $callback, $_, $args ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Collect node information from hardware control point - elsif ( $command eq "rscan" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - scanVM( $callback, $_ ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Set the boot state for a node - elsif ( $command eq "nodeset" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - nodeSet( $callback, $_, $args ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Get the MAC address of a node - elsif ( $command eq "getmacs" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - getMacs( $callback, $_, $args ); - - # Exit process - exit(0); - } - else { - - # Ran out of resources - die "Error: Could not fork\n"; - } - - } # End of foreach - } # End of case - - # Boot to network - elsif ( $command eq "rnetboot" ) { - foreach (@nodes) { - $pid = xCAT::Utils->xfork(); - - # Parent process - if ($pid) { - push( @children, $pid ); - } - - # Child process - elsif ( $pid == 0 ) { - netBoot( $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 removeVM - - Description : Remove a virtual server - - This will delete the user entry from user directory - Arguments : Node to remove - Returns : Nothing - Example : removeVM($callback, $node); - -=cut - -#------------------------------------------------------- -sub removeVM { - - # Get inputs - my ( $callback, $node ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Power off userID - my $out = `ssh $hcp "$::DIR/stopvs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - - # Delete user entry - $out = `ssh $hcp "$::DIR/deletevs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - - # Check for errors - my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - return; - } - - # Remove node from 'zvm', 'nodelist', 'nodetype', 'noderes', and 'nodehm' tables - # Save node entry in 'mac' table - xCAT::zvmUtils->delTabEntry( 'zvm', '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 ); - - return; -} - -#------------------------------------------------------- - -=head3 changeVM - - Description : Change a virtual server configuration - Arguments : Node - Option - - Options supported: - add3390 [disk pool] [device address] [cylinders] [mode] [read password] [write password] [multi password] - add9336 [disk pool] [virtual device] [block size] [mode] [blocks] [read password] [write password] [multi password] - addnic [address] [type] [device count] - addprocessor [address] - addvdisk [userID] [device address] [size] - connectnic2guestlan [address] [lan] [owner] - connectnic2vswitch [address] [vswitch] - dedicatedevice [virtual device] [real device] [mode] - deleteipl - formatdisk [disk address] [multi password] - disconnectnic [address] - grantvswitch [VSwitch] - removedisk [virtual device] - removenic [address] - removeprocessor [address] - replacevs [user directory entry] - setipl [ipl target] [load parms] [parms] - setpassword [password] - - Returns : Nothing - Example : changeVM($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub changeVM { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Output string - my $out; - - # add3390 [disk pool] [device address] [cylinders] [mode] [read password] [write password] [multi password] - if ( $args->[0] eq "--add3390" ) { - my $pool = $args->[1]; - my $addr = $args->[2]; - my $cyl = $args->[3]; - my $mode = $args->[4]; - my $readPw = $args->[5]; - my $writePw = $args->[6]; - my $multiPw = $args->[7]; - - $out = `ssh $hcp "$::DIR/add3390 $userId $pool $addr $cyl $mode $readPw $writePw $multiPw"`; - } - -# add9336 [disk pool] [virtual device address] [block size] [blocks] [mode] [read password] [write password] [multi password] - elsif ( $args->[0] eq "--add9336" ) { - my $pool = $args->[1]; - my $addr = $args->[2]; - my $blksize = $args->[3]; - my $blks = $args->[4]; - my $mode = $args->[5]; - my $readPw = $args->[6]; - my $writePw = $args->[7]; - my $multiPw = $args->[8]; - $out = `ssh $hcp "$::DIR/add9336 $userId $pool $addr $blksize $blks $mode $readPw $writePw $multiPw"`; - } - - # addnic [address] [type] [device count] - elsif ( $args->[0] eq "--addnic" ) { - my $addr = $args->[1]; - my $type = $args->[2]; - my $devcount = $args->[3]; - - $out = `ssh $hcp "$::DIR/addnic $userId $addr $type $devcount"`; - } - - # addprocessor [address] - elsif ( $args->[0] eq "--addprocessor" ) { - my $addr = $args->[1]; - - $out = `ssh "$hcp $::DIR/addprocessor $userId $addr"`; - } - - # addvdisk [device address] [size] - elsif ( $args->[0] eq "--addvdisk" ) { - my $addr = $args->[1]; - my $size = $args->[2]; - - $out = `ssh $hcp "$::DIR/addvdisk $userId $addr $size"`; - } - - # connectnic2guestlan [address] [lan] [owner] - elsif ( $args->[0] eq "--connectnic2guestlan" ) { - my $addr = $args->[1]; - my $lan = $args->[2]; - my $owner = $args->[3]; - - $out = `ssh $hcp "$::DIR/connectnic2guestlan $userId $addr $lan $owner"`; - } - - # connectnic2vswitch [address] [vswitch] - elsif ( $args->[0] eq "--connectnic2vswitch" ) { - my $addr = $args->[1]; - my $vswitch = $args->[2]; - - # Connect to VSwitch - $out = `ssh $hcp "$::DIR/connectnic2vswitch $userId $addr $vswitch"`; - - # Grant access to VSWITCH for Linux user - $out .= "$node: Granting access to VSWITCH for $userId\n "; - $out .= `ssh $hcp "vmcp set vswitch $vswitch grant $userId"`; - } - - # dedicatedevice [virtual device] [real device] [mode] - elsif ( $args->[0] eq "--dedicatedevice" ) { - my $vaddr = $args->[1]; - my $raddr = $args->[2]; - my $mode = $args->[3]; - - $out = `ssh $hcp "$::DIR/dedicatedevice $userId $vaddr $raddr $mode"`; - } - - # deleteipl - elsif ( $args->[0] eq "--deleteipl" ) { - $out = `ssh $hcp "$::DIR/deleteipl $userId"`; - } - - # formatdisk [address] [multi password] - elsif ( $args->[0] eq "--formatdisk" ) { - - # Get disk address - my $addr = $args->[1]; - my $lnkAddr = $addr + 1000; - my $multiPw = $args->[2]; - - # Check if new disk address is used - my $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $addr ); - - # If new disk address is used, generate new disk address - while ( $rc == 0 ) { - - # Sleep 2 seconds to let existing disk appear - sleep(2); - $lnkAddr = $lnkAddr + 1; - - # Check again - $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $lnkAddr ); - } - - # Load VMCP module on HCP - xCAT::zvmCPUtils->loadVmcp($hcp); - - # Link target disk - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $userId $addr $lnkAddr MW $multiPw"`; - - # Check for errors - if ( $out =~ m/not linked/i ) { - xCAT::zvmUtils->printLn( $callback, "$node: Linking disk... Failed" ); - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - return; - } - - # Enable disk - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $lnkAddr ); - - # Determine device node - $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$lnkAddr("`; - my @words = split( ' ', $out ); - my $devNode = $words[6]; - - # Format target disk (only ECKD supported) - $out = `ssh $hcp "dasdfmt -b 4096 -y -f /dev/$devNode"`; - - # Check for errors - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$node: Formating disk... Failed" ); - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - - # Disable disk - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $lnkAddr ); - - # Detatch disk - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp det $lnkAddr"`; - - # Check for errors - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$node: Detaching disk... Failed" ); - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - return; - } - - return; - } - else { - xCAT::zvmUtils->printLn( $callback, "$node: Formating disk... Done" ); - } - - # Disable disk - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $lnkAddr ); - - # Detatch disk - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp det $lnkAddr"`; - - # Check for errors - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$node: Detaching disk... Failed" ); - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - return; - } - - $out = ""; - } - - # grantvswitch [VSwitch] - elsif ( $args->[0] eq "--grantvswitch" ) { - my $vsw = $args->[1]; - $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $hcp, $userId, $vsw ); - } - - # disconnectnic [address] - elsif ( $args->[0] eq "--disconnectnic" ) { - my $addr = $args->[1]; - $out = `ssh $hcp "$::DIR/disconnectnic $userId $addr"`; - } - - # removedisk [virtual device address] - elsif ( $args->[0] eq "--removedisk" ) { - my $addr = $args->[1]; - $out = `ssh $hcp "$::DIR/removemdisk $userId $addr"`; - } - - # removenic [address] - elsif ( $args->[0] eq "--removenic" ) { - my $addr = $args->[1]; - $out = `ssh $hcp "$::DIR/removenic $userId $addr"`; - } - - # removeprocessor [address] - elsif ( $args->[0] eq "--removeprocessor" ) { - my $addr = $args->[1]; - $out = `ssh $hcp "$::DIR/removeprocessor $userId $addr"`; - } - - # replacevs [file] - elsif ( $args->[0] eq "--replacevs" ) { - my $file = $args->[1]; - - # Target system (HCP) -- root@gpok2.endicott.ibm.com - my $target = "root@"; - $target .= $hcp; - if ($file) { - - # SCP file over to HCP - $out = `scp $file $target:$file`; - - # Replace user directory entry - $out = `ssh $hcp "$::DIR/replacevs $userId $file"`; - } - else { - $out = "$node: (Error) No user entry file specified"; - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; - } - } - - # setipl [ipl target] [load parms] [parms] - elsif ( $args->[0] eq "--setipl" ) { - my $trgt = $args->[1]; - my $loadparms = $args->[2]; - my $parms = $args->[3]; - $out = `ssh $hcp "$::DIR/setipl $userId $trgt $loadparms $parms"`; - } - - # setpassword [password] - elsif ( $args->[0] eq "--setpassword" ) { - my $pw = $args->[1]; - $out = `ssh $hcp "$::DIR/setpassword $userId $pw"`; - } - - # Otherwise, print out error - else { - $out = "$node: (Error) Option not supported"; - } - - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; -} - -#------------------------------------------------------- - -=head3 powerVM - - Description : Power on or off a virtual server - Arguments : Node - Option [on|off|reset|stat] - Returns : Nothing - Example : powerVM($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub powerVM { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Output string - my $out; - - # Power on virtual server - if ( $args->[0] eq 'on' ) { - $out = `ssh $hcp "$::DIR/startvs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - } - - # Power off virtual server - elsif ( $args->[0] eq 'off' ) { - $out = `ssh $hcp "$::DIR/stopvs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - } - - # Get the status (on|off) - elsif ( $args->[0] eq 'stat' ) { - - # Output is different on SLES 11 - $out = `ssh $hcp "vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userId.*/on/'`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - } - - # Reset a virtual server - elsif ( $args->[0] eq 'reset' ) { - - $out = `ssh $hcp "$::DIR/stopvs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - - # Wait for output - while ( `vmcp q user $userId 2>/dev/null | sed 's/HCPCQU045E.*/Done/'` != "Done" ) { - - # Do nothing - } - - $out = `ssh $hcp "$::DIR/startvs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - } - else { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); - } - return; -} - -#------------------------------------------------------- - -=head3 scanVM - - Description : Get node information from HCP - Arguments : HCP node - Returns : Nothing - Example : scanVM($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub scanVM { - - # Get inputs - my ( $callback, $node ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Print output string - # [Node name]: - # objtype=node - # id=[userID] - # os=[Operating system] - # arch=[Architecture] - # hcp=[HCP node name] - # groups=[Group] - # mgt=zvm - # - # gpok123: - # objtype=node - # id=LINUX123 - # os=SUSE Linux Enterprise Server 10 (s390x) - # arch=s390x - # hcp=gpok456.endicott.ibm.com - # groups=all - # mgt=zvm - - # Output string - my $str = ""; - - # Get nodes managed by this HCP - # Look in 'zvm' table - my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); - my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $hcp . "%'", 'node', 'userid' ); - - my $out; - my $managedNode; - my $id; - my $os; - my $arch; - my $groups; - - # Search for nodes managed by given HCP - # Get 'node' and 'userid' properties - foreach (@entries) { - $managedNode = $_->{'node'}; - - # Get groups - @propNames = ('groups'); - $propVals = xCAT::zvmUtils->getNodeProps( 'nodelist', $managedNode, @propNames ); - $groups = $propVals->{'groups'}; - - # Load VMCP module - xCAT::zvmCPUtils->loadVmcp($managedNode); - - # Get userID - $id = xCAT::zvmCPUtils->getUserId($managedNode); - - # Get operating system - $os = xCAT::zvmUtils->getOs($managedNode); - - # Get architecture - $arch = xCAT::zvmCPUtils->getArch($managedNode); - - # Create output string - $str .= "$managedNode:\n"; - $str .= " objtype=node\n"; - $str .= " userid=$id\n"; - $str .= " os=$os\n"; - $str .= " arch=$arch\n"; - $str .= " hcp=$hcp\n"; - $str .= " groups=$groups\n"; - $str .= " mgt=zvm\n\n"; - } - - xCAT::zvmUtils->printLn( $callback, "$str" ); - return; -} - -#------------------------------------------------------- - -=head3 inventoryVM - - Description : Get virtual server hardware and software inventory - Arguments : Node - Type of inventory (config|all) - Returns : Nothing - Example : inventoryVM($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub inventoryVM { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Output string - my $str = ""; - - # Load VMCP module - xCAT::zvmCPUtils->loadVmcp($node); - - # Get configuration - if ( $args->[0] eq 'config' ) { - - # Get z/VM host for specified node - my $host = xCAT::zvmCPUtils->getHost($node); - - # Get architecture - my $arch = xCAT::zvmUtils->getArch($node); - - # Get operating system - my $os = xCAT::zvmUtils->getOs($node); - - # Get privileges - my $priv = xCAT::zvmCPUtils->getPrivileges($node); - - # Get memory configuration - my $memory = xCAT::zvmCPUtils->getMemory($node); - - # Get processors configuration - my $proc = xCAT::zvmCPUtils->getCpu($node); - - $str .= "z/VM UserID: $userId\n"; - $str .= "z/VM Host: $host\n"; - $str .= "Operating System: $os\n"; - $str .= "Architecture: $arch\n"; - $str .= "HCP: $hcp\n"; - $str .= "Privileges: \n$priv\n"; - $str .= "Total Memory: $memory\n"; - $str .= "Processors: \n$proc\n"; - } - elsif ( $args->[0] eq 'all' ) { - - # Get z/VM host for specified node - my $host = xCAT::zvmCPUtils->getHost($node); - - # Get architecture - my $arch = xCAT::zvmUtils->getArch($node); - - # Get operating system - my $os = xCAT::zvmUtils->getOs($node); - - # Get privileges - my $priv = xCAT::zvmCPUtils->getPrivileges($node); - - # Get memory configuration - my $memory = xCAT::zvmCPUtils->getMemory($node); - - # Get processors configuration - my $proc = xCAT::zvmCPUtils->getCpu($node); - - # Get disks configuration - my $storage = xCAT::zvmCPUtils->getDisks($node); - - # Get NICs configuration - my $nic = xCAT::zvmCPUtils->getNic($node); - - # Create output string - $str .= "z/VM UserID: $userId\n"; - $str .= "z/VM Host: $host\n"; - $str .= "Operating System: $os\n"; - $str .= "Architecture: $arch\n"; - $str .= "HCP: $hcp\n"; - $str .= "Privileges: \n$priv\n"; - $str .= "Total Memory: $memory\n"; - $str .= "Processors: \n$proc\n"; - $str .= "Disks: \n$storage\n"; - $str .= "NICs: \n$nic\n"; - } - else { - $str = "$node: (Error) Option not supported"; - xCAT::zvmUtils->printLn( $callback, "$str" ); - return; - } - - # Append hostname (e.g. gpok3) in front - $str = xCAT::zvmUtils->appendHostname( $node, $str ); - - xCAT::zvmUtils->printLn( $callback, "$str" ); - return; -} - -#------------------------------------------------------- - -=head3 listVM - - Description : Print user directory entry - Arguments : Node - Returns : Nothing - Example : listVM($callback, $node); - -=cut - -#------------------------------------------------------- -sub listVM { - - # Get inputs - my ( $callback, $node ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Get user entry - my $out = `ssh $hcp "$::DIR/getuserentry $userId"`; - xCAT::zvmUtils->printLn( $callback, "$out" ); - - return; -} - -#------------------------------------------------------- - -=head3 makeVM - - Description : Create a virtual server - - This assigns a unique MAC address to the virtual server - Arguments : Node - User entry text file - Returns : Nothing - Example : makeVM($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub makeVM { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Get user entry file (if any) - my $userEntry = $args->[0]; - - my $out; - my $target = "root@" . $hcp; - if ($userEntry) { - - # Grab first NICDEF statement in user entry - $out = `cat $userEntry | grep "NICDEF"`; - my @lines = split( '\n', $out ); - my @words = split( ' ', $lines[0] ); - - # Get LAN name - my $netName; - if ( $words[5] =~ m/SYSTEM/i ) { - $netName = $words[6]; - } - else { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing network name" ); - return; - } - - # Get MAC address in 'mac' table - my $macId; - my $generateNew = 0; - @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); - if ($propVals) { - - # Get MAC suffix (MACID) - $macId = $propVals->{'mac'}; - $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); - $macId = substr( $macId, 6 ); - } - else { - - # If no MACID is found, get one - $macId = xCAT::zvmUtils->getMacID($hcp); - if ( !$macId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not generate MACID" ); - return; - } - - # Set flag to generate new MACID after virtual server is created - $generateNew = 1; - } - - # Append MACID at the end of the NICDEF statement in user entry text file - $out = `sed --in-place -e "s,$netName,$netName MACID $macId,g" $userEntry`; - - # SCP file over to HCP - $out = `scp $userEntry $target:$userEntry`; - - # Create virtual server - $out = `ssh $hcp "$::DIR/createvs $userId $userEntry"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - - # Check output - my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == 0 ) { - - # Get HCP MAC address - xCAT::zvmCPUtils->loadVmcp($hcp); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp q nic" | grep $netName`; - @lines = split( "\n", $out ); - @words = split( " ", $lines[0] ); - - # Extract MAC prefix - my $prefix = $words[1]; - $prefix = xCAT::zvmUtils->replaceStr( $prefix, "-", "" ); - $prefix = substr( $prefix, 0, 6 ); - - # Generate MAC address - my $mac = $prefix . $macId; - - # If length is less than 12, append a zero - if ( length($mac) != 12 ) { - $mac = "0" . $mac; - } - - $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 ); - - # Save MAC address in 'mac' table - xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac ); - - # Generate new MACID - if ( $generateNew == 1 ) { - $mac = xCAT::zvmUtils->generateMacId($hcp); - } - } - } - else { - - # Create NOLOG virtual server - $out = `ssh $hcp "$::DIR/createvs $userId"`; - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - } - - return; -} - -#------------------------------------------------------- - -=head3 cloneVM - - Description : Clone a virtual server - Arguments : Node - Disk pool - Disk multi password - Returns : Nothing - Example : cloneVM($callback, $targetNode, $args); - -=cut - -#------------------------------------------------------- -sub cloneVM { - - # Get inputs - my ( $callback, $tgtNode, $args ) = @_; - - # Return code for each command - my $rc; - - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Cloning" ); - - # Get node properties from 'zvm' table - my @propNames = ( 'hcp', 'userid' ); - my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames ); - - # Get HCP - my $hcp = $propVals->{'hcp'}; - if ( !$hcp ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node HCP" ); - return; - } - - # Get node userID - my $targetUserId = $propVals->{'userid'}; - if ( !$targetUserId ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node ID" ); - return; - } - - # Get source node - my $sourceNode = $args->[0]; - if ( !$sourceNode ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing source node" ); - return; - } - - # Get source node properties from 'zvm' table - @propNames = ( 'hcp', 'userid' ); - $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); - - # Get HCP - my $srcHcp = $propVals->{'hcp'}; - if ( !$srcHcp ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing source node HCP" ); - return; - } - - # Get node userID - my $sourceId = $propVals->{'userid'}; - if ( !$sourceId ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing source node ID" ); - return; - } - - # 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)" ); - return; - } - - # Get target IP from /etc/hosts - my $out = `cat /etc/hosts | grep $tgtNode`; - my @lines = split( '\n', $out ); - my @words = split( ' ', $lines[0] ); - my $targetIp = $words[0]; - if ( !$targetIp ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing IP for $tgtNode in /etc/hosts" ); - return; - } - - # Get disk pool and multi password - my $i; - my %inputs; - foreach $i ( 1 .. 2 ) { - if ( $args->[$i] ) { - - # Split parameters by '=' - @words = split( "=", $args->[$i] ); - - # Create hash array - $inputs{ $words[0] } = $words[1]; - } - } - - # Get disk pool - my $pool = $inputs{"pool"}; - if ( !$pool ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing pool ID" ); - return; - } - - # Get multi password - my $tgtPw = $inputs{"pw"}; - if ( !$tgtPw ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing read/write/multi password" ); - return; - } - - # Get MDisk statements of source node - my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sourceNode ); - - # Save user directory entry as /tmp/hostname.txt - my $userEntry = "/tmp/$tgtNode.txt"; - - # Remove existing user entry if any - $out = `rm $userEntry`; - - # Get user entry of source node - $out = xCAT::zvmUtils->getUserEntryWODisk( $callback, $sourceNode, $userEntry ); - - # Replace source userID with target userID - $out = `sed --in-place -e "s,$sourceId,$targetUserId,g" $userEntry`; - - # Get target MAC address in 'mac' table - my $targetMac; - my $macId; - my $generateNew = 0; # Flag to generate new MACID - @propNames = ('mac'); - $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 ); - } - else { - - # If no MACID is found, get one - $macId = xCAT::zvmUtils->getMacID($hcp); - if ( !$macId ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not generate MACID" ); - return; - } - - # Create MAC address (target) - $targetMac = xCAT::zvmUtils->createMacAddr( $tgtNode, $macId ); - - # Set flag to generate new MACID after virtual server is created - $generateNew = 1; - } - - # Open user entry of source node and find NICDEF - $out = `cat $userEntry | grep "NICDEF"`; - @lines = split( '\n', $out ); - if ( @lines < 1 ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) No NICDEF statement found in user entry" ); - return; - } - - my $foundMacId = 0; - my $srcMacId; - my $netName; - - # Replace MACID (source) - foreach (@lines) { - - # Get LAN name - @words = split( ' ', $_ ); - $netName = $words[6]; - foreach (@words) { - - # If MACID declaration is found, get MACID value - if ( $foundMacId == 1 ) { - $srcMacId = $_; - $foundMacId = 0; - } - - # Find MACID declaration - if ( $_ =~ m/MACID/i ) { - $foundMacId = 1; - } - } # End of foreach (@words) - - # If MACID is found, replace MACID (source) with MACID (target) - if ($srcMacId) { - - $out = `sed --in-place -e "s,$srcMacId,$macId,g" $userEntry`; - last; - } # End of if ($srcMacId) - } # End of foreach (@lines) - - # SCP user entry file over to HCP - xCAT::zvmUtils->sendFile( $hcp, $userEntry, $userEntry ); - - # Create new virtual server - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" ); - $out = `ssh $hcp "$::DIR/createvs $targetUserId $userEntry"`; - - # Exit on bad output - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; - } - - # Save MAC address in 'mac' table - xCAT::zvmUtils->setNodeProp( 'mac', $tgtNode, 'mac', $targetMac ); - - # Generate new MACID - if ( $generateNew == 1 ) { - $out = xCAT::zvmUtils->generateMacId($hcp); - } - - # Load VMCP module on HCP and source node - xCAT::zvmCPUtils->loadVmcp($hcp); - xCAT::zvmCPUtils->loadVmcp($sourceNode); - - # Get VSwitch of master node - my @vswitchId = xCAT::zvmCPUtils->getVswitchId($sourceNode); - - # Grant access to VSwitch for Linux user - # GuestLan do not need permissions - my $netType = xCAT::zvmCPUtils->getNetworkType( $hcp, $netName ); - if ( $netType eq "VSWITCH" ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Granting VSwitch access" ); - foreach (@vswitchId) { - $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $hcp, $targetUserId, $_ ); - - # Check for errors - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - - # Exit on bad output - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; - } - } # End of foreach (@vswitchId) - } # End of if ( $netType eq "VSWITCH" ) - - # Add MDisk to target user directory entry - my @tgtDisks; - my $addr; - my $type; - my $mode; - my $cyl; - my $srcMultiPw; - foreach (@srcDisks) { - - # Get disk address - @words = split( ' ', $_ ); - $addr = $words[1]; - push( @tgtDisks, $addr ); - $type = $words[2]; - $mode = $words[6]; - $srcMultiPw = $words[9]; - - # Add ECKD disk - if ( $type eq '3390' ) { - - # Get disk size (cylinders) - $out = `ssh -o ConnectTimeout=5 $sourceNode "vmcp q v dasd" | grep "DASD $addr"`; - @words = split( ' ', $out ); - $cyl = xCAT::zvmUtils->trimStr( $words[5] ); - - # Add ECKD disk - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" ); - $out = `ssh $hcp "$::DIR/add3390 $targetUserId $pool $addr $cyl $mode $tgtPw $tgtPw $tgtPw"`; - - # Exit on bad output - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; - } - } - - # Add FBA disk - elsif ( $type eq '9336' ) { - - # -- To be supported -- - # Get disk size (blocks) - # Add disk - } - - } - - # Link, format, and copy source disks - my $srcAddr; - my $tgtAddr; - my $srcDevNode; - my $tgtDevNode; - foreach (@tgtDisks) { - - # New disk address - $srcAddr = $_ + 1000; - $tgtAddr = $_ + 2000; - - # Check if new disk address is used (source) - $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $srcAddr ); - - # If disk address is used (source) - while ( $rc == 0 ) { - - # Generate a new disk address - # Sleep 2 seconds to let existing disk appear - sleep(2); - $srcAddr = $srcAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $srcAddr ); - } - - # Check if new disk address is used (target) - $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtAddr ); - - # If disk address is used (target) - while ( $rc == 0 ) { - - # Generate a new disk address - # Sleep 2 seconds to let existing disk appear - sleep(2); - $tgtAddr = $tgtAddr + 1; - $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtAddr ); - } - - # Link source disk to HCP - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking source disk ($_)" ); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $sourceId $_ $srcAddr RR $srcMultiPw"`; - - # If source disk is not linked - my $try = 0; - while ( ( $out =~ m/not linked/i ) && $try < 20 ) { - - # Try to link again - # CP directory not yet updated - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to link source disk ($_)" ); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $sourceId $_ $srcAddr RR $srcMultiPw"`; - $try = $try + 1; - sleep(10); - } - - # If source disk is not linked - if ( $out =~ m/not linked/i ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link source disk ($_)" ); - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); - - # Exit - return; - } - - # Link target disk to HCP - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($_)" ); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $_ $tgtAddr MR $tgtPw"`; - - # If target disk is not linked and not mounted - if ( ( $out =~ m/not linked/i ) && ( $out =~ m/not mounted/i ) ) { - xCAT::zvmUtils->printLn( $callback, - "$tgtNode: (Error) Failed to link target disk ($_). Disk is not mounted to system" ); - - # Go through each line - # Sample: HCPLNM108E LINUX5 0101 not linked; volid DM615B not mounted - my $line; - my $volid; - @lines = split( ';', $out ); - foreach $line (@lines) { - if ( $line =~ m/volid/i ) { - - # Get VOLID - @words = split( ' ', $line ); - if ( $words[0] eq "volid" ) { - $volid = $words[1]; - - # Mount target disk - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Attaching VOLID ($volid) to SYSTEM" ); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp attach volid $volid to SYSTEM"`; - - # Check return - if ( $out =~ m/ATTACHED TO SYSTEM/i ) { - - # Link target disk - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again to link target disk ($_)" ); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $_ $tgtAddr MW $tgtPw"`; - - # Exit loop - last; - } - else { - xCAT::zvmUtils->printLn( $callback, - "$tgtNode: (Error) Failed to mount target disk ($volid)" ); - return; - } - } - } # End of if ( $line =~ m/volid/i ) - } # End of foreach $line (@lines) - } # End of if ( ( $out =~ m/not linked/i ) && ( $out =~ m/not mounted/i ) ) - - # If target disk is not linked - my $try = 0; - while ( ( $out =~ m/not linked/i ) && $try < 20 ) { - - # Try to link again - # CP directory not yet updated - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to link target disk ($_)" ); - $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $_ $tgtAddr MW $tgtPw"`; - $try = $try + 1; - sleep(10); - } - - # 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" ); - - # Detatch source disk from HCP - $out = `ssh $hcp "vmcp det $srcAddr"`; - - # Exit - return; - } - - # Use FLASHCOPY - xCAT::zvmUtils->printLn( $callback, - "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr) using FLASHCOPY" ); - $out = xCAT::zvmCPUtils->flashCopy( $hcp, $srcAddr, $tgtAddr ); - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - - # Use Linux DD - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not supported. Using Linux DD" ); - - # Enable source disk - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $srcAddr ); - - # Enable target disk - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtAddr ); - - # Determine source device node - $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$srcAddr("`; - @words = split( ' ', $out ); - $srcDevNode = $words[6]; - - # Determine target device node - $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtAddr("`; - @words = split( ' ', $out ); - $tgtDevNode = $words[6]; - - # Format target disk - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" ); - $out = `ssh $hcp "dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; - - # Check for errors - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$out" ); - 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 $hcp "dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096"`; - - # Check for error - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; - } - - # Sleep 2 seconds to let the system settle - sleep(2); - } - - # Disable and enable target disk - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtAddr ); - - # Determine target device node (it might have changed) - $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtAddr("`; - @words = split( ' ', $out ); - $tgtDevNode = $words[6]; - - # Get disk address that is the root partition (/) - my $rootPartitionAddr = xCAT::zvmUtils->getRootDiskAddr($sourceNode); - if ( $_ eq $rootPartitionAddr ) { - - # Set network configuration - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Setting network configuration" ); - - # Mount target disk - my $cloneMntPt = "/mnt/$targetUserId"; - $tgtDevNode .= "1"; - $out = `ssh $hcp "mkdir $cloneMntPt"`; - $out = `ssh $hcp "mount /dev/$tgtDevNode $cloneMntPt"`; - - # Set hostname - $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" $cloneMntPt/etc/HOSTNAME`; - - # If Red Hat -- Set hostname in /etc/sysconfig/network - my $os = xCAT::zvmUtils->getOs($sourceNode); - if ( $os =~ m/Red Hat/i ) { - $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" $cloneMntPt/etc/sysconfig/network`; - } - - # Set IP address - my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); - - # Get network configuration file - # Location of this file depends on the OS - my $ifcfg = xCAT::zvmUtils->getIfcfg($sourceNode); - my $ifcfgPath = $cloneMntPt; - $ifcfgPath .= $ifcfg; - $out = -`ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" \ -e "s/$sourceIp/$targetIp/g" $cloneMntPt/etc/hosts`; - $out = `ssh $hcp sed --in-place -e "s/$sourceIp/$targetIp/g" \ -e "s/$sourceNode/$tgtNode/g" $ifcfgPath`; - - # Set MAC address (If necessary) - # Remove LLADDR and UNIQUE parameters and append with correct values - $out = `ssh $hcp "cat $ifcfgPath" | grep -v "LLADDR" | grep -v "UNIQUE" > /tmp/network_config`; - $out = `echo "LLADDR='$targetMac'" >> /tmp/network_config`; - $out = `echo "UNIQUE=''" >> /tmp/network_config`; - xCAT::zvmUtils->sendFile( $hcp, "/tmp/network_config", $ifcfgPath ); - - # Set to hardware configuration -- Only for layer 2 - my $layer = xCAT::zvmCPUtils->getNetworkLayer($sourceNode); - if ( $layer == 2 ) { - if ( $os =~ m/Red Hat/i ) { - my $srcMac; - - # Get source MAC address in 'mac' table - @propNames = ('mac'); - $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $sourceNode, @propNames ); - if ($propVals) { - - # Get MAC address - $srcMac = $propVals->{'mac'}; - } - else { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Could not find MAC address of $sourceNode" ); - - # Unmount disk - $out = `ssh $hcp "umount $cloneMntPt"`; - - # Disable disks - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $srcAddr ); - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); - - # Detatch disks from HCP - $out = `ssh $hcp "vmcp det $srcAddr"`; - $out = `ssh $hcp "vmcp det $tgtAddr"`; - - return; - } - - # Set MAC address - $out = `ssh $hcp sed --in-place -e "s/$srcMac/$targetMac/g" $ifcfgPath`; - } - else { - - # Get hardware configuration - my $hwcfg = xCAT::zvmUtils->getHwcfg($sourceNode); - my $hwcfgPath = $cloneMntPt; - - # Set layer 2 support - $hwcfgPath .= $hwcfg; - $out = `ssh $hcp "cat $hwcfgPath" | grep -v "QETH_LAYER2_SUPPORT" > /tmp/hardware_config`; - $out = `echo "QETH_LAYER2_SUPPORT='1'" >> /tmp/hardware_config`; - xCAT::zvmUtils->sendFile( $hcp, "/tmp/hardware_config", $hwcfgPath ); - } - } # End of if ( $layer == 2 ) - - # Flush disk - $out = `ssh $hcp "sync"`; - - # Unmount disk - $out = `ssh $hcp "umount $cloneMntPt"`; - } - - # Disable disks - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $srcAddr ); - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); - - # Detatch disks from HCP - $out = `ssh $hcp "vmcp det $srcAddr"`; - $out = `ssh $hcp "vmcp det $tgtAddr"`; - - sleep(5); - } # End of foreach (@tgtDisks) - - # Add node to DHCP - $out = `makedhcp -a`; - - # Power on target virtual server - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Powering on" ); - $out = `ssh $hcp "$::DIR/startvs $targetUserId"`; - - # Check for error - $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$out" ); - return; - } - - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Done" ); - return; -} - -#------------------------------------------------------- - -=head3 nodeSet - - Description : Set the boot state for a node - - Punch initrd, kernel, and parmfile to node reader - - Layer 2 and 3 VSwitch/Lan supported - - Uses 1st NICDEF in the user entry - Arguments : Node - Returns : Nothing - Example : nodeSet($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub nodeSet { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get action - my $action = $args->[0]; - if ( !( $action eq "install" ) ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); - return; - } - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Get node root password - @propNames = ('password'); - $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" ); - return; - } - - # Get Linux distribution - @propNames = ('current_osimage'); - $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames ); - my $distr = $propVals->{'current_osimage'}; - if ( !$distr ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system to be deployed on this node" ); - return; - } - - # Get autoyast/kickstart template - @propNames = ('profile'); - $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $distr, @propNames ); - my $profile = $propVals->{'profile'}; - if ( !$distr ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile for this node" ); - return; - } - - # Get host IP and hostname from /etc/hosts - my $out = `cat /etc/hosts | grep $node`; - my @words = split( ' ', $out ); - my $hostIP = $words[0]; - my $hostname = $words[2]; - if ( !$hostIP || !$hostname ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); - return; - } - - # Get NIC address from user entry - $out = `ssh $hcp "$::DIR/getuserentry $userId" | grep "NICDEF"`; - if ( !$out ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing NICDEF statement in user entry of node" ); - return; - } - - # Grab first NICDEF address - my @lines = split( '\n', $out ); - @words = split( ' ', $lines[0] ); - my $readChannel = "0.0.0" . ( $words[1] + 0 ); - my $writeChannel = "0.0.0" . ( $words[1] + 1 ); - my $dataChannel = "0.0.0" . ( $words[1] + 2 ); - - # Get network type (Layer 2 or 3) - my $netName = $words[6]; - $out = `ssh $hcp "vmcp q lan $netName"`; - if ( !$out ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing NICDEF statement in user entry of node" ); - return; - } - - # Go through each line - my $layer = 3; # Default to layer 3 - @lines = split( '\n', $out ); - foreach (@lines) { - - # If the line contains ETHERNET, then it is a layer 2 network - if ( $_ =~ m/ETHERNET/i ) { - $layer = 2; - } - } - - # Get MAC address -- Only for layer 2 - my $mac = ""; - my @propNames; - my $propVals; - if ( $layer == 2 ) { - - # Search 'mac' table for node - @propNames = ('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 - if ( !$mac ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing MAC address of node" ); - return; - } - } - - # Get domain from site table - my $siteTab = xCAT::Table->new('site'); - my $domainHash = $siteTab->getAttribs( { key => "domain" }, 'value' ); - my $domain = $domainHash->{'value'}; - - # Get first 3 octets of node IP (IPv4) - @words = split( /\./, $hostIP ); - my $octets = "$words[0].$words[1].$words[2]"; - - # Get networks in 'networks' table - my $entries = xCAT::zvmUtils->getAllTabEntries('networks'); - - # Go through each network - my $network; - foreach (@$entries) { - - # Get network - $network = $_->{'net'}; - - # If networks contains the first 3 octets of the node IP - if ( $network =~ m/$octets/i ) { - - # Exit loop - last; - } - else { - $network = ""; - } - } - - # If no network found - if ( !$network ) { - - # Exit - xCAT::zvmUtils->printLn( $callback, - "$node: (Error) Node does not belong to any networks in the networks table" ); - return; - } - - @propNames = ( 'mask', 'gateway', 'tftpserver', 'nameservers' ); - $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); - my $mask = $propVals->{'mask'}; - my $gateway = $propVals->{'gateway'}; - my $ftp = $propVals->{'tftpserver'}; - my $nameserver = $propVals->{'nameservers'}; - if ( !$network || !$mask || !$ftp || !$nameserver ) { - - # It is acceptable to not have a gateway - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing network information" ); - return; - } - - # Get broadcast address of NIC - my $ifcfg = xCAT::zvmUtils->getIfcfgByNic( $hcp, $readChannel ); - $out = `cat $ifcfg | grep "BROADCAST"`; - @words = split( '=', $out ); - my $broadcast = $words[1]; - $broadcast = xCAT::zvmUtils->trimStr($broadcast); - $broadcast = xCAT::zvmUtils->replaceStr( $broadcast, "'", "" ); - - # Load VMCP module on HCP - xCAT::zvmCPUtils->loadVmcp($hcp); - - # Sample paramter file exists in installation CD -- Use that as a guide - my $sampleParm; - my $parmHeader; - my $parms; - my $parmFile; - - # If punch is successful -- Look for this string - my $searchStr = "created and transferred"; - - # Default parameters -- SLES - 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) - - # Default parameters -- RHEL - my $netType = "qeth"; - my $portName = "FOOBAR"; - my $portNo = "0"; - - # SUSE installation - my $template; - if ( $distr =~ m/sles/i ) { - - # Create directory in FTP root (/install) to hold template - $out = `mkdir -p /install/custom/install/sles`; - - # Copy autoyast template - $template = "/install/custom/install/sles/$profile"; - $out = `cp /opt/xcat/share/xcat/install/sles/$profile $template`; - - # Edit template - my $device = "qeth-bus-ccw-$readChannel"; - my $chanIds = "$readChannel $writeChannel $dataChannel"; - - $out = -`sed --in-place -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" $template`; - - # Read sample parmfile in /install/sles10.2/s390x/1/boot/s390x/ - $sampleParm = "/install/$distr/s390x/1/boot/s390x/parmfile"; - 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 ) { - $parmHeader = xCAT::zvmUtils->trimStr($_); - } - } - - # Close sample parmfile - close(SAMPLEPARM); - - # Create parmfile -- Limited to 10 lines - # End result should be: - # ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb - # HostIP=10.0.0.5 Hostname=gpok5.endicott.ibm.com - # Gateway=10.0.0.1 Netmask=255.255.255.0 - # Broadcast=10.0.0.0 Layer2=0 - # ReadChannel=0.0.0800 WriteChannel=0.0.0801 DataChannel=0.0.0802 - # Nameserver=9.0.2.11 Portname=OSAPORT - # Install=ftp://10.0.0.1/sles10.2/s390x/1/ - # UseVNC=1 VNCPassword=123456 - # InstNetDev=osa OsaInterface=qdio OsaMedium=eth Manual=0 - my $ay = "ftp://$ftp/custom/install/sles/$profile"; - - $parms = $parmHeader . "\n"; - $parms = $parms . "AutoYaST=$ay\n"; - $parms = $parms . "HostIP=$hostIP Hostname=$hostname\n"; - $parms = $parms . "Gateway=$gateway Netmask=$mask\n"; - - # Set layer in autoyast profile - if ( $layer == 2 ) { - $parms = $parms . "Broadcast=$network Layer2=1 OSAHWaddr=$mac\n"; - } - else { - $parms = $parms . "Broadcast=$network Layer2=0\n"; - } - - $parms = $parms . "ReadChannel=$readChannel WriteChannel=$writeChannel DataChannel=$dataChannel\n"; - $parms = $parms . "Nameserver=$nameserver Portname=$portName\n"; - $parms = $parms . "Install=ftp://$ftp/$distr/s390x/1/\n"; - $parms = $parms . "UseVNC=1 VNCPassword=123456\n"; - $parms = $parms . "InstNetDev=$instNetDev OsaInterface=$osaInterface OsaMedium=$osaMedium Manual=0\n"; - - # Write to parmfile - $parmFile = "/tmp/parm"; - open( PARMFILE, ">$parmFile" ); - print PARMFILE "$parms"; - close(PARMFILE); - - # Send kernel, parmfile, and initrd to reader to HCP - $out = `cp /install/$distr/s390x/1/boot/s390x/vmrdr.ikr /tmp/kernel`; - $out = `cp /install/$distr/s390x/1/boot/s390x/initrd /tmp/initrd`; - xCAT::zvmUtils->sendFile( $hcp, "/tmp/kernel", "/tmp/kernel" ); - xCAT::zvmUtils->sendFile( $hcp, "/tmp/parm", "/tmp/parm" ); - xCAT::zvmUtils->sendFile( $hcp, "/tmp/initrd", "/tmp/initrd" ); - - # Set the virtual unit record devices online on HCP - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "c" ); - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "d" ); - - # Purge reader - $out = xCAT::zvmCPUtils->purgeReader( $hcp, $userId ); - xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); - - # Punch kernel to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, "/tmp/kernel", "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( $hcp, $userId, "/tmp/parm", "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( $hcp, $userId, "/tmp/initrd", "sles.initrd", "" ); - xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); - if ( $out =~ m/Failed/i ) { - return; - } - } - - # RHEL installation - elsif ( $distr =~ m/rhel/i ) { - - # Create directory in FTP root (/install) to hold template - $out = `mkdir -p /install/custom/install/rh`; - - # Copy kickstart template - $template = "/install/custom/install/rh/$profile"; - $out = `cp /opt/xcat/share/xcat/install/rh/$profile $template`; - - # Edit template - my $url = "ftp://$ftp/$distr/s390x/"; - $out = -`sed --in-place -e "s,replace_url,$url,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" $template`; - - # Read sample parmfile in /install/rhel5.3/s390x/images - $sampleParm = "/install/$distr/s390x/images/generic.prm"; - 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 ) { - $parmHeader = xCAT::zvmUtils->trimStr($_); - } - } - - # Close sample parmfile - close(SAMPLEPARM); - - # Get mdisk address - my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $node ); - my $dasd = ""; - my $i = 0; - foreach (@mdisks) { - $i = $i + 1; - @words = split( ' ', $_ ); - - # Do not put a comma at the end of the last disk address - if ( $i == @mdisks ) { - $dasd = $dasd . "0.0.$words[1]"; - } - else { - $dasd = $dasd . "0.0.$words[1],"; - } - } - - # Create parmfile -- Limited to 80 characters/line, maximum of 11 lines - # End result should be: - # ramdisk_size=40000 root=/dev/ram0 ro ip=off - # ks=ftp://10.0.0.1/rhel5.3/s390x/compute.rhel5.s390x.tmpl - # RUNKS=1 cmdline - # DASD=0.0.0100 HOSTNAME=gpok4.endicott.ibm.com - # NETTYPE=qeth IPADDR=10.0.0.4 - # SUBCHANNELS=0.0.0800,0.0.0801,0.0.0800 - # NETWORK=10.0.0.0 NETMASK=255.255.255.0 - # SEARCHDNS=endicott.ibm.com BROADCAST=10.0.0.255 - # GATEWAY=10.0.0.1 DNS=9.0.2.11 MTU=1500 - # PORTNAME=UNASSIGNED PORTNO=0 LAYER2=0 - # vnc vncpassword=123456 - my $ks = "ftp://$ftp/custom/install/rh/$profile"; - - $parms = $parmHeader . "\n"; - $parms = $parms . "ks=$ks\n"; - $parms = $parms . "RUNKS=1 cmdline\n"; - $parms = $parms . "DASD=$dasd HOSTNAME=$hostname\n"; - $parms = $parms . "NETTYPE=$netType IPADDR=$hostIP\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 ) { - $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=1 MACADDR=$mac\n"; - } - else { - $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=0\n"; - } - - $parms = $parms . "vnc vncpassword=123456\n"; - - # Write to parmfile - $parmFile = "/tmp/parm"; - open( PARMFILE, ">$parmFile" ); - print PARMFILE "$parms"; - close(PARMFILE); - - # Send kernel, parmfile, conf, and initrd to reader to HCP - $out = `cp /install/$distr/s390x/images/kernel.img /tmp/kernel`; - $out = `cp /install/$distr/s390x/images/initrd.img /tmp/initrd`; - xCAT::zvmUtils->sendFile( $hcp, "/tmp/kernel", "/tmp/kernel" ); - xCAT::zvmUtils->sendFile( $hcp, "/tmp/parm", "/tmp/parm" ); - xCAT::zvmUtils->sendFile( $hcp, "/tmp/initrd", "/tmp/initrd" ); - - # Set the virtual unit record devices online - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "c" ); - $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "d" ); - - # Purge reader - $out = xCAT::zvmCPUtils->purgeReader( $hcp, $userId ); - xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); - - # Punch kernel to reader on HCP - $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, "/tmp/kernel", "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( $hcp, $userId, "/tmp/parm", "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( $hcp, $userId, "/tmp/initrd", "rhel.initrd", "" ); - xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); - if ( $out =~ m/Failed/i ) { - return; - } - } - - return; -} - -#------------------------------------------------------- - -=head3 getMacs - - Description : Get the MAC address of a given node - - Requires the node be online - - Saves MAC address in 'mac' table - Arguments : Node - Returns : Nothing - Example : getMacs($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub getMacs { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Get MAC address in 'mac' table - my @propNames = ('mac'); - my $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); - my $mac; - if ($propVals) { - - # Get MAC address - $mac = $propVals->{'mac'}; - xCAT::zvmUtils->printLn( $callback, "$node: $mac" ); - return; - } - - # If MAC address is not in the 'mac' table, get it using VMCP - xCAT::zvmCPUtils->loadVmcp($node); - - # Get xCat MN Lan/VSwitch name - my $out = `vmcp q v nic | egrep -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++ ) { - - # Extract VSwitch name - 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] ); - $searchStr = $searchStr . "$words[4]"; - } - - 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 $node "vmcp q v nic" | egrep -i "$searchStr"`; - if ( !$out ) { - xCAT::zvmUtils->printLn( $callback, "$node: Failed to find MAC address" ); - return; - } - - @lines = split( '\n', $out ); - @words = split( ' ', $lines[0] ); - my $mac = $words[1]; - - # Replace - with : - $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 ); - - return; -} - -#------------------------------------------------------- - -=head3 rNetBoot - - Description : Boot to network - Arguments : Node - Returns : Nothing - Example : netBoot($callback, $node, $args); - -=cut - -#------------------------------------------------------- -sub netBoot { - - # Get inputs - my ( $callback, $node, $args ) = @_; - - # Get node properties from 'zvm' table - 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" ); - return; - } - - # Get node userID - my $userId = $propVals->{'userid'}; - if ( !$userId ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); - return; - } - - # Get IPL - my @ipl = split( '=', $args->[0] ); - if ( !( $ipl[0] eq "ipl" ) ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IPL" ); - return; - } - - # Boot node - my $out = `ssh $hcp "$::DIR/startvs $userId"`; - my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); - if ( $rc == -1 ) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Boot failed" ); - return; - } - else { - xCAT::zvmUtils->printLn( $callback, "$node: $out" ); - } - - # IPL when virtual server is online - sleep(5); - $out = xCAT::zvmCPUtils->sendCPCmd( $hcp, $userId, "IPL $ipl[1]" ); - xCAT::zvmUtils->printLn( $callback, "$node: Booting from $ipl[1]... Done" ); - - return; -} +# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + xCAT plugin to support z/VM + +=cut + +#------------------------------------------------------- +package xCAT_plugin::zvm; +use xCAT::Client; +use xCAT::zvmUtils; +use xCAT::zvmCPUtils; +use xCAT::MsgUtils; +use Sys::Hostname; +use xCAT::Table; +use xCAT::Utils; +use Getopt::Long; +use strict; + +# If the following line is not included, you get: +# /opt/xcat/lib/perl/xCAT_plugin/zvm.pm did not return a true value +1; + +#------------------------------------------------------- + +=head3 handled_commands + + Return list of commands handled by this plugin + +=cut + +#------------------------------------------------------- +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', + updatenode => 'nodehm:mgt', + }; +} + +#------------------------------------------------------- + +=head3 preprocess_request + + Check and setup for hierarchy + +=cut + +#------------------------------------------------------- +sub preprocess_request { + my $req = shift; + my $callback = shift; + + # Hash array + my %sn; + + # Scalar variable + my $sn; + + # Array + my @requests; + + # 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::Utils->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. Useage: zvm \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 $request = shift; + my $callback = shift; + my $nodes = $request->{node}; + my $command = $request->{command}->[0]; + my $args = $request->{arg}; + my $envs = $request->{env}; + my %rsp; + my $rsp; + my @nodes = @$nodes; + my $host = hostname(); + + # Directory where executables are + $::DIR = "/opt/zhcp/bin"; + + # Process ID for xfork() + my $pid; + + # Child process IDs + my @children; + + # --- Power on or off a node --- + if ( $command eq "rpower" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + powerVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Hardware and software inventory --- + elsif ( $command eq "rinv" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + inventoryVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # ---- Create a virtual server ---- + elsif ( $command eq "mkvm" ) { + + # Determine if the argument is a node + my $clone = 'FALSE'; + if ( $args->[0] ) { + $clone = xCAT::zvmUtils->isZvmNode( $args->[0] ); + } + + # --- Clone virtual server --- + if ( $clone eq 'TRUE' ) { + cloneVM( $callback, \@nodes, $args ); + } + + # --- Create user entry --- + # Create node based on directory entry + # or create a NOLOG if no entry is provided + else { + foreach (@nodes) { + + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + + makeVM( $callback, $_, $args ); + + # Exit process + exit(0); + } # End of elsif + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of else + } # End of case + + # --- Remove a virtual server --- + elsif ( $command eq "rmvm" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + removeVM( $callback, $_ ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Print the user entry --- + elsif ( $command eq "lsvm" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + listVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Change the user entry --- + elsif ( $command eq "chvm" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + changeVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Collect node information from hardware control point --- + elsif ( $command eq "rscan" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + scanVM( $callback, $_ ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Set the boot state for a node --- + elsif ( $command eq "nodeset" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + nodeSet( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Get the MAC address of a node --- + elsif ( $command eq "getmacs" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + getMacs( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Boot from network --- + elsif ( $command eq "rnetboot" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + netBoot( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # --- Update the node --- + elsif ( $command eq "updatenode" ) { + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + updateNode( $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 removeVM + + Description : Delete the user entry from user directory + Arguments : Node to remove + Returns : Nothing + Example : removeVM($callback, $node); + +=cut + +#------------------------------------------------------- +sub removeVM { + + # Get inputs + my ( $callback, $node ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Power off userID + my $out = `ssh $hcp "$::DIR/stopvs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Delete user entry + $out = `ssh $hcp "$::DIR/deletevs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Check for errors + my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == -1 ) { + return; + } + + # Remove node from 'zvm', 'nodelist', 'nodetype', 'noderes', and 'nodehm' tables + # Save node entry in 'mac' table + xCAT::zvmUtils->delTabEntry( 'zvm', '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 ); + + return; +} + +#------------------------------------------------------- + +=head3 changeVM + + Description : Change a virtual server configuration + Arguments : Node + Option + + Options supported: + add3390 [disk pool] [device address] [cylinders] [mode] [read password] [write password] [multi password] + add3390active [device address] [mode] + add9336 [disk pool] [virtual device] [block size] [mode] [blocks] [read password] [write password] [multi password] + addnic [address] [type] [device count] + addprocessor [address] + addvdisk [userID] [device address] [size] + connectnic2guestlan [address] [lan] [owner] + connectnic2vswitch [address] [vswitch] + copydisk [target address] [source node] [source address] + dedicatedevice [virtual device] [real device] [mode] + deleteipl + formatdisk [disk address] [multi password] + disconnectnic [address] + grantvswitch [VSwitch] + removedisk [virtual device] + removenic [address] + removeprocessor [address] + replacevs [user directory entry] + setipl [ipl target] [load parms] [parms] + setpassword [password] + + Returns : Nothing + Example : changeVM($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub changeVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Output string + my $out; + + # add3390 [disk pool] [device address] [cylinders] [mode] [read password] [write password] [multi password] + if ( $args->[0] eq "--add3390" ) { + my $pool = $args->[1]; + my $addr = $args->[2]; + my $cyl = $args->[3]; + my $mode = $args->[4]; + my $readPw = $args->[5]; + my $writePw = $args->[6]; + my $multiPw = $args->[7]; + + $out = `ssh $hcp "$::DIR/add3390 $userId $pool $addr $cyl $mode $readPw $writePw $multiPw"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # add3390active [device address] [mode] + elsif ( $args->[0] eq "--add3390active" ) { + my $addr = $args->[1]; + my $mode = $args->[2]; + + $out = `ssh $hcp "$::DIR/add3390active $userId $addr $mode"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # add9336 [disk pool] [virtual device address] [block size] [blocks] [mode] [read password] [write password] [multi password] + elsif ( $args->[0] eq "--add9336" ) { + my $pool = $args->[1]; + my $addr = $args->[2]; + my $blksize = $args->[3]; + my $blks = $args->[4]; + my $mode = $args->[5]; + my $readPw = $args->[6]; + my $writePw = $args->[7]; + my $multiPw = $args->[8]; + + $out = `ssh $hcp "$::DIR/add9336 $userId $pool $addr $blksize $blks $mode $readPw $writePw $multiPw"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # addnic [address] [type] [device count] + elsif ( $args->[0] eq "--addnic" ) { + my $addr = $args->[1]; + my $type = $args->[2]; + my $devcount = $args->[3]; + + $out = `ssh $hcp "$::DIR/addnic $userId $addr $type $devcount"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # addprocessor [type] address] + elsif ( $args->[0] eq "--addprocessor" ) { + my $type = $args->[1]; + my $addr = $args->[2]; + + $out = `ssh $hcp "$::DIR/addprocessor $userId $type $addr"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # addprocessoractive [address] [type] + elsif ( $args->[0] eq "--addprocessoractive" ) { + my $addr = $args->[1]; + my $type = $args->[2]; + + $out = xCAT::zvmCPUtils->defineCpu( $node, $addr, $type ); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # addvdisk [device address] [size] + elsif ( $args->[0] eq "--addvdisk" ) { + my $addr = $args->[1]; + my $size = $args->[2]; + + $out = `ssh $hcp "$::DIR/addvdisk $userId $addr $size"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # connectnic2guestlan [address] [lan] [owner] + elsif ( $args->[0] eq "--connectnic2guestlan" ) { + my $addr = $args->[1]; + my $lan = $args->[2]; + my $owner = $args->[3]; + + $out = `ssh $hcp "$::DIR/connectnic2guestlan $userId $addr $lan $owner"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # connectnic2vswitch [address] [vswitch] + elsif ( $args->[0] eq "--connectnic2vswitch" ) { + my $addr = $args->[1]; + my $vswitch = $args->[2]; + + # Connect to VSwitch + $out = `ssh $hcp "$::DIR/connectnic2vswitch $userId $addr $vswitch"`; + + # Grant access to VSWITCH for Linux user + $out .= "Granting access to VSWITCH for $userId\n "; + $out .= `ssh $hcp "vmcp set vswitch $vswitch grant $userId"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # copydisk [target address] [source node] [source address] + elsif ( $args->[0] eq "--copydisk" ) { + my $tgtNode = $node; + my $targetUserId = $userId; + my $tgtAddr = $args->[1]; + my $srcNode = $args->[2]; + my $srcAddr = $args->[3]; + + # Get source userID + @propNames = ( 'hcp', 'userid' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $srcNode, @propNames ); + my $sourceId = $propVals->{'userid'}; + + # --- Link and copy disk --- + my $rc; + my $try; + my $srcDevNode; + my $tgtDevNode; + + # Link target disk to HCP + my $tgtLinkAddr; + $try = 10; + while ( $try > 0 ) { + + # New disk address + $tgtLinkAddr = $tgtAddr + 1000; + + # Check if new disk address is used (target) + $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtLinkAddr ); + + # If disk address is used (target) + 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( $hcp, $tgtLinkAddr ); + } + + # Link target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" ); + $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $tgtAddr $tgtLinkAddr MR"`; + + # If link fails + if ( $out =~ m/not linked/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 ($tgtAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); + + # Exit + return; + } + + # Link source disk to HCP + my $srcLinkAddr; + $try = 10; + while ( $try > 0 ) { + + # New disk address + $srcLinkAddr = $srcAddr + 2000; + + # Check if new disk address is used (source) + $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $srcLinkAddr ); + + # If disk address is used (source) + 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( $hcp, $srcLinkAddr ); + } + + # Link source disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking source disk ($srcAddr) as ($srcLinkAddr)" ); + $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $sourceId $srcAddr $srcLinkAddr MR"`; + + # If link fails + if ( $out =~ m/not linked/i ) { + + # Wait before trying again + sleep(5); + + $try = $try - 1; + } else { + last; + } + } # 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" ); + + # Exit + return; + } + + # --- Use FLASHCOPY --- + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcLinkAddr) to target disk ($tgtLinkAddr) using FLASHCOPY" ); + $out = xCAT::zvmCPUtils->flashCopy( $hcp, $srcLinkAddr, $tgtLinkAddr ); + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + + # --- Use Linux DD --- + my @words; + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not supported. Using Linux DD" ); + + # Enable disks + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtLinkAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $srcLinkAddr ); + + # Determine source device node + $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$srcLinkAddr("`; + @words = split( ' ', $out ); + $srcDevNode = $words[6]; + + # Determine target device node + $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtLinkAddr("`; + @words = split( ' ', $out ); + $tgtDevNode = $words[6]; + + # Format target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" ); + $out = `ssh $hcp "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" ); + 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 ($srcDevNode) to target disk ($tgtDevNode)" ); + $out = `ssh $hcp "dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096"`; + + # Check for error + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtLinkAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $srcLinkAddr ); + + # Detatch disks from HCP + $out = `ssh $hcp "vmcp det $tgtLinkAddr"`; + $out = `ssh $hcp "vmcp det $srcLinkAddr"`; + + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + } + + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtLinkAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $srcLinkAddr ); + + # Detatch disks from HCP + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching source disk ($srcLinkAddr)" ); + $out = `ssh $hcp "vmcp det $tgtLinkAddr"`; + $out = `ssh $hcp "vmcp det $srcLinkAddr"`; + + $out = "$tgtNode: Done"; + } + + # dedicatedevice [virtual device] [real device] [mode] + elsif ( $args->[0] eq "--dedicatedevice" ) { + my $vaddr = $args->[1]; + my $raddr = $args->[2]; + my $mode = $args->[3]; + + $out = `ssh $hcp "$::DIR/dedicatedevice $userId $vaddr $raddr $mode"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # deleteipl + elsif ( $args->[0] eq "--deleteipl" ) { + $out = `ssh $hcp "$::DIR/deleteipl $userId"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # exchangesshkeys [password] + elsif ( $args->[0] eq "--exchangesshkeys" ) { + + # Get password + my $password = $args->[1]; + + # Exchange SSH keys + $out = `DSH_REMOTE_PASSWORD=$password xdsh $node -K`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # formatdisk [address] [multi password] + elsif ( $args->[0] eq "--formatdisk" ) { + my $tgtNode = $node; + my $targetUserId = $userId; + my $tgtAddr = $args->[1]; + + # --- Link and format disk --- + my $rc; + my $try; + my $tgtDevNode; + + # Link target disk to HCP + my $tgtLinkAddr; + $try = 10; + while ( $try > 0 ) { + + # New disk address + $tgtLinkAddr = $tgtAddr + 1000; + + # Check if new disk address is used (target) + $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtLinkAddr ); + + # If disk address is used (target) + 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( $hcp, $tgtLinkAddr ); + } + + # Link target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" ); + $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $tgtAddr $tgtLinkAddr MR"`; + + # If link fails + if ( $out =~ m/not linked/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 ($tgtAddr)" ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); + + # Exit + return; + } + + # --- Format disk --- + my @words; + if ( $rc == -1 ) { + + # Enable disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtLinkAddr ); + + # Determine target device node + $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtLinkAddr("`; + @words = split( ' ', $out ); + $tgtDevNode = $words[6]; + + # Format target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" ); + $out = `ssh $hcp "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" ); + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + } + + # Disable disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtLinkAddr ); + + # Detatch disk from HCP + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" ); + $out = `ssh $hcp "vmcp det $tgtLinkAddr"`; + + $out = "$tgtNode: Done"; + } + + # grantvswitch [VSwitch] + elsif ( $args->[0] eq "--grantvswitch" ) { + my $vsw = $args->[1]; + + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $hcp, $userId, $vsw ); + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # disconnectnic [address] + elsif ( $args->[0] eq "--disconnectnic" ) { + my $addr = $args->[1]; + + $out = `ssh $hcp "$::DIR/disconnectnic $userId $addr"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # removedisk [virtual device address] + elsif ( $args->[0] eq "--removedisk" ) { + my $addr = $args->[1]; + + $out = `ssh $hcp "$::DIR/removemdisk $userId $addr"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # removenic [address] + elsif ( $args->[0] eq "--removenic" ) { + my $addr = $args->[1]; + + $out = `ssh $hcp "$::DIR/removenic $userId $addr"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # removeprocessor [address] + elsif ( $args->[0] eq "--removeprocessor" ) { + my $addr = $args->[1]; + + $out = `ssh $hcp "$::DIR/removeprocessor $userId $addr"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # replacevs [file] + elsif ( $args->[0] eq "--replacevs" ) { + my $file = $args->[1]; + + # Target system (HCP) -- root@gpok2.endicott.ibm.com + my $target = "root@"; + $target .= $hcp; + if ($file) { + + # SCP file over to HCP + $out = `scp $file $target:$file`; + + # Replace user directory entry + $out = `ssh $hcp "$::DIR/replacevs $userId $file"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + else { + $out = "$node: (Error) No user entry file specified"; + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + } + + # setipl [ipl target] [load parms] [parms] + elsif ( $args->[0] eq "--setipl" ) { + my $trgt = $args->[1]; + my $loadparms = $args->[2]; + my $parms = $args->[3]; + + $out = `ssh $hcp "$::DIR/setipl $userId $trgt $loadparms $parms"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # setpassword [password] + elsif ( $args->[0] eq "--setpassword" ) { + my $pw = $args->[1]; + + $out = `ssh $hcp "$::DIR/setpassword $userId $pw"`; + $out = xCAT::zvmUtils->appendHostname( $node, $out ); + } + + # Otherwise, print out error + else { + $out = "$node: (Error) Option not supported"; + } + + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; +} + +#------------------------------------------------------- + +=head3 powerVM + + Description : Power on or off a virtual server + Arguments : Node + Option [on|off|reset|stat] + Returns : Nothing + Example : powerVM($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub powerVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Output string + my $out; + + # Power on virtual server + if ( $args->[0] eq 'on' ) { + $out = `ssh $hcp "$::DIR/startvs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + # Power off virtual server + elsif ( $args->[0] eq 'off' ) { + $out = `ssh $hcp "$::DIR/stopvs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + # Get the status (on|off) + elsif ( $args->[0] eq 'stat' ) { + + # Output is different on SLES 11 + $out = `ssh $hcp "vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userId.*/on/'`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + # Reset a virtual server + elsif ( $args->[0] eq 'reset' ) { + + $out = `ssh $hcp "$::DIR/stopvs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Wait for output + while ( `vmcp q user $userId 2>/dev/null | sed 's/HCPCQU045E.*/Done/'` != "Done" ) { + # Do nothing + } + + $out = `ssh $hcp "$::DIR/startvs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + else { + xCAT::zvmUtils->printLn( $callback, + "$node: (Error) Option not supported" ); + } + return; +} + +#------------------------------------------------------- + +=head3 scanVM + + Description : Get node information from HCP + Arguments : HCP node + Returns : Nothing + Example : scanVM($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub scanVM { + + # Get inputs + my ( $callback, $node ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Print output string + # [Node name]: + # objtype=node + # id=[userID] + # os=[Operating system] + # arch=[Architecture] + # hcp=[HCP node name] + # groups=[Group] + # mgt=zvm + # + # gpok123: + # objtype=node + # id=LINUX123 + # os=SUSE Linux Enterprise Server 10 (s390x) + # arch=s390x + # hcp=gpok456.endicott.ibm.com + # groups=all + # mgt=zvm + + # Output string + my $str = ""; + + # Get nodes managed by this HCP + # Look in 'zvm' table + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); + my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $hcp . "%'", 'node', 'userid' ); + + my $out; + my $managedNode; + my $id; + my $os; + my $arch; + my $groups; + + # Search for nodes managed by given HCP + # Get 'node' and 'userid' properties + foreach (@entries) { + $managedNode = $_->{'node'}; + + # Get groups + @propNames = ('groups'); + $propVals = + xCAT::zvmUtils->getNodeProps( 'nodelist', $managedNode, @propNames ); + $groups = $propVals->{'groups'}; + + # Load VMCP module + xCAT::zvmCPUtils->loadVmcp($managedNode); + + # Get userID + $id = xCAT::zvmCPUtils->getUserId($managedNode); + + # Get operating system + $os = xCAT::zvmUtils->getOs($managedNode); + + # Get architecture + $arch = xCAT::zvmCPUtils->getArch($managedNode); + + # Create output string + $str .= "$managedNode:\n"; + $str .= " objtype=node\n"; + $str .= " userid=$id\n"; + $str .= " os=$os\n"; + $str .= " arch=$arch\n"; + $str .= " hcp=$hcp\n"; + $str .= " groups=$groups\n"; + $str .= " mgt=zvm\n\n"; + } + + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; +} + +#------------------------------------------------------- + +=head3 inventoryVM + + Description : Get virtual server hardware and software inventory + Arguments : Node + Type of inventory (config|all) + Returns : Nothing + Example : inventoryVM($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub inventoryVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Output string + my $str = ""; + + # Load VMCP module + xCAT::zvmCPUtils->loadVmcp($node); + + # Get configuration + if ( $args->[0] eq 'config' ) { + + # Get z/VM host for specified node + my $host = xCAT::zvmCPUtils->getHost($node); + + # Get architecture + my $arch = xCAT::zvmUtils->getArch($node); + + # Get operating system + my $os = xCAT::zvmUtils->getOs($node); + + # Get privileges + my $priv = xCAT::zvmCPUtils->getPrivileges($node); + + # Get memory configuration + my $memory = xCAT::zvmCPUtils->getMemory($node); + + # Get processors configuration + my $proc = xCAT::zvmCPUtils->getCpu($node); + + $str .= "z/VM UserID: $userId\n"; + $str .= "z/VM Host: $host\n"; + $str .= "Operating System: $os\n"; + $str .= "Architecture: $arch\n"; + $str .= "HCP: $hcp\n"; + $str .= "Privileges: \n$priv\n"; + $str .= "Total Memory: $memory\n"; + $str .= "Processors: \n$proc\n"; + } + elsif ( $args->[0] eq 'all' ) { + + # Get z/VM host for specified node + my $host = xCAT::zvmCPUtils->getHost($node); + + # Get architecture + my $arch = xCAT::zvmUtils->getArch($node); + + # Get operating system + my $os = xCAT::zvmUtils->getOs($node); + + # Get privileges + my $priv = xCAT::zvmCPUtils->getPrivileges($node); + + # Get memory configuration + my $memory = xCAT::zvmCPUtils->getMemory($node); + + # Get processors configuration + my $proc = xCAT::zvmCPUtils->getCpu($node); + + # Get disks configuration + my $storage = xCAT::zvmCPUtils->getDisks($node); + + # Get NICs configuration + my $nic = xCAT::zvmCPUtils->getNic($node); + + # Create output string + $str .= "z/VM UserID: $userId\n"; + $str .= "z/VM Host: $host\n"; + $str .= "Operating System: $os\n"; + $str .= "Architecture: $arch\n"; + $str .= "HCP: $hcp\n"; + $str .= "Privileges: \n$priv\n"; + $str .= "Total Memory: $memory\n"; + $str .= "Processors: \n$proc\n"; + $str .= "Disks: \n$storage\n"; + $str .= "NICs: \n$nic\n"; + } + else { + $str = "$node: (Error) Option not supported"; + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; + } + + # Append hostname (e.g. gpok3) in front + $str = xCAT::zvmUtils->appendHostname( $node, $str ); + + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; +} + +#------------------------------------------------------- + +=head3 listVM + + Description : Show node configuration + Arguments : Node + Option + + Options supported: + getnetworknames + getnetwork [networkname] + diskpoolnames + diskpool [pool name] [space (free or used)] + + Returns : Nothing + Example : listVM($callback, $node); + +=cut + +#------------------------------------------------------- +sub listVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + my $out; + + # Get disk pool names + if ( $args->[0] eq "--diskpoolnames" ) { + $out = `ssh $hcp "$::DIR/getdiskpoolnames $userId"`; + } + + # Get disk pool configuration + elsif ( $args->[0] eq "--diskpool" ) { + my $pool = $args->[1]; + my $space = $args->[2]; + + $out = `ssh $hcp "$::DIR/getdiskpool $userId $pool $space"`; + } + + # Get network names + elsif ( $args->[0] eq "--getnetworknames" ) { + $out = xCAT::zvmCPUtils->getNetworkNames($hcp); + } + + # Get network + elsif ( $args->[0] eq "--getnetwork" ) { + my $netName = $args->[1]; + + $out = xCAT::zvmCPUtils->getNetwork( $hcp, $netName ); + } + + # Get user entry + elsif ( !$args->[0] ) { + $out = `ssh $hcp "$::DIR/getuserentry $userId"`; + } + + 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" ); + + return; +} + +#------------------------------------------------------- + +=head3 makeVM + + Description : Create a virtual server + - A unique MAC address will be assigned to the virtual server + Arguments : Node + User entry text file (optional) + Returns : Nothing + Example : makeVM($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub makeVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Get user entry file (if any) + my $userEntry = $args->[0]; + + # Create virtual server + my $out; + my $target = "root@" . $hcp; + if ($userEntry) { + + # Get MAC address in 'mac' table + my $macId; + my $generateNew = 0; + @propNames = ('mac'); + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); + if ($propVals) { + + # Get MAC suffix (MACID) + $macId = $propVals->{'mac'}; + $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); + $macId = substr( $macId, 6 ); + } + else { + + # If no MACID is found, get one + $macId = xCAT::zvmUtils->getMacID($hcp); + if ( !$macId ) { + xCAT::zvmUtils->printLn( $callback, + "$node: (Error) Could not generate MACID" ); + return; + } + + # Set flag to generate new MACID after virtual server is created + $generateNew = 1; + } + + # SCP file over to HCP + $out = `scp $userEntry $target:$userEntry`; + + # Create virtual server + $out = `ssh $hcp "$::DIR/createvs $userId $userEntry"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Check output + my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == 0 ) { + + # Get HCP MAC address + # The HCP should only have (1) network -- (1) MAC address + xCAT::zvmCPUtils->loadVmcp($hcp); + $out = `ssh -o ConnectTimeout=5 $hcp "vmcp q nic" | grep "MAC:"`; + my @lines = split( "\n", $out ); + my @words = split( " ", $lines[0] ); + + # Extract MAC prefix + my $prefix = $words[1]; + $prefix = xCAT::zvmUtils->replaceStr( $prefix, "-", "" ); + $prefix = substr( $prefix, 0, 6 ); + + # Generate MAC address + my $mac = $prefix . $macId; + + # If length is less than 12, append a zero + if ( length($mac) != 12 ) { + $mac = "0" . $mac; + } + + $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 ); + + # Save MAC address in 'mac' table + xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac ); + + # Generate new MACID + if ( $generateNew == 1 ) { + $out = xCAT::zvmUtils->generateMacId($hcp); + } + } + } + else { + + # Create NOLOG virtual server + $out = `ssh $hcp "$::DIR/createvs $userId"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + return; +} + +#------------------------------------------------------- + +=head3 cloneVM + + Description : Clone a virtual server + Arguments : Node + Disk pool + Disk password + Returns : Nothing + Example : cloneVM($callback, $targetNode, $args); + +=cut + +#------------------------------------------------------- +sub cloneVM { + + # Get inputs + my ( $callback, $nodes, $args ) = @_; + + # Get nodes + my @nodes = @$nodes; + + # Return code for each command + my $rc; + my $out; + + # Child process IDs + my @children; + + # Process ID for xfork() + my $pid; + + # Get source node + my $sourceNode = $args->[0]; + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); + + # Get HCP + my $srcHcp = $propVals->{'hcp'}; + + # Get node userID + my $sourceId = $propVals->{'userid'}; + + foreach (@nodes) { + xCAT::zvmUtils->printLn( $callback, "$_: Cloning $sourceNode" ); + + # Exit if 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" ); + return; + } + + # Exit if missing source user ID + if ( !$sourceId ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node ID" ); + return; + } + + # Get target node + @propNames = ( 'hcp', 'userid' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames ); + + # Get target HCP + my $tgtHcp = $propVals->{'hcp'}; + + # Get node userID + my $tgtId = $propVals->{'userid'}; + + # Exit if missing target 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 node ID" ); + return; + } + + # Exit if source and target HCP are not equal + if ($srcHcp ne $tgtHcp) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Source and target HCP are not equal" ); + return; + } + + # --- Get MAC address --- + my $targetMac; + my $macId; + my $generateNew = 0; # Flag to generate new MACID + @propNames = ('mac'); + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $_, @propNames ); + if (!$propVals) { + + # If no MACID is found, get one + $macId = xCAT::zvmUtils->getMacID($tgtHcp); + if ( !$macId ) { + xCAT::zvmUtils->printLn( $callback, "$_: (Error) Could not generate MACID" ); + return; + } + + # Create MAC address (target) + $targetMac = xCAT::zvmUtils->createMacAddr( $_, $macId ); + + # Save MAC address in 'mac' table + xCAT::zvmUtils->setNodeProp( 'mac', $_, 'mac', $targetMac ); + + # Generate new MACID + $out = xCAT::zvmUtils->generateMacId($tgtHcp); + } + } + + # --- Link source disks --- + # Get MDisk statements of source node + my @words; + my $addr; + my $srcMultiPw; + my $linkAddr; + + # Hash table of source disk addresses + # $srcLinkAddr[$addr] = $linkAddr + my %srcLinkAddr; + my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sourceNode ); + foreach (@srcDisks) { + + # Get disk address + @words = split( ' ', $_ ); + $addr = $words[1]; + $srcMultiPw = $words[9]; + + # If source disk is not linked + my $try = 10; + while ( $try > 0 ) { + # New disk address + $linkAddr = $addr + 1000; + + # Check if new disk address is used (source) + $rc = xCAT::zvmUtils->isAddressUsed( $srcHcp, $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( $srcHcp, $linkAddr ); + } + + $srcLinkAddr { $addr } = $linkAddr; + + # Link source disk to HCP + foreach (@nodes) { + xCAT::zvmUtils->printLn( $callback, "$_: Linking source disk ($addr) as ($linkAddr)" ); + } + $out = `ssh -o ConnectTimeout=5 $srcHcp "vmcp link $sourceId $addr $linkAddr RR $srcMultiPw"`; + + if ( $out =~ m/not linked/i ) { + # 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 ) { + foreach (@nodes) { + xCAT::zvmUtils->printLn( $callback, "$_: Failed" ); + } + + # Exit + return; + } + + # Enable source disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $srcHcp, "-e", $linkAddr ); + + } # End of foreach (@srcDisks) + + # Get user entry of source node + my $srcUserEntry = "/tmp/$sourceNode.txt"; + $out = `rm $srcUserEntry`; + $out = xCAT::zvmUtils->getUserEntryWODisk( $callback, $sourceNode, $srcUserEntry ); + + # Check if user entry is valid + $out = `cat $srcUserEntry`; + + # If output contains USER LINUX123, then user entry is good + if ( $out =~ m/USER $sourceId/i ) { + + # --- Clone source node --- + foreach (@nodes) { + $pid = xCAT::Utils->xfork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + clone( $callback, $_, $args, \@srcDisks, \%srcLinkAddr ); + + # Exit process + exit(0); + } # End of elsif + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + } + + # --- Detatch source disks --- + for $addr ( keys %srcLinkAddr ) { + $linkAddr = $srcLinkAddr { $addr }; + + # Disable source disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $srcHcp, "-d", $linkAddr ); + $out = `ssh -o ConnectTimeout=5 $srcHcp "vmcp det $linkAddr"`; + + foreach (@nodes) { + xCAT::zvmUtils->printLn( $callback, "$_: Detatching source disk ($addr) at ($linkAddr)" ); + } + } + + # --- Done --- + foreach (@nodes) { + xCAT::zvmUtils->printLn( $callback, "$_: Done" ); + } + + return; +} + +#------------------------------------------------------- + +=head3 clone + + Description : Clone a virtual server + Arguments : Target node + Disk pool + Disk password (optional) + Source disks + Source disk link addresses + Returns : Nothing + Example : cloneVM($callback, $targetNode, $args); + +=cut + +#------------------------------------------------------- +sub clone { + + # Get inputs + my ( $callback, $tgtNode, $args, $srcDisksRef, $srcLinkAddrRef ) = @_; + + # Get source node properties from 'zvm' table + my $sourceNode = $args->[0]; + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); + + # Get HCP + my $srcHcp = $propVals->{'hcp'}; + + # Get node userID + my $sourceId = $propVals->{'userid'}; + + # Get source disks + my @srcDisks = @$srcDisksRef; + my %srcLinkAddr = %$srcLinkAddrRef; + + # Return code for each command + my $rc; + + # Get node properties from 'zvm' table + @propNames = ( 'hcp', 'userid' ); + $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames ); + + # Get HCP + my $hcp = $propVals->{'hcp'}; + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node HCP" ); + return; + } + + # Get node userID + my $targetUserId = $propVals->{'userid'}; + if ( !$targetUserId ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node ID" ); + return; + } + + # 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)" ); + return; + } + + # Get target IP from /etc/hosts + my $targetIp = xCAT::zvmUtils->getIp($tgtNode); + if ( !$targetIp ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing IP for $tgtNode in /etc/hosts" ); + return; + } + + my $out; + my @lines; + my @words; + + # Get disk pool and multi password + my $i; + my %inputs; + foreach $i ( 1 .. 2 ) { + if ( $args->[$i] ) { + + # Split parameters by '=' + @words = split( "=", $args->[$i] ); + + # Create hash array + $inputs{ $words[0] } = $words[1]; + } + } + + # Get disk pool + my $pool = $inputs{"pool"}; + if ( !$pool ) { + xCAT::zvmUtils->printLn( $callback, + "$tgtNode: (Error) Missing pool ID" ); + return; + } + + # Get multi password + # It is Ok not have a password + my $tgtPw = $inputs{"pw"}; + + # Get the network name the HCP is on + $out = `ssh $hcp "vmcp q v nic" | egrep -i "VSWITCH|LAN"`; + @lines = split( '\n', $out ); + my $line = xCAT::zvmUtils->trimStr($lines[0]); + @words = split( ' ', $line ); + my $netName = $words[4]; + + # Get the NICDEF address of the network on the source node + xCAT::zvmCPUtils->loadVmcp($sourceNode); + $out = `ssh $sourceNode "vmcp q v nic"`; + @lines = split( '\n', $out ); + my $i; + my $netAddr; + my @tmp; + for ( $i = 0 ; $i < @lines ; $i++ ) { + if ( $lines[$i] =~ m/$netName/i ) { + $line = xCAT::zvmUtils->trimStr( $lines[$i - 1] ); + @words = split( ' ', $line ); + @tmp = split( /\./, $words[1] ); + $netAddr = $tmp[0]; + last; + } + } + + # Exit if network address is not found + if (!$netAddr) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Node is not on the same network ($netName) as the hardware control point" ); + return; + } + + $netAddr = "0.0." . $netAddr; + + # Set IP address + my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); + + # Save user directory entry as /tmp/hostname.txt + # The source user entry is retrieved in cloneVM() + my $userEntry = "/tmp/$tgtNode.txt"; + my $srcUserEntry = "/tmp/$sourceNode.txt"; + + # Remove existing user entry if any + $out = `rm $userEntry`; + $out = `ssh -o ConnectTimeout=5 $hcp "rm $userEntry"`; + + # Copy user entry of source node + $out = `cp $srcUserEntry $userEntry`; + + # Replace source userID with target userID + $out = `sed --in-place -e "s,$sourceId,$targetUserId,g" $userEntry`; + + # Get target MAC address in 'mac' table + my $targetMac; + my $generateNew = 0; # Flag to generate new MACID + @propNames = ('mac'); + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $tgtNode, @propNames ); + if ($propVals) { + + # Get MACID + $targetMac = $propVals->{'mac'}; + } else { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing MAC address" ); + return; + } + + # SCP user entry file over to HCP + xCAT::zvmUtils->sendFile( $hcp, $userEntry, $userEntry ); + + # --- Create new virtual server --- + my $try = 10; + while ($try > 0) { + if ($try > 9) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" ); + } else { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to create user directory entry" ); + } + $out = `ssh $hcp "$::DIR/createvs $targetUserId $userEntry"`; + + # Check if user entry is created + $out = `ssh $hcp "$::DIR/getuserentry $targetUserId"`; + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + + if ($rc == -1) { + # Wait before trying again + sleep(5); + + $try = $try - 1; + } else { + last; + } + } + + # Exit on bad output + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry" ); + return; + } + + # Load VMCP module on HCP and source node + xCAT::zvmCPUtils->loadVmcp($hcp); + xCAT::zvmCPUtils->loadVmcp($sourceNode); + + # Get VSwitch of master node + my @vswitchId = xCAT::zvmCPUtils->getVswitchId($sourceNode); + + # Grant access to VSwitch for Linux user + # GuestLan do not need permissions + foreach (@vswitchId) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Granting VSwitch ($_) access for $targetUserId" ); + $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $hcp, $targetUserId, $_ ); + + # Check for errors + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == -1 ) { + + # Exit on bad output + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + return; + } + } # End of foreach (@vswitchId) + + # --- Add MDisk to target user entry --- + my $addr; + my @tgtDisks; + my $type; + my $mode; + my $cyl; + my $srcMultiPw; + foreach (@srcDisks) { + + # Get disk address + @words = split( ' ', $_ ); + $addr = $words[1]; + push( @tgtDisks, $addr ); + $type = $words[2]; + $mode = $words[6]; + $srcMultiPw = $words[9]; + + # Add ECKD disk + if ( $type eq '3390' ) { + + # Get disk size (cylinders) + $out = `ssh -o ConnectTimeout=5 $sourceNode "vmcp q v dasd" | grep "DASD $addr"`; + @words = split( ' ', $out ); + $cyl = xCAT::zvmUtils->trimStr( $words[5] ); + + $try = 10; + while ( $try > 0 ) { + + # Add ECKD 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 $hcp "$::DIR/add3390 $targetUserId $pool $addr $cyl $mode $tgtPw $tgtPw $tgtPw"`; + + # Check output + $rc = xCAT::zvmUtils->checkOutput( $callback, $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 create user entry" ); + return; + } + } # End of if ( $type eq '3390' ) + + # Add FBA disk + elsif ( $type eq '9336' ) { + + # -- To be supported -- + # Get disk size (blocks) + # Add disk + } + } + + # Check if the number of disks in target user entry + # is equal to the number of disks added + my @disks; + $try = 10; + while ( $try > 0 ) { + # Get disks within user entry + $out = `ssh $hcp "$::DIR/getuserentry $targetUserId" | grep "MDISK"`; + @disks = split( '\n', $out ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks added (" . @tgtDisks . "). Disks in user entry (" . @disks . ")" ); + + 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) Disks not present in user entry"); + return; + } + + # --- Link, format, and copy source disks --- + my $srcAddr; + my $tgtAddr; + my $srcDevNode; + my $tgtDevNode; + foreach (@tgtDisks) { + + # --- Link target disk --- + $try = 10; + while ( $try > 0 ) { + + # New disk address + $srcAddr = $srcLinkAddr { $_ }; + $tgtAddr = $_ + 2000; + + # Check if new disk address is used (target) + $rc = xCAT::zvmUtils->isAddressUsed( $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( $hcp, $tgtAddr ); + } + + # Link target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($_) as ($tgtAddr)" ); + $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $_ $tgtAddr MR $tgtPw"`; + + # If link fails + if ( $out =~ m/not linked/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; + } + + # --- Use FLASHCOPY --- + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr) using FLASHCOPY" ); + $out = xCAT::zvmCPUtils->flashCopy( $hcp, $srcAddr, $tgtAddr ); + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + + # --- Use Linux DD --- + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not supported. Using Linux DD" ); + + # Enable target disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtAddr ); + + # Determine source device node + $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$srcAddr("`; + @words = split( ' ', $out ); + $srcDevNode = $words[6]; + + # Determine target device node + $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtAddr("`; + @words = split( ' ', $out ); + $tgtDevNode = $words[6]; + + # Format target disk + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" ); + $out = `ssh $hcp "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" ); + 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 $hcp "dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096"`; + + # Check for error + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + } + + # Disable and enable target disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtAddr ); + + # Determine target device node (it might have changed) + $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtAddr("`; + @words = split( ' ', $out ); + $tgtDevNode = $words[6]; + + # Get disk address that is the root partition (/) + my $rootPartitionAddr = xCAT::zvmUtils->getRootDiskAddr($sourceNode); + if ( $_ eq $rootPartitionAddr ) { + + # --- Set network configuration --- + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Setting network configuration" ); + + # Mount target disk + my $cloneMntPt = "/mnt/$targetUserId"; + $tgtDevNode .= "1"; + $out = `ssh $hcp "mkdir $cloneMntPt"`; + $out = `ssh $hcp "mount /dev/$tgtDevNode $cloneMntPt"`; + + # Set hostname + $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" $cloneMntPt/etc/HOSTNAME`; + + # If Red Hat -- Set hostname in /etc/sysconfig/network + my $os = xCAT::zvmUtils->getOs($sourceNode); + if ( $os =~ m/Red Hat/i ) { + $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" $cloneMntPt/etc/sysconfig/network`; + } + + # Set IP address + my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); + + # Get network configuration file + # Location of this file depends on the OS + my $ifcfg = xCAT::zvmUtils->getIfcfgByNic( $sourceNode, $netAddr ); + my $ifcfgPath = $cloneMntPt; + $ifcfgPath .= $ifcfg; + $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" \ -e "s/$sourceIp/$targetIp/g" $cloneMntPt/etc/hosts`; + $out = `ssh $hcp sed --in-place -e "s/$sourceIp/$targetIp/g" \ -e "s/$sourceNode/$tgtNode/g" $ifcfgPath`; + + # Set MAC address + my $networkFile = $tgtNode . "_network_config"; + if ( $os =~ m/Red Hat/i ) { + # Red Hat only + $out = `ssh $hcp "cat $ifcfgPath" | grep -v "MACADDR" > /tmp/$networkFile`; + $out = `echo "MACADDR='$targetMac'" >> /tmp/$networkFile`; + } else { + # SuSE only + $out = `ssh $hcp "cat $ifcfgPath" | grep -v "LLADDR" | grep -v "UNIQUE" > /tmp/$networkFile`; + $out = `echo "LLADDR='$targetMac'" >> /tmp/$networkFile`; + $out = `echo "UNIQUE=''" >> /tmp/$networkFile`; + } + xCAT::zvmUtils->sendFile( $hcp, "/tmp/$networkFile", $ifcfgPath ); + + # Set to hardware configuration -- Only for layer 2 + my $layer = xCAT::zvmCPUtils->getNetworkLayer($sourceNode); + if ( $layer == 2 ) { + + # --- Red Hat --- + if ( $os =~ m/Red Hat/i ) { + my $srcMac; + + # Get source MAC address in 'mac' table + @propNames = ('mac'); + $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $sourceNode, @propNames ); + if ($propVals) { + + # Get MAC address + $srcMac = $propVals->{'mac'}; + } + else { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Could not find MAC address of $sourceNode" ); + + # Unmount disk + $out = `ssh $hcp "umount $cloneMntPt"`; + + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); + + # Detatch disks from HCP + $out = `ssh $hcp "vmcp det $tgtAddr"`; + + return; + } + + # Set MAC address + $out = `ssh $hcp sed --in-place -e "s/$srcMac/$targetMac/g" $ifcfgPath`; + } + + # --- SuSE --- + else { + + # Get hardware configuration + my $hwcfg = xCAT::zvmUtils->getHwcfg($sourceNode); + my $hwcfgPath = $cloneMntPt; + + # Set layer 2 support + $hwcfgPath .= $hwcfg; + my $hardwareFile = $tgtNode . "_hardware_config"; + $out = `ssh $hcp "cat $hwcfgPath" | grep -v "QETH_LAYER2_SUPPORT" > /tmp/$hardwareFile`; + $out = `echo "QETH_LAYER2_SUPPORT='1'" >> /tmp/$hardwareFile`; + xCAT::zvmUtils->sendFile( $hcp, "/tmp/$hardwareFile", $hwcfgPath ); + } + } # End of if ( $layer == 2 ) + + # Flush disk + $out = `ssh $hcp "sync"`; + + # Unmount disk + $out = `ssh $hcp "umount $cloneMntPt"`; + } + + # Disable disks + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); + + # Detatch disks from HCP + $out = `ssh $hcp "vmcp det $tgtAddr"`; + + sleep(5); + } # End of foreach (@tgtDisks) + + # Add node to DHCP + $out = `makedhcp -a`; + + # Power on target virtual server + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Powering on" ); + $out = `ssh $hcp "$::DIR/startvs $targetUserId"`; + + # Check for error + $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); + return; + } +} + +#------------------------------------------------------- + +=head3 nodeSet + + Description : Set the boot state for a node + - Punch initrd, kernel, and parmfile to node reader + - Layer 2 and 3 VSwitch/Lan supported + Arguments : Node + Returns : Nothing + Example : nodeSet($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub nodeSet { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get action + my $action = $args->[0]; + if ( !( $action eq "install" ) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); + return; + } + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Get node root password + @propNames = ('password'); + $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" ); + return; + } + + # Get Linux distribution + @propNames = ('current_osimage'); + $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames ); + my $distr = $propVals->{'current_osimage'}; + if ( !$distr ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system to be deployed on this node" ); + return; + } + + # Get autoyast/kickstart template + @propNames = ('profile'); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $distr, @propNames ); + my $profile = $propVals->{'profile'}; + if ( !$distr ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile for this node" ); + return; + } + + # Get host IP and hostname from /etc/hosts + my $out = `cat /etc/hosts | grep $node`; + my @words = split( ' ', $out ); + my $hostIP = $words[0]; + my $hostname = $words[2]; + if ( !$hostIP || !$hostname ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); + return; + } + + # Get NIC address from user entry + $out = `ssh $hcp "$::DIR/getuserentry $userId" | grep "NICDEF"`; + if ( !$out ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing NICDEF statement in user entry of node" ); + return; + } + + # Grab first NICDEF address + my @lines = split( '\n', $out ); + @words = split( ' ', $lines[0] ); + my $readChannel = "0.0.0" . ( $words[1] + 0 ); + my $writeChannel = "0.0.0" . ( $words[1] + 1 ); + my $dataChannel = "0.0.0" . ( $words[1] + 2 ); + + # Get network type (Layer 2 or 3) + my $netName = $words[6]; + $out = `ssh $hcp "vmcp q lan $netName"`; + if ( !$out ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing NICDEF statement in user entry of node" ); + return; + } + + # Go through each line + my $layer = 3; # Default to layer 3 + @lines = split( '\n', $out ); + foreach (@lines) { + + # If the line contains ETHERNET, then it is a layer 2 network + if ( $_ =~ m/ETHERNET/i ) { + $layer = 2; + } + } + + # Get MAC address -- Only for layer 2 + my $mac = ""; + my @propNames; + my $propVals; + if ( $layer == 2 ) { + + # Search 'mac' table for node + @propNames = ('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 + if ( !$mac ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing MAC address of node" ); + return; + } + } + + # Get domain from site table + my $siteTab = xCAT::Table->new('site'); + my $domainHash = $siteTab->getAttribs( { key => "domain" }, 'value' ); + my $domain = $domainHash->{'value'}; + + # Get first 3 octets of node IP (IPv4) + @words = split( /\./, $hostIP ); + my $octets = "$words[0].$words[1].$words[2]"; + + # Get networks in 'networks' table + my $entries = xCAT::zvmUtils->getAllTabEntries('networks'); + + # Go through each network + my $network; + foreach (@$entries) { + + # Get network + $network = $_->{'net'}; + + # If networks contains the first 3 octets of the node IP + if ( $network =~ m/$octets/i ) { + + # Exit loop + last; + } + else { + $network = ""; + } + } + + # If no network found + if ( !$network ) { + + # Exit + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any networks in the networks table" ); + return; + } + + @propNames = ( 'mask', 'gateway', 'tftpserver', 'nameservers' ); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); + my $mask = $propVals->{'mask'}; + my $gateway = $propVals->{'gateway'}; + my $ftp = $propVals->{'tftpserver'}; + my $nameserver = $propVals->{'nameservers'}; + if ( !$network || !$mask || !$ftp || !$nameserver ) { + + # It is acceptable to not have a gateway + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing network information" ); + return; + } + + # Get broadcast address of NIC + my $ifcfg = xCAT::zvmUtils->getIfcfgByNic( $hcp, $readChannel ); + $out = `cat $ifcfg | grep "BROADCAST"`; + @words = split( '=', $out ); + my $broadcast = $words[1]; + $broadcast = xCAT::zvmUtils->trimStr($broadcast); + $broadcast = xCAT::zvmUtils->replaceStr( $broadcast, "'", "" ); + + # Load VMCP module on HCP + xCAT::zvmCPUtils->loadVmcp($hcp); + + # Sample paramter file exists in installation CD -- Use that as a guide + my $sampleParm; + my $parmHeader; + my $parms; + my $parmFile; + my $kernelFile; + my $initFile; + + # If punch is successful -- Look for this string + my $searchStr = "created and transferred"; + + # Default parameters -- SLES + 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) + + # Default parameters -- RHEL + my $netType = "qeth"; + my $portName = "FOOBAR"; + my $portNo = "0"; + + # SUSE installation + my $template; + if ( $distr =~ m/sles/i ) { + + # Create directory in FTP root (/install) to hold template + $out = `mkdir -p /install/custom/install/sles`; + + # Copy autoyast template + $template = "/install/custom/install/sles/$profile"; + $out = `cp /opt/xcat/share/xcat/install/sles/$profile $template`; + + # Edit template + my $device = "qeth-bus-ccw-$readChannel"; + my $chanIds = "$readChannel $writeChannel $dataChannel"; + + $out = `sed --in-place -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" $template`; + + # Read sample parmfile in /install/sles10.2/s390x/1/boot/s390x/ + $sampleParm = "/install/$distr/s390x/1/boot/s390x/parmfile"; + 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 ) { + $parmHeader = xCAT::zvmUtils->trimStr($_); + } + } + + # Close sample parmfile + close(SAMPLEPARM); + + # Create parmfile -- Limited to 10 lines + # End result should be: + # ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb + # HostIP=10.0.0.5 Hostname=gpok5.endicott.ibm.com + # Gateway=10.0.0.1 Netmask=255.255.255.0 + # Broadcast=10.0.0.0 Layer2=1 OSAHWaddr=02:00:01:FF:FF:FF + # ReadChannel=0.0.0800 WriteChannel=0.0.0801 DataChannel=0.0.0802 + # Nameserver=9.0.2.11 Portname=OSAPORT Portno=0 + # Install=ftp://10.0.0.1/sles10.2/s390x/1/ + # UseVNC=1 VNCPassword=123456 + # InstNetDev=osa OsaInterface=qdio OsaMedium=eth Manual=0 + my $ay = "ftp://$ftp/custom/install/sles/$profile"; + + $parms = $parmHeader . "\n"; + $parms = $parms . "AutoYaST=$ay\n"; + $parms = $parms . "HostIP=$hostIP Hostname=$hostname\n"; + $parms = $parms . "Gateway=$gateway Netmask=$mask\n"; + + # Set layer in autoyast profile + if ( $layer == 2 ) { + $parms = $parms . "Broadcast=$network Layer2=1 OSAHWaddr=$mac\n"; + } + else { + $parms = $parms . "Broadcast=$network Layer2=0\n"; + } + + $parms = $parms . "ReadChannel=$readChannel WriteChannel=$writeChannel DataChannel=$dataChannel\n"; + $parms = $parms . "Nameserver=$nameserver Portname=$portName Portno=0\n"; + $parms = $parms . "Install=ftp://$ftp/$distr/s390x/1/\n"; + $parms = $parms . "UseVNC=1 VNCPassword=123456\n"; + $parms = $parms . "InstNetDev=$instNetDev OsaInterface=$osaInterface OsaMedium=$osaMedium Manual=0\n"; + + # Write to parmfile + $parmFile = "/tmp/" . $node . "Parm"; + open( PARMFILE, ">$parmFile" ); + print PARMFILE "$parms"; + close(PARMFILE); + + # Send kernel, parmfile, and initrd to reader to HCP + $kernelFile = "/tmp/" . $node . "Kernel"; + $initFile = "/tmp/" . $node . "Initrd"; + $out = `cp /install/$distr/s390x/1/boot/s390x/vmrdr.ikr $kernelFile`; + $out = `cp /install/$distr/s390x/1/boot/s390x/initrd $initFile`; + xCAT::zvmUtils->sendFile( $hcp, $kernelFile, $kernelFile ); + xCAT::zvmUtils->sendFile( $hcp, $parmFile, $parmFile ); + xCAT::zvmUtils->sendFile( $hcp, $initFile, $initFile ); + + # Set the virtual unit record devices online on HCP + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "c" ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "d" ); + + # Purge reader + $out = xCAT::zvmCPUtils->purgeReader( $hcp, $userId ); + xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); + + # Punch kernel to reader on HCP + $out = xCAT::zvmCPUtils->punch2Reader( $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( $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( $hcp, $userId, $initFile, "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." ); + } + + # RHEL installation + elsif ( $distr =~ m/rhel/i ) { + + # Create directory in FTP root (/install) to hold template + $out = `mkdir -p /install/custom/install/rh`; + + # Copy kickstart template + $template = "/install/custom/install/rh/$profile"; + $out = `cp /opt/xcat/share/xcat/install/rh/$profile $template`; + + # Edit template + my $url = "ftp://$ftp/$distr/s390x/"; + $out = `sed --in-place -e "s,replace_url,$url,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" $template`; + + # Read sample parmfile in /install/rhel5.3/s390x/images + $sampleParm = "/install/$distr/s390x/images/generic.prm"; + 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 ) { + $parmHeader = xCAT::zvmUtils->trimStr($_); + } + } + + # Close sample parmfile + close(SAMPLEPARM); + + # Get mdisk address + my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $node ); + my $dasd = ""; + my $i = 0; + foreach (@mdisks) { + $i = $i + 1; + @words = split( ' ', $_ ); + + # Do not put a comma at the end of the last disk address + if ( $i == @mdisks ) { + $dasd = $dasd . "0.0.$words[1]"; + } + else { + $dasd = $dasd . "0.0.$words[1],"; + } + } + + # Create parmfile -- Limited to 80 characters/line, maximum of 11 lines + # End result should be: + # ramdisk_size=40000 root=/dev/ram0 ro ip=off + # ks=ftp://10.0.0.1/rhel5.3/s390x/compute.rhel5.s390x.tmpl + # RUNKS=1 cmdline + # DASD=0.0.0100 HOSTNAME=gpok4.endicott.ibm.com + # NETTYPE=qeth IPADDR=10.0.0.4 + # SUBCHANNELS=0.0.0800,0.0.0801,0.0.0800 + # NETWORK=10.0.0.0 NETMASK=255.255.255.0 + # SEARCHDNS=endicott.ibm.com BROADCAST=10.0.0.255 + # GATEWAY=10.0.0.1 DNS=9.0.2.11 MTU=1500 + # PORTNAME=UNASSIGNED PORTNO=0 LAYER2=0 + # vnc vncpassword=123456 + my $ks = "ftp://$ftp/custom/install/rh/$profile"; + + $parms = $parmHeader . "\n"; + $parms = $parms . "ks=$ks\n"; + $parms = $parms . "RUNKS=1 cmdline\n"; + $parms = $parms . "DASD=$dasd HOSTNAME=$hostname\n"; + $parms = $parms . "NETTYPE=$netType IPADDR=$hostIP\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 ) { + $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=1 MACADDR=$mac\n"; + } + else { + $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=0\n"; + } + + $parms = $parms . "vnc vncpassword=123456\n"; + + # Write to parmfile + $parmFile = "/tmp/" . $node . "Parm"; + open( PARMFILE, ">$parmFile" ); + print PARMFILE "$parms"; + close(PARMFILE); + + # Send kernel, parmfile, conf, and initrd to reader to HCP + $kernelFile = "/tmp/" . $node . "Kernel"; + $initFile = "/tmp/" . $node . "Initrd"; + + $out = `cp /install/$distr/s390x/images/kernel.img $kernelFile`; + $out = `cp /install/$distr/s390x/images/initrd.img $initFile`; + xCAT::zvmUtils->sendFile( $hcp, $kernelFile, $kernelFile ); + xCAT::zvmUtils->sendFile( $hcp, $parmFile, $parmFile ); + xCAT::zvmUtils->sendFile( $hcp, $initFile, $initFile ); + + # Set the virtual unit record devices online + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "c" ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "d" ); + + # Purge reader + $out = xCAT::zvmCPUtils->purgeReader( $hcp, $userId ); + xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); + + # Punch kernel to reader on HCP + $out = xCAT::zvmCPUtils->punch2Reader( $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( $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( $hcp, $userId, $initFile, "rhel.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." ); + } + + return; +} + +#------------------------------------------------------- + +=head3 getMacs + + Description : Get the MAC address of a given node + - Requires the node be online + - Saves MAC address in 'mac' table + Arguments : Node + Returns : Nothing + Example : getMacs($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub getMacs { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Get MAC address in 'mac' table + my @propNames = ('mac'); + my $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); + my $mac; + if ($propVals) { + + # Get MAC address + $mac = $propVals->{'mac'}; + xCAT::zvmUtils->printLn( $callback, "$node: $mac" ); + return; + } + + # If MAC address is not in the 'mac' table, get it using VMCP + xCAT::zvmCPUtils->loadVmcp($node); + + # Get xCat MN Lan/VSwitch name + my $out = `vmcp q v nic | egrep -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++ ) { + + # Extract VSwitch name + 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] ); + $searchStr = $searchStr . "$words[4]"; + } + + 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 $node "vmcp q v nic" | egrep -i "$searchStr"`; + if ( !$out ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find MAC address" ); + return; + } + + @lines = split( '\n', $out ); + @words = split( ' ', $lines[0] ); + my $mac = $words[1]; + + # Replace - with : + $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 ); + + return; +} + +#------------------------------------------------------- + +=head3 netBoot + + Description : Boot from network + Arguments : Node + Address to IPL from + Returns : Nothing + Example : netBoot($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub netBoot { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Get IPL + my @ipl = split( '=', $args->[0] ); + if ( !( $ipl[0] eq "ipl" ) ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IPL" ); + return; + } + + # Boot node + my $out = `ssh $hcp "$::DIR/startvs $userId"`; + my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); + if ( $rc == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Boot failed" ); + return; + } + else { + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + + # IPL when virtual server is online + sleep(5); + $out = xCAT::zvmCPUtils->sendCPCmd( $hcp, $userId, "IPL $ipl[1]" ); + xCAT::zvmUtils->printLn( $callback, "$node: Booting from $ipl[1]... Done" ); + + return; +} + +#------------------------------------------------------- + +=head3 updateNode + + Description : Update node + Arguments : Node + Option + + Options supported: + release [updated version] + + Returns : Nothing + Example : updateNode($callback, $node, $args); + +=cut + +#------------------------------------------------------- +sub updateNode { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get node properties from 'zvm' table + 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" ); + return; + } + + # Get node userID + my $userId = $propVals->{'userid'}; + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); + return; + } + + # Get host IP and hostname from /etc/hosts + my $out = `cat /etc/hosts | grep $node`; + my @words = split( ' ', $out ); + my $hostIP = $words[0]; + my $hostname = $words[2]; + if ( !$hostIP || !$hostname ) { + xCAT::zvmUtils->printLn( $callback, + "$node: (Error) Missing IP for $node in /etc/hosts" ); + return; + } + + # Get first 3 octets of node IP (IPv4) + @words = split( /\./, $hostIP ); + my $octets = "$words[0].$words[1].$words[2]"; + + # Get networks in 'networks' table + my $entries = xCAT::zvmUtils->getAllTabEntries('networks'); + + # Go through each network + my $network; + foreach (@$entries) { + + # Get network + $network = $_->{'net'}; + + # If networks contains the first 3 octets of the node IP + if ( $network =~ m/$octets/i ) { + + # Exit loop + last; + } + else { + $network = ""; + } + } + + # If no network found + if ( !$network ) { + + # Exit + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any networks in the networks table" ); + return; + } + + # Get FTP server + @propNames = ('tftpserver'); + $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); + my $ftp = $propVals->{'tftpserver'}; + if ( !$ftp ) { + + # It is acceptable to not have a gateway + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing FTP server" ); + return; + } + + # Update node operating system + if ( $args->[0] eq "--release" ) { + my $version = $args->[1]; + + if ( !$version ) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system release" ); + return; + } + + # Get node operating system + my $os = xCAT::zvmUtils->getOs($node); + + # 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" ); + return; + } + + # Generate FTP path to operating system image + my $path; + if ($version =~ m/sles/i) { + # SuSE Enterprise Linux path - ftp://10.0.0.1/sles10.3/s390x/1/ + $path = "ftp://$ftp/$version/s390x/1/"; + + # Add installation source using rug + $out = `ssh $node "rug sa -t zypp $path $version"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Subscribe to catalog + $out = `ssh $node "rug sub $version"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Refresh services + $out = `ssh $node "rug ref"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + + # Update + $out = `ssh $node "rug up -y"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } else { + # Red Hat Enterprise Linux path - ftp://10.0.0.1/rhel5.4/s390x/Server/ + $path = "ftp://$ftp/$version/s390x/Server/"; + + # Check if file.repo already has this repository location + $out = `ssh $node "cat /etc/yum.repos.d/file.repo"`; + if ($out =~ m/[$version]/i) { + + # Send over release key + my $key = "/install/rhel5.4/s390x/RPM-GPG-KEY-redhat-release"; + my $tmp = "/tmp/RPM-GPG-KEY-redhat-release"; + xCAT::zvmUtils->sendFile($node, $key, $tmp); + + # Import key + $out = `ssh $node "rpm --import /tmp/$key"`; + + # Upgrade + $out = `ssh $node "yum upgrade -y"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } else { + + # Create repository + $out = `ssh $node "echo [$version] >> /etc/yum.repos.d/file.repo"`; + $out = `ssh $node "echo baseurl=$path >> /etc/yum.repos.d/file.repo"`; + $out = `ssh $node "echo enabled=1 >> /etc/yum.repos.d/file.repo"`; + + # Send over release key + my $key = "/install/rhel5.4/s390x/RPM-GPG-KEY-redhat-release"; + my $tmp = "/tmp/RPM-GPG-KEY-redhat-release"; + xCAT::zvmUtils->sendFile($node, $key, $tmp); + + # Import key + $out = `ssh $node "rpm --import $tmp"`; + + # Upgrade + $out = `ssh $node "yum upgrade -y"`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + } + } + + # Otherwise, print out error + else { + $out = "$node: (Error) Option not supported"; + } + + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; +}