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/trunk@16696 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
phamt 2013-06-19 16:17:23 +00:00
parent 9c7b61f341
commit 62761908f2
4 changed files with 2751 additions and 582 deletions

View File

@ -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

View File

@ -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/<pool-name>.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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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