From 69f6fa56938e2dd990fb517689206f741348de2a Mon Sep 17 00:00:00 2001 From: phamt Date: Wed, 19 Jun 2013 15:58:26 +0000 Subject: [PATCH] Added support for capture and deploy of system images for z/VM. This includes capturing ECKD/FBA and native SCSI/FCP devices. Each device is captured and stored as an image (.img) file. Note that mkvm has to be used to create the VM definition and chvm has to be used to add the disks. nodeset is used to put the .img contents onto existing disks. git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/branches/2.8@16692 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd --- perl-xCAT/xCAT/zvmCPUtils.pm | 4 +- perl-xCAT/xCAT/zvmUtils.pm | 682 ++++++- xCAT-server/lib/xcat/plugins/zvm.pm | 2163 +++++++++++++++------ xCAT-server/share/xcat/scripts/xcatconf4z | 484 +++++ 4 files changed, 2751 insertions(+), 582 deletions(-) create mode 100644 xCAT-server/share/xcat/scripts/xcatconf4z diff --git a/perl-xCAT/xCAT/zvmCPUtils.pm b/perl-xCAT/xCAT/zvmCPUtils.pm index 749adbe07..cac661f95 100644 --- a/perl-xCAT/xCAT/zvmCPUtils.pm +++ b/perl-xCAT/xCAT/zvmCPUtils.pm @@ -208,7 +208,7 @@ sub getNic { Arguments : User (root or non-root) Node Returns : Network names - Example : my $lans = xCAT::zvmCPUtils->getNetworkNames($node); + Example : my $lans = xCAT::zvmCPUtils->getNetworkNames($user, $node); =cut @@ -260,7 +260,7 @@ sub getNetworkNames { Arguments : User (root or non-root) Node Returns : Array of networks names - Example : my @networks = xCAT::zvmCPUtils->getNetworkNamesArray($node); + Example : my @networks = xCAT::zvmCPUtils->getNetworkNamesArray($user, $node); =cut diff --git a/perl-xCAT/xCAT/zvmUtils.pm b/perl-xCAT/xCAT/zvmUtils.pm index f28e4b3b7..306b1ad1b 100644 --- a/perl-xCAT/xCAT/zvmUtils.pm +++ b/perl-xCAT/xCAT/zvmUtils.pm @@ -12,6 +12,9 @@ package xCAT::zvmUtils; use xCAT::MsgUtils; use xCAT::Utils; use xCAT::Table; +use xCAT::NetworkUtils; +use File::Copy; +use File::Basename; use strict; use warnings; 1; @@ -288,7 +291,7 @@ sub printLn { my $rsp; my $type = "I"; if ($str =~ m/error/i) { # Set to print error if the string contains error - $type = "E"; + $type = "E"; } $rsp->{data}->[0] = "$str"; @@ -870,6 +873,36 @@ sub checkOutput { #------------------------------------------------------- +=head3 checkOutputExtractReason + + Description : Check the return of given output. If bad, extract the reason. + Arguments : Output string + Reason (passed as a reference) + Returns : 0 Good output + -1 Bad output + Example : my $rtn = xCAT::zvmUtils->checkOutput($callback, $out, \$reason); + +=cut + +#------------------------------------------------------- +sub checkOutputExtractReason { + my ( $class, $callback, $out, $reason ) = @_; + + # Check output string + my @outLn = split("\n", $out); + foreach (@outLn) { + # If output contains 'ERROR: ', return -1 and pass back the reason. + if ($_ =~ /(.*?ERROR: )/) { + $$reason = substr($_, index($_, "ERROR: ") + length("ERROR: ")); + return -1; + } + } + + return 0; +} + +#------------------------------------------------------- + =head3 getDeviceNode Description : Get the device node for a given address @@ -892,14 +925,14 @@ sub getDeviceNode { # Determine device node my $out = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep ".$tgtAddr("`; - my @words = split( ' ', $out ); + my @words = split(' ', $out); my $tgtDevNode; # /proc/dasd/devices look similar to this: # 0.0.0100(ECKD) at ( 94: 0) is dasda : active at blocksize: 4096, 1802880 blocks, 7042 MB # Look for the string 'is' my $i = 0; - while ( $tgtDevNode ne 'is' ) { + while ($tgtDevNode ne 'is') { $tgtDevNode = $words[$i]; $i++; } @@ -909,6 +942,39 @@ sub getDeviceNode { #------------------------------------------------------- +=head3 getDeviceNodeAddr + + Description : Get the virtual device address for a given device node + Arguments : User (root or non-root) + Node + Device node + Returns : Virtual device address + Example : my $addr = xCAT::zvmUtils->getDeviceNodeAddr($user, $node, $deviceNode); + +=cut + +#------------------------------------------------------- +sub getDeviceNodeAddr { + my ( $class, $user, $node, $deviceNode ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Find device node and determine virtual address + # /proc/dasd/devices look similar to this: + # 0.0.0100(ECKD) at ( 94: 0) is dasda : active at blocksize: 4096, 1802880 blocks, 7042 MB + my $addr = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep -i "is $deviceNode"`; + $addr =~ s/ +/ /g; + $addr =~ s/^0.0.([0-9a-f]*).*/$1/; + chomp($addr); + + return $addr; +} + +#------------------------------------------------------- + =head3 isAddressUsed Description : Check if a given address is used @@ -1507,7 +1573,7 @@ sub getFreeAddress { } else { # When the node is down, use zHCP to get its user directory entry # Get HCP - my @propNames = ( 'hcp', 'userid' ); + my @propNames = ('hcp', 'userid'); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; @@ -1525,9 +1591,9 @@ sub getFreeAddress { } # Get all defined device address - $allUsedAddr = `cat $allUsedAddr | awk '$1 ~/^($deviceTypesUserDir)/ {print $2}' | sort`; + $allUsedAddr = `cat $userDirEntry | awk '$1 ~/^($deviceTypesUserDir)/ {print $2}' | sort`; # Get all linked device address - $allUsedAddr .= `cat $allUsedAddr | awk '$1 ~/^(LINK)/ {print $4}' | sort`; + $allUsedAddr .= `cat $userDirEntry | awk '$1 ~/^(LINK)/ {print $4}' | sort`; } # Loop to get the lowest free address @@ -1577,7 +1643,7 @@ sub getUsedCpuTime { $time =~ s/^\s+//; $time =~ s/\s+$//; if (!$time) { - $time = 0; + $time = 0; } # Not found, return 0 @@ -2071,7 +2137,7 @@ sub smapi4xcat { my $out = `ssh $user\@$hcp "$sudo $dir/smcli Query_API_Functional_Level -T $hcpUserId"`; $out = xCAT::zvmUtils->trimStr($out); if ( !($out =~ m/V6.2/i || $out =~ m/V6.1/i || $out =~ m/V5.4/i) ) { - return 1; + return 1; } # Check if SMAPI EXEC exists @@ -2172,20 +2238,20 @@ sub querySSI { #------------------------------------------------------- sub rExecute { - my ( $class, $user, $node, $cmd ) = @_; - - my $out; - my $sudo = "sudo"; + my ( $class, $user, $node, $cmd ) = @_; + + my $out; + my $sudo = "sudo"; if ($user eq "root") { - # Just execute the command if root + # Just execute the command if root $out = `ssh $user\@$node "$cmd"`; return $out; } - # Encapsulate command in single quotes - $cmd = "'" . $cmd . "'"; - $out = `ssh $user\@$node "$sudo sh -c $cmd"`; - return $out; + # Encapsulate command in single quotes + $cmd = "'" . $cmd . "'"; + $out = `ssh $user\@$node "$sudo sh -c $cmd"`; + return $out; } #------------------------------------------------------- @@ -2202,8 +2268,8 @@ sub rExecute { #------------------------------------------------------- sub getUsedFcpDevices { - my ( $class, $user, $hcp ) = @_; - + my ( $class, $user, $hcp ) = @_; + # Directory where zFCP pools are my $pool = "/var/opt/zhcp/zfcp"; @@ -2211,15 +2277,15 @@ sub getUsedFcpDevices { if ($user eq "root") { $sudo = ""; } - - # Grep the pools for used or allocated zFCP devices - my %usedDevices; - my @args; - my @devices = split("\n", `ssh $user\@$hcp "$sudo cat $pool/*.conf" | egrep -i "used|allocated"`); - foreach (@devices) { - @args = split(",", $_); - - # Sample pool configuration file: + + # Grep the pools for used or allocated zFCP devices + my %usedDevices; + my @args; + my @devices = split("\n", `ssh $user\@$hcp "$sudo cat $pool/*.conf" | egrep -i "used|allocated"`); + foreach (@devices) { + @args = split(",", $_); + + # Sample pool configuration file: # #status,wwpn,lun,size,range,owner,channel,tag # used,1000000000000000,2000000000000110,8g,3B00-3B3F,ihost1,1a23,$root_device$ # free,1000000000000000,2000000000000111,,3B00-3B3F,,, @@ -2228,9 +2294,561 @@ sub getUsedFcpDevices { # Push used or allocated devices into hash if ($args[6]) { - $usedDevices{uc($args[6])} = 1; + $usedDevices{uc($args[6])} = 1; } - } - - return %usedDevices; + } + + return %usedDevices; +} + +#------------------------------------------------------- + +=head3 establishMount + + Description : Establish an NFS mount point on a zHCP system. + Arguments : Sudoer user name + Sudo keyword + zHCP hostname + Local directory to remotely mount + Mount access ('ro' for read only, 'rw' for read write) + Directory as known to zHCP (out) + Returns : 0 - Mounted, or zHCP and MN are on the same system + 1 - Mount failed + Example : establishMount( $callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$provMethod", "ro", \$remoteDeployDir ); + +=cut + +#------------------------------------------------------- +sub establishMount { + # Get inputs + my ($class, $callback, $sudoer, $sudo, $hcp, $localDir, $access, $mountedPt) = @_; + my $out; + + # If the target system is not on this system then establish the NFS mount point. + my $hcpIP = xCAT::NetworkUtils->getipaddr( $hcp ); + if (! defined $hcpIP) { + xCAT::zvmUtils->printLn( $callback, "(Error) Unable to obtain the IP address of the hcp node" ); + return 1; + } + + my $masterIp = xCAT::TableUtils->get_site_Master(); + if (! defined $masterIp) { + xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to obtain the management node IP address from the site table" ); + return 1; + } + + if ($masterIp eq $hcpIP) { + # xCAT MN and zHCP are on the same box and will use the same directory without the need for an NFS mount. + $$mountedPt = $localDir; + } else { + # Determine the hostname for this management node + my $masterHostname = Sys::Hostname::hostname(); + if (! defined $masterHostname) { + # For some reason, the xCAT MN's hostname is not known. We pass along the IP address instead. + $masterHostname = $masterIp; + } + + xCAT::zvmUtils->printSyslog( "establishMount() Preparing the NFS mount point on zHCP ($hcpIP) to xCAT MN $masterHostname($masterIp) for $localDir" ); + + # Prepare the staging mount point on zHCP, if they need to be established + $$mountedPt = "/mnt/$masterHostname$localDir"; + my $rc = `ssh $sudoer\@$hcp "$sudo mkdir -p $$mountedPt && mount -t nfs -o $access $masterIp:$localDir $$mountedPt; echo \\\$?"`; + + # Return code = 0 (mount succeeded) or 32 (mount already exists) + if ($rc != '0' && $rc != '32') { + xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to establish zHCP mount point: $$mountedPt" ); + return 1; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 getFreeRepoSpace + + Description : Get the free space of image repository under /install + Arguments : Node + Returns : The available space for /install + Example : my $free = getFreeRepoSpace($callback, $node); + +=cut + +#------------------------------------------------------- +sub getFreeRepoSpace { + # Get inputs + my ($class, $user, $node) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Check if node is the management node + my @entries = xCAT::TableUtils->get_site_attribute("master"); + my $master = xCAT::zvmUtils->trimStr($entries[0]); + my $ip = xCAT::NetworkUtils->getipaddr($node); + $ip = xCAT::zvmUtils->trimStr($ip); + my $mn = 0; + if ($master eq $ip) { + # If the master IP and node IP match, then it is the management node + my $out = `$sudo /bin/df -h /install | sed 1d`; + $out =~ s/\h+/ /g; + my @results = split(' ', $out); + return ($results[3]); + } + + return; +} + +#------------------------------------------------------- + +=head3 findAndUpdatezFcpPool + + Description : Find and update a SCSI/FCP device in a given storage pool. + xCAT will find and update the SCSI/FCP device in all known pools based on the unique WWPN/LUN combo. + Arguments : Message header + User (root or non-root) + zHCP + Storage pool + Criteria hash including: + - Status (free, reserved, or used) + - zFCP channel + - WWPN + - LUN + - Size requested + - Owner + - Tag + Returns : Results hash including: + - Return code (0 = Success, -1 = Failure) + - zFCP device (if one is requested) + - WWPN + - LUN + Example : my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $header, $user, $hcp, $pool, $criteriaRef); + +=cut + +#------------------------------------------------------- +sub findAndUpdatezFcpPool { + # Get inputs + my ($class, $callback, $header, $user, $hcp, $pool, $criteriaRef) = @_; + + # Determine if sudo is used + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where executables are on zHCP + my $dir = "/opt/zhcp/bin"; + + # Directory where FCP disk pools are on zHCP + my $zfcpDir = "/var/opt/zhcp/zfcp"; + + my %results = ('rc' => -1); # Default to error + + # Extract criteria + my %criteria = %$criteriaRef; + my $status = defined($criteria{status}) ? $criteria{status} : ""; + my $fcpDevice = defined($criteria{fcp}) ? $criteria{fcp} : ""; + my $wwpn = defined($criteria{wwpn}) ? $criteria{wwpn} : ""; + my $lun = defined($criteria{lun}) ? $criteria{lun} : ""; + my $size = defined($criteria{size}) ? $criteria{size} : ""; + my $owner = defined($criteria{owner}) ? $criteria{owner} : ""; + my $tag = defined($criteria{tag}) ? $criteria{tag} : ""; + + # Check required arguments: pool, status + # If you do not know what to update, why update! + if (!$pool && !$status) { + return \%results; + } + + # Check status + if ($status !~ m/^(free|used|reserved)$/i) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) Status not recognized. Status can be free, used, or reserved."); + return \%results; + } + + # Check FCP device syntax + if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f]/i)) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid FCP channel address $fcpDevice."); + return \%results; + } + + # Check WWPN and LUN syntax + if ( $wwpn && ($wwpn =~ /[^0-9a-f;"]/i) ) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid world wide portname $wwpn." ); + return \%results; + } if ( $lun && ($lun =~ /[^0-9a-f]/i) ) { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid logical unit number $lun." ); + return \%results; + } + + # Size can be M(egabytes) or G(igabytes). Convert size into MB. + my $originSize = $size; + if ($size) { + if ($size =~ m/G/i) { + # Convert to MegaBytes + $size =~ s/\D//g; + $size = int($size) * 1024 + } elsif ($size =~ m/M/i || !$size) { + # Do nothing + } else { + xCAT::zvmUtils->printLn( $callback, "$header: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)." ); + return \%results; + } + } + + # Check if WWPN and LUN are given + # WWPN can be given as a semi-colon separated list (multipathing) + my $useWwpnLun = 0; + if ($wwpn && $lun) { + xCAT::zvmUtils->printLn($callback, "$header: Using given WWPN and LUN"); + $useWwpnLun = 1; + + # Make sure WWPN and LUN do not have 0x prefix + $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + } + + # Find disk pool (create one if non-existent) + my $out; + if (!(`ssh $user\@$hcp "$sudo test -d $zfcpDir && echo Exists"`)) { + # Create pool directory + $out = `ssh $user\@$hcp "$sudo mkdir -p $zfcpDir"`; + } + + # Find if disk pool exists + if (!(`ssh $user\@$hcp "$sudo test -e $zfcpDir/$pool.conf && echo Exists"`)) { + # Return if xCAT is expected to find a FCP device, but no disk pool exists. + xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP storage pool does not exist"); + return \%results; + } + + # Find a free disk in the pool + # FCP devices are contained in /var/opt/zhcp/zfcp/.conf + my $range = ""; + my $sizeFound = "*"; + my @info; + if (!$useWwpnLun) { + # Find a suitable pair of WWPN and LUN in device pool based on requested size + # Sample pool configuration file: + # #status,wwpn,lun,size,range,owner,channel,tag + # used,1000000000000000,2000000000000110,8g,3B00-3B3F,ihost1,1a23,$root_device$ + # free,1000000000000000,2000000000000111,,3B00-3B3F,,, + # free,1230000000000000;4560000000000000,2000000000000112,,3B00-3B3F,,, + my @devices = split("\n", `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | egrep -i ^free`); + $sizeFound = 0; + foreach (@devices) { + @info = split(',', $_); + + # Check if the size is sufficient. Convert size into MB. + if ($info[3] =~ m/G/i) { + # Convert to MegaBytes + $info[3] =~ s/\D//g; + $info[3] = int($info[3]) * 1024 + } elsif ($info[3] =~ m/M/i) { + # Do nothing + $info[3] =~ s/\D//g; + } else { + next; + } + + # Find optimal disk based on requested size + if ($sizeFound && $info[3] >= $size && $info[3] < $sizeFound) { + $sizeFound = $info[3]; + $wwpn = $info[1]; + $lun = $info[2]; + $range = $info[4]; + } elsif (!$sizeFound && $info[3] >= $size) { + $sizeFound = $info[3]; + $wwpn = $info[1]; + $lun = $info[2]; + $range = $info[4]; + } + } + + # Do not continue if no devices can be found + if (!$wwpn && !$lun) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) A suitable device of $size" . "M or larger could not be found"); + return \%results; + } + } else { + # Find given WWPN and LUN. Do not continue if device is used + my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$wwpn,$lun"`; + chomp($select); + + @info = split(',', $select); + + if ($size) { + if ($info[3] =~ m/G/i) { + # Convert to MegaBytes + $info[3] =~ s/\D//g; + $info[3] = int($info[3]) * 1024 + } elsif ($info[3] =~ m/M/i) { + # Do nothing + $info[3] =~ s/\D//g; + } else { + next; + } + + # Do not continue if specified device does not have enough capacity + if ($info[3] < $size) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun is not large enough"); + return \%results; + } + } + + # Find range of the specified disk + $range = $info[4]; + } + + # If there are multiple paths, take the 1st one + # Handle multi-pathing in postscript because autoyast/kickstart does not support it. + my $origWwpn = $wwpn; + if ($wwpn =~ m/;/i) { + @info = split(';', $wwpn); + $wwpn = xCAT::zvmUtils->trimStr($info[0]); + } + + xCAT::zvmUtils->printLn($callback, "$header: Found FCP device 0x$wwpn/0x$lun"); + + # Find a free FCP device based on the given range + if ($fcpDevice =~ m/^auto/i) { + my @ranges; + my $min; + my $max; + my $found = 0; + + if ($range =~ m/;/i) { + @ranges = split(';', $range); + } else { + push(@ranges, $range); + } + + if (!$found) { + # If the node has an eligible FCP device, use it + my @deviceList = xCAT::zvmUtils->getDedicates($callback, $user, $owner); + foreach (@deviceList) { + # Check if this devide is eligible (among the range specified for disk $lun) + @info = split(' ', $_); + my $candidate = $info[2]; + foreach (@ranges) { + ($min, $max) = split('-', $_); + if (hex($candidate) >= hex($min) && hex($candidate) <= hex($max)) { + $found = 1; + $fcpDevice = uc($candidate); + + last; + } + } + + if ($found) { + xCAT::zvmUtils->printLn($callback, "$header: Found eligible FCP channel $fcpDevice"); + last; + } + } + } + + if (!$found) { + # If the node has no eligible FCP device, find a free one for it. + my %usedDevices = xCAT::zvmUtils->getUsedFcpDevices($user, $hcp); + + my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + + # Find a free FCP channel + $out = `ssh $user\@$hcp "$sudo $dir/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; + my @devices = split( "\n", $out ); + for (my $i = 0; $i < @devices; $i++) { + # Extract the device number and status + $fcpDevice = $devices[$i]; + $fcpDevice =~ s/^FCP device number:(.*)/$1/; + $fcpDevice =~ s/^\s+//; + $fcpDevice =~ s/\s+$//; + + $i++; + my $fcpStatus = $devices[$i]; + $fcpStatus =~ s/^Status:(.*)/$1/; + $fcpStatus =~ s/^\s+//; + $fcpStatus =~ s/\s+$//; + + # Only look at free FCP devices + if ($fcpStatus =~ m/free/i) { + # If the device number is within the specified range, exit out of loop + # Range: 3B00-3C00;4B00-4C00;5E12-5E12 + foreach (@ranges) { + ($min, $max) = split('-', $_); + if (hex($fcpDevice) >= hex($min) && hex($fcpDevice) <= hex($max)) { + $fcpDevice = uc($fcpDevice); + + # Used found FCP channel if not in use or allocated + if (!$usedDevices{$fcpDevice}) { + $found = 1; + last; + } + } + } + } + + # Break out of loop if FCP channel is found + if ($found) { + xCAT::zvmUtils->printLn($callback, "$header: Found FCP channel within acceptable range $fcpDevice"); + last; + } + } + } + + # Do not continue if no FCP channel is found + if (!$found) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) A suitable FCP channel could not be found"); + return \%results; + } + } + + # If there are multiple devices (multipathing), take the 1st one + if ($fcpDevice) { + if ($fcpDevice =~ m/;/i) { + @info = split(';', $fcpDevice); + $fcpDevice = xCAT::zvmUtils->trimStr($info[0]); + } + + # Make sure channel has a length of 4 + while (length($fcpDevice) < 4) { + $fcpDevice = "0" . $fcpDevice; + } + } + + # Mark WWPN and LUN as used, free, or reserved and set the owner/channel appropriately + # This config file keeps track of the owner of each device, which is useful in nodeset + $size = $size . "M"; + my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf" | grep -i "$lun" | grep -i "$wwpn"`; + chomp($select); + if ($select) { + @info = split(',', $select); + + if (!$info[3]) { + $info[3] = $size; + } + + # Do not update if WWPN/LUN pair is specified but the pair does not exist + if (!($info[1] =~ m/$wwpn/i)) { + xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun does not exists"); + return \%results; + } + + # Entry order: status,wwpn,lun,size,range,owner,channel,tag + # The following are never updated: wwpn, lun, size, and range + my $update = "$status,$info[1],$info[2],$info[3],$info[4],$owner,$fcpDevice,$tag"; + my $expression = "'s#" . $select . "#" .$update . "#i'"; + $out = `ssh $user\@$hcp "$sudo sed --in-place -e $expression $zfcpDir/$pool.conf"`; + } else { + # Insert device entry into file + $out = `ssh $user\@$hcp "$sudo echo \"$status,$origWwpn,$lun,$size,,$owner,$fcpDevice,$tag\" >> $zfcpDir/$pool.conf"`; + } + + # Generate results hash + %results = ( + 'rc' => 0, + 'fcp' => $fcpDevice, + 'wwpn' => $wwpn, + 'lun' => $lun + ); + return \%results; +} + +#------------------------------------------------------- + +=head3 findzFcpDevicePool + + Description : Find the zFCP storage pool that contains the given zFCP device + Arguments : User (root or non-root) + zHCP + WWPN + LUN + Returns : Storage pool where zFCP device resides + Example : my $pool = xCAT::zvmUtils->findzFcpDevicePool($user, $hcp, $wwpn, $lun); + +=cut + +#------------------------------------------------------- +sub findzFcpDevicePool { + + # Get inputs + my ( $class, $user, $hcp, $wwpn, $lun ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where FCP disk pools are on zHCP + my $zfcpDir = "/var/opt/zhcp/zfcp"; + + # Find the pool that contains the SCSI/FCP device + my @pools = split("\n", `ssh $user\@$hcp "$sudo grep -i -l \"$wwpn,$lun\" $zfcpDir/*.conf"`); + my $pool = ""; + if (scalar(@pools)) { + $pool = basename($pools[0]); + $pool =~ s/\.[^.]+$//; # Do not use extension + } + + return $pool; +} + +#------------------------------------------------------- + +=head3 findzFcpDeviceAttr + + Description : Find the zFCP device attributes + Arguments : User (root or non-root) + zHCP + Storage pool + WWPN + LUN + Returns : Architecture of node + Example : my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($user, $hcp, $wwpn, $lun); + +=cut + +#------------------------------------------------------- +sub findzFcpDeviceAttr { + + # Get inputs + my ( $class, $user, $hcp, $pool, $wwpn, $lun ) = @_; + + my $sudo = "sudo"; + if ($user eq "root") { + $sudo = ""; + } + + # Directory where FCP disk pools are on zHCP + my $zfcpDir = "/var/opt/zhcp/zfcp"; + + # Find the SCSI/FCP device + # Entry order: status,wwpn,lun,size,range,owner,channel,tag + my @info = split("\n", `ssh $user\@$hcp "$sudo grep \"$wwpn,$lun\" $zfcpDir/$pool.conf"`); + my $entry = $info[0]; + chomp($entry); + + # Do not continue if no device is found + my %attrs = (); + if (!$entry) { + return \%attrs; + } + + @info = split(',', $entry); + %attrs = ( + 'status' => $info[0], + 'wwpn' => $info[1], + 'lun' => $info[2], + 'size' => $info[3], + 'range' => $info[4], + 'owner' => $info[5], + 'fcp' => $info[6], + 'tag' => $info[7] + ); + + return \%attrs; } \ No newline at end of file diff --git a/xCAT-server/lib/xcat/plugins/zvm.pm b/xCAT-server/lib/xcat/plugins/zvm.pm index e35c29f50..e2b4108b4 100644 --- a/xCAT-server/lib/xcat/plugins/zvm.pm +++ b/xCAT-server/lib/xcat/plugins/zvm.pm @@ -19,6 +19,11 @@ use xCAT::Utils; use xCAT::TableUtils; use xCAT::ServiceNodeUtils; use xCAT::NetworkUtils; +use XML::Simple; +use File::Basename; +use File::Copy; +use File::Path; +use Time::HiRes; use POSIX; use Getopt::Long; use strict; @@ -143,7 +148,7 @@ sub process_request { # Directory where zFCP disk pools are on zHCP $::ZFCPPOOL = "/var/opt/zhcp/zfcp"; - + # Use sudo or not # This looks in the passwd table for a key = sudoer ($::SUDOER, $::SUDO) = xCAT::zvmUtils->getSudoer(); @@ -693,7 +698,7 @@ sub removeVM { my @info = split(',', $_); $update = "free,$info[1],$info[2],$info[3],$info[4],,,"; $expression = "'s#" . $_ . "#" .$update . "#i'"; - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e $expression $::ZFCPPOOL/$pool.conf"`; + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e $expression $::ZFCPPOOL/$pool.conf"`; } if (@luns) { @@ -829,7 +834,7 @@ sub changeVM { xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $userId -v $addr -t 3390 -a AUTOG -r $pool -u 1 -z $cyl -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"); # Add to active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); @@ -902,7 +907,7 @@ sub changeVM { xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create_DM -T $userId -v $addr -t 9336 -a AUTOG -r $pool -u 2 -z $blks -m $mode -f 1 -R $readPw -W $writePw -M $multiPw"); # Add to active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Disk_Create -T $userId -v $addr -m $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Disk_Create -T $userId -v $addr -m $mode"); @@ -929,7 +934,7 @@ sub changeVM { my $devcount = $args->[3]; # Add to active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out = `ssh $::SUDOER\@$node "/sbin/vmcp define nic $addr type $type"`; } @@ -996,226 +1001,94 @@ sub changeVM { # addzfcp [pool] [device address (or auto)] [loaddev (0 or 1)] [size] [tag (optional)] [wwpn (optional)] [lun (optional)] elsif ( $args->[0] eq "--addzfcp" ) { - # Find a free disk in the pool - # zFCP devices are contained in /var/opt/zhcp/zfcp/.conf - my $pool = lc($args->[1]); - my $device = $args->[2]; - my $argsSize = @{$args}; - if ($argsSize < 5 || $argsSize > 8) { + if ( ($argsSize != 5) && ($argsSize != 6) && ($argsSize != 8) ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } + my $pool = lc($args->[1]); + my $device = $args->[2]; my $loaddev = int($args->[3]); if ($loaddev != 0 && $loaddev != 1) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) The loaddev can be 0 or 1" ); return; } - - # Size can be M(egabytes) or G(igabytes) - my $size = $args->[4]; - if ($size =~ m/G/i) { - # Convert to MegaBytes - $size =~ s/\D//g; - $size = int($size) * 1024 - } elsif ($size =~ m/M/i || !$size) { - # Do nothing - } else { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)." ); - return; - } - + my $size = $args->[4]; # Tag specifies what to replace in the autoyast/kickstart template, e.g. $root_device$ # This argument is optional my $tag = $args->[5]; # Check if WWPN and LUN are given # WWPN can be given as a semi-colon separated list - my $wwpn; - my $lun; + my $wwpn = ""; + my $lun = ""; my $useWwpnLun = 0; if ($argsSize == 8) { $useWwpnLun = 1; $wwpn = $args->[6]; $lun = $args->[7]; - - # Make sure WWPN and LUN do not have 0x prefix - $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); - $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); } - # Find disk pool (create one if non-existent) - if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -d $::ZFCPPOOL && echo Exists"`)) { - # Create pool directory - $out = `ssh $::SUDOER\@$hcp "$::SUDO mkdir -p $::ZFCPPOOL"`; - } - - if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -e $::ZFCPPOOL/$pool.conf && echo Exists"`)) { - if (!$useWwpnLun) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Device pool does not exist" ); - return; - } - - # Create pool configuration file - # Update file with given WWPN and LUN - $out = `ssh $::SUDOER\@$hcp "$::SUDO echo '#status,wwpn,lun,size,owner,channel,tag' > $::ZFCPPOOL/$pool.conf"`; - $out = `ssh $::SUDOER\@$hcp "$::SUDO echo \"free,$wwpn,$lun,,,,\" >> $::ZFCPPOOL/$pool.conf"`; - } - - my $sizeFound = "*"; - my $range = ""; - my @info; - my @tmp; - - # Find a suitable pair of WWPN and LUN in device pool based on requested size - # Sample pool configuration file: - # #status,wwpn,lun,size,range,owner,channel,tag - # used,1000000000000000,2000000000000110,8g,3B00-3B3F,ihost1,1a23,$root_device$ - # free,1000000000000000,2000000000000111,,3B00-3B3F,,, - # free,1230000000000000;4560000000000000,2000000000000112,,3B00-3B3F,,, - if (!$useWwpnLun) { - my @devices = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -i free`); - $sizeFound = 0; - foreach (@devices) { - @info = split(',', $_); - - # Check if the size is sufficient - if ($info[3] =~ m/G/i) { - # Convert to MegaBytes - $info[3] =~ s/\D//g; - $info[3] = int($info[3]) * 1024 - } elsif ($info[3] =~ m/M/i) { - # Do nothing - $info[3] =~ s/\D//g; - } else { - next; - } - - # Find optimal disk based on requested size - if ($sizeFound && $info[3] >= $size && $info[3] < $sizeFound) { - $sizeFound = $info[3]; - $wwpn = $info[1]; - $lun = $info[2]; - $range = $info[4]; - } elsif (!$sizeFound && $info[3] >= $size) { - $sizeFound = $info[3]; - $wwpn = $info[1]; - $lun = $info[2]; - $range = $info[4]; - } - } + # Find a suitable SCSI/FCP device in the zFCP storage pool + my %criteria; + my $resultsRef; + if ($useWwpnLun) { + %criteria = ( + 'status' => 'used', + 'fcp' => $device, + 'wwpn' => $wwpn, + 'lun' => $lun, + 'size' => $size, + 'owner' => $node, + 'tag' => $tag + ); + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + } else { + # Do not know the WWPN or LUN in this case + %criteria = ( + 'status' => 'used', + 'fcp' => $device, + 'size' => $size, + 'owner' => $node, + 'tag' => $tag + ); + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); } - # If there are multiple paths, take the 1st one - # Handle multi-pathing in postscript because autoyast/kickstart does not support it. - my $origWwpn = $wwpn; - if ($wwpn =~ m/;/i) { - @tmp = split(';', $wwpn); - $wwpn = xCAT::zvmUtils->trimStr($tmp[0]); - } - - # Do not continue if no devices can be found - if (!$wwpn && !$lun) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) A suitable device could not be found"); + my %results = %$resultsRef; + if ($results{'rc'} == -1) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to add zFCP device"); return; } - xCAT::zvmUtils->printLn($callback, "$node: Using device with WWPN/LUN of $wwpn/$lun"); - # Find a free FCP device based on the given range - my @ranges; - my $min; - my $max; - if ($device =~ m/auto/i) { - my %usedDevices = xCAT::zvmUtils->getUsedFcpDevices($::SUDOER, $hcp); - - if ($device =~ m/,/i) { - @ranges = split(';', $range); - } else { - push(@ranges, $range); - } - - # Find a free FCP device channel - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; - xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId | egrep -i FCP device number|Status"); - my @devices = split( "\n", $out ); - my $status; - my $found = 0; - my $i; - for ($i = 0; $i < @devices; $i++) { - # Extract the device number and status - $device = $devices[$i]; - $device =~ s/^FCP device number:(.*)/$1/; - $device =~ s/^\s+//; - $device =~ s/\s+$//; - - $i++; - $status = $devices[$i]; - $status =~ s/^Status:(.*)/$1/; - $status =~ s/^\s+//; - $status =~ s/\s+$//; - - # Only look at free FCP devices - if ($status =~ m/free/i) { - # If the device number is within the specified range, exit out of loop - # Range: 3B00-3C00;4B00-4C00 - foreach (@ranges) { - ($min, $max) = split('-', $_); - if (hex($device) >= hex($min) && hex($device) <= hex($max)) { - $found = 1; - $device = uc($device); - - # Used found zFCP channel if not in use or allocated - if (!$usedDevices{$device}) { - last; - } - } - } - } - - # Break out of loop if FCP device channel is found - if ($found) { - last; - } - } - - # Do not continue if no FCP channel is found - if (!$found) { - xCAT::zvmUtils->printLn($callback, "$node: (Error) A suitable FCP device channel could not be found"); - return; - } - } - - # If there are multiple devices (multipathing), take the 1st one - my $origDevice = $device; - if ($device =~ m/;/i) { - @tmp = split(';', $device); - $device = xCAT::zvmUtils->trimStr($tmp[0]); - } - - # Make sure channel has a length of 4 - while (length($device) < 4) { - $device = "0" . $device; - } + # Obtain the device assigned by xCAT + $device = $results{'fcp'}; + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; # Get user directory entry my $userEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; - xCAT::zvmUtils->printSyslog("smcli Image_Query_DM -T $userId | sed '\$d'"); # Find DEDICATE statement in the entry (dedicate one if one does not exist) my $dedicate = `echo "$userEntry" | egrep -i "DEDICATE $device"`; if (!$dedicate) { - $out = `chvm $node --dedicatedevice $device $device 0`; + $out = `/opt/xcat/bin/chvm $node --dedicatedevice $device $device 0`; xCAT::zvmUtils->printLn($callback, "$out"); - if (xCAT::zvmUtils->checkOutput( $callback, $out ) == -1) { + if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { + # Exit if dedicate failed return; } } - # Configure FCP inside node (if online) + # Configure native SCSI/FCP inside node (if online) my $cmd; - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { + # Add the dedicated device to the active config + # Ignore any errors since it might be already dedicated + $out = `ssh $::SUDOER\@$node "$::SUDO $::DIR/smcli Image_Device_Dedicate -T $userId -v $device -r $device -R MR"`; + xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate -T $userId -v $device -r $device -R MR"); # Online device $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $node, "-e", "0.0." . $device); @@ -1226,7 +1099,11 @@ sub changeVM { # Set WWPN and LUN in sysfs $device = lc($device); - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$wwpn > /sys/bus/ccw/drivers/zfcp/0.0.$device/port_add"); + $wwpn = lc($wwpn); + + # For the version above RHEL6 or SLES11, the port_add is removed + # Keep the code here for lower editions, of course, ignore the potential errors + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$wwpn > /sys/bus/ccw/drivers/zfcp/0.0.$device/port_add"); $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$device/0x$wwpn/unit_add"); # Get source node OS @@ -1250,7 +1127,7 @@ sub changeVM { $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$wwpn:0x$lun >> /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$device"); } elsif ( $os =~ m/sles11/i ) { - $out = `ssh $::SUDOER\@$node "$::SUDO /sbin/zfcp_host_configure 0.0.$device 1"`; + $out = `ssh $::SUDOER\@$node "$::SUDO /sbin/zfcp_host_configure 0.0.$device 1"`; if ($out) { xCAT::zvmUtils->printLn($callback, "$node: $out"); } @@ -1266,64 +1143,48 @@ sub changeVM { # Check if the file already contains the zFCP channel $out = `ssh $::SUDOER\@$node "$::SUDO cat /etc/udev/rules.d/51-zfcp-0.0.$device.rules" | egrep -i "ccw/0.0.$device]online"`; if (!$out) { - $tmp = "'ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.$device\", IMPORT{program}=\"collect 0.0.$device \%k 0.0.$device zfcp\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - - $tmp = "'ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.$device \%k 0.0.$device zfcp\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; - - $tmp = "'ACTION==\"add\", ENV{COLLECT_0.0.$device}==\"0\", ATTR{[ccw/0.0.$device]online}=\"1\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; + $tmp = "'ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.$device\", IMPORT{program}=\"collect 0.0.$device \%k 0.0.$device zfcp\"'"; + $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); + $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; + + $tmp = "'ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.$device \%k 0.0.$device zfcp\"'"; + $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); + $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; + + $tmp = "'ACTION==\"add\", ENV{COLLECT_0.0.$device}==\"0\", ATTR{[ccw/0.0.$device]online}=\"1\"'"; + $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); + $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; } - $tmp = "'ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x$wwpn\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.$device\", ATTR{[ccw/0.0.$device]0x$wwpn/unit_add}=\"0x$lun\"'"; - $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); - $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; + $tmp = "'ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x$wwpn\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.$device\", ATTR{[ccw/0.0.$device]0x$wwpn/unit_add}=\"0x$lun\"'"; + $tmp = xCAT::zvmUtils->replaceStr($tmp, '"', '\\"'); + $out = `ssh $::SUDOER\@$node "echo $tmp | $::SUDO tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; } elsif ( $os =~ m/rhel/i ) { $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo \"0.0.$device 0x$wwpn 0x$lun\" >> /etc/zfcp.conf"); if ($os =~ m/rhel6/i) { - $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo add > /sys/bus/ccw/devices/0.0.$device/uevent"); + $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo add > /sys/bus/ccw/devices/0.0.$device/uevent"); } } xCAT::zvmUtils->printLn($callback, "$node: Configuring FCP device to be persistent... Done"); + $out = ""; } - # Mark WWPN and LUN as used and set the owner/channel - # This config file keeps track of the owner of each device, which is useful in nodeset - $size = $size . "M"; - my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -i $wwpn | egrep -i $lun`; - chomp($select); - if ($select) { - @info = split(',', $select); - if (!$info[3]) { - $info[3] = $size; - } - - # Entry order: status,wwpn,lun,size,owner,channel,tag - my $update = "used,$info[1],$lun,$info[3],$info[4],$node,$device,$tag"; - my $expression = "'s#" . $select . "#" .$update . "#i'"; - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e $expression $::ZFCPPOOL/$pool.conf"`; - } else { - # Insert device entry into file - $out = `ssh $::SUDOER\@$hcp "$::SUDO echo \"used,$origWwpn,$lun,$size,,$node,$origDevice,$tag\" >> $::ZFCPPOOL/$pool.conf"`; - } - - xCAT::zvmUtils->printLn($callback, "$node: Adding FCP device... Done"); - # Set loaddev statement in directory entry if ($loaddev) { - $out = `chvm $node --setloaddev $wwpn $lun`; + $out = `/opt/xcat/bin/chvm $node --setloaddev $wwpn $lun`; xCAT::zvmUtils->printLn($callback, "$out"); + if (xCAT::zvmUtils->checkOutput( $callback, $out ) == -1) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to set LOADDEV statement in the directory entry"); + return; + } + $out = ""; } - $out = ""; + xCAT::zvmUtils->printLn($callback, "$node: Adding zFCP device $device/$wwpn/$lun... Done"); } - + # connectnic2guestlan [address] [lan] [owner] elsif ( $args->[0] eq "--connectnic2guestlan" ) { my $addr = $args->[1]; @@ -1331,7 +1192,7 @@ sub changeVM { my $owner = $args->[3]; # Connect to LAN in active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_LAN -T $userId -v $addr -l $lan -o $owner"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_LAN -T $userId -v $addr -l $lan -o $owner"); @@ -1356,7 +1217,7 @@ sub changeVM { xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_Vswitch_DM -T $userId -v $addr -n $vswitch"); # Connect to VSwitch in active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Virtual_Network_Adapter_Connect_Vswitch -T $userId -v $addr -n $vswitch"`; xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Adapter_Connect_Vswitch -T $userId -v $addr -n $vswitch"); @@ -1610,7 +1471,39 @@ sub changeVM { $out = "$tgtNode: Done"; } - + + # createfilesysnode [source file] [target file] + elsif ( $args->[0] eq "--createfilesysnode" ) { + my $srcFile = $args->[1]; + my $tgtFile = $args->[2]; + + my $argsSize = @{$args}; + if ($argsSize != 3) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + return; + } + + $out = `ssh $::SUDOER\@$node "$::SUDO /usr/bin/stat --printf=%n $tgtFile"`; + if ($out eq $tgtFile) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) $tgtFile already exists"); + return; + } + + $out = `ssh $::SUDOER\@$node "$::SUDO /usr/bin/stat --printf=%n $srcFile"`; + if ($out ne $srcFile) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) $srcFile does not exist"); + return; + } + + $out = `ssh $::SUDOER\@$node "$::SUDO /usr/bin/stat -L --printf=%t:%T $srcFile"`; + if ($out != '') { + my @device = split(":", $out); + my $major = sprintf("%d", hex($device[0])); + my $minor = sprintf("%d", hex($device[1])); + $out = `ssh $::SUDOER\@$node "$::SUDO /bin/mknod $tgtFile b $major $minor "`; + } + } + # dedicatedevice [virtual device] [real device] [mode (1 or 0)] elsif ( $args->[0] eq "--dedicatedevice" ) { my $vaddr = $args->[1]; @@ -1623,7 +1516,7 @@ sub changeVM { xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Dedicate device to active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Dedicate -T $userId -v $vaddr -r $raddr -R $mode"`; xCAT::zvmUtils->printSyslog("smcli Image_Device_Dedicate -T $userId -v $vaddr -r $raddr -R $mode"); @@ -1678,8 +1571,8 @@ sub changeVM { # If link fails if ( $out =~ m/not linked/i || $out =~ m/DASD $tgtLinkAddr forced R\/O/i ) { - # Detatch link because only linked as R/O - `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; + # Detatch link because only linked as R/O + `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp det $tgtLinkAddr"`; # Wait before trying again sleep(5); @@ -1751,6 +1644,67 @@ sub changeVM { $out = xCAT::zvmUtils->appendHostname( $node, $out ); } + # punchfile [file path] [class (optional)] [remote host (optional)] + elsif ( $args->[0] eq "--punchfile" ) { + # Punch a file to a the node reader + my $argsSize = @{$args}; + if (($argsSize < 2) || ($argsSize > 4)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + my $filePath = $args->[1]; + my $class = "A"; # Default spool class should be A + my $remoteHost; + if ($argsSize > 2) { + $class = $args->[2]; + } if ($argsSize > 3) { + $remoteHost = $args->[3]; # Must be specified as user@host + } + + # Obtain file name + my $fileName = basename($filePath); + + # Validate class + if ($class !~ /^[a-zA-Z0-9]$/) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid spool class: $class. It should be 1-character alphanumeric" ); + return; + } + + # If a remote host is specified, obtain the file from the remote host + # The xCAT public SSH key must have been already setup if this is to work + my $rc; + if (defined $remoteHost) { + $rc = `/usr/bin/scp $remoteHost:$filePath /tmp/$fileName 2>/dev/null; echo $?`; + } else { + $rc = `/bin/cp $filePath /tmp/$fileName 2>/dev/null; echo $?`; + } + + if ($rc != '0') { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy over source file" ); + return; + } + + # Set up punch device and class + $rc = `ssh $::SUDOER\@$hcp "$::SUDO cio_ignore -r d"`; + xCAT::zvmUtils->disableEnableDisk($::SUDOER, $hcp, "-e", "d"); + $rc = `ssh $::SUDOER\@$hcp "$::SUDO vmcp spool punch class $class"`; + + # Send over file to zHCP and punch it to the node reader + $filePath = "/tmp/$fileName"; + xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $filePath, $filePath); + $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $filePath, $fileName, ""); + + # No extra steps are needed if the punch succeeded or failed, just output the results + xCAT::zvmUtils->printLn( $callback, "$node: Punching $fileName to reader... $out" ); + + # Remove temporary file and restore punch class + `rm -rf $filePath`; + `ssh $::SUDOER\@$hcp "$::SUDO rm -f /tmp/$fileName"`; + `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp spool punch class A"`; + $out = ""; + } + # purgerdr elsif ( $args->[0] eq "--purgerdr" ) { # Purge the reader of node @@ -1775,7 +1729,7 @@ sub changeVM { my $addr = $args->[1]; # Remove from active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out = xCAT::zvmUtils->disableEnableDisk( $::SUDOER, $node, "-d", $addr ); $out = `ssh $node "/sbin/vmcp det $addr"`; @@ -1786,13 +1740,30 @@ sub changeVM { xCAT::zvmUtils->printSyslog("smcli Image_Disk_Delete_DM -T $userId -v $addr -e 0"); $out = xCAT::zvmUtils->appendHostname( $node, $out ); } - + + # removefilesysnode [target file] + elsif ( $args->[0] eq "--removefilesysnode" ) { + my $tgtFile = $args->[1]; + + my $argsSize = @{$args}; + if ($argsSize != 2) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + # Unmount this disk, but ignore the output + $out = `ssh $::SUDOER\@$node "$::SUDO umount $tgtFile"`; + $out = `ssh $::SUDOER\@$node "$::SUDO rm -f $tgtFile"`; + + xCAT::zvmUtils->printLn($callback, "$node: Removing file system node $tgtFile... Done"); + } + # removenic [address] elsif ( $args->[0] eq "--removenic" ) { my $addr = $args->[1]; # Remove from active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out = `ssh $node "/sbin/vmcp det nic $addr"`; } @@ -1858,7 +1829,7 @@ sub changeVM { # Replace user directory entry (if necessary) if ($updateEntry) { - $out = `chvm $node --replacevs $userEntryFile`; + $out = `/opt/xcat/bin/chvm $node --replacevs $userEntryFile`; xCAT::zvmUtils->printLn($callback, "$out"); # Delete directory entry file @@ -1870,59 +1841,86 @@ sub changeVM { $out = ""; } - # removezfcp [device address] [wwpn] [lun] + # removezfcp [device address] [wwpn] [lun] [persist (0 or 1)] elsif ( $args->[0] eq "--removezfcp" ) { my $device = $args->[1]; my $wwpn = $args->[2]; my $lun = $args->[3]; - - my $argsSize = @{$args}; - if ($argsSize != 4) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); - return; - } - - # Make sure WWPN and LUN do not have 0x prefix + my $persist = "0"; # Optional + + # Delete 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); - - # Make sure channel has a length of 4 - while (length($device) < 4) { - $device = "0" . $device; + + my $argsSize = @{$args}; + if ($argsSize != 4 && $argsSize != 5) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + return; } - # Update device pool - my $update; - my $expression; - - # Go through each pool - # It is okay not to find the LUN in a device pool. xCAT should - # continue to delete device from node. - my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO ls $::ZFCPPOOL"`); - my $pool; - my $tmp; - foreach (@pools) { - $tmp = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$_" | egrep -i $wwpn | egrep -i $lun`; - chomp($tmp); - if ($tmp) { - # Mark WWPN and LUN as free and delete owner/channel - $pool = xCAT::zvmUtils->replaceStr( $_, ".conf", "" ); - - # Update entry: status,wwpn,lun,size,range,owner,channel,tag - my @info = split(',', $tmp); - $update = "free,$info[1],$lun,$info[3],$info[4],,,"; - $expression = "'s#" . $tmp . "#" .$update . "#i'"; - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e $expression $::ZFCPPOOL/$pool.conf"`; - - xCAT::zvmUtils->printLn($callback, "$node: Updating FCP device pool... Done"); - } + if ($argsSize == 5) { + $persist = $args->[4]; } + # Check the value of persist + if ($persist !~ /^[01]$/) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Persist can only be 0 or 1"); + return; + } + $persist = int($persist); + + # Find the pool that contains the SCSI/FCP device + my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun); + if (!$pool) { + # Continue to try and remove the SCSI/FCP device even when it is not found in a storage pool + xCAT::zvmUtils->printLn( $callback, "$node: Could not find FCP device in any FCP storage pool" ); + } else { + xCAT::zvmUtils->printLn( $callback, "$node: Found FCP device in $pool" ); + + # If the device is not known, try to find it in the storage pool + if ($device !~ /^[0-9a-f]/i) { + my $select = `ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | grep -i "$wwpn,$lun"`; + chomp($select); + my @info = split(',', $select); + if ($device) { + $device = $info[6]; + } + } + + my $status = "free"; + my $owner = ""; + if ($persist) { + # Keep the device reserved if persist = 1 + $status = "reserved"; + $owner = $node; + } + + my %criteria = ( + 'status' => $status, + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $owner, + ); + my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + my %results = %$resultsRef; + + if ($results{'rc'} == -1) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find zFCP device"); + return; + } + + # Obtain the device assigned by xCAT + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; + } + # De-configure SCSI over FCP inside node (if online) - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { # Delete WWPN and LUN from sysfs $device = lc($device); + $wwpn = lc($wwpn); + # unit_remove does not exist on SLES 10! $out = xCAT::zvmUtils->rExecute($::SUDOER, $node, "echo 0x$lun > /sys/bus/ccw/drivers/zfcp/0.0.$device/0x$wwpn/unit_remove"); @@ -1933,15 +1931,16 @@ sub changeVM { # RHEL: /etc/zfcp.conf # SLES 10: /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-* # SLES 11: /etc/udev/rules.d/51-zfcp* + my $expression = ""; if ( $os =~ m/sles10/i ) { $expression = "/$lun/d"; - $out = `ssh $::SUDOER\@$node "$::SUDO sed --in-place -e $expression /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$device"`; + $out = `ssh $::SUDOER\@$node "$::SUDO sed -i -e $expression /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$device"`; } elsif ( $os =~ m/sles11/i ) { $expression = "/$lun/d"; - $out = `ssh $::SUDOER\@$node "$::SUDO sed --in-place -e $expression /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; + $out = `ssh $::SUDOER\@$node "$::SUDO sed -i -e $expression /etc/udev/rules.d/51-zfcp-0.0.$device.rules"`; } elsif ( $os =~ m/rhel/i ) { $expression = "/$lun/d"; - $out = `ssh $::SUDOER\@$node "$::SUDO sed --in-place -e $expression /etc/zfcp.conf"`; + $out = `ssh $::SUDOER\@$node "$::SUDO sed -i -e $expression /etc/zfcp.conf"`; } xCAT::zvmUtils->printLn($callback, "$node: De-configuring FCP device on host... Done"); @@ -1979,8 +1978,7 @@ sub changeVM { `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Unlock_DM -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Unlock_DM -T $userId"); - # Delete file on xCAT and zHCP - `rm -rf $file`; + # Delete file on zHCP `ssh $::SUDOER\@$hcp "rm -rf $file"`; } else { xCAT::zvmUtils->printLn( $callback, "$node: (Error) File does not exist" ); @@ -1997,10 +1995,10 @@ sub changeVM { # Write directory entry into temporary file # because directory entry cannot be remotely echoed into stdin foreach (@lines) { - if ($_) { + if ($_) { $_ = "'" . $_ . "'"; `ssh $::SUDOER\@$hcp "echo $_ >> $file"`; - } + } } # Lock image @@ -2121,7 +2119,7 @@ sub changeVM { # Replace user directory entry (if necessary) if ($updateEntry) { - $out = `chvm $node --replacevs $userEntryFile`; + $out = `/opt/xcat/bin/chvm $node --replacevs $userEntryFile`; xCAT::zvmUtils->printLn( $callback, "$out"); # Delete directory entry file @@ -2143,7 +2141,7 @@ sub changeVM { xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Undedicate device in active configuration - my $ping = `pping $node`; + my $ping = `/opt/xcat/bin/pping $node`; if (!($ping =~ m/noping/i)) { $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Device_Undedicate -T $userId -v $vaddr"`; xCAT::zvmUtils->printSyslog("smcli Image_Device_Undedicate -T $userId -v $vaddr"); @@ -2305,10 +2303,10 @@ sub powerVM { # Power off virtual server (gracefully) elsif ( $args->[0] eq 'softoff' ) { - if (`pping $node` !~ m/noping/i) { + if (`/opt/xcat/bin/pping $node` !~ m/noping/i) { $out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -h now"`; sleep(15); # Wait 15 seconds before logging user off - } + } $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Deactivate -T $userId"`; xCAT::zvmUtils->printSyslog("smcli Image_Deactivate -T $userId"); @@ -2349,14 +2347,14 @@ sub powerVM { # Reboot a virtual server elsif ( $args->[0] eq 'reboot' ) { my $timeout = 0; - $out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -r now"; echo $?`; - if ( $out != 0 ) { - xCAT::zvmUtils->printLn( $callback, "$node: Connect to $userId... Failed\n" ); + $out = `ssh -o ConnectTimeout=10 $::SUDOER\@$node "shutdown -r now &>/dev/null && echo Done"`; + if (!($out =~ m/Done/)) { + xCAT::zvmUtils->printLn( $callback, "$node: Connecting to $node... Failed\n" ); return; } # Wait until node is down or 180 seconds - while ((`pping $node` !~ m/noping/i) && $timeout < 180) { + while ((`/opt/xcat/bin/pping $node` !~ m/noping/i) && $timeout < 180) { sleep(1); $timeout++; } @@ -2369,7 +2367,7 @@ sub powerVM { # Wait until node is up or 180 seconds $timeout = 0; - while ((`pping $node` =~ m/noping/i) && $timeout < 180) { + while ((`/opt/xcat/bin/pping $node` =~ m/noping/i) && $timeout < 180) { sleep(1); $timeout++; } @@ -2622,7 +2620,7 @@ sub inventoryVM { my $str = ""; # Check if node is pingable - if (`pping $node | egrep -i "noping"`) { + if (`/opt/xcat/bin/pping $node | egrep -i "noping"`) { $str = "$node: (Error) Host is unreachable"; xCAT::zvmUtils->printLn( $callback, "$str" ); return; @@ -2731,7 +2729,7 @@ sub inventoryVM { $str .= "Architecture: $arch\n"; $str .= "HCP: $hcp\n"; $str .= "Uptime: $uptime\n"; - $str .= "CPU Used Time: $cputime\n"; + $str .= "CPU Used Time: $cputime\n"; $str .= "Privileges: \n$priv\n"; $str .= "Total Memory: $memory\n"; $str .= "Max Memory: $maxMem\n"; @@ -2741,6 +2739,17 @@ sub inventoryVM { $str .= "zFCP: \n$zfcp\n"; } $str .= "NICs: \n$nic\n"; + } elsif ( $args->[0] eq '--freerepospace' ) { + + # Get /install available disk size + my $freespace = xCAT::zvmUtils->getFreeRepoSpace($::SUDOER, $node); + + # Create output string + if ($freespace) { + $str .= "Free Image Repository: $freespace\n"; + } else { + return; + } } else { $str = "$node: (Error) Option not supported"; xCAT::zvmUtils->printLn( $callback, "$str" ); @@ -2930,8 +2939,8 @@ sub makeVM { } # If one of the options above are given, create the user without a directory entry file - if ($profileName || $password || $memorySize || $diskPool || $diskSize) { - if (!$profileName || !$password || !$memorySize || !$diskPool || !$diskSize) { + if ($profileName || $password || $memorySize) { + if (!$profileName || !$password || !$memorySize) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more required parameter(s)" ); return; } @@ -2940,12 +2949,7 @@ sub makeVM { if (!$privilege) { $privilege = 'G'; } - - # Default disk virtual device to 0100 if none is given - if (!$diskVdev) { - $diskVdev = "0100"; - } - + # Generate temporary user directory entry file my $userEntryFile = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount); if ( $userEntryFile == -1 ) { @@ -2954,19 +2958,31 @@ sub makeVM { } # Create a new user in z/VM without disks - $out = `mkvm $node $userEntryFile`; + $out = `/opt/xcat/bin/mkvm $node $userEntryFile`; xCAT::zvmUtils->printLn( $callback, "$out"); if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { # The error would have already been printed under mkvm return; } - # Add disk(s) to this new user - $out = `chvm $node --add3390 $diskPool $diskVdev $diskSize`; - xCAT::zvmUtils->printLn( $callback, "$out"); - if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { - # The error would have already been printed under chvm - return; + # If one of the disk operations are given, add disk(s) to this new user + if ($diskPool || $diskSize) { + if (!$diskPool || !$diskSize) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more required parameter(s) for adding disk" ); + return; + } + + # Default disk virtual device to 0100 if none is given + if (!$diskVdev) { + $diskVdev = "0100"; + } + + $out = `/opt/xcat/bin/chvm $node --add3390 $diskPool $diskVdev $diskSize`; + xCAT::zvmUtils->printLn( $callback, "$out"); + if (xCAT::zvmUtils->checkOutput($callback, $out) == -1) { + # The error would have already been printed under chvm + return; + } } # Remove the temporary file @@ -2996,26 +3012,26 @@ sub makeVM { $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); $macId = substr( $macId, 6 ); } else { - $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; - - # Get USER Prefix - my $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -i "USER Prefix:"`; + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; + + # Get USER Prefix + my $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -i "USER Prefix:"`; $prefix =~ s/(.*?)USER Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; - + # Get MACADDR Prefix instead if USER Prefix is not defined if (!$prefix) { - $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -i "MACADDR Prefix:"`; + $prefix = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q vmlan" | egrep -i "MACADDR Prefix:"`; $prefix =~ s/(.*?)MACADDR Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; - if (!$prefix) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not find the MACADDR/USER prefix of the z/VM system" ); - xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the node's zHCP($hcp) is correct, the node is online, and the SSH keys are setup for the zHCP" ); - return; - } + if (!$prefix) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not find the MACADDR/USER prefix of the z/VM system" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Verify that the node's zHCP($hcp) is correct, the node is online, and the SSH keys are setup for the zHCP" ); + return; + } } # Generate MAC address @@ -3102,10 +3118,10 @@ sub makeVM { $oldNicDef = `cat $userEntry | egrep -i "NICDEF" | egrep -i "$netName"`; if ($oldNicDef) { $oldNicDef = xCAT::zvmUtils->trimStr($oldNicDef); - $nicDef = xCAT::zvmUtils->replaceStr( $oldNicDef, $netName, "$netName MACID $macId" ); + $nicDef = xCAT::zvmUtils->replaceStr($oldNicDef, $netName, "$netName MACID $macId"); # Append MACID at the end - $out = `sed --in-place -e "s,$oldNicDef,$nicDef,i" $userEntry`; + $out = `sed -i -e "s,$oldNicDef,$nicDef,i" $userEntry`; } } @@ -3119,7 +3135,7 @@ sub makeVM { $id = $words[1]; # Change userID in user entry to match userID defined in xCAT - $out = `sed --in-place -e "s,$id,$userId,i" $userEntry`; + $out = `sed -i -e "s,$id,$userId,i" $userEntry`; # SCP file over to zHCP $out = `scp $userEntry $target:$userEntry`; @@ -3149,9 +3165,9 @@ sub makeVM { # Remove user entry on xCAT $out = `rm -rf $userEntry`; - } elsif ($stdin) { + } elsif ($stdin) { # Take directory entry from stdin - $stdin = $::STDIN; + $stdin = $::STDIN; # If the directory entry contains a NICDEF statement, append MACID to the end # User must select the right one (layer) based on template chosen @@ -3439,19 +3455,19 @@ sub cloneVM { # Get disk size (cylinders or blocks) # ECKD or FBA disk if ( $type eq '3390' || $type eq '9336' ) { - my @lines = split( '\n', $srcDiskDet ); - - # Loop through each line - for ( $i = 0 ; $i < @lines ; $i++ ) { - $lines[$i] =~ s/MDISK=//g; - - # Extract NIC address - @words = ($lines[$i] =~ m/=(\S+)/g); - my $srcDiskAddr = $words[0]; + my @lines = split( '\n', $srcDiskDet ); + + # Loop through each line + for ( $i = 0 ; $i < @lines ; $i++ ) { + $lines[$i] =~ s/MDISK=//g; + + # Extract NIC address + @words = ($lines[$i] =~ m/=(\S+)/g); + my $srcDiskAddr = $words[0]; $srcDiskSize{$srcDiskAddr} = $words[3]; xCAT::zvmUtils->printSyslog("addr:$addr type:$type srcDiskAddr:$srcDiskAddr srcDiskSize:$words[3]"); - } + } } # If source disk is not linked @@ -3599,13 +3615,13 @@ sub cloneVM { if ( $out =~ m/USER $sourceId/i ) { # Turn off source node - if (`pping $sourceNode` =~ m/ ping/i) { + if (`/opt/xcat/bin/pping $sourceNode` =~ m/ ping/i) { $out = `ssh -o ConnectTimeout=10 $sourceNode "shutdown -h now"`; sleep(90); # Wait 1.5 minutes before logging user off - foreach (@nodes) { - xCAT::zvmUtils->printLn( $callback, "$_: Shutting down $sourceNode" ); - } + foreach (@nodes) { + xCAT::zvmUtils->printLn( $callback, "$_: Shutting down $sourceNode" ); + } } $out = `ssh $::SUDOER\@$srcHcp "$::SUDO $::DIR/smcli Image_Deactivate -T $sourceId"`; @@ -3829,7 +3845,7 @@ sub clone { $out = `cp $srcUserEntry $userEntry`; # Replace source userID with target userID - $out = `sed --in-place -e "s,$sourceId,$tgtUserId,i" $userEntry`; + $out = `sed -i -e "s,$sourceId,$tgtUserId,i" $userEntry`; # Get target MAC address in 'mac' table my $targetMac; @@ -3873,7 +3889,7 @@ sub clone { $oldMacId = xCAT::zvmUtils->trimStr($oldMacId); # Replace old MACID - $out = `sed --in-place -e "s,$oldMacId,$macId,i" $userEntry`; + $out = `sed -i -e "s,$oldMacId,$macId,i" $userEntry`; } else { # Find NICDEF statement @@ -3882,7 +3898,7 @@ sub clone { my $nicDef = xCAT::zvmUtils->replaceStr( $oldNicDef, $hcpNetName, "$hcpNetName MACID $macId" ); # Append MACID at the end - $out = `sed --in-place -e "s,$oldNicDef,$nicDef,i" $userEntry`; + $out = `sed -i -e "s,$oldNicDef,$nicDef,i" $userEntry`; } } @@ -3963,7 +3979,7 @@ sub clone { $type = $words[2]; $mode = $words[6]; if (!$mode) { - $mode = "MR"; + $mode = "MR"; } # Add 0 in front if address length is less than 4 @@ -4160,7 +4176,7 @@ sub clone { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr) using FLASHCOPY" ); if (xCAT::zvmUtils->smapi4xcat($::SUDOER, $hcp)) { $out = xCAT::zvmCPUtils->smapiFlashCopy($::SUDOER, $hcp, $sourceId, $srcAddr, $tgtUserId, $tgtAddr); - xCAT::zvmUtils->printSyslog("smapiFlashCopy:$out"); + xCAT::zvmUtils->printSyslog("smapiFlashCopy: $out"); # Exit if flashcopy completed successfully # Otherwsie, try CP FLASHCOPY @@ -4196,7 +4212,7 @@ sub clone { # Flashcopy source disk $out = xCAT::zvmCPUtils->flashCopy( $::SUDOER, $hcp, $srcAddr, $tgtAddr ); - xCAT::zvmUtils->printSyslog("flashCopy:$out"); + xCAT::zvmUtils->printSyslog("flashCopy: $out"); $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); @@ -4257,8 +4273,8 @@ sub clone { $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync && $::SUDO echo $?"`; $out = xCAT::zvmUtils->trimStr($out); if (int($out) != 0) { - # If $? is not 0 then there was an error during Linux dd - $out = "(Error) Failed to copy /dev/$srcDevNode"; + # If $? is not 0 then there was an error during Linux dd + $out = "(Error) Failed to copy /dev/$srcDevNode"; } xCAT::zvmUtils->printSyslog("dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096 oflag=sync"); @@ -4318,16 +4334,27 @@ EOM"`; my $cloneMntPt = "/mnt/$tgtUserId/$tgtDevNode"; # Disk can contain more than 1 partition. Find the right one (not swap) - $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*"`; - xCAT::zvmUtils->printSyslog("file -s /dev/$tgtDevNode*"); + # Check if /usr/bin/file is available + if (`ssh $::SUDOER\@$hcp "$::SUDO test -f /usr/bin/file && echo Exists"`) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*"`; + xCAT::zvmUtils->printSyslog("file -s /dev/$tgtDevNode*"); + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode*"`; + xCAT::zvmUtils->printSyslog("fdisk -l /dev/$tgtDevNode*"); + } xCAT::zvmUtils->printSyslog("$out"); $out = ""; $try = 5; while (!$out && $try > 0) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*" | grep -v swap | grep -o "$tgtDevNode\[1-9\]"`; + # Check if /usr/bin/file is available + if (`ssh $::SUDOER\@$hcp "$::SUDO test -f /usr/bin/file && echo Exists"`) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/file -s /dev/$tgtDevNode*" | grep -v swap | grep -o "$tgtDevNode\[1-9\]"`; + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/fdisk -l /dev/$tgtDevNode* | grep -v swap | grep -o $tgtDevNode\[1-9\]"`; + } $out = xCAT::zvmUtils->trimStr($out); - xCAT::zvmUtils->printSyslog("file -s /dev/$tgtDevNode* | grep -v swap | grep -o $tgtDevNode\[0-9\]"); + xCAT::zvmUtils->printSyslog("fdisk -l /dev/$tgtDevNode* | grep -v swap | grep -o $tgtDevNode\[1-9\]"); xCAT::zvmUtils->printSyslog("$out"); # Wait before trying again @@ -4360,7 +4387,7 @@ EOM"`; } if (!(`ssh $::SUDOER\@$hcp "$::SUDO ls $cloneMntPt"`)) { - xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed to mount /dev/$tgtDevNode. Skipping device." ); + xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed to mount /dev/$tgtDevNode. Skipping device." ); } # Is this the partition containing /etc? @@ -4368,13 +4395,13 @@ EOM"`; #*** Set network configuration *** # Set hostname xCAT::zvmUtils->printLn( $callback, "$tgtNode: Setting network configuration" ); - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/HOSTNAME"`; - xCAT::zvmUtils->printSyslog("sed --in-place -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/HOSTNAME"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/HOSTNAME"`; + xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/HOSTNAME"); # If Red Hat - Set hostname in /etc/sysconfig/network if ( $srcOs =~ m/rhel/i ) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/sysconfig/network"`; - xCAT::zvmUtils->printSyslog("sed --in-place -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/sysconfig/network"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" $cloneMntPt/etc/sysconfig/network"`; + xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i $cloneMntPt/etc/sysconfig/network"); } # Get network layer @@ -4387,65 +4414,65 @@ EOM"`; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts my @files; - if ( $srcOs =~ m/rhel/i ) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"`; - xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"); - xCAT::zvmUtils->printSyslog("$out"); - @files = split('\n', $out); - @words = split( ':', $files[0] ); - $srcIfcfg = $words[0]; - } - - # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ( $srcOs =~ m/sles10/i ) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"`; - xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"); - xCAT::zvmUtils->printSyslog("$out"); + if ( $srcOs =~ m/rhel/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"`; + xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network-scripts"); + xCAT::zvmUtils->printSyslog("$out"); @files = split('\n', $out); @words = split( ':', $files[0] ); $srcIfcfg = $words[0]; - } - - # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network - elsif ( $srcOs =~ m/sles11/i ) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"`; - xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"); - xCAT::zvmUtils->printSyslog("$out"); + } + + # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network + elsif ( $srcOs =~ m/sles10/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"`; + xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-qeth*"); + xCAT::zvmUtils->printSyslog("$out"); @files = split('\n', $out); @words = split( ':', $files[0] ); $srcIfcfg = $words[0]; - } - + } + + # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network + elsif ( $srcOs =~ m/sles11/i ) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"`; + xCAT::zvmUtils->printSyslog("grep -H -i -r $srcNicAddr $cloneMntPt/etc/sysconfig/network/ifcfg-eth*"); + xCAT::zvmUtils->printSyslog("$out"); + @files = split('\n', $out); + @words = split( ':', $files[0] ); + $srcIfcfg = $words[0]; + } + my $ifcfgPath = $srcIfcfg; - # Change IP, network, and mask - # Go through each network - my $tgtNetwork = ""; - my $tgtMask; - foreach (@$netEntries) { - - # Get network and mask - $tgtNetwork = $_->{'net'}; - $tgtMask = $_->{'mask'}; - - # If the host IP address is in this subnet, return - if (xCAT::NetworkUtils->ishostinsubnet($targetIp, $tgtMask, $tgtNetwork)) { - - # Exit loop - last; - } else { - $tgtNetwork = ""; - } - } + # Change IP, network, and mask + # Go through each network + my $tgtNetwork = ""; + my $tgtMask; + foreach (@$netEntries) { + + # Get network and mask + $tgtNetwork = $_->{'net'}; + $tgtMask = $_->{'mask'}; - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e \"s/$sourceNode/$tgtNode/i\" \ -e \"s/$sourceIp/$targetIp/i\" $cloneMntPt/etc/hosts"`; - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e \"s/$sourceIp/$targetIp/i\" \ -e \"s/$sourceNode/$tgtNode/i\" $ifcfgPath"`; - xCAT::zvmUtils->printSyslog("sed --in-place -e s/$sourceNode/$tgtNode/i \ -e s/$sourceIp/$targetIp/i $cloneMntPt/etc/hosts"); - xCAT::zvmUtils->printSyslog("sed --in-place -e s/$sourceIp/$targetIp/i \ -e s/$sourceNode/$tgtNode/i $ifcfgPath"); + # If the host IP address is in this subnet, return + if (xCAT::NetworkUtils->ishostinsubnet($targetIp, $tgtMask, $tgtNetwork)) { + + # Exit loop + last; + } else { + $tgtNetwork = ""; + } + } + + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceNode/$tgtNode/i\" \ -e \"s/$sourceIp/$targetIp/i\" $cloneMntPt/etc/hosts"`; + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$sourceIp/$targetIp/i\" \ -e \"s/$sourceNode/$tgtNode/i\" $ifcfgPath"`; + xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceNode/$tgtNode/i \ -e s/$sourceIp/$targetIp/i $cloneMntPt/etc/hosts"); + xCAT::zvmUtils->printSyslog("sed -i -e s/$sourceIp/$targetIp/i \ -e s/$sourceNode/$tgtNode/i $ifcfgPath"); if ($tgtNetwork && $tgtMask) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e \"s/$srcNetwork/$tgtNetwork/i\" \ -e \"s/$srcMask/$tgtMask/i\" $ifcfgPath"`; - xCAT::zvmUtils->printSyslog("sed --in-place -e s/$srcNetwork/$tgtNetwork/i \ -e s/$srcMask/$tgtMask/i $ifcfgPath"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$srcNetwork/$tgtNetwork/i\" \ -e \"s/$srcMask/$tgtMask/i\" $ifcfgPath"`; + xCAT::zvmUtils->printSyslog("sed -i -e s/$srcNetwork/$tgtNetwork/i \ -e s/$srcMask/$tgtMask/i $ifcfgPath"); } # Set MAC address @@ -4481,13 +4508,13 @@ EOM"`; # Set to hardware configuration (only for layer 2) if ( $layer == 2 ) { if ( $srcOs =~ m/rhel/i && $srcMac ) { - #*** Red Hat Linux *** - + #*** Red Hat Linux *** + # Set MAC address - $out = `ssh $::SUDOER\@$hcp "$::SUDO sed --in-place -e \"s/$srcMac/$targetMac/i\" $ifcfgPath"`; - xCAT::zvmUtils->printSyslog("sed --in-place -e s/$srcMac/$targetMac/i $ifcfgPath"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO sed -i -e \"s/$srcMac/$targetMac/i\" $ifcfgPath"`; + xCAT::zvmUtils->printSyslog("sed -i -e s/$srcMac/$targetMac/i $ifcfgPath"); } else { - #*** SuSE Linux *** + #*** SuSE Linux *** # Get hardware configuration # hwcfg-qeth file is in /etc/sysconfig/hardware @@ -4528,7 +4555,7 @@ EOM"`; # Update DHCP (only if it is running) $out = `service dhcpd status`; if (!($out =~ m/unused/i || $out =~ m/stopped/i)) { - $out = `makedhcp -a`; + $out = `/opt/xcat/bin/makedhcp -a`; } # Power on target virtual server @@ -4569,14 +4596,14 @@ sub nodeSet { # Get zHCP my $hcp = $propVals->{'hcp'}; - if ( !$hcp ) { + if (!$hcp) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node user ID my $userId = $propVals->{'userid'}; - if ( !$userId ) { + if (!$userId) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing user ID" ); return; } @@ -4585,16 +4612,39 @@ sub nodeSet { xCAT::zvmUtils->printSyslog("sudoer:$::SUDOER zHCP:$hcp sudo:$::SUDO"); - my $action = $args->[0]; + # Parse the possible operands + my $osImg; + my $remoteHost; + my $transport; + my $device; + my $action; + + foreach my $arg ( @$args ) { + if ($arg =~ m/^osimage=/i) { + $osImg = $arg; + $osImg =~ s/^osimage=//; + } elsif ($arg =~ m/^device=/i) { + $device = $arg; + $device =~ s/device=//; + } elsif ($arg =~ m/^remotehost=/i) { + $remoteHost = $arg; + $remoteHost =~ s/remotehost=//; + } elsif ($arg =~ m/^transport=/i) { + $transport = $arg; + $transport =~ s/transport=//; + } else { + # If not a recognized operand with a value then it must be an action + $action = $arg; + } + } # Handle case where osimage is specified my $os; my $arch; my $profile; my $provMethod; - my $osImg = $args->[0]; - if ($osImg =~ m/osimage=/i) { - $osImg =~ s/osimage=//; + if (defined $osImg) { + $osImg =~ s/osimage=//; $osImg =~ s/^\s+//; $osImg =~ s/\s+$//; @@ -4603,11 +4653,11 @@ sub nodeSet { # Update nodetype table with os, arch, and profile based on osimage if ( !$propVals->{'profile'} || !$propVals->{'provmethod'} || !$propVals->{'osvers'} || !$propVals->{'osarch'} ) { - # Exit - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile, provmethod, osvers, or osarch for osimage" ); - xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Provide profile, provmethod, osvers, and osarch in the osimage definition" ); - return; - } + # Exit + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile, provmethod, osvers, or osarch for osimage" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Solution) Provide profile, provmethod, osvers, and osarch in the osimage definition" ); + return; + } # Update nodetype table with osimage attributes for node my %propHash = ( @@ -4639,7 +4689,7 @@ sub nodeSet { $profile = $propVals->{'profile'}; # If no OS, arch, or profile is found - if ( !$os || !$arch || !$profile ) { + if (!$os || !$arch || !$profile) { # Exit xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node OS, arch, and profile in nodetype table" ); @@ -4711,9 +4761,9 @@ sub nodeSet { my @words = split( ' ', $out ); my $hostIP = $words[0]; my $hostname = $words[2]; - if (!($hostname =~ m/./i)) { - $hostname = $words[1]; - } + if (!($hostname =~ m/./i)) { + $hostname = $words[1]; + } if ( !$hostIP || !$hostname ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); @@ -4957,10 +5007,10 @@ sub nodeSet { @words = split(/\./, $mask); my ($maskUnpack) = unpack("N", pack( "C4", @words )); - # Calculate broadcase address by inverting the netmask and do a logical or with network address - my $math = ( $ipUnpack & $maskUnpack ) + ( ~ $maskUnpack ); - @words = unpack("C4", pack( "N", $math )) ; - my $broadcast = join(".", @words); + # Calculate broadcast address by inverting the netmask and do a logical or with network address + my $math = ( $ipUnpack & $maskUnpack ) + ( ~ $maskUnpack ); + @words = unpack("C4", pack( "N", $math )) ; + my $broadcast = join(".", @words); # Load VMCP module on HCP $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; @@ -5051,18 +5101,18 @@ sub nodeSet { close (FILE); # Add appropriate software packages or patterns - $out = `sed --in-place -e "s,replace_software_packages,$packages,g" \ -e "s,replace_software_patterns,$patterns,g" $customTmpl`; + $out = `sed -i -e "s,replace_software_packages,$packages,g" \ -e "s,replace_software_patterns,$patterns,g" $customTmpl`; # Copy postscript into template - $out = `sed --in-place -e "//r $postScript" $customTmpl`; + $out = `sed -i -e "//r $postScript" $customTmpl`; # Copy the contents of /install/postscripts/xcatpostinit1 - $out = `sed --in-place -e "/replace_xcatpostinit1/r $postInit" $customTmpl`; - $out = `sed --in-place -e "s,replace_xcatpostinit1,,g" $customTmpl`; + $out = `sed -i -e "/replace_xcatpostinit1/r $postInit" $customTmpl`; + $out = `sed -i -e "s,replace_xcatpostinit1,,g" $customTmpl`; # Copy the contents of /install/postscripts/xcatinstallpost - $out = `sed --in-place -e "/replace_xcatinstallpost/r $postBoot" $customTmpl`; - $out = `sed --in-place -e "s,replace_xcatinstallpost,,g" $customTmpl`; + $out = `sed -i -e "/replace_xcatinstallpost/r $postBoot" $customTmpl`; + $out = `sed -i -e "s,replace_xcatinstallpost,,g" $customTmpl`; # Edit template my $device; @@ -5077,7 +5127,7 @@ sub nodeSet { } $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" \ -e "s,replace_nic_addr,$readChannel,g" \ -e "s,replace_master,$master,g" \ -e "s,replace_install_dir,$installDir,g" $customTmpl`; +`sed -i -e "s,replace_host_address,$hostIP,g" \ -e "s,replace_long_name,$hostname,g" \ -e "s,replace_short_name,$node,g" \ -e "s,replace_domain,$domain,g" \ -e "s,replace_hostname,$node,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_broadcast,$broadcast,g" \ -e "s,replace_device,$device,g" \ -e "s,replace_ipaddr,$hostIP,g" \ -e "s,replace_lladdr,$mac,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_network,$network,g" \ -e "s,replace_ccw_chan_ids,$chanIds,g" \ -e "s,replace_ccw_chan_mode,FOOBAR,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_root_password,$passwd,g" \ -e "s,replace_nic_addr,$readChannel,g" \ -e "s,replace_master,$master,g" \ -e "s,replace_install_dir,$installDir,g" $customTmpl`; # Attach SCSI FCP devices (if any) # Go through each pool @@ -5109,10 +5159,10 @@ sub nodeSet { $wwpn = xCAT::zvmUtils->trimStr($tmp[0]); } - if ($device =~ m/;/i) { - @tmp = split(';', $device); - $device = xCAT::zvmUtils->trimStr($tmp[0]); - } + if ($device =~ m/;/i) { + @tmp = split(';', $device); + $device = xCAT::zvmUtils->trimStr($tmp[0]); + } # Make sure WWPN and LUN do not have 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); @@ -5129,7 +5179,7 @@ sub nodeSet { $lun = lc($lun); # Find tag in template and attach SCSI device associated with it - $out = `sed --in-place -e "s#$tag#/dev/disk/by-path/ccw-0.0.$device-zfcp-0x$wwpn:0x$lun#i" $customTmpl`; + $out = `sed -i -e "s#$tag#/dev/disk/by-path/ccw-0.0.$device-zfcp-0x$wwpn:0x$lun#i" $customTmpl`; # Generate section $zfcpSection .= <\\ END my $expression = "'s#" . $find . "#" .$replace . "#i'"; - $out = `sed --in-place -e $expression $customTmpl`; + $out = `sed -i -e $expression $customTmpl`; xCAT::zvmUtils->printLn($callback, "$node: Inserting FCP devices into template... Done"); } @@ -5187,8 +5237,8 @@ END # UseVNC=1 VNCPassword=12345678 # InstNetDev=osa OsaInterface=qdio OsaMedium=eth Manual=0 if (!$repo) { - $repo = "http://$nfs/$os/s390x/1"; - } + $repo = "http://$nfs/$os/s390x/1"; + } my $ay = "http://$nfs/custom/install/sles/" . $node . "." . $profile . ".tmpl"; @@ -5302,18 +5352,18 @@ END close (FILE); # Add appropriate software packages or patterns - $out = `sed --in-place -e "s,replace_software_packages,$packages,g" $customTmpl`; + $out = `sed -i -e "s,replace_software_packages,$packages,g" $customTmpl`; # Copy postscript into template - $out = `sed --in-place -e "/%post/r $postScript" $customTmpl`; + $out = `sed -i -e "/%post/r $postScript" $customTmpl`; # Copy the contents of /install/postscripts/xcatpostinit1 - $out = `sed --in-place -e "/replace_xcatpostinit1/r $postInit" $customTmpl`; - $out = `sed --in-place -e "s,replace_xcatpostinit1,,g" $customTmpl`; + $out = `sed -i -e "/replace_xcatpostinit1/r $postInit" $customTmpl`; + $out = `sed -i -e "s,replace_xcatpostinit1,,g" $customTmpl`; # Copy the contents of /install/postscripts/xcatinstallpost - $out = `sed --in-place -e "/replace_xcatinstallpost/r $postBoot" $customTmpl`; - $out = `sed --in-place -e "s,replace_xcatinstallpost,,g" $customTmpl`; + $out = `sed -i -e "/replace_xcatinstallpost/r $postBoot" $customTmpl`; + $out = `sed -i -e "s,replace_xcatinstallpost,,g" $customTmpl`; # Edit template if (!$repo) { @@ -5321,7 +5371,7 @@ END } $out = -`sed --in-place -e "s,replace_url,$repo,g" \ -e "s,replace_ip,$hostIP,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_hostname,$hostname,g" \ -e "s,replace_rootpw,$passwd,g" \ -e "s,replace_master,$master,g" \ -e "s,replace_install_dir,$installDir,g" $customTmpl`; +`sed -i -e "s,replace_url,$repo,g" \ -e "s,replace_ip,$hostIP,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_hostname,$hostname,g" \ -e "s,replace_rootpw,$passwd,g" \ -e "s,replace_master,$master,g" \ -e "s,replace_install_dir,$installDir,g" $customTmpl`; # Attach SCSI FCP devices (if any) # Go through each pool @@ -5376,7 +5426,7 @@ END $zfcpSection = "zfcp --devnum 0.0.$device --wwpn 0x$wwpn --fcplun 0x$lun" . '\n'; # Look for replace_zfcp keyword in template and replace it - $out = `sed --in-place -e "s,$tag,$zfcpSection,i" $customTmpl`; + $out = `sed -i -e "s,$tag,$zfcpSection,i" $customTmpl`; $hasZfcp = 1; } } @@ -5454,11 +5504,11 @@ END # Concat dedicated devices and DASD together if ($devices) { - if ($dasd) { + if ($dasd) { $dasd = $dasd . "," . $devices; - } else { - $dasd = $devices; - } + } else { + $dasd = $devices; + } } # Create parmfile -- Limited to 80 characters/line, maximum of 11 lines @@ -5692,11 +5742,152 @@ END } xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot." ); + } elsif ( $action eq "netboot" ) { + + # Obtain the location of the install root directory + my $installRoot = xCAT::TableUtils->getInstallDir(); + + # Verify the image exists + my $imageFile; + my $deployImgDir = "$installRoot/$action/$os/$arch/$profile"; + my @imageFiles = glob "$deployImgDir/*.img"; + if (@imageFiles == 0) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir does not contain image files" ); + return; + } elsif (@imageFiles > 1) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) $deployImgDir contains more than the expected number of image files" ); + return; + } else { + $imageFile = (split('/', $imageFiles[0]))[-1]; + } + + if (! defined $device) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Image device was not specified" ); + return; + } + + # Prepare the deployable netboot mount point on zHCP, if they need to be established. + my $remoteDeployDir; + my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$action", "ro", \$remoteDeployDir); + if ( $rc ) { + # Mount failed + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: Deploying the image using the zHCP node" ); + + # Copy the image to the target disk using the zHCP node + xCAT::zvmUtils->printSyslog( "nodeset() unpackdiskimage $userId $device $remoteDeployDir/$os/$arch/$profile/$imageFile" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/unpackdiskimage $userId $device $remoteDeployDir/$os/$arch/$profile/$imageFile"`; + $rc = $?; + + my $reasonString = ""; + $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); + if ($rc != 0) { + my $reason = "Reason: $reasonString"; + xCAT::zvmUtils->printSyslog( "nodeset() unpackdiskimage of $userId $device failed. $reason" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to deploy the image to $userId $device. $reason" ); + return; + } + + # If the transport file was specified then setup the transport disk. + if ($transport) { + my $transImgDir = "$installRoot/staging/transport"; + if(!-d $transImgDir) { + mkpath($transImgDir); + } + + # Create unique transport directory and copy the transport file to it + my $transportDir = `/bin/mktemp -d $installDir/staging/transport/XXXXXX`; + chomp($transportDir); + if ($remoteHost) { + # Copy the transport file from the remote system to the local transport directory. + xCAT::zvmUtils->printLn( $callback, "/usr/bin/scp -B $remoteHost:$transport $transportDir" ); + $out = `/usr/bin/scp -v -B $remoteHost:$transport $transportDir`; + $rc = $?; + } else { + # Safely copy the transport file from a local directory. + $out = `/bin/cp $transport $transportDir`; + $rc = $?; + } + + if ($rc != 0) { + # Copy failed Get rid of the unique directory that was going to receive the copy. + rmtree $transportDir; + xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to copy the transport file"); + return; + } + + # Purge the target node's reader + $out = xCAT::zvmCPUtils->purgeReader( $::SUDOER, $hcp, $userId ); + xCAT::zvmUtils->printLn($callback, "$node: Purging reader... Done"); + + # Online zHCP's punch + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/chccwdev -e 00d && echo $?"`; + if ($out != '0') { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to online the zHCP's punch"); + return; + } + + # Load VMCP module on HCP + $out = `ssh -o ConnectTimeout=5 $::SUDOER\@$hcp "/sbin/modprobe vmcp"`; + if ($out != '0') { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to load the vmcp module on the zHCP node"); + return; + } + + # Set the punch to class 'x' + $out = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp spool punch class x"`; + if ($out != '0') { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to spool the punch on the zHCP node"); + return; + } + + # Punch files to node's reader so it could be pulled on boot + # Reader = transport disk + my @files = glob "$transportDir/*"; + foreach (@files) { + my $file = basename($_); + my $filePath = "/tmp/$node-" . $file; + + # Spool file only accepts [A-Za-z] and file name can only be 8-characters long + my @filePortions = split( '\.', $file ); + if (( @filePortions > 2 ) || + ( $filePortions[0] =~ m/[^a-zA-Z0-9]/ ) || ( length($filePortions[0]) > 8 ) || ( length($filePortions[0]) < 1 ) || + ( $filePortions[1] =~ m/[^a-zA-Z0-9]{1,8}/ ) || ( length($filePortions[1]) > 8 )) { + $out = `/bin/rm -rf $transportDir`; + xCAT::zvmUtils->printLn($callback, "$node: (Error) $file contains a file name or file type portion that is longer than 8 characters, or not alphanumeric "); + return; + } + + xCAT::zvmUtils->sendFile($::SUDOER, $hcp, $_, $filePath); + + my $punchOpt = ""; + if ($file =~ /.txt/ || $file =~ /.sh/) { + $punchOpt = "-t"; + } + $out = xCAT::zvmCPUtils->punch2Reader($::SUDOER, $hcp, $userId, $filePath, "$file", $punchOpt); + + # Clean up file + `ssh $::SUDOER\@$hcp "$::SUDO /bin/rm $filePath"`; + + xCAT::zvmUtils->printLn($callback, "$node: Punching $file to reader... $out"); + if ($out =~ m/Failed/i) { + # Clean up transport directory + $out = `/bin/rm -rf $transportDir`; + return; + } + } + + # Clean up transport directory + $out = `/bin/rm -rf $transportDir`; + xCAT::zvmUtils->printLn( $callback, "$node: Completed deploying image($os-$arch-netboot-$profile)" ); + } } else { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); return; } - + return; } @@ -5921,7 +6112,7 @@ sub updateNode { my $hostIP = $words[0]; my $hostname = $words[2]; if (!($hostname =~ m/./i)) { - $hostname = $words[1]; + $hostname = $words[1]; } if ( !$hostIP || !$hostname ) { @@ -6291,6 +6482,13 @@ sub changeHypervisor { return; } + # Get zHCP shortname because $hcp could be zhcp.endicott.ibm.com + my $hcpNode = $hcp; + if ($hcp =~ /./) { + my @tmp = split(/\./, $hcp); + $hcpNode = $tmp[0]; # Short hostname of zHCP + } + # Get zHCP user ID my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); $hcpUserId =~ tr/a-z/A-Z/; @@ -6449,7 +6647,7 @@ sub changeHypervisor { xCAT::zvmUtils->printSyslog("smcli Virtual_Network_Vswitch_Create -T $hcpUserId $argStr"); } - # addzfcp2pool [pool] [status] [wwpn] [lun] [size] [range] [owner (optional)] + # addzfcp2pool [pool] [status] [wwpn] [lun] [size] [range (optional)] [owner (optional)] elsif ( $args->[0] eq "--addzfcp2pool" ) { # zFCP disk pool located on zHCP at /var/opt/zhcp/zfcp/{pool}.conf # Entries contain: status,wwpn,lun,size,range,owner,channel,tag @@ -6469,23 +6667,63 @@ sub changeHypervisor { if ($size =~ m/G/i || $size =~ m/M/i || !$size) { # Do nothing } else { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)." ); + xCAT::zvmUtils->printLn($callback, "$node: (Error) Size not recognized. Size can be M(egabytes) or G(igabytes)."); + return; + } + + # Status can be free/used/reserved + chomp($status); + if ($status !~ m/^(free|used|reserved)$/i) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Status not recognized. Status can be free, used, or reserved."); return; } # Make sure WWPN and LUN do not have 0x prefix + $wwpn = xCAT::zvmUtils->replaceStr($wwpn, '"', ""); # Strip off enclosing quotes $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + if ($wwpn =~ /[^0-9a-f;"]/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid world wide portname $wwpn." ); + return; + } + if ($lun =~ /[^0-9a-f]/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid logical unit number $lun." ); + return; + } + + # You cannot have a unique SCSI/FCP device in multiple pools + my @pools = split("\n", `ssh $::SUDOER\@$hcp "$::SUDO grep -i -l \",$wwpn,$lun\" $::ZFCPPOOL/*.conf"`); + if (scalar(@pools)) { + foreach (@pools) { + my $otherPool = basename($_); + $otherPool =~ s/\.[^.]+$//; # Do not use extension + + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $wwpn/$lun already exists in $otherPool." ); + } + + return; + } # Optional parameters my $range = ""; my $owner = ""; if ($argsSize > 6) { $range = $args->[6]; - } - if ($argsSize > 7) { + } if ($argsSize > 7) { $owner = $args->[7]; } + + # Verify syntax of FCP channel range + if ($range =~ /[^0-9a-f\-;]/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid FCP device range. An acceptable range can be specified as 1A80-1B90 or 1A80-1B90;2A80-2B90." ); + return; + } + + # Owner must be specified if status is used + if ($status =~ m/used/i && !$owner) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Owner must be specified if status is used." ); + return; + } # Find disk pool (create one if non-existent) if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -d $::ZFCPPOOL && echo Exists"`)) { @@ -6495,10 +6733,10 @@ sub changeHypervisor { # Change the file owner if using a sudoer if ($::SUDOER ne "root") { - my $priv = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO stat -c \"%G:%U\" /var/opt/zhcp"`); - if (!($priv =~ m/$::SUDOER:users/i)) { + my $priv = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/stat -c \"%G:%U\" /var/opt/zhcp"`); + if (!($priv =~ m/$::SUDOER:users/i)) { `ssh $::SUDOER\@$hcp "$::SUDO chown -R $::SUDOER:users /var/opt/zhcp"`; - } + } } if (!(`ssh $::SUDOER\@$hcp "$::SUDO test -e $::ZFCPPOOL/$pool.conf && echo Exists"`)) { @@ -6506,12 +6744,6 @@ sub changeHypervisor { $out = `ssh $::SUDOER\@$hcp "$::SUDO echo '#status,wwpn,lun,size,range,owner,channel,tag' > $::ZFCPPOOL/$pool.conf"`; xCAT::zvmUtils->printLn( $callback, "$node: New zFCP device pool $pool created" ); } - - # Do not update if the LUN already exists - if (`ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | grep $lun`) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device already exists" ); - return; - } # Update file with given WWPN, LUN, size, and owner my $entry = "'" . "$status,$wwpn,$lun,$size,$range,$owner,," . "'"; @@ -6520,6 +6752,468 @@ sub changeHypervisor { $out = ""; } + # copyzfcp [device address (or auto)] [source wwpn] [source lun] [target wwpn (optional)] [target lun (option)] + elsif ( $args->[0] eq "--copyzfcp" ) { + my $fcpDevice = $args->[1]; + my $srcWwpn = $args->[2]; + my $srcLun = $args->[3]; + + my $argsSize = @{$args}; + if ($argsSize != 4 && $argsSize != 6) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + # Check if WWPN and LUN are given + my $useWwpnLun = 0; + my $tgtWwpn; + my $tgtLun; + if ($argsSize == 6) { + $useWwpnLun = 1; + $tgtWwpn = $args->[4]; + $tgtLun = $args->[5]; + + # Make sure WWPN and LUN do not have 0x prefix + $tgtWwpn = xCAT::zvmUtils->replaceStr($tgtWwpn, "0x", ""); + $tgtLun = xCAT::zvmUtils->replaceStr($tgtLun, "0x", ""); + } + + # Find the pool that contains the SCSI/FCP device + my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $srcWwpn, $srcLun); + if (!$pool) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool" ); + return; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: Found FCP device in $pool" ); + } + + # Get source device's attributes + my $srcDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $srcWwpn, $srcLun); + my %srcDisk = %$srcDiskRef; + if (!defined($srcDisk{'lun'}) && !$srcDisk{'lun'}) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Source zFCP device $srcWwpn/$srcLun does not exists" ); + return; + } + my $srcSize = $srcDisk{'size'}; + + # If target disk is specified, check whether it is large enough + my $tgtSize; + if ($useWwpnLun) { + my $tgtDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $tgtWwpn, $tgtLun); + my %tgtDisk = %$tgtDiskRef; + if (!defined($tgtDisk{'lun'}) && !$tgtDisk{'lun'}) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun does not exists" ); + return; + } + $tgtSize = $tgtDisk{'size'}; + + # Convert size unit to M for comparision + if ($srcSize =~ m/G/i) { + $srcSize =~ s/\D//g; + $srcSize = int($srcSize) * 1024 + } + if ($tgtSize =~ m/G/i) { + $tgtSize =~ s/\D//g; + $tgtSize = int($srcSize) * 1024 + } + + if ($tgtSize < $srcSize) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun is not large enough" ); + return; + } + } + + # Attach source disk to zHCP + $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $srcSize "" $srcWwpn $srcLun | sed 1d`; + if ($out !~ /Done/) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Source zFCP device $srcWwpn/$srcLun cannot be attached"); + return; + } + + # Obtain source FCP channel + $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/; + my $srcFcpDevice = lc($1); + + # Attach target disk to zHCP + my $isTgtAttached = 0; + if ($useWwpnLun) { + $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $tgtSize "" $tgtWwpn $tgtLun | sed 1d`; + if ($out !~ /Done/) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Target zFCP device $tgtWwpn/$tgtLun cannot be attached"); + } else { + $isTgtAttached = 1; + } + } else { + # Try to obtain a target disk automatically if target disk is not specified + $out = `/opt/xcat/bin/chvm $hcpNode --addzfcp $pool $fcpDevice 0 $srcSize | sed 1d`; + if ($out !~ /Done/) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Cannot find a suitable target zFCP device"); + } else { + $isTgtAttached = 1; + } + } + + # Obtain target disk FCP channel, WWPN, and LUN + $out =~ /Adding zFCP device ([0-9a-f]*)\/([0-9a-f]*)\/([0-9a-f]*).*/; + my $tgtFcpDevice = lc($1); + $tgtWwpn = lc($2); + $tgtLun = lc($3); + + if (!$isTgtAttached) { + # Release source disk from zHCP + $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $fcpDevice $srcWwpn $srcLun 0`; + return; + } + + # Get device node of source disk and target disk + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/readlink /dev/disk/by-path/ccw-0.0.$srcFcpDevice-zfcp-0x$srcWwpn:0x$srcLun"`; + chomp($out); + my @srcDiskInfo = split('/', $out); + my $srcDiskNode = pop(@srcDiskInfo); + chomp($out); + xCAT::zvmUtils->printLn( $callback, "$node: Device name of $tgtFcpDevice/$srcWwpn/$srcLun is $srcDiskNode"); + + $out = `ssh $::SUDOER\@$hcp "$::SUDO /usr/bin/readlink /dev/disk/by-path/ccw-0.0.$tgtFcpDevice-zfcp-0x$tgtWwpn:0x$tgtLun"`; + chomp($out); + my @tgtDiskInfo = split('/', $out); + my $tgtDiskNode = pop(@tgtDiskInfo); + chomp($tgtDiskNode); + xCAT::zvmUtils->printLn( $callback, "$node: Device name of $tgtFcpDevice/$tgtWwpn/$tgtLun is $tgtDiskNode"); + + my $presist = 0; + my $rc = "Failed"; + if (!$srcDiskNode || !$tgtDiskNode) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Could not find device nodes for source or target disk."); + } else { + # Copy source disk to target disk (512 block size) + xCAT::zvmUtils->printLn( $callback, "$node: Copying source disk ($srcDiskNode) to target disk ($tgtDiskNode)" ); + $out = `ssh $::SUDOER\@$hcp "$::SUDO /bin/dd if=/dev/$srcDiskNode of=/dev/$tgtDiskNode bs=512 oflag=sync && $::SUDO echo $?"`; + $out = xCAT::zvmUtils->trimStr($out); + if (int($out) != 0) { + # If $? is not 0 then there was an error during Linux dd + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to copy /dev/$srcDiskNode"); + } + + $presist = 1; # Keep target device as reserved + $rc = "Done"; + + # Sleep 2 seconds to let the system settle + sleep(2); + } + + # Detatch source and target disks + xCAT::zvmUtils->printLn($callback, "$node: Detatching source and target disks"); + $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $srcFcpDevice $srcWwpn $srcLun $presist`; + $out = `/opt/xcat/bin/chvm $hcpNode --removezfcp $tgtFcpDevice $tgtWwpn $tgtLun $presist`; + + # Restore original source device attributes + my %criteria = ( + 'status' => $srcDisk{'status'}, + 'wwpn' => $srcDisk{'wwpn'}, + 'lun' => $srcDisk{'lun'}, + 'size' => $srcDisk{'size'}, + 'range' => $srcDisk{'range'}, + 'owner' => $srcDisk{'owner'}, + 'fcp' => $srcDisk{'fcp'}, + 'tag' => $srcDisk{'tag'} + ); + my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + my %results = %$resultsRef; + if ($results{'rc'} == -1) { + # Unable to reserve the volume and FCP channel + xCAT::zvmUtils->printLn($callback, "$node: (Error) Source disk attributes cannot be restored in table"); + } + + xCAT::zvmUtils->printLn( $callback, "$node: Copying zFCP device... $rc"); + if ($rc eq "Done") { + xCAT::zvmUtils->printLn( $callback, "$node: Source disk copied onto zFCP device $tgtWwpn/$tgtLun"); + } + $out = ""; + } + + # capturezfcp [profile] [wwpn] [lun] + elsif ( $args->[0] eq "--capturezfcp" ) { + my $profile = $args->[1]; + my $wwpn = $args->[2]; + my $lun = $args->[3]; + + # Verify required properties are defined + if (!defined($profile) || !defined($wwpn) || !defined($lun)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing one or more of the required parameters: profile, wwpn, or lun" ); + return; + } + + # Make sure WWPN and LUN do not have 0x prefix + $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + + # Obtain the location of the install root directory + my $installRoot = xCAT::TableUtils->getInstallDir(); + + xCAT::zvmUtils->printSyslog("changeHypervisor() Preparing the staging directory"); + + # Create the staging area location for the image + my $os = "unknown"; # Since we do not inspect the disk contents nor care + my $provMethod = "raw"; + my $arch = "s390x"; + my $stagingImgDir = "$installRoot/staging/$os/$arch/$profile"; + + if(-d $stagingImgDir) { + unlink $stagingImgDir; + } + mkpath($stagingImgDir); + + # Prepare the staging mount point on zHCP, if they need to be established. + my $remoteStagingDir; + my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/staging", "rw", \$remoteStagingDir); + if ($rc) { + # Mount failed. + rmtree "$stagingImgDir"; + return; + } + + # Find the pool that contains the SCSI/FCP device + my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun); + if (!$pool) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool"); + return; + } else { + xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool"); + } + + # Get source device's attributes + my $srcDiskRef = xCAT::zvmUtils->findzFcpDeviceAttr($::SUDOER, $hcp, $pool, $wwpn, $lun); + my %srcDisk = %$srcDiskRef; + if (!defined($srcDisk{'lun'}) && !$srcDisk{'lun'}) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Source zFCP device $wwpn/$lun does not exists"); + return; + } + + # Reserve the volume and associated FCP channel for the zHCP node + my %criteria = ( + 'status' => 'used', + 'fcp' => 'auto', + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $hcpNode + ); + my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + my %results = %$resultsRef; + + my $device = $results{'fcp'}; + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; + + if ($results{'rc'} == -1) { + # Unable to reserve the volume and FCP channel + xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be reserved"); + rmtree "$stagingImgDir"; + return; + } + + xCAT::zvmUtils->printLn($callback, "$node: Capturing volume using zHCP node"); + + # Drive the capture on the zHCP node + xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage $device 0x$wwpn/0x$lun $remoteStagingDir/$os/$arch/$profile/0x${wwpn}_0x${lun}.img"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/creatediskimage $device 0x$wwpn 0x$lun $remoteStagingDir/$os/$arch/$profile/${wwpn}_${lun}.img"`; + $rc = $?; + + # Check for capture errors + my $reasonString = ""; + $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); + if ($rc != 0) { + my $reason = "Reason: $reasonString"; + xCAT::zvmUtils->printSyslog("changeHypervisor() creatediskimage of volume 0x$wwpn/0x$lun failed. $reason"); + xCAT::zvmUtils->printLn($callback, "$node: (Error) Image capture of volume 0x$wwpn/0x$lun failed on the zHCP node. $reason"); + rmtree "$stagingImgDir" ; + return; + } + + # Restore original source device attributes + my %criteria = ( + 'status' => $srcDisk{'status'}, + 'wwpn' => $srcDisk{'wwpn'}, + 'lun' => $srcDisk{'lun'}, + 'size' => $srcDisk{'size'}, + 'range' => $srcDisk{'range'}, + 'owner' => $srcDisk{'owner'}, + 'fcp' => $srcDisk{'fcp'}, + 'tag' => $srcDisk{'tag'} + ); + my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + my %results = %$resultsRef; + if ($results{'rc'} == -1) { + # Unable to reserve the volume and FCP channel + xCAT::zvmUtils->printLn($callback, "$node: (Error) Source disk attributes cannot be restored in table"); + } + + my $imageName = "$os-$arch-$provMethod-$profile"; + my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile"; + + xCAT::zvmUtils->printLn($callback, "$node: Moving the image files to the deployable directory: $deployImgDir"); + + # Move the image directory to the deploy directory + mkpath($deployImgDir); + + my @stagedFiles = glob "$stagingImgDir/*"; + foreach my $oldFile (@stagedFiles) { + move($oldFile, $deployImgDir) or die "$node: (Error) Could not move $oldFile to $deployImgDir: $!\n"; + } + + # Remove the staging directory + rmtree "$stagingImgDir" ; + + xCAT::zvmUtils->printSyslog("changeHypervisor() Updating the osimage table"); + + my $osTab = xCAT::Table->new('osimage',-create => 1,-autocommit => 0); + my %keyHash; + + unless ($osTab) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Unable to open table 'osimage'"); + return 0; + } + + $keyHash{provmethod} = $provMethod; + $keyHash{profile} = $profile; + $keyHash{osvers} = $os; + $keyHash{osarch} = $arch; + $keyHash{imagetype} = 'linux'; + $keyHash{imagename} = $imageName; + + $osTab->setAttribs({imagename => $imageName }, \%keyHash); + $osTab->commit; + + xCAT::zvmUtils->printSyslog("changeHypervisor() Updating the linuximage table"); + + my $linuxTab = xCAT::Table->new('linuximage',-create => 1,-autocommit => 0); + + %keyHash = (); + $keyHash{imagename} = $imageName; + $keyHash{rootimgdir} = $deployImgDir; + + $linuxTab->setAttribs({imagename => $imageName }, \%keyHash ); + $linuxTab->commit; + + xCAT::zvmUtils->printLn($callback, "$node: Completed capturing the volume. Image($imageName) is stored at $deployImgDir"); + $out = ""; + } + + # deployzfcp [imageName] [wwpn] [lun] + elsif ( $args->[0] eq "--deployzfcp" ) { + my $imageName = $args->[1]; + my $wwpn = $args->[2]; + my $lun = $args->[3]; + + # Verify required properties are defined + if ( !defined($imageName) || !defined($wwpn) || !defined($lun)) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Missing one or more arguments: image name, wwpn, or lun"); + return; + } + + # Make sure WWPN and LUN do not have 0x prefix + $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + + # Obtain the location of the install root directory + my $installRoot = xCAT::TableUtils->getInstallDir(); + + # Build the image location from the image name + my @nameParts = split('-', $imageName); + if (!defined $nameParts[3]) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) The image name is not valid"); + return; + } + my $profile = $nameParts[3]; + my $os = "unknown"; + my $provMethod = "raw"; + my $arch = "s390x"; + + my $deployImgDir = "$installRoot/$provMethod/$os/$arch/$profile"; + + # Find the image filename. + my $imageFile; + my @imageFiles = glob "$deployImgDir/*.img"; + if (@imageFiles == 0) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir does not contain image files"); + return; + } elsif (@imageFiles > 1) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) $deployImgDir contains more than the expected number of image files"); + return; + } else { + $imageFile = (split( '/', $imageFiles[0]))[-1]; + } + + # Prepare the deployable netboot mount point on zHCP, if they need to be established. + my $remoteDeployDir; + my $rc = xCAT::zvmUtils->establishMount($callback, $::SUDOER, $::SUDO, $hcp, "$installRoot/$provMethod", "ro", \$remoteDeployDir); + if ($rc) { + # Mount failed. + return; + } + + # Find the pool that contains the SCSI/FCP device + my $pool = xCAT::zvmUtils->findzFcpDevicePool($::SUDOER, $hcp, $wwpn, $lun); + if (!$pool) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Failed to find FCP device in any zFCP storage pool"); + return; + } else { + xCAT::zvmUtils->printLn($callback, "$node: Found FCP device in $pool"); + } + + # Reserve the volume and associated FCP channel for the zHCP node. + my %criteria = ( + 'status' => 'used', + 'fcp' => 'auto', + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $hcpNode + ); + my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + my %results = %$resultsRef; + + # Obtain the device assigned by xCAT + my $device = $results{'fcp'}; + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; + + if ($results{'rc'} == -1) { + # Unable to reserve the volume and FCP channel + xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be reserved"); + return; + } + + xCAT::zvmUtils->printLn($callback, "$node: Deploying volume using zHCP node"); + + # Drive the deploy on the zHCP node + xCAT::zvmUtils->printSyslog("changeHypervisor() unpackdiskimage $device 0x$wwpn 0x$lun $remoteDeployDir/$os/$arch/$profile/$imageFile"); + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/unpackdiskimage $device 0x$wwpn 0x$lun $remoteDeployDir/$os/$arch/$profile/$imageFile"`; + $rc = $?; + + # Release the volume from the zHCP node + %criteria = ( + 'status' => 'reserved', + 'wwpn' => $wwpn, + 'lun' => $lun + ); + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + if ($results{'rc'} == -1) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) zFCP device cannot be released"); + } + + # Check for deploy errors + my $reasonString = ""; + $rc = xCAT::zvmUtils->checkOutputExtractReason($callback, $out, \$reasonString); + if ($rc != 0) { + my $reason = "Reason: $reasonString"; + xCAT::zvmUtils->printSyslog("changeHypervisor() unpackdiskimage of volume 0x$wwpn/0x$lun failed. $reason"); + xCAT::zvmUtils->printLn($callback, "$node: (Error) Image deploy to volume 0x$wwpn/0x$lun failed on the zHCP node. $reason"); + return; + } + + xCAT::zvmUtils->printLn($callback, "$node: Completed deploying image($imageName)"); + $out = ""; + } + # removediskfrompool [function] [region] [group] elsif ( $args->[0] eq "--removediskfrompool" ) { my $funct = $args->[1]; @@ -6588,7 +7282,7 @@ sub changeHypervisor { # Delete a VSWITCH $out = `ssh $hcp "$::DIR/smcli Virtual_Network_Vswitch_Delete -T $hcpUserId -n $name"`; xCAT::zvmUtils->printSyslog("ssh $hcp $::DIR/smcli Virtual_Network_Vswitch_Delete -T $hcpUserId -n $name"); - } + } # removezfcpfrompool [pool] [lun] [wwpn (optional)] elsif ( $args->[0] eq "--removezfcpfrompool" ) { @@ -6598,12 +7292,26 @@ sub changeHypervisor { my $wwpn; my $argsSize = @{$args}; if ($argsSize == 4) { - $wwpn = $args->[3]; + $wwpn = $args->[3]; } elsif ($argsSize > 4) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } + # Make sure WWPN and LUN do not have 0x prefix + $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); + $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); + + # Verify WWPN and LUN have the correct syntax + if ($wwpn =~ /[^0-9a-f;"]/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid world wide port name $wwpn." ); + return; + } + if ($lun =~ /[^0-9a-f,]/i) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Invalid logical unit number $lun." ); + return; + } + my @luns; if ($lun =~ m/,/i) { @luns = split( ',', $lun ); @@ -6621,35 +7329,136 @@ sub changeHypervisor { my $entry; my @args; foreach (@luns) { - # Make sure WWPN and LUN do not have 0x prefix - $_ = xCAT::zvmUtils->replaceStr($_, "0x", ""); - # Entry should contain: status, wwpn, lun, size, range, owner, channel, tag - $entry = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | grep $_`); + $entry = xCAT::zvmUtils->trimStr(`ssh $::SUDOER\@$hcp "$::SUDO cat $::ZFCPPOOL/$pool.conf" | egrep -i $_`); # Do not update if LUN does not exists if (!$entry) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $_ does not exists" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $_ does not exist" ); return; } # Do not update if WWPN/LUN combo does not exists @args = split(',', $entry); if ($wwpn && !($args[1] =~ m/$wwpn/i)) { - xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $wwpn/$_ does not exists" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) zFCP device $wwpn/$_ does not exists" ); return; } - # Update file with given WWPN, LUN, size, and owner - $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "sed --in-place -e /$entry/d $::ZFCPPOOL/$pool.conf"); + # Update file with given WWPN and LUN + $entry = "'" . $entry . "'"; + $out = xCAT::zvmUtils->rExecute($::SUDOER, $hcp, "sed -i -e /$entry/d $::ZFCPPOOL/$pool.conf"); if ($wwpn) { xCAT::zvmUtils->printLn( $callback, "$node: Removing zFCP device $wwpn/$_ from $pool pool... Done" ); } else { - xCAT::zvmUtils->printLn( $callback, "$node: Removing zFCP device $_ from $pool pool... Done" ); + xCAT::zvmUtils->printLn( $callback, "$node: Removing zFCP device $_ from $pool pool... Done" ); } } $out = ""; } + # releasezfcp [pool] [wwpn] [lun] + elsif ( $args->[0] eq "--releasezfcp" ) { + my $pool = lc($args->[1]); + my $wwpn = lc($args->[2]); + my $lun = lc($args->[3]); + + my $argsSize = @{$args}; + if ($argsSize != 4) { + xCAT::zvmUtils->printLn($callback, "$node: (Error) Wrong number of parameters"); + return; + } + + my $device = ""; + + # In case multiple LUNs are given, push LUNs into an array to be processed + my @luns; + if ($lun =~ m/,/i) { + @luns = split( ',', $lun ); + } else { + push(@luns, $lun); + } + + # Go through each LUN + foreach (@luns) { + my %criteria = ( + 'status' => 'free', + 'wwpn' => $wwpn, + 'lun' => $_ + ); + + my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + my %results = %$resultsRef; + if ($results{'rc'} == 0) { + xCAT::zvmUtils->printLn($callback, "$node: Releasing FCP device... Done"); + xCAT::zvmUtils->printLn($callback, "$node: FCP device 0x$wwpn/0x$_ was released"); + } else { + xCAT::zvmUtils->printLn($callback, "$node: Releasing FCP device... Failed"); + } + } + } + + # reservezfcp [pool] [status] [owner] [device address (or auto)] [size] [wwpn (optional)] [lun (optional)] + elsif ( $args->[0] eq "--reservezfcp" ) { + my $pool = lc($args->[1]); + my $status = $args->[2]; + my $owner = $args->[3]; + my $device = $args->[4]; + my $size = $args->[5]; + + my $argsSize = @{$args}; + if ($argsSize != 6 && $argsSize != 8) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); + return; + } + + # Obtain the FCP device, WWPN, and LUN (if any) + my $wwpn = ""; + my $lun = ""; + if ($argsSize == 8) { + $wwpn = lc($args->[6]); + $lun = lc($args->[7]); + + # Ignore the size if the WWPN and LUN are given + $size = ""; + } + + my %criteria; + my $resultsRef; + if ($wwpn && $lun) { + %criteria = ( + 'status' => $status, + 'fcp' => $device, + 'wwpn' => $wwpn, + 'lun' => $lun, + 'owner' => $owner + ); + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + } else { + # Do not know the WWPN or LUN in this case + %criteria = ( + 'status' => $status, + 'fcp' => $device, + 'size' => $size, + 'owner' => $owner + ); + $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $node, $::SUDOER, $hcp, $pool, \%criteria); + } + + my %results = %$resultsRef; + + # Obtain the device assigned by xCAT + $device = $results{'fcp'}; + $wwpn = $results{'wwpn'}; + $lun = $results{'lun'}; + + if ($results{'rc'} == 0) { + xCAT::zvmUtils->printLn($callback, "$node: Reserving FCP device... Done"); + xCAT::zvmUtils->printLn($callback, "$node: FCP device $device/0x$wwpn/0x$lun was reserved"); + } else { + xCAT::zvmUtils->printLn($callback, "$node: Reserving FCP device... Failed"); + } + } + # resetsmapi elsif ( $args->[0] eq "--resetsmapi" ) { # IMPORTANT: @@ -6686,7 +7495,6 @@ sub changeHypervisor { # Pass arguments directly to smcli $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli $str"`; - xCAT::zvmUtils->printSyslog("smcli $str"); } # Otherwise, print out error @@ -6917,7 +7725,7 @@ sub inventoryHypervisor { my $space = $args->[1]; my $details = 0; if ($argsSize == 3 && $args->[2] eq "details") { - $details = 1; + $details = 1; } # Display the status of real FCP Adapter devices using System_WWPN_Query @@ -6926,11 +7734,11 @@ sub inventoryHypervisor { my $devNo; my $status; if ($space eq "active" || $space eq "free" || $space eq "offline") { - if ($details) { - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId"`; - xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId"); - - @devices = split( "\n", $out ); + if ($details) { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId"`; + xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId"); + + @devices = split( "\n", $out ); for ($i = 0; $i < @devices; $i++) { # Extract the device number and status $devNo = $devices[$i]; @@ -6953,30 +7761,30 @@ sub inventoryHypervisor { $i = $i + 4; } } - } else { - $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; + } else { + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_WWPN_Query -T $hcpUserId" | egrep -i "FCP device number|Status"`; xCAT::zvmUtils->printSyslog("smcli System_WWPN_Query -T $hcpUserId | egrep -i FCP device number|Status"); - @devices = split( "\n", $out ); - for ($i = 0; $i < @devices; $i++) { - # Extract the device number and status - $devNo = $devices[$i]; - $devNo =~ s/^FCP device number:(.*)/$1/; - $devNo =~ s/^\s+//; - $devNo =~ s/\s+$//; - - $i++; - $status = $devices[$i]; - $status =~ s/^Status:(.*)/$1/; - $status =~ s/^\s+//; - $status =~ s/\s+$//; - - # Only print out devices matching query - if ($status =~ m/$space/i) { - $str .= "$devNo\n"; - } - } - } + @devices = split( "\n", $out ); + for ($i = 0; $i < @devices; $i++) { + # Extract the device number and status + $devNo = $devices[$i]; + $devNo =~ s/^FCP device number:(.*)/$1/; + $devNo =~ s/^\s+//; + $devNo =~ s/\s+$//; + + $i++; + $status = $devices[$i]; + $status =~ s/^Status:(.*)/$1/; + $status =~ s/^\s+//; + $status =~ s/\s+$//; + + # Only print out devices matching query + if ($status =~ m/$space/i) { + $str .= "$devNo\n"; + } + } + } } else { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Query supported on active, free, or offline devices" ); } @@ -6984,7 +7792,7 @@ sub inventoryHypervisor { # luns [fcp_device] (supported only on z/VM 6.2) elsif ( $args->[0] eq "--luns" ) { - # Find the LUNs accessible thru given zFCP device + # Find the LUNs accessible thru given zFCP device my $fcp = lc($args->[1]); my $argsSize = @{$args}; if ($argsSize < 2) { @@ -7010,10 +7818,10 @@ sub inventoryHypervisor { $wwpn = $_; if (!scalar($map{$wwpn})) { - $map{$wwpn} = {}; + $map{$wwpn} = {}; } } elsif ($_ =~ "Logical unit number:") { - $_ =~ s/^\s+Logical unit number:(.*)/$1/; + $_ =~ s/^\s+Logical unit number:(.*)/$1/; $_ =~ s/^\s+//; $_ =~ s/\s+$//; $lun = $_; @@ -7032,13 +7840,13 @@ sub inventoryHypervisor { xCAT::zvmUtils->printLn($callback, "#status,wwpn,lun,size,range,owner,channel,tag"); foreach $wwpn (sort keys %map) { foreach $lun (sort keys %{$map{$wwpn}}) { - # status, wwpn, lun, size, range, owner, channel, tag - $size = sprintf("%.1f", $map{$wwpn}{$lun}/1073741824); # Convert size to GB - - if ($size > 0) { - $size .= "G"; - xCAT::zvmUtils->printLn($callback, "unknown,$wwpn,$lun,$size,,,,"); - } + # status, wwpn, lun, size, range, owner, channel, tag + $size = sprintf("%.1f", $map{$wwpn}{$lun}/1073741824); # Convert size to GB + + if ($size > 0) { + $size .= "G"; + xCAT::zvmUtils->printLn($callback, "unknown,$wwpn,$lun,$size,,,,"); + } } } @@ -7093,7 +7901,7 @@ sub inventoryHypervisor { } # ssi - elsif ( $args->[0] eq "--ssi" ) { + elsif ( $args->[0] eq "--ssi" ) { $str = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli SSI_Query"`; xCAT::zvmUtils->printSyslog("smcli SSI_Query"); } @@ -7177,13 +7985,13 @@ sub inventoryHypervisor { # wwpn [fcp_device] (supported only on z/VM 6.2) elsif ( $args->[0] eq "--wwpns" ) { - my $fcp = lc($args->[1]); - my $argsSize = @{$args}; + my $fcp = lc($args->[1]); + my $argsSize = @{$args}; if ($argsSize < 2) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Wrong number of parameters" ); return; } - + $out = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp" | egrep -i "World wide port number:"`; xCAT::zvmUtils->printSyslog("smcli System_FCP_Free_Query -T $hcpUserId -k fcp_dev=$fcp | egrep -i World wide port number:"); @@ -7191,19 +7999,19 @@ sub inventoryHypervisor { my %uniqueWwpns; foreach (@wwpns) { # Extract the device number - if ($_ =~ "World wide port number:") { - $_ =~ s/^\s+World wide port number:(.*)/$1/; - $_ =~ s/^\s+//; - $_ =~ s/\s+$//; - - # Save only unique WWPNs - $uniqueWwpns{$_} = 1; + if ($_ =~ "World wide port number:") { + $_ =~ s/^\s+World wide port number:(.*)/$1/; + $_ =~ s/^\s+//; + $_ =~ s/\s+$//; + + # Save only unique WWPNs + $uniqueWwpns{$_} = 1; } } my $wwpn; for $wwpn ( keys %uniqueWwpns ) { - $str .= "$wwpn\n"; + $str .= "$wwpn\n"; } } @@ -7392,7 +8200,7 @@ sub migrateVM { # Change the zHCP if migration successful if ($isMigrated) { - `nodech $node zvm.hcp=$destHcp zvm.parent=$destination`; + `/opt/xcat/bin/nodech $node zvm.hcp=$destHcp zvm.parent=$destination`; } else { xCAT::zvmUtils->printLn( $callback, "$node: Could not determine progress of relocation" ); } @@ -7497,7 +8305,7 @@ sub evacuate { # Begin migration # Required keys: target_identifier, destination, action, immediate, and max_total - $out = `rmigrate $nodes action=MOVE destination=$destination immediate=NO max_total=NOLIMIT`; + $out = `/opt/xcat/bin/rmigrate $nodes action=MOVE destination=$destination immediate=NO max_total=NOLIMIT`; xCAT::zvmUtils->printLn( $callback, "$out" ); return; @@ -7550,21 +8358,42 @@ sub eventLog { return; } + # Check if node is the management node + my @entries = xCAT::TableUtils->get_site_attribute("master"); + my $master = xCAT::zvmUtils->trimStr($entries[0]); + my $ip = xCAT::NetworkUtils->getipaddr($node); + $ip = xCAT::zvmUtils->trimStr($ip); + my $mn = 0; + if ($master eq $ip) { + # If the master IP and node IP match, then it is the management node + xCAT::zvmUtils->printLn( $callback, "$node: This is the management node" ); + $mn = 1; + } + # Just clear the log my $out = ''; if ($clear) { - $out = `ssh $::SUDOER\@$node "cat /dev/null > $srcLog"`; + if ($mn) { + $out = `cat /dev/null > $srcLog`; + } else { + $out = `ssh $::SUDOER\@$node "cat /dev/null > $srcLog"`; + } + xCAT::zvmUtils->printLn( $callback, "$node: Clearing event log ($srcLog)... Done" ); return; } # Just set the logging options if ($options) { - $out = `echo -e \"$options\" > /tmp/$node.tracing`; - $out = `ssh $::SUDOER\@$node "rm -rf $srcLog"`; - $out = `cat /tmp/$node.tracing | ssh $::SUDOER\@$node "cat > /tmp/$node.tracing"`; - $out = `ssh $::SUDOER\@$node "mv /tmp/$node.tracing $srcLog"`; - $out = `rm -rf /tmp/$node.tracing`; + if ($mn) { + $out = `echo -e \"$options\" > $srcLog`; + } else { + $out = `echo -e \"$options\" > /tmp/$node.tracing`; + $out = `ssh $::SUDOER\@$node "rm -rf $srcLog"`; + $out = `cat /tmp/$node.tracing | ssh $::SUDOER\@$node "cat > /tmp/$node.tracing"`; + $out = `ssh $::SUDOER\@$node "mv /tmp/$node.tracing $srcLog"`; + $out = `rm -rf /tmp/$node.tracing`; + } xCAT::zvmUtils->printLn( $callback, "$node: Setting event logging options... Done" ); return; @@ -7580,17 +8409,255 @@ sub eventLog { } # Copy over event log onto xCAT - xCAT::zvmUtils->printLn( $callback, "$node: Retrieving event log ($srcLog)" ); + xCAT::zvmUtils->printLn( $callback, "$node: Retrieving event log ($srcLog)" ); + if ($mn) { + if (!(`test -e $srcLog && echo Exists`)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Specified log does not exist" ); + return; + } + + $out = `cp $srcLog $tgtLog`; + } else { + if (!(`ssh $::SUDOER\@$node "test -e $srcLog && echo Exists"`)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Specified log does not exist" ); + return; + } + + $out = `scp $::SUDOER\@$node:$srcLog $tgtLog`; + } - if (!(`ssh $::SUDOER\@$node "test -e $srcLog && echo Exists"`)) { - xCAT::zvmUtils->printLn( $callback, "$node: Specified log does not exist" ); + if ( -e $tgtLog ) { + xCAT::zvmUtils->printLn( $callback, "$node: Log copied to $tgtLog" ); + $out = `chmod -R 644 $tgtLog/*`; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to copy log" ); + } +} + +#------------------------------------------------------- + +=head3 imageCapture + + Description : Capture a disk image from a Linux system on z/VM. + Arguments : Node + OS + Archictecture + Profile + Device information + Returns : Nothing + Example : imageCapture( $callback, $node, $os, $arch, $profile, $osimg, $device ); + +=cut + +#------------------------------------------------------- +sub imageCapture { + my ($class, $callback, $node, $os, $arch, $profile, $osimg, $device) = @_; + my $rc; + my $out = ''; + my $reason = ""; + + xCAT::zvmUtils->printSyslog( "imageCapture() $node:$node os:$os arch:$arch profile:$profile osimg:$osimg device:$device" ); + + # Verify required properties are defined + if (!defined($os) || !defined($arch) || !defined($profile)) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) One or more of the required properties is not specified: os version, architecture or profile" ); return; } - $out = `scp $::SUDOER\@$node:$srcLog $tgtLog`; - if ( -e $tgtLog ) { - xCAT::zvmUtils->printLn( $callback, "$node: Log copied to $tgtLog" ); + # Ensure the architecture property is 's390x' + if ($arch ne 's390x') { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Architecture was $arch instead of 's390x'. 's390x' will be used instead of the specified value." ); + $arch = 's390x'; + } + + # Obtain the location of the install root directory + my $installRoot = xCAT::TableUtils->getInstallDir(); + + # Directory where executables are on zHCP. + # Using a local variable to hold the directory information because this routine is called from another module. + my $dir = "/opt/zhcp/bin"; + + # Use sudo or not + # This looks in the passwd table for a key = sudoer + my ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); + + # Get node properties from 'zvm' table + my @propNames = ( 'hcp', 'userid' ); + my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); + + # Get zHCP + my $hcp = $propVals->{'hcp'}; + if (!$hcp) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); + return; + } + + # Get zHCP user ID + my $hcpUserId = xCAT::zvmCPUtils->getUserId($::SUDOER, $hcp); + $hcpUserId =~ tr/a-z/A-Z/; + + # Get capture target's user ID + my $targetUserId = $propVals->{'userid'}; + $targetUserId =~ tr/a-z/A-Z/; + + # Get node properties from 'zvm' table + @propNames = ( 'ip', 'hostnames' ); + $propVals = xCAT::zvmUtils->getNodeProps('hosts', $node, @propNames); + + # Check if node is pingable + if (`/opt/xcat/bin/pping $node | egrep -i "noping"`) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Host is unreachable" ); + return; + } + + my $vaddr; + my $devName; + # Set the default is device option was specified without any parameters. + if (!$device) { + $devName = "/dev/root"; + } + + # Obtain the device number from the target system. + if ($devName eq '/dev/root') { + # Determine which Linux device is associated with the root directory + $out = `ssh $sudoer\@$node $sudo cat /proc/cmdline | tr " " "\\n" | grep "^root=" | cut -c6-`; + if ($out) { + $out = `ssh $sudoer\@$node $sudo "/usr/bin/readlink -f $out"`; + if ($out) { + $devName = substr($out, 5); + $devName =~ s/\s+$//; + $devName =~ s/\d+$//; + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable locate the device associated with the root directory" ); + return; + } + } else { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable locate the device associated with the root directory" ); + return; + } } else { - xCAT::zvmUtils->printLn( $callback, "$node: Failed to copy log" ); - } -} \ No newline at end of file + $devName = substr $devName, 5; + } + + $vaddr = xCAT::zvmUtils->getDeviceNodeAddr($sudoer, $node, $devName); + if (!$vaddr) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable determine the device being captured" ); + return 0; + } + + # Shutdown and logoff the virtual machine so that its disks are stable for the capture step. + xCAT::zvmUtils->printSyslog( "imageCapture() Shutting down $node prior to disk capture" ); + $out = `ssh -o ConnectTimeout=10 $node "shutdown -h now"`; + sleep(15); # Wait 15 seconds to let shutdown start before logging user off + + # If the OS is not shutdown and the machine is enabled for shutdown signals + # then deactivate will cause CP to send the shutdown signal and + # wait an additional (z/VM installation configurable) time before forcing + # the virtual machine off the z/VM system. + xCAT::zvmUtils->printSyslog( "$sudo $dir/smcli Image_Deactivate -T $targetUserId" ); + $out = `ssh $sudoer\@$hcp "$sudo $dir/smcli Image_Deactivate -T $targetUserId"`; + xCAT::zvmUtils->printSyslog( "imageCapture() smcli response: $out" ); + + xCAT::zvmUtils->printSyslog( "imageCapture() Preparing the staging directory" ); + + # Create the staging area location for the image + my $stagingImgDir = "$installRoot/staging/$os/$arch/$profile"; + if(-d $stagingImgDir) { + unlink $stagingImgDir; + } + mkpath($stagingImgDir); + + # Prepare the staging mount point on zHCP, if they need to be established. + my $remoteStagingDir; + $rc = xCAT::zvmUtils->establishMount( $callback, $sudoer, $sudo, $hcp, "$installRoot/staging", "rw", \$remoteStagingDir ); + if ($rc) { + # Mount failed + rmtree "$stagingImgDir"; + return; + } + + xCAT::zvmUtils->printLn( $callback, "$node: Capturing the image using zHCP node" ); + + # Drive the capture on the zHCP node + xCAT::zvmUtils->printSyslog( "imageCapture() creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img" ); + $out = `ssh $sudoer\@$hcp "$sudo $dir/creatediskimage $targetUserId $vaddr $remoteStagingDir/$os/$arch/$profile/${vaddr}.img"`; + $rc = $?; + + # If the capture failed then clean up and return + my $reasonString = ""; + $rc = xCAT::zvmUtils->checkOutputExtractReason( $callback, $out, \$reasonString ); + if ($rc != 0) { + $reason = "Reason: $reasonString"; + xCAT::zvmUtils->printSyslog( "imageCapture() creatediskimage of $targetUserId $vaddr failed. $reason" ); + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Image capture of $targetUserId $vaddr failed on the zHCP node. $reason" ); + rmtree "$stagingImgDir" ; + return; + } + + # Now that all image files have been successfully created, move them to the deployable directory. + my $imageName = "$os-$arch-netboot-$profile"; + my $deployImgDir = "$installRoot/netboot/$os/$arch/$profile"; + + xCAT::zvmUtils->printLn( $callback, "$node: Moving the image files to the deployable directory: $deployImgDir" ); + + my @stagedFiles = glob "$stagingImgDir/*.img"; + if (!@stagedFiles) { + rmtree "$stagingImgDir"; + xCAT::zvmUtils->printLn( $callback, "$node: (Error) No image files were created" ); + return 0; + } + + mkpath($deployImgDir); + + foreach my $oldFile (@stagedFiles) { + $rc = move($oldFile, $deployImgDir); + $reason = $!; + if ($rc == 0) { + # Move failed + rmtree "$stagingImgDir"; + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not move $oldFile to $deployImgDir. $reason" ); + return; + } + } + + # Remove the staging directory and files + rmtree "$stagingImgDir"; + + xCAT::zvmUtils->printSyslog( "imageCapture() Updating the osimage table" ); + + # Update osimage table + my $osTab = xCAT::Table->new('osimage',-create => 1,-autocommit => 0); + my %keyHash; + + unless ($osTab) { + xCAT::zvmUtils->printLn( $callback, "$node: (Error) Unable to open table 'osimage'" ); + return; + } + + $keyHash{provmethod} = 'netboot'; + $keyHash{profile} = $profile; + $keyHash{osvers} = $os; + $keyHash{osarch} = $arch; + $keyHash{imagetype} = 'linux'; + $keyHash{osname} = 'Linux'; + $keyHash{imagename} = $imageName; + + $osTab->setAttribs({imagename => $imageName}, \%keyHash); + $osTab->commit; + + xCAT::zvmUtils->printSyslog( "imageCapture() Updating the linuximage table" ); + + # Update linuximage table + my $linuxTab = xCAT::Table->new('linuximage',-create => 1,-autocommit => 0); + + %keyHash = (); + $keyHash{imagename} = $imageName; + $keyHash{rootimgdir} = $deployImgDir; + + $linuxTab->setAttribs({imagename => $imageName}, \%keyHash); + $linuxTab->commit; + + xCAT::zvmUtils->printLn( $callback, "$node: Completed capturing the image($imageName) and stored at $deployImgDir" ); + + return; +} diff --git a/xCAT-server/share/xcat/scripts/xcatconf4z b/xCAT-server/share/xcat/scripts/xcatconf4z new file mode 100644 index 000000000..64b9c6238 --- /dev/null +++ b/xCAT-server/share/xcat/scripts/xcatconf4z @@ -0,0 +1,484 @@ +#!/bin/sh +# IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html + +### BEGIN INIT INFO +# Provides: xcatconfinit +# Default-Start: 2 3 5 +# Default-stop: 0 1 4 6 +# Required-Start: $syslog +# Should-Start: +# Required-Stop: +# Short-Description: xCAT disk initialization and configuration +# Description: Reads class x files from the reader and acts based on the type of file. +# Files of filetype "disk" cause SCSI disks be configured. +# Other files are used to generate an ISO9660 disk for transporting xCAT configurations. +### END INIT INFO + +############################################################################### +# Authorized senders of configuration files listed here. Specify a list a +# blank delimitted list of userids with each userid in uppercase +# (e.g. "ZHCP" or "ZHCP ZHCP2"), or '*' to indicate all are authorized. +# If nothing is specified then this function will not process any +# configuration files in the reader. +############################################################################### +authorizedSenders='' + +function getOsVersion { + # @Description: + # Returns the Linux distro version in an easy to parse format. + # @Input: + # None + # @Output: + # os - Variable set with OS and version information. For example: + # "rhel62" or "sles11sp2" + # @Code: + release=`cat /etc/*release` + + if echo $release | grep -i "SUSE Linux Enterprise Server" > /dev/null ; then + os='sles' + version=`echo "$release" | grep "VERSION =" | sed \ + -e 's/^.*VERSION =//' \ + -e 's/\s*$//' \ + -e 's/.//' \ + -e 's/[^0-9]*([0-9]+).*/$1/'` + os=$os$version; + + # Append service level + level=`echo "$release" | grep "LEVEL =" | sed \ + -e 's/^.*LEVEL =//' \ + -e 's/\s*$//' \ + -e 's/.//' \ + -e 's/[^0-9]*([0-9]+).*/$1/'` + os=$os'sp'$level + + elif version=`echo $release | grep -i "Red Hat Enterprise Linux Server"`; then + os='rhel' + version=`echo $version | sed \ + -e 's/[A-Za-z\/\.\(\)]//g' \ + -e 's/^ *//g' \ + -e 's/ *$//g' \ + -e 's/\s.*$//'` + os=$os$version + fi + return +} + +function onlineDevice { + # @Description: + # Brings a Linux device online. + # @Input: + # Device number, e.g. "0.0.000c" + # @Output: + # Return code indicates success or failure + # @Code: + device=$1 + local funcName="onlineDevice" + rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) + if (( rc != 0 )); then + if [[ -e /sbin/cio_ignore ]]; then + rc=$(/sbin/cio_ignore -r 0.0.$device > /dev/null; echo $?) + fi + rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) + if (( rc != 0 )); then + echo "xcatconfinit $funcName (Error) Could not activate the virtual reader" + return 1 + fi + fi + return 0 +} + +function pullReader { + # @Description: + # Reads class x spool files from the reader if sent by an authorized sender. + # Drives special processing functions for files of a specific type. + # Files with a filetype of: + # tgz are unpacked into the transport directory + # disk files are read and cause the setupDisk function to be driven + # all other files are unpacked into the transport directory + # @Input: + # None + # @Output: + # Return code indicates success if reader was brought online. + # @Code: + local funcName="pullReader" + /sbin/modprobe vmcp + + # Online reader + rc= onlineDevice "000c" + if (( rc != 0 )); then + return $rc + fi + + # Grab the spool Id, class file name, and file type + eval records=($(/usr/sbin/vmur li | tail -n +2 | cut -c 1-72 | awk '{print $1":"$2":"$3":"$10":"$11"\n"}' )) + + # Process each spool file that is class "x" + for record in "${records[@]}" + do + record=$(echo $record | tr ":" " ") + set $record + originid=$1 + spoolid=$2 + class=$3 + filename=$4 + type=$5 + + if [[ $class != "X" ]]; then + # Spool file is not of the class required for processing by this script. + continue + fi + + if [[ $authorizedSenders != "*" ]]; then + if [[ " $authorizedSenders " != *" $originid "* ]]; then + # Originator is not authorized to send configuration files. + continue + fi + fi + + if [[ -n $type ]]; then + file="$filename.$type" + else + file=$filename + fi + + # Receive the spool file + echo "Downloading record $spoolid: $file" + + if [[ $type == "txt" ]] || [[ $type == "sh" ]]; then + # Receiving text + rc=$(/usr/sbin/vmur re -t $spoolid $file) + elif [[ $type == "tgz" ]]; then + rc=$(/usr/sbin/vmur re $spoolid $file) + /bin/tar xzf $file -C $transportdir + rm $file + elif [[ $type == "disk" ]]; then + rc=$(/usr/sbin/vmur re $spoolid $file) + if (( rc == 0 )); then + setupDisk $transportdir'/'$file + rc=0 + fi + else + # Receive block + rc=$(/usr/sbin/vmur re $spoolid $file) + fi + + if (( rc != 0 )); then + echo "xcatconfinit funcName (Error) Failed to download record $spoolid" + fi + done + return 0 +} + +function setupIso { + # @Description: + # Makes an ISO filesystem using the contents of the transport directory and + # creates a loop device pointing to the ISO image. If an "init.sh" script + # exists in the transport directory then it is driven. + # @Input: + # None + # @Output: + # None + # @Code: + # Create ISO based on transport directory + iso="/var/opt/xcat/transport.iso" + /usr/bin/mkisofs -l -o $iso $transportdir + + # Create loop back device pointing to ISO9660 image + nextLoopDev=`/sbin/losetup -f` + if [[ -n $nextLoopDev ]]; then + /sbin/losetup $nextLoopDev $iso + else + return + fi + + # Execute init script (if one exists) + if [[ -e ${transportdir}/init.sh ]]; then + chmod 755 ${transportdir}/init.sh + ${transportdir}/init.sh + fi +} + +function setupDisk { + # @Description: + # Processes a disk file for the following functions: + # create a file system node + # Setup a SCSI volume + # Removes a SCSI volume + # @Input: + # Location and name of the disk file. + # @Output: + # None + # @Code: + diskFile=$1 + local funcName="setupDisk" + + # Read the file and verify we want to handle it + if ! grep -q "# xCAT Init" "$diskFile"; then + # File is not one that we handle. Leave it alone. + return + fi + + # Read the file now that we know it is our file + oldIFS=$IFS + IFS=$'\n' + for line in $(cat "$diskFile"); do + if [[ $line == \#* ]]; then + # Ignore comment lines + continue + fi + keyName=${line%\=*} + value=${line#*\=} + value=$(echo ${value} | sed -e 's/^ *//g') + newKey='xcat_'$keyName + eval $newKey=$value + done + IFS=$oldIFS + + # Remove the disk file after we have read it + rm $diskFile + + ########################################################################## + # Handle creating a file system node + # Disk file input parameters: + # action - "createfilesysnode" + # srcFile - location/name of the source file for the mknod command + # tgtFile - location/name of the target file for the mknod command + ########################################################################## + if [[ $xcat_action == "createfilesysnode" ]]; then + echo "Creating a file system node, source: $xcat_srcFile, target: $xcat_tgtFile" + + if [[ ! -n $xcat_srcFile ]]; then + echo "xcatconfinit $funcName (Error) Source file for creating a file system node was not specified" + return + fi + + if [[ ! -n $xcat_tgtFile ]]; then + echo "xcatconfinit $funcName (Error) Target file for creating a file system node was not specified" + return + fi + if [[ -e $xcat_tgtFile ]]; then + echo "xcatconfinit $funcName (Error) Target file for creating a file system node already exists" + return + fi + + out=`stat -L --printf=%t:%T $xcat_srcFile` + if (( $? != 0 )); then + echo "xcatconfinit $funcName (Error) Unable to stat the source file: $xcat_srcFile" + return + fi + + major=${out%:*} + major=$(echo ${major} | sed -e 's/^ *//g') + minor=${out#*:} + minor=$(echo ${minor} | sed -e 's/^ *//g') + + mknod $xcat_tgtFile b 0x$major 0x$minor + + ########################################################################## + # Handle removing a file system node + # Disk file input parameters: + # action - "removefilesysnode" + # tgtFile - location/name of the target file for the mknod command + ########################################################################## + elif [[ $xcat_action == "removefilesysnode" ]]; then + echo "Removing a file system node, target: $xcat_tgtFile" + if [[ ! -n $xcat_tgtFile ]]; then + echo "xcatconfinit $funcName (Error) Target file for creating a file system node was not specified" + return + fi + + umount "$xcat_tgtFile" + rm -f "$xcat_tgtFile" + + ########################################################################## + # Handle adding a SCSI volume + # Disk file input parameters: + # action - "addScsiVolume" + # fcpAddr - FCP device address + # wwpn - WWPN number + # lun - LUN number + ########################################################################## + elif [[ $xcat_action == "addScsiVolume" ]]; then + echo "Adding a SCSI Volume, FCP addr: $xcat_fcpAddr, WWPN: $xcat_wwpn, LUN: $xcat_lun" + + # Validate the input + if [[ ! -n $xcat_fcpAddr ]]; then + echo "xcatconfinit $funcName (Error) FCP address was not specified" + return + fi + xcat_fcpAddr=`echo $xcat_fcpAddr | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_wwpn ]]; then + echo "xcatconfinit $funcName (Error) WWPN was not specified" + return + fi + xcat_wwpn=`echo $xcat_wwpn | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_lun ]]; then + echo "xcatconfinit $funcName (Error) LUN was not specified" + return + fi + xcat_lun=`echo $xcat_lun | tr '[A-Z]' '[a-z]'` + + # Online the device + rc= onlineDevice $xcat_fcpAddr + if (( rc != 0 )); then + return + fi + + # Set WWPN and LUN in sysfs + if [[ -e /sys/bus/ccw/drivers/zfcp/0.0.$xcat_fcpAddr/port_add ]]; then + echo 0x$xcat_wwpn > /sys/bus/ccw/drivers/zfcp/0.0.$xcat_fcpAddr/port_add + fi + echo 0x$xcat_lun > /sys/bus/ccw/drivers/zfcp/0.0.$xcat_fcpAddr/0x$xcat_wwpn/unit_add + + # Set WWPN and LUN in configuration files + # RHEL: /etc/zfcp.conf + # SLES 10: /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-* + # SLES 11: /etc/udev/rules.d/51-zfcp* + if [[ $os == sles10* ]]; then + /sbin/zfcp_host_configure 0.0.$xcat_fcpAddr 1 + /sbin/zfcp_disk_configure 0.0.$xcat_fcpAddr $xcat_wwpn $xcat_lun 1 + echo "0x$xcat_wwpn:0x$xcat_lun" >> /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$xcat_fcpAddr + elif [[ $os == sles11* ]]; then + /sbin/zfcp_host_configure 0.0.$xcat_fcpAddr 1 + /sbin/zfcp_disk_configure 0.0.$xcat_fcpAddr $xcat_wwpn $xcat_lun 1 + + # Configure zFCP device to be persistent + touch /etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules + + # Check if the file already contains the zFCP channel + out=`cat "/etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules" | egrep -i "ccw/0.0.$xcat_fcpAddr]online"` + if [[ ! $out ]]; then + echo "ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.$xcat_fcpAddr\", IMPORT{program}=\"collect 0.0.$xcat_fcpAddr %k 0.0.$xcat_fcpAddr zfcp\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules + echo "ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.$xcat_fcpAddr %k 0.0.$xcat_fcpAddr zfcp\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules + echo "ACTION==\"add\", ENV{COLLECT_0.0.$xcat_fcpAddr}==\"0\", ATTR{[ccw/0.0.$xcat_fcpAddr]online}=\"1\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules + fi + + echo "ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x$xcat_wwpn\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.$xcat_fcpAddr\", ATTR{[ccw/0.0.$xcat_fcpAddr]0x$xcat_wwpn/unit_add}=\"0x$xcat_lun\"" \ + | tee -a /etc/udev/rules.d/51-zfcp-0.0.$device.rules + elif [[ $os == rhel* ]]; then + echo "0.0.$xcat_fcpAddr 0x$xcat_wwpn 0x$xcat_lun" >> /etc/zfcp.conf + + if [[ $os == rhel6* ]]; then + echo "add" > /sys/bus/ccw/devices/0.0.$xcat_fcpAddr/uevent + fi + fi + + # Settle the file system so when we are done the device is fully available + if [[ $(which udevadm 2> /dev/null) != '' ]]; then + udevadm settle + else + udevsettle + fi + if [[ ! -e "/dev/disk/by-path/ccw-0.0.${xcat_fcpAddr}-zfcp-0x${xcat_wwpn}:0x${xcat_lun}" ]]; then + # Sometimes the file takes longer to appear. We will wait up to 3 minutes. + maxTime=0 + for time in 1 2 2 5 10 10 30 60 60 + do + if [[ -e "/dev/disk/by-path/ccw-0.0.${xcat_fcpAddr}-zfcp-0x${xcat_wwpn}:0x${xcat_lun}" ]]; then + # Leave the loop now that the file exists + break + fi + maxTime=$maxTime+$time + echo "Sleeping for $time seconds to allow /dev/disk/by-path/ccw-0.0.${xcat_fcpAddr}-zfcp-0x${xcat_wwpn}:0x${xcat_lun} to be created" + sleep $time + done + fi + if [[ ! -e "/dev/disk/by-path/ccw-0.0.${xcat_fcpAddr}-zfcp-0x${xcat_wwpn}:0x${xcat_lun}" ]]; then + echo "/dev/disk/by-path/ccw-0.0.${xcat_fcpAddr}-zfcp-0x${xcat_wwpn}:0x${xcat_lun} did not appear in $maxTime seconds, continuing." + fi + + ########################################################################## + # Handle removing a SCSI volume + # Disk file input parameters: + # action - "removeScsiVolume" + # fcpAddr - FCP device address + # wwpn - WWPN number + # lun - LUN number + ########################################################################## + elif [[ $xcat_action == "removeScsiVolume" ]]; then + echo "Removing a SCSI Volume, FCP addr: $xcat_fcpAddr, WWPN: $xcat_wwpn, LUN: $xcat_lun" + + # Validate the input + if [[ ! -n $xcat_fcpAddr ]]; then + echo "xcatconfinit $funcName (Error) FCP address was not specified" + return + fi + xcat_fcpAddr=`echo $xcat_fcpAddr | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_wwpn ]]; then + echo "xcatconfinit $funcName (Error) WWPN was not specified" + return + fi + xcat_wwpn=`echo $xcat_wwpn | tr '[A-Z]' '[a-z]'` + + if [[ ! -n $xcat_lun ]]; then + echo "xcatconfinit $funcName (Error) LUN was not specified" + return + fi + xcat_lun=`echo $xcat_lun | tr '[A-Z]' '[a-z]'` + + # Delete the SCSI device + scsiDevice=`lszfcp -l 0x$xcat_lun | grep 0x$xcat_lun | cut -d " " -f2` + if [[ -n $scsiDevice ]]; then + echo 1 > "/sys/bus/scsi/devices/$scsiDevice/delete" + fi + + # Delete WWPN and LUN from sysfs + if [[ -e /sys/bus/ccw/drivers/zfcp/0.0.$xcat_fcpAddr/0x$xcat_wwpn/unit_remove ]]; then + if [[ $(which udevadm 2> /dev/null) != '' ]]; then + udevadm settle + else + udevsettle + fi + echo 0x$xcat_lun > /sys/bus/ccw/drivers/zfcp/0.0.$xcat_fcpAddr/0x$xcat_wwpn/unit_remove + fi + + # Delete WWPN and LUN from configuration files + # RHEL: /etc/zfcp.conf + # SLES 10: /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-* + # SLES 11: /etc/udev/rules.d/51-zfcp* + if [[ $os == sles10* ]]; then + expression="/$xcat_lun/d" + sed --in-place -e $expression /etc/sysconfig/hardware/hwcfg-zfcp-bus-ccw-0.0.$xcat_fcpAddr + elif [[ $os == sles11* ]]; then + expression="/$xcat_lun/d" + sed --in-place -e $expression /etc/udev/rules.d/51-zfcp-0.0.$xcat_fcpAddr.rules + elif [[ $os == rhel* ]]; then + expression="/$xcat_lun/d" + sed --in-place -e $expression /etc/zfcp.conf + fi + fi + + return +} + +############################################################################ +# Main Code Section +############################################################################ +case "$1" in + start) + if [[ -z "$authorizedSenders" ]]; then + echo "xcatconfinit is disabled. There are no authorized senders of configuration files." + else + echo "xcatconfinit is starting" + transportdir="/var/opt/xcat/transport" + rm -Rf $transportdir + /bin/mkdir -p $transportdir + cd $transportdir + + # Get Linux version + getOsVersion + + pullReader + setupIso + fi + ;; + stop|status|restart|reload|force-reload) + # Do nothing + ;; +esac