# IBM(c) 2013-2016 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 This is a utility plugin for z/VM. =cut #------------------------------------------------------- package xCAT::zvmUtils; use xCAT::MsgUtils; use xCAT::Utils; use xCAT::Table; use xCAT::NetworkUtils; use File::Basename; use Net::Ping; use strict; use warnings; use Encode; use JSON; use Data::Dumper; use Cwd; 1; my $locOpenStackUpdateName = '/var/lib/sspmod/setnewname.py'; # Files which contain OS distribution and version information. my $locEtcDebianVersion = '/etc/debian_version'; my $locEtcFedoraRelease = '/etc/fedora-release'; my $locEtcIssue = '/etc/issue'; my $locEtcLsbRelease = '/etc/lsb-release'; my $locEtcOsRelease = '/etc/os-release'; my $locEtcRedhatRelease = '/etc/redhat-release'; my $locEtcStarRelease = '/etc/*-release'; my $locEtcSuseRelease = '/etc/SuSE-release'; my $locEtcUnitedLinux = '/etc/UnitedLinux-release'; my $locAllEtcVerFiles = "/etc/*-release /etc/issue /etc/debian_version"; # Supported Operating System distros and versions my %supportedVersions = ( rhel5 => 1, rhel6 => 1, sles10 => 1, sles11 => 1, ); #------------------------------------------------------- =head3 getNodeProps Description : Get node properties Arguments : Table Node Properties Returns : Node properties from given table Example : my $propVals = xCAT::zvmUtils->getNodeProps($tabName, $node, $propNames); =cut #------------------------------------------------------- sub getNodeProps { # Get inputs my ( $class, $tabName, $node, @propNames ) = @_; # Get table my $tab = xCAT::Table->new($tabName); # Get property values my $propVals = $tab->getNodeAttribs( $node, [@propNames] ); return ($propVals); } #------------------------------------------------------- =head3 getTabPropsByKey Description : Get table entry properties by key Arguments : Table Key name Key value Requested properties Returns : Table entry properties Example : my $propVals = xCAT::zvmUtils->getTabPropsByKey($tabName, $key, $keyValue, @reqProps); =cut #------------------------------------------------------- sub getTabPropsByKey { # Get inputs my ( $class, $tabName, $key, $keyVal, @propNames ) = @_; # Get table my $tab = xCAT::Table->new($tabName); my $propVals; # Get table attributes matching given key $propVals = $tab->getAttribs( { $key => $keyVal }, @propNames ); return ($propVals); } #------------------------------------------------------- =head3 getAllTabEntries Description : Get all entries within given table Arguments : Table name Returns : All table entries Example : my $entries = xCAT::zvmUtils->getAllTabEntries($tabName); =cut #------------------------------------------------------- sub getAllTabEntries { # Get inputs my ( $class, $tabName ) = @_; # Get table my $tab = xCAT::Table->new($tabName); my $entries; # Get all entries within given table $entries = $tab->getAllEntries(); return ($entries); } #------------------------------------------------------- =head3 setNodeProp Description : Set a node property in a given table Arguments : Table Node Property name Property value Returns : Nothing Example : xCAT::zvmUtils->setNodeProp($tabName, $node, $propName, $propVal); =cut #------------------------------------------------------- sub setNodeProp { # Get inputs my ( $class, $tabName, $node, $propName, $propVal ) = @_; # Get table my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Set property $tab->setAttribs( { 'node' => $node }, { $propName => $propVal } ); # Save table $tab->commit; return; } #------------------------------------------------------- =head3 setNodeProps Description : Set node properties in a given table Arguments : Table Node Reference to property name/value hash Returns : Nothing Example : xCAT::zvmUtils->setNodeProps($tabName, $node, \%propHash); =cut #------------------------------------------------------- sub setNodeProps { # Get inputs my ( $class, $tabName, $node, $propHash ) = @_; # Get table my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Set property $tab->setAttribs( { 'node' => $node }, $propHash ); # Save table $tab->commit; return; } #------------------------------------------------------- =head3 delTabEntry Description : Delete a table entry Arguments : Table Key name Key value Returns : Nothing Example : xCAT::zvmUtils->delTabEntry($tabName, $keyName, $keyVal); =cut #------------------------------------------------------- sub delTabEntry { # Get inputs my ( $class, $tabName, $keyName, $keyVal ) = @_; # Get table my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); # Delete entry from table my %key = ( $keyName => $keyVal ); $tab->delEntries( \%key ); # Save table $tab->commit; return; } #------------------------------------------------------- =head3 tabStr Description : Tab a string (4 spaces) Arguments : String Returns : Tabbed string Example : my $str = xCAT::zvmUtils->tabStr($str); =cut #------------------------------------------------------- sub tabStr { # Get inputs my ( $class, $inStr ) = @_; my @lines = split( "\n", $inStr ); # Tab output my $outStr; foreach (@lines) { $outStr .= " $_\n"; } return ($outStr); } #------------------------------------------------------- =head3 trimStr Description : Trim the whitespaces in a string Arguments : String Returns : Trimmed string Example : my $str = xCAT::zvmUtils->trimStr($str); =cut #------------------------------------------------------- sub trimStr { # Get string my ( $class, $str ) = @_; # Trim right $str =~ s/\s*$//; # Trim left $str =~ s/^\s*//; return ($str); } #------------------------------------------------------- =head3 replaceStr Description : Replace a given pattern in a string Arguments : String Returns : New string Example : my $str = xCAT::zvmUtils->replaceStr($str, $pattern, $replacement); =cut #------------------------------------------------------- sub replaceStr { # Get string my ( $class, $str, $pattern, $replacement ) = @_; # Replace string $str =~ s/$pattern/$replacement/g; return ($str); } #------------------------------------------------------- =head3 printLn Description : Print a string to stdout Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printLn($callback, $str); =cut #------------------------------------------------------- sub printLn { # Get inputs my ( $class, $callback, $str ) = @_; # Print string my $rsp; my $type = "I"; if ($str =~ m/(\(error\)|\s*failed)/i) { # Set to print error if the string contains (error) or starts with failed $type = "E"; } $rsp->{data}->[0] = "$str"; xCAT::MsgUtils->message( $type, $rsp, $callback ); # xCAT::MsgUtils->message( "S", $str ); # Print to syslog return; } #------------------------------------------------------- =head3 printSyslog Description : Print a string to syslog Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printSyslog($str); =cut #------------------------------------------------------- sub printSyslog { # Get inputs my ( $class, $str ) = @_; # Prepend where this message came from $str = $class . " " . $str; # Print string xCAT::MsgUtils->message( "S", $str ); return; } #------------------------------------------------------- =head3 isZvmNode Description : Determines if a given node is in the 'zvm' table Arguments : Node Returns : TRUE Node exists FALSE Node does not exists Example : my $out = xCAT::zvmUtils->isZvmNode($node); =cut #------------------------------------------------------- sub isZvmNode { # Get inputs my ( $class, $node ) = @_; # Look in 'zvm' table my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); my @results = $tab->getAllAttribsWhere( "node = '" . $node . "'", 'userid' ); foreach (@results) { # Return 'TRUE' if given node is in the table if ($_->{'userid'}) { return 1; } } return 0; } #------------------------------------------------------- =head3 getHwcfg Description : Get the hardware configuration file path (SUSE only) e.g. /etc/sysconfig/hardwarehwcfg-qeth-bus-ccw-0.0.0600 Arguments : User (root or non-root) Node Returns : Hardware configuration file path Example : my $hwcfg = xCAT::zvmUtils->getHwcfg($user, $node); =cut #------------------------------------------------------- sub getHwcfg { # Get inputs my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get OS my $os = xCAT::zvmUtils->getOs($user, $node); # Get network configuration file path my $out; my @parms; # If it is SUSE - hwcfg-qeth file is in /etc/sysconfig/hardware if ( $os =~ m/SUSE/i ) { #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"`; my $cmd = "$sudo ls /etc/sysconfig/hardware/hwcfg-qeth*"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } @parms = split( '\n', $out ); return ( $parms[0] ); } # If no file is found - Return nothing return; } #------------------------------------------------------- =head3 getIp Description : Get the IP address of a given node Arguments : Node Returns : IP address of given node Example : my $ip = xCAT::zvmUtils->getIp($node); =cut #------------------------------------------------------- sub getIp { # Get inputs my ( $class, $node ) = @_; # Get IP address # You need the extra space in the pattern, # else it will confuse gpok2 with gpok21 my $out = `cat /etc/hosts | egrep -i "$node | $node."`; my @parms = split( ' ', $out ); return $parms[0]; } #------------------------------------------------------- =head3 getIfcfg Description : Get the network configuration file path of a given node * Red Hat - /etc/sysconfig/network-scripts/ifcfg-eth * SUSE - /etc/sysconfig/network/ifcfg-qeth Arguments : User (root or non-root) Node Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfg($user, $node); =cut #------------------------------------------------------- sub getIfcfg { # Get inputs my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get OS my $os = xCAT::zvmUtils->getOs($user, $node); # Get network configuration file path my $out; my @parms; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts if ( $os =~ m/Red Hat/i ) { #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; my $cmd = "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } @parms = split( '\n', $out ); return ( $parms[0] ); } # If it is SUSE - ifcfg-qeth file is in /etc/sysconfig/network elsif ( $os =~ m/SUSE/i ) { #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"`; my $cmd = "$sudo ls /etc/sysconfig/network/ifcfg-qeth*"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } @parms = split( '\n', $out ); return ( $parms[0] ); } # If no file is found - Return nothing return; } #------------------------------------------------------- =head3 getIfcfgByNic Description : Get the network configuration file path of a given node Arguments : User (root or non-root) Node NIC address Returns : Network configuration file path Example : my $ifcfg = xCAT::zvmUtils->getIfcfgByNic($user, $node, $nic); =cut #------------------------------------------------------- sub getIfcfgByNic { # Get inputs my ( $class, $user, $node, $nic ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get OS my $os = xCAT::zvmUtils->getOs($user, $node); # Get network configuration file path my $out; my @parms; # If it is Red Hat - ifcfg-qeth file is in /etc/sysconfig/network-scripts if ( $os =~ m/Red Hat/i ) { #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"`; my $cmd = "$sudo ls /etc/sysconfig/network-scripts/ifcfg-eth*"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } @parms = split( '\n', $out ); # Go through each line foreach (@parms) { my $filename = $_; # If the network file contains the NIC address #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | egrep -i "$nic"`; my $cmd = $sudo . ' cat $filename'; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i "$nic"`; if ($out) { # Return network file path return ($filename); } } } # If it is SLES 10 - ifcfg-qeth file is in /etc/sysconfig/network elsif ( $os =~ m/SUSE Linux Enterprise Server 10/i ) { #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-qeth*" | grep -i "$nic"`; my $cmd = $sudo . ' ls /etc/sysconfig/network/ifcfg-qeth*'; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i "$nic"`; @parms = split( '\n', $out ); return ( $parms[0] ); } # If it is SLES 11 - ifcfg-qeth file is in /etc/sysconfig/network elsif ( $os =~ m/SUSE Linux Enterprise Server 11/i ) { # Get a list of ifcfg-eth files found #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo ls /etc/sysconfig/network/ifcfg-eth*"`; my $cmd = "$sudo ls /etc/sysconfig/network/ifcfg-eth*"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } my @file = split( '\n', $out ); # Go through each ifcfg-eth file foreach (@file) { # If the network file contains the NIC address #$out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo cat $_" | grep -i "$nic"`; my $cmd = $sudo . ' cat $_'; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i "$nic"`; if ($out) { # Return ifcfg-eth file path return ($_); } } } # If no file is found - Return nothing return; } #------------------------------------------------------- =head3 sendFile Description : SCP a file to a given node Arguments : User (root or non-root) Node Source file Target file Returns : Return code from SCP Example : $rc = xCAT::zvmUtils->sendFile($user, $node, $srcFile, $trgtFile); =cut #------------------------------------------------------- sub sendFile { # Get inputs my ( $class, $user, $node, $srcFile, $trgtFile ) = @_; my $out; my $rc; # Create destination string my $dest = "$user\@$node"; # SCP directory entry file over to HCP foreach my $wait ( 1, 2, 3, 5, 8, 15, 22, 34, 60 ) { $out = `/usr/bin/scp $srcFile $dest:$trgtFile 2>&1`; $rc = $?; if ( $rc == 0 ) { last; } xCAT::zvmUtils->printSyslog("SCP $srcFile $dest:$trgtFile - failed with rc: $rc, out: $out"); sleep( $wait ); } return $rc; } #------------------------------------------------------- =head3 getRootDeviceAddr Description : Get the root device address of a given node Arguments : User (root or non-root) Node Returns : Root device address Example : my $deviceAddr = xCAT::zvmUtils->getRootDeviceAddr($user, $node); =cut #------------------------------------------------------- sub getRootDeviceAddr { # Get inputs my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get the root device node # LVM is not supported #my $out = `ssh $user\@$node "mount" | grep "/ type" | sed 's/1//'`; my $cmd = $sudo . ' mount'; my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i "/ type" | sed \'s/1//\'`; my @parms = split( " ", $out ); @parms = split( "/", xCAT::zvmUtils->trimStr( $parms[0] ) ); my $devNode = $parms[0]; # Get disk address #$out = `ssh $user\@$node "cat /proc/dasd/devices" | grep "$devNode" | sed 's/(ECKD)//' | sed 's/(FBA )//' | sed 's/0.0.//'`; $cmd = $sudo . ' cat /proc/dasd/devices'; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i "$devNode" | sed "s/(ECKD)//" | sed "s/(FBA )//" | sed \'s/0.0.//\'`; @parms = split( " ", $out ); return ( $parms[0] ); } #------------------------------------------------------- =head3 disableEnableDisk Description : Disable/enable a disk for a given node Arguments : User (root or non-root) Node Device address Option (-d|-e) Returns : successful: String indicating the enable was performed and ending with "Done". unsuccessful: Error message from the last attempt Example : my $out = xCAT::zvmUtils->disableEnableDisk($callback, $user, $node, $option, $devAddr); =cut #------------------------------------------------------- sub disableEnableDisk { # Get inputs my ( $class, $user, $node, $option, $devAddr ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Disable/enable disk my $out = ''; if ( $option eq "-d" || $option eq "-e" ) { # Can't guarantee the success of online/offline disk, need to wait # Until it's done because we may detach the disk after -d option # or use the disk after the -e option my @sleepTime = (1,2,3,5,8,15,22,34,60,60,60,60,60,90,120); foreach (@sleepTime) { my $cmd = "$sudo /sbin/chccwdev $option $devAddr 2>&1"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { # try again if an error xCAT::zvmUtils->printSyslog( "$node: Error received on cmd: $cmd, output: $out" ); sleep($_); } else { return "ssh $user\@$node $sudo /sbin/chccwdev $option $devAddr... Done"; } } } return "Error: failed to ssh $user\@$node $sudo /sbin/chccwdev $option $devAddr, Output: $out"; } #------------------------------------------------------- =head3 getMdisks Description : Get the MDISK statements in the user entry of a given node Arguments : User (root or non-root) Node Returns : MDISK statements array or or string containing (Error)... Example : my @mdisks = xCAT::zvmUtils->getMdisks($callback, $user, $node); =cut #------------------------------------------------------- sub getMdisks { # Get inputs my ( $class, $callback, $user, $node ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get HCP my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userID my $userId = $propVals->{'userid'}; my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo $dir/getuserentry $userId"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/getuserentry $userId\"", $hcp, "getMdisks", $out, $node ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "MDISK"`; # Get MDISK statements my @lines = split( '\n', $out ); my @disks; foreach (@lines) { # skip comment lines that start with * or whitespace * if ( $_ =~ m/^\s*\*/) { next; } $_ = xCAT::zvmUtils->trimStr($_); # Save MDISK statements push( @disks, $_ ); } return (@disks); } #------------------------------------------------------- =head3 getDedicates Description : Get the DEDICATE statements in the user entry of a given node Arguments : User (root or non-root) Node Returns : DEDICATE statements array or string containing (Error)... Example : my @dedicates = xCAT::zvmUtils->getDedicates($callback, $user, $node); =cut #------------------------------------------------------- sub getDedicates { # Get inputs my ( $class, $callback, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Directory where executables are my $dir = '/opt/zhcp/bin'; # Get zHCP my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userId my $userId = $propVals->{'userid'}; my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "commandString", $hcp, "getMdisks", $out, $node ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "DEDICATE"`; # Get DEDICATE statements my @lines = split( '\n', $out ); my @dedicates; foreach (@lines) { $_ = xCAT::zvmUtils->trimStr($_); # Save statements push( @dedicates, $_ ); } return (@dedicates); } #------------------------------------------------------- =head3 getCommands Description : Get the COMMAND statements in the user entry of a given node Arguments : User (root or non-root) Node Returns : COMMAND statements array or or string containing (Error)... Example : my @commands = xCAT::zvmUtils->getCommands($callback, $user, $node); =cut #------------------------------------------------------- sub getCommands { # Get inputs my ( $class, $callback, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Directory where executables are my $dir = '/opt/zhcp/bin'; # Get zHCP my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userId my $userId = $propVals->{'userid'}; my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli Image_Query_DM -T $userId\"", $hcp, "getCommands", $out, $node ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "COMMAND"`; # Get COMMAND statements my @lines = split( '\n', $out ); my @commands; foreach (@lines) { $_ = xCAT::zvmUtils->trimStr($_); # Save statements push( @commands, $_ ); } return (@commands); } #------------------------------------------------------- =head3 getUserEntryWODisk Description : Get the user entry of a given node without MDISK statments, and save it to a file Arguments : User (root or non-root) Node File name to save user entry under Returns : Nothing, or string containing (Error)... Example : my $out = xCAT::zvmUtils->getUserEntryWODisk($callback, $user, $node, $file); =cut #------------------------------------------------------- sub getUserEntryWODisk { # Get inputs my ( $class, $callback, $user, $node, $file ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Directory where executables are my $dir = '/opt/zhcp/bin'; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); return; } my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo $dir/smcli Image_Query_DM -T $userId"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli Image_Query_DM -T $userId\"", $hcp, "getUserEntryWODisk", $out, $node ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | sed '\$d' | grep -a -i -v "MDISK"`; # Create a file to save output open( DIRENTRY, ">$file" ); # Save output my @lines = split( '\n', $out ); foreach (@lines) { # Trim line $_ = xCAT::zvmUtils->trimStr($_); # Write directory entry into file print DIRENTRY "$_\n"; } close(DIRENTRY); return; } #------------------------------------------------------- =head3 appendHostname Description : Append a hostname in front of a given string Arguments : Hostname String Returns : String appended with hostname Example : my $str = xCAT::zvmUtils->appendHostname($hostname, $str); =cut #------------------------------------------------------- sub appendHostname { my ( $class, $hostname, $str ) = @_; # Append hostname to every line my @outLn = split( "\n", $str ); $str = ""; foreach (@outLn) { $str .= "$hostname: " . $_ . "\n"; } return $str; } #------------------------------------------------------- =head3 checkOutput Description : Check the return of given output Arguments : Output string Returns : 0 Good output -1 Bad output Example : my $rtn = xCAT::zvmUtils->checkOutput( $out ); =cut #------------------------------------------------------- sub checkOutput { my ( $class, $out ) = @_; # Check output string my @outLn = split( "\n", $out ); foreach (@outLn) { # If output contains 'Failed', or Error return -1 if ( $_ =~ m/Failed/i || $_ =~ m/Error/i ) { return -1; } } return 0; } #------------------------------------------------------- =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( $out, \$reason ); =cut #------------------------------------------------------- sub checkOutputExtractReason { my ( $class, $out, $reason ) = @_; # Check output string my @outLn = split("\n", $out); foreach (@outLn) { # If output contains 'ERROR: ', return -1 and pass back the reason. if ($_ =~ /(.*?ERROR: )/) { $$reason = substr($_, index($_, "ERROR: ") + length("ERROR: ")); return -1; } } return 0; } #------------------------------------------------------- =head3 checkSSH_Rc Description : Check for SSH errors, and command return code failure Arguments : $? command that was issued (do not pass in a password in cmd string, just mask it out) node the SSH is targeting function name the SSH call was done in optional command output optional final target node (used to prefix "Node: " to error message. Helps in tracing hcp calls on behalf of a node) optional syslog supression, "NONE", "SSHONLY" defaults to syslog message of any SSH or command error Returns : rc = 0 Good output rc = 255 Error occurred in SSH rc > 0 Command error $outmsg = error message string if $rc !=0 plus syslog entry for non zero errors Example : ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "Command being issued", $hcp, "testfunc", $out, $node, "NONE"); =cut #------------------------------------------------------- sub checkSSH_Rc { my ( $class, $rc, $cmd, $tgtNode, $functionName, $cmdOutput, $finalTargetNode, $syslogOptions ) = @_; my $msgTxt = ''; my $logit = 2; if (!defined $cmdOutput) { $cmdOutput = ''; } elsif ( $cmdOutput ne '' ) { $cmdOutput = " Output: " . $cmdOutput . " "; } if (!defined $finalTargetNode) { $finalTargetNode = ''; } else { $finalTargetNode = "Node: " . $finalTargetNode . " "; } if (defined $syslogOptions) { if ( $syslogOptions =~ m/NONE/i ) { $logit = 0; } elsif ( $syslogOptions =~ m/SSHONLY/i ) { $logit = 1; } } $rc = $rc >> 8; if ( $rc == 255 ) { # SSH failure to communicate with $tgtNode. $msgTxt = "$finalTargetNode" . "(Error) In $functionName(), SSH communication to $tgtNode failed for command: $cmd$cmdOutput"; if ($logit > 0 ) { xCAT::zvmUtils->printSyslog("$msgTxt"); } return ($rc, $msgTxt); } elsif ( $rc != 0 ) { # Generic failure of the command. $msgTxt = "$finalTargetNode" . "(Error) In $functionName(), command to $tgtNode failed with return code $rc for: $cmd$cmdOutput"; if ($logit > 1 ) { xCAT::zvmUtils->printSyslog("$msgTxt"); } return ($rc, $msgTxt); } return 0; } #------------------------------------------------------- =head3 getDeviceNode Description : Get the device node for a given address Arguments : User (root or non-root) Node Disk address Returns : Device node Example : my $devNode = xCAT::zvmUtils->getDeviceNode($user, $node, $tgtAddr); =cut #------------------------------------------------------- sub getDeviceNode { my ( $class, $user, $node, $tgtAddr ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Determine device node #my $out = `ssh $user\@$node "$sudo cat /proc/dasd/devices" | grep ".$tgtAddr("`; my $cmd = $sudo . ' cat /proc/dasd/devices'; my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i ".$tgtAddr("`; my @words = split(' ', $out); my $tgtDevNode; # /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') { $tgtDevNode = $words[$i]; $i++; } return $words[$i]; } #------------------------------------------------------- =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"`; my $cmd = $sudo . ' cat /proc/dasd/devices'; my $addr = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $addr ) == -1) { return $addr; } $addr = `echo "$addr" | egrep -a -i "is $deviceNode"`; $addr =~ s/ +/ /g; $addr =~ s/^0.0.([0-9a-f]*).*/$1/; chomp($addr); return $addr; } #------------------------------------------------------- =head3 isAddressUsed Description : Check if a given address is used Arguments : User (root or non-root) Node Disk address Returns : 0 Address used -1 Address not used Example : my $ans = xCAT::zvmUtils->isAddressUsed($user, $node, $address); =cut #------------------------------------------------------- sub isAddressUsed { my ( $class, $user, $node, $address ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Search for disk address #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v dasd" | grep "DASD $address"`; my $cmd = $sudo . ' /sbin/vmcp q v dasd'; my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i "DASD $address"`; if ($out) { return 0; } return -1; } #------------------------------------------------------- =head3 getMacID Description : Get the MACID from /opt/zhcp/conf/next_macid on the HCP Arguments : User (root or non-root) zHCP Returns : MACID Example : my $macId = xCAT::zvmUtils->getMacID($user, $hcp); =cut #------------------------------------------------------- sub getMacID { my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Check /opt/zhcp/conf directory on HCP my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -d /opt/zhcp/conf && echo 'Directory exists'"`; if ( $out =~ m/Directory exists/i ) { # Check next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -e /opt/zhcp/conf/next_macid && echo 'File exists'"`; if ( $out =~ m/File exists/i ) { # Do nothing } else { # Create next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo 'FFFFF0' > /opt/zhcp/conf/next_macid"`; } } else { # Create /opt/zhcp/conf directory # Create next_mac - Contains next MAC address to use $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo mkdir /opt/zhcp/conf"`; $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo 'FFFFF0' > /opt/zhcp/conf/next_macid"`; } # Read /opt/zhcp/conf/next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /opt/zhcp/conf/next_macid"`; my $macId = xCAT::zvmUtils->trimStr($out); return $macId; } #------------------------------------------------------- =head3 generateMacId Description : Generate a new MACID Arguments : User (root or non-root) zHCP Returns : Nothing Example : my $macId = xCAT::zvmUtils->generateMacId($user, $hcp); =cut #------------------------------------------------------- sub generateMacId { my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Check /opt/zhcp/conf directory on HCP my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -d /opt/zhcp/conf && echo 'Directory exists'"`; if ( $out =~ m/Directory exists/i ) { # Check next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo test -e /opt/zhcp/conf/next_macid && echo 'File exists'"`; if ( $out =~ m/File exists/i ) { # Do nothing } else { # Create next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo 'FFFFF0' > /opt/zhcp/conf/next_macid"`; $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /bin/chmod 666 /opt/zhcp/conf/next_macid"`; } } else { # Create /opt/zhcp/conf directory # Create next_mac - Contains next MAC address to use $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo mkdir /opt/zhcp/conf"`; $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo 'FFFFF0' > /opt/zhcp/conf/next_macid"`; $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /bin/chmod 666 /opt/zhcp/conf/next_macid"`; } # Read /opt/zhcp/conf/next_macid file $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /opt/zhcp/conf/next_macid"`; my $macId = xCAT::zvmUtils->trimStr($out); my $int; if ($macId) { # Convert hexadecimal - decimal $int = hex($macId); $macId = sprintf( "%d", $int ); # Generate new MAC suffix $macId = $macId - 1; # Convert decimal - hexadecimal $macId = sprintf( "%X", $macId ); # Save new MACID $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo echo $macId > /opt/zhcp/conf/next_macid"`; } return; } #------------------------------------------------------- =head3 createMacAddr Description : Create a MAC address using the HCP MAC prefix and a given MAC suffix Arguments : User (root or non-root) Node MAC suffix Returns : MAC address or string containing (Error)... Example : my $mac = xCAT::zvmUtils->createMacAddr($user, $node, $suffix); =cut #------------------------------------------------------- sub createMacAddr { my ( $class, $user, $node, $suffix ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get node properties from 'zvm' table my @propNames = ('hcp'); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { return -1; } # Get USER Prefix my $outmsg; my $rc; my $prefix; my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo /sbin/vmcp q vmlan"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo /sbin/vmcp q vmlan\"", $hcp, "createMacAddr", $out, $node ); if ($rc != 0) { return $outmsg; } $prefix = `echo "$out" | egrep -a -i "USER Prefix:"`; $prefix =~ s/(.*?)USER Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; # Get MACADDR Prefix instead if USER Prefix is not defined, use saved out data if (!$prefix) { $prefix = `echo "$out" | egrep -a -i "MACADDR Prefix:"`; $prefix =~ s/(.*?)MACADDR Prefix:(.*)/$2/; $prefix =~ s/^\s+//; $prefix =~ s/\s+$//; if (!$prefix) { return -1; } } # Generate MAC address of source node my $mac = $prefix . $suffix; # If length is less than 12, append a zero if ( length($mac) != 12 ) { $mac = "0" . $mac; } # Format MAC address $mac = substr( $mac, 0, 2 ) . ":" . substr( $mac, 2, 2 ) . ":" . substr( $mac, 4, 2 ) . ":" . substr( $mac, 6, 2 ) . ":" . substr( $mac, 8, 2 ) . ":" . substr( $mac, 10, 2 ); return $mac; } #------------------------------------------------------- =head3 getOs Description : Get the operating system of a given node Arguments : User (root or non-root) Node Returns : Operating system name Example : my $osName = xCAT::zvmUtils->getOs($user, $node); =cut #------------------------------------------------------- sub getOs { # Get inputs my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get operating system #my $out = `ssh -o ConnectTimeout=10 $user\@$node "$sudo cat /etc/*release" | egrep -v "LSB_VERSION"`; my $cmd = $sudo . ' cat /etc/*release'; my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = `echo "$out" | egrep -a -i -v "LSB_VERSION"`; my @results = split( '\n', $out ); return ( xCAT::zvmUtils->trimStr( $results[0] ) ); } #------------------------------------------------------- =head3 getArch Description : Get the architecture of a given node Arguments : User (root or non-root) Node Returns : Architecture of node Example : my $arch = xCAT::zvmUtils->getArch($user, $node); =cut #------------------------------------------------------- sub getArch { # Get inputs my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get host using VMCP #my $arch = `ssh $user\@$node "$sudo uname -m"`; my $cmd = "$sudo uname -m"; my $arch = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $arch ) == -1) { return $arch; } return ( xCAT::zvmUtils->trimStr($arch) ); } #------------------------------------------------------- =head3 getUserProfile Description : Get the user profile Arguments : User (root or non-root) Profile name Returns : User profile Example : my $profile = xCAT::zvmUtils->getUserProfile($user, $hcp, $name); =cut #------------------------------------------------------- sub getUserProfile { # Get inputs my ( $class, $user, $hcp, $profile ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Set directory where executables are on zHCP my $hcpDir = "/opt/zhcp/bin"; my $out; # Set directory for cache my $cache = '/var/opt/zhcp/cache'; # If the cache directory does not exist if (!(`ssh $user\@$hcp "$sudo test -d $cache && echo Exists"`)) { # Create cache directory $out = `ssh $user\@$hcp "$sudo mkdir -p $cache"`; } # Set output file name my $file = "$cache/$profile.profile"; # If a cache for the user profile exists if (`ssh $user\@$hcp "$sudo ls $file"`) { # Get current Epoch my $curTime = time(); # Get time of last change as seconds since Epoch my $fileTime = xCAT::zvmUtils->trimStr(`ssh $user\@$hcp "$sudo stat -c %Z $file"`); # If the current time is greater than 5 minutes of the file timestamp my $interval = 300; # 300 seconds = 5 minutes * 60 seconds/minute if ( $curTime > $fileTime + $interval ) { # Get user profiles and save it in a file $out = `ssh $user\@$hcp "$sudo $hcpDir/smcli Profile_Query_DM -T $profile > $file"`; } } else { # Get user profiles and save it in a file $out = `ssh $user\@$hcp "$sudo $hcpDir/smcli Profile_Query_DM -T $profile > $file"`; } # Return the file contents $out = `ssh $user\@$hcp "$sudo cat $file"`; return $out; } #------------------------------------------------------- =head3 getVswitchIdsFromDirectory Description : Get the nicdef switch names of a given node Arguments : User (root or non-root) zHCP Userid Returns : Vswitch names array or or string containing (Error)... Example : my $vSwitchNamers = xCAT::zvmCPUtils->getVswitchIdsFromDirectory($user, $hcp, $userId); =cut #------------------------------------------------------- sub getVswitchIdsFromDirectory { # Get inputs my ( $class, $user, $hcp ,$userId ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my @vswitch; # Get VSwitchs in directory # only get lines that have SYSTEM switchname in them # smcli Image_Definition_Query_DM -T xcat -k nicdef # NICDEF=VDEV=0600 TYPE=QDIO LAN=SYSTEM SWITCHNAME=XCATVSW1 # NICDEF=VDEV=0700 TYPE=QDIO LAN=SYSTEM SWITCHNAME=XCATVSW2 # my $outmsg; my $rc; xCAT::zvmUtils->printSyslog("Calling: ssh $user\@$hcp $sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF | egrep -i 'SWITCHNAME'"); my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k NICDEF\"", $hcp, "getVswitchIdsFromDirectory", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i 'SWITCHNAME'`; # if there is nothing found, log that and return; if ( !length($out) ) { xCAT::zvmUtils->printSyslog("No SWITCHNAME found in NICDEF statement for userid $userId"); return (@vswitch); } xCAT::zvmUtils->printSyslog("$userId output: $out"); my @lines = split( '\n', $out ); my @parms; my $vswitchToken = ''; foreach (@lines) { @parms = split( ' ', $_ ); $vswitchToken = $parms[3]; @parms = split( '=', $vswitchToken ); push( @vswitch, $parms[1] ); } return (@vswitch); } #------------------------------------------------------- =head3 inArray Description : Checks if a value exists in an array Arguments : Search value Search array Returns : The searched expression Example : my $rtn = xCAT::zvmUtils->inArray($needle, @haystack); =cut #------------------------------------------------------- sub inArray { # Get inputs my ( $class, $needle, @haystack ) = @_; return grep{ $_ eq $needle } @haystack; } #------------------------------------------------------- =head3 getOsVersion Description : Get the operating system of a given node Arguments : User (root or non-root) Node or IP Address Callback handle (optional). Allows the routine, to write error messages when SSH fails. Returns : Operating system name & version as a single word. Otherwise, an empty string. Example : my $os = xCAT::zvmUtils->getOsVersion($user, $node); my $os = xCAT::zvmUtils->getOsVersion($user, $node, $callback); =cut #------------------------------------------------------- sub getOsVersion { # Get inputs my ( $class, $user, $node, $callback ) = @_; my $osVer = ''; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Contact the system to extract the possible files which contain pertinent data. #my $releaseInfo = `ssh -qo ConnectTimeout=2 $user\@$node "$sudo ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; my $cmd = $sudo . ' ls /dev/null '. $locAllEtcVerFiles . ' 2>/dev/null'; my $releaseInfo = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); if (xCAT::zvmUtils->checkOutput( $releaseInfo ) == -1) { return ''; } $releaseInfo = `echo "$releaseInfo" | xargs grep ''`; $osVer = buildOsVersion( $callback, $releaseInfo, 'all' ); return $osVer; } #------------------------------------------------------- =head3 getOSFromIP Description : Get the operating system name and version for the system at the specified IP Address. Arguments : Callback handle z/VM userid of the system IP Address Type of IP address (not currently used) Returns : Operating system name & version as a single word. Otherwise, an empty string. Example : my $os = xCAT::zvmUtils->getOSFromIP( $callback, $userid, $ipAddr, $ipVersion }; =cut #------------------------------------------------------- sub getOSFromIP { my ( $class, $callback, $activeSystem, $ipAddr, $ipVersion ) = @_; my $osVer = ''; my $rc = 0; # Get operating system my $releaseInfo = `ssh -qo ConnectTimeout=2 $ipAddr "ls /dev/null $locAllEtcVerFiles 2>/dev/null"`; $rc = $? >> 8; if ( $rc == 0 ) { $releaseInfo = `echo "$releaseInfo" | xargs grep ''`; $osVer = buildOsVersion( $callback, $releaseInfo, 'all' ); } else { if ( $callback ) { # We have a callback so we can write an error message. if ( $rc == 255 ) { my $rsp; push @{$rsp->{data}}, "Unable to communicate with $activeSystem at $ipAddr using ssh."; xCAT::MsgUtils->message( "E", $rsp, $callback ); } else { my $rsp; push @{$rsp->{data}}, "Received an unexpected ssh return code $rc from $activeSystem at $ipAddr."; xCAT::MsgUtils->message( "E", $rsp, $callback ); } } } return $osVer; } #------------------------------------------------------- =head3 stripHeader Description : Scans an input string to find only lines that match a specified header string and return an array of those lines without the prefix. Arguments : Multi-line string to scan String to match Returns : Array of matching lines. Example : my @lines = stripHeader( $releaseInfo, '^/etc/os-release:' ); ($ver) = stripHeader( $releaseInfo, "^$locEtcUnitedLinux:" ); =cut #------------------------------------------------------- sub stripHeader { my ( $inputLines, $matchString ) = @_; my @outputLines; my @lines = split( /\n/, $inputLines ); foreach my $line ( @lines ){ if ( $line =~ m/$matchString/ ) { $line =~ s/$matchString//g; chomp $line; if ( $line eq '' ) { next } push @outputLines, $line; } } return @outputLines; } #------------------------------------------------------- =head3 buildOsVersion Description : Build the OS version string from the provided data files. Note: Only RHEL and SLES are supported z/VM xCAT supported distributions. This routine has code for other distributions supported by common xCAT. If z/VM ever supports another distribution then we need to verify that the code gets the correct version information. Arguments : Callback (or undef) Operating system data file(s) output Type of data to return: 'all' - $os$ver.$rel 'all_comma' - $os,$ver.$rel 'os' - $os 'version' - $ver 'release' - $rel '' - $os$ver Returns : Operating system name & version as a single word. Example : my $osVer = buildOsVersion( $callback, $releaseInfo, $type ); =cut #------------------------------------------------------- sub buildOsVersion { my $callback = shift; my $releaseInfo = shift; my $type = shift; my $os = 'unknown'; # OS indicator, e.g. "rhel", "SLES" my $ver = ''; # Version indicator, e.g. "7.1", my $version = ''; my $rel = ''; my $line = ''; my @lines; # Test strings that were used when we added the original support. These # strings are what we used to simulate the other common Linux distros. #$releaseInfo = "$locEtcSuseRelease:SUSE Linux Enterprise Server 11 (s390x)\n$locEtcSuseRelease:VERSION = 11\n$locEtcSuseRelease:PATCHLEVEL = 4.32"; #$releaseInfo = "$locEtcDebianVersion:1.0\n$locEtcIssue:debian release"; #$releaseInfo = "$locEtcUnitedLinux:2.1a"; #$releaseInfo = "$locEtcLsbRelease:Ubuntu\n$locEtcLsbRelease: DISTRIB_ID=Ubuntu\n$locEtcLsbRelease: DISTRIB_RELEASE=4.1"; my @relOut = split('\n', $releaseInfo); if ( grep( /^$locEtcOsRelease:/, @relOut ) ) { my $version = ''; my $version_id; my $id; my $id_like; my $name; my $prettyname; my $verrel = ''; my @text = stripHeader( $releaseInfo, "^$locEtcOsRelease:" ); foreach my $line ( @text ) { if ( $line =~ /^\s*VERSION=\"?([0-9\.]+).*/ ) { $version = $1; } if($line =~ /^\s*VERSION_ID=\"?([0-9\.]+).*/){ $version_id = $1; } if ( $line =~ /^\s*Base release\s?([0-9\.]+).*/ ) { $version = $1; $id = 'BASE'; } if ( $line =~ /^\s*ID=\"?([0-9a-z\_\-\.]+).*/ ) { $id = $1; } if ( $line =~ /^\s*ID_LIKE=\"?([0-9a-z\_\-\.]+).*/ ) { $id_like = $1; } if ( $line =~ /^\s*NAME=\"?(.*)/ ) { $name = $1; } if($line =~ /^\s*PRETTY_NAME=\"?(.*)/){ $prettyname = $1; } } $os = $id; if ( !$os and $id_like ) { $os = $id_like; } $verrel = $version; if ( !$verrel and $version_id ) { $verrel = $version_id; } if( !$name and $prettyname ){ $name = $prettyname; } # Note: xcat::Utils->osver() sets this value with an 's' but zvm.pm # does not use it. So for now, we don't set 's'. #if ( $os =~ /rhel/ and $name =~ /Server/i ) { # # $os = "rhels"; #} if ( $verrel =~ /([0-9]+)\.?(.*)/ ) { $ver = $1; $rel = $2; } } elsif ( grep( /^$locEtcRedhatRelease:/, @relOut ) ) { my @text = stripHeader( $releaseInfo, "^$locEtcRedhatRelease:" ); my $line = $text[0]; chomp( $line ); $os = "rh"; my $verrel = $line; $ver = $line; if ( $type ) { $verrel =~ s/[^0-9]*([0-9.]+).*/$1/; ($ver,$rel) = split /\./, $verrel; } else { $ver=~ tr/\.//; $ver =~ s/[^0-9]*([0-9]+).*/$1/; } if ( $line =~ /AS/ ) { $os = 'rhas' } elsif ( $line =~ /ES/ ) { $os = 'rhes' } elsif ( $line =~ /WS/ ) { $os = 'rhws' } elsif ( $line =~ /Server/ ) { if ( $type ) { $os = 'rhel'; # Note: xcat::Utils->osver() sets this value with an 's' but zvm.pm # does not use it. So for now, we don't set 's'. #$os = 'rhels'; } else { $os = 'rhserver'; } } elsif ( $line =~ /Client/ ) { if ( $type ) { $os = 'rhel'; } else { $os = 'rhclient'; } } elsif ( grep( /$locEtcFedoraRelease:/, @relOut ) ) { $os = 'rhfc' } } elsif ( grep( /^$locEtcSuseRelease:/, @relOut ) ) { my @lines = stripHeader( $releaseInfo, "^$locEtcSuseRelease:" ); if ( grep /SLES|Enterprise Server/, @lines ) { $os = "sles" } if ( grep /SLEC/, @lines ) { $os = "slec" } $ver = $lines[0]; $ver =~ tr/\.//; $ver =~ s/[^0-9]*([0-9]+).*/$1/; $rel = $lines[2]; $rel =~ tr/\.//; $rel =~ s/[^0-9]*([0-9]+).*/$1/; } elsif ( grep( /^$locEtcUnitedLinux:/, @relOut ) ) { # Note: Not a z/VM xCAT supported distribution. # If we ever support this then we need to verify this code # gets the correct version information. ($ver) = stripHeader( $releaseInfo, "^$locEtcUnitedLinux:" ); $os = "ul"; $ver =~ tr/\.//; $ver =~ s/[^0-9]*([0-9]+).*/$1/; } elsif ( grep( /$locEtcLsbRelease:/, @relOut ) ) { # Ubuntu release my @text = stripHeader( $releaseInfo, "^$locEtcLsbRelease:" ); chomp( @text ); my $distrib_id = ''; my $distrib_rel = ''; foreach ( @text ) { if ( $_ =~ /^\s*DISTRIB_ID=(.*)$/ ) { $distrib_id = $1; # last DISTRIB_ID value in file used } elsif ( $_ =~ /^\s*DISTRIB_RELEASE=(.*)$/ ) { $distrib_rel = $1; # last DISTRIB_RELEASE value in file used } } if ( $distrib_id =~ /^(Ubuntu|"Ubuntu")\s*$/ ) { $os = "ubuntu"; if ( $distrib_rel =~ /^(.*?)\s*$/ ) { # eliminate trailing blanks, if any $distrib_rel = $1; } if ( $distrib_rel =~ /^"(.*?)"$/ ) { # eliminate enclosing quotes, if any $distrib_rel = $1; } $ver = $distrib_rel; } } elsif ( grep( /^$locEtcDebianVersion:/, @relOut ) ) { # Debian release if ( grep( /^$locEtcIssue:/, @relOut ) ) { ($line) = stripHeader( $releaseInfo, "^$locEtcIssue:" ); if ( $line =~ /debian.*/i ) { $os = "debian"; ($ver) = stripHeader( $releaseInfo, "^$locEtcDebianVersion:" ); } } } my $outString = ''; if ( $type eq 'all_comma' ) { if ( $rel ne "") { $outString = "$os,$ver.$rel"; } else { $outString = "$os,$ver"; } } elsif ( $type eq 'all' ) { if ( $rel ne "") { $outString = "$os$ver.$rel"; } else { $outString = "$os$ver"; } } elsif ( $type eq 'os' ) { $outString = $os; } elsif ( $type eq 'version' ) { $outString = $ver; } elsif ( $type eq 'release' ) { $outString = $os; } else { $outString = "$os$ver"; } return $outString; } #------------------------------------------------------- =head3 getZfcpInfo Description : Get the zFCP device info Arguments : User (root or non-root) Node Returns : zFCP device info Example : my $info = xCAT::zvmUtils->getZfcpInfo($user, $node); =cut #------------------------------------------------------- sub getZfcpInfo { # Get inputs my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get zFCP device info #my $info = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/lszfcp -D"`; my $cmd = "$sudo /sbin/lszfcp -D"; my $info = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER # will not check for connection errors here my @zfcp = split("\n", $info); if (!$info || $info =~ m/No zfcp support/i || $info =~ m/No fcp devices found/i) { return; } # Get SCSI device and their attributes #my $scsi = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/lsscsi"`; $cmd = "$sudo /usr/bin/lsscsi"; my $scsi = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $info ) == -1) { return $info; } $info = ""; my @args; my $tmp; my $id; my $device; my $wwpn; my $lun; my $size; foreach (@zfcp) { @args = split(" ", $_); $id = $args[1]; @args = split("/", $args[0]); $device = $args[0]; $wwpn = $args[1]; $lun = $args[2]; # Make sure WWPN and LUN do not have 0x prefix $wwpn = xCAT::zvmUtils->replaceStr($wwpn, "0x", ""); $lun = xCAT::zvmUtils->replaceStr($lun, "0x", ""); # Find the device name $tmp = `echo "$scsi" | egrep -i $id`; $tmp = substr($tmp, index($tmp, "/dev/")); chomp($tmp); # Find the size in MiB #$size = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /usr/bin/sg_readcap $tmp" | egrep -i "Device[[:space:]]size:"`; my $cmd = $sudo . ' /usr/bin/sg_readcap ' . $tmp; $size = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $size ) == -1) { return $size; } $size = `echo "$size" | egrep -a -i "Device[[:space:]]size:"`; $size =~ s/Device size: //g; @args = split(",", $size); $size = xCAT::zvmUtils->trimStr($args[1]); $info .= "Device: $device WWPN: 0x$wwpn LUN: 0x$lun Size: $size\n"; } $info = xCAT::zvmUtils->tabStr($info); return ($info); } #------------------------------------------------------- =head3 isHypervisor Description : Determines if a given node is in the 'hypervisor' table Arguments : Node Returns : 1 Node exists 0 Node does not exists Example : my $out = xCAT::zvmUtils->isHypervisor($node); =cut #------------------------------------------------------- sub isHypervisor { # Get inputs my ( $class, $node ) = @_; # Look in 'zvm' table my $tab = xCAT::Table->new( "hypervisor", -create => 1, -autocommit => 0 ); my @results = $tab->getAllAttribsWhere( "node = '" . $node . "'", 'type' ); foreach (@results) { # Return 'TRUE' if given node is in the table if ($_->{"type"} eq "zvm") { return 1; } } return 0; } #------------------------------------------------------- =head3 isOSVerSupported Description : Determine if the specified OS version supported. Arguments : OS version string (e.g. RHEL6, RHEL6.1, SLES11sp2) Returns : 1 - version is supported 0 - version is not supported Example : my $supported = xCAT::zvmUtils->isOSVerSupported( $os ); =cut #------------------------------------------------------- sub isOSVerSupported { my ( $class, $osVer ) = @_; # Keep just the OS distro name and the version, ie. drop any release info. $osVer = lc( $osVer ); if ( $osVer =~ /([a-z]+[0-9]+)/ ) { $osVer = $1; } # Check against our list of supported versions. if ( $supportedVersions{$osVer} ) { return 1; } else { return 0; } } #------------------------------------------------------- =head3 getSudoer Description : Retrieve sudoer user name Arguments : Node Returns : Sudoer user name Sudo keyword Example : my ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); =cut #------------------------------------------------------- sub getSudoer { # Get inputs my ( $class ) = @_; # Use sudo or not on zHCP my @propNames = ('username'); my $propVals = xCAT::zvmUtils->getTabPropsByKey( 'passwd', 'key', 'sudoer', @propNames ); my $sudo = "sudo"; my $user = $propVals->{'username'}; if (!$user) { $user = "root"; } if ($user eq "root") { $sudo = ""; } return ($user, $sudo); } #------------------------------------------------------- =head3 getFreeAddress Description : Get a free(unused) virtual address Arguments : User (root or non-root) Node Type (vmcp or non-vmcp) Returns : vdev An address which is free to use -1 No free address is left Example : my $vdev = xCAT::zvmUtils->getFreeAddress($user, $node, $type); =cut #------------------------------------------------------- sub getFreeAddress { my ( $class, $user, $node, $type ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Although 0000 maybe is free, we do not use it my $freeAddress = 1; my $freeAddressHex = sprintf('%04X', $freeAddress); # All device type names in VM, do not contain CPU #my $deviceTypesVm = 'CONS|CTCA|DASD|FCP|GRAF|LINE|MSGD|OSA|PRT|PUN|RDR|SWCH|TAPE'; my $deviceTypesVm = '^CONS|^CTCA|^DASD|^FCP|^GRAF|^LINE|^MSGD|^OSA|^PRT|^PUN|^RDR|^SWCH|^TAPE'; # All device type names in user directory, do not contain CPU my $deviceTypesUserDir = 'CONSOLE|MDISK|NICDEF|SPOOL|RDEVICE'; # Search for all address that is in use my $allUsedAddr; if ($type eq 'vmcp') { # When the node is up, vmcp can be used #$allUsedAddr = `ssh -o ConnectTimeout=5 $user\@$node "$sudo /sbin/vmcp q v all | awk '\$1 ~/^($deviceTypesVm)/ {print \$2}' | sort"`; my $cmd = $sudo . '/sbin/vmcp q v all'; my $allUsedAddr = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $allUsedAddr ) == -1) { return -1; } $allUsedAddr = `echo '$allUsedAddr' | egrep -a -i "$deviceTypesVm"`; } else { # When the node is down, use zHCP to get its user directory entry # Get HCP my @propNames = ('hcp', 'userid'); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $hcp = $propVals->{'hcp'}; # Get node userID my $userId = $propVals->{'userid'}; # Get user directory entry my $userDirEntry = `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $userId"`; # Get profile if user directory entry include a profile if ($userDirEntry =~ "INCLUDE ") { my $profileName = `cat $userDirEntry | awk '\$1 ~/^(INCLUDE)/ {print \$2}'`; $profileName = xCAT::zvmUtils->trimStr($profileName); $userDirEntry .= `ssh $::SUDOER\@$hcp "$::SUDO $::DIR/smcli Image_Query_DM -T $profileName"`; } # Get all defined device address $allUsedAddr = `cat $userDirEntry | awk '\$1 ~/^($deviceTypesUserDir)/ {print \$2}' | sort`; # Get all linked device address $allUsedAddr .= `cat $userDirEntry | awk '\$1 ~/^(LINK)/ {print \$4}' | sort`; } # Loop to get the lowest free address while ($freeAddress < 65536 && $allUsedAddr =~ $freeAddressHex) { $freeAddress++; $freeAddressHex = sprintf('%04X', $freeAddress); } if ($freeAddress < 65536) { return $freeAddressHex; } return -1; } #------------------------------------------------------- =head3 getUsedCpuTime Description : Get used CPU time of instance Arguments : User (root or non-root) zHCP (to query on) node Returns : In nanoseconds for used CPU time or string containing (Error)... Example : my $out = xCAT::zvmUtils->getUsedCpuTime($hcp, $node); =cut #------------------------------------------------------- sub getUsedCpuTime { my ( $class, $user, $hcp , $node ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $userId = xCAT::zvmCPUtils->getUserId($user, $node); # Call IUO function to query CPU used time my $outmsg; my $rc; my $time = `ssh $user\@$hcp "$sudo $dir/smcli Image_Performance_Query -T $userId -c 1"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli Image_Performance_Query -T $userId -c 1\"", $hcp, "getUsedCpuTime", $time, $node ); if ($rc != 0) { return $outmsg; } $time = `echo "$time" | egrep -a -i "Used CPU time:"`; $time =~ s/^Used CPU time:(.*)/$1/; $time =~ s/"//g; $time =~ s/^\s+//; $time =~ s/\s+$//; if (!$time) { $time = 0; } # Not found, return 0 return $time; } #------------------------------------------------------- =head3 getUpTime Description : Get running time of an instance Arguments : User (root or non-root) Node Returns : Running time Example : my $out = xCAT::zvmUtils->getUpTime($user, $node); =cut #------------------------------------------------------- sub getUpTime { my ( $class, $user, $node ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } #my $out = `ssh -o ConnectTimeout=5 $user\@$node "$sudo uptime"`; my $cmd = "$sudo uptime"; my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER if (xCAT::zvmUtils->checkOutput( $out ) == -1) { return $out; } $out = xCAT::zvmUtils->trimStr($out); $out =~ /.*up +(?:(\d+) days?,? +)?(\d+):(\d+),.*/; my $uptime; if (!$1 && !$2) { # Special case for less than 1 hour, will display X min $out =~ /.*up +(\d+) min,.*/; $uptime = "0 days $3 min"; } elsif (!$1) { # Special case for less than 1 day, will display X hr X min $uptime = "0 days $2 hr $3 min"; } else { $uptime = "$1 days $2 hr $3 min"; } return ($uptime); } #------------------------------------------------------- =head3 getSizeFromByte Description : Return disk size (G or M) from given bytes Arguments : Bytes Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromByte($bytes); =cut #------------------------------------------------------- sub getSizeFromByte { my ( $class, $bytes ) = @_; my $size = ($bytes)/(1024*1024); if ($size > (1024*5)) { $size = ($size / 1024); # If the size > 5G, will use G to represent $size = sprintf("%.1f",$size); $size = $size . 'G'; } else { # If the size < 5G, will use M to represent $size = sprintf("%d",$size); $size = $size . 'M'; } return ($size); } #------------------------------------------------------- =head3 getSizeFromCyl Description : Return disk size (G or M) from given cylinders Arguments : Node Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromCyl($cyl); =cut #------------------------------------------------------- sub getSizeFromCyl { my ($class, $cyl) = @_; my $bytes = ($cyl * 737280); my $size = xCAT::zvmUtils->getSizeFromByte($bytes); return ($size); } #------------------------------------------------------- =head3 getSizeFromPage Description : Return disk size (G or M) from given pages Arguments : Page Returns : Size string Example : my $out = xCAT::zvmUtils->getSizeFromPage($page); =cut #------------------------------------------------------- sub getSizeFromPage { my ( $class, $page ) = @_; my $bytes = ($page * 4096); my $size = xCAT::zvmUtils->getSizeFromByte($bytes); return ($size); } #------------------------------------------------------- =head3 getLparCpuTotal Description : Get total count of logical CPUs in the LPAR Arguments : User (root or non-root) zHCP Returns : Total CPU count or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparCpuTotal($user, $hcp); =cut #------------------------------------------------------- sub getLparCpuTotal { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getLparCpuTotal", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "LPAR CPUs Total"`; my @results = split(' ', $out); return ($results[3]); } #------------------------------------------------------- =head3 getLparCpuUsed Description : Get count of used logical CPUs in the LPAR Arguments : User (root or non-root) zHCP Returns : Used CPU count or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparCpuUsed($user, $hcp); =cut #------------------------------------------------------- sub getLparCpuUsed { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getLparCpuUsed", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "LPAR CPUs Configured"`; my @results = split(' ', $out); return ($results[3]); } #------------------------------------------------------- =head3 getCecModel Description : Get the model of this CEC (LPAR) Arguments : User (root or non-root) zHCP Returns : Model of this CEC or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getCecModel($user, $hcp); =cut #------------------------------------------------------- sub getCecModel { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getCecModel", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "^Type:"`; my @results = split(' ', $out); return ($results[1]); } #------------------------------------------------------- =head3 getCecVendor Description : Get the vendor of this CEC (LPAR) Arguments : User (root or non-root) zHCP Returns : Vendor of this CEC or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getCecVendor($user, $hcp); =cut #------------------------------------------------------- sub getCecVendor { my ( $class, $user, $hcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getCecVendor", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "Manufacturer"`; my @results = split(' ', $out); return ($results[1]); } #------------------------------------------------------- =head3 getHypervisorInfo Description : Get the info(name & version) for this hypervisor Arguments : User (root or non-root) zHCP Returns : Name & version of this hypervisor or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getHypervisorInfo($user, $hcp); =cut #------------------------------------------------------- sub getHypervisorInfo { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh -o ConnectTimeout=5 $user\@$hcp "$sudo cat /proc/sysinfo"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=5 $user\@$hcp \"$sudo cat /proc/sysinfo\"", $hcp, "getHypervisorInfo", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "VM00 Control Program"`; my @results = split(' ', $out); my $str = "$results[3] $results[4]"; return ($str); } #------------------------------------------------------- =head3 getLparMemoryTotal Description : Get the total physical memory of this LPAR Arguments : User (root or non-root) zHCP Returns : Total physical memory or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparMemoryTotal($user, $hcp); =cut #------------------------------------------------------- sub getLparMemoryTotal { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Info_Query"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli System_Info_Query\"", $hcp, "getLparMemoryTotal", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "real storage"`; my @results = split(' ', $out); return ($results[5]); } #------------------------------------------------------- =head3 getLparMemoryOffline Description : Get the offline physical memory of this LPAR Arguments : User (root or non-root) zHCP Returns : Offline physical memory or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparMemoryOffline($user, $hcp); =cut #------------------------------------------------------- sub getLparMemoryOffline { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Info_Query"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli System_Info_Query\"", $hcp, "getLparMemoryOffline", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "real storage"`; my @results = split(' ', $out); return ($results[14]); } #------------------------------------------------------- =head3 getLparMemoryUsed Description : Get the used physical memory of this LPAR Arguments : User (root or non-root) zHCP Returns : Used physical memory or string containing (Error)... Example : my $out = xCAT::zvmCPUtils->getLparMemoryUsed($user, $hcp); =cut #------------------------------------------------------- sub getLparMemoryUsed { my ($class, $user, $hcp) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli System_Performance_Info_Query "`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo /opt/zhcp/bin/smcli System_Performance_Info_Query \"", $hcp, "getLparMemoryUsed", $out ); if ($rc != 0) { return $outmsg; } $out = `echo "$out" | egrep -a -i "Used memory pages:"`; my @results = split(':', $out); my $page = xCAT::zvmUtils->trimStr( $results[1] ); my $size = xCAT::zvmUtils->getSizeFromPage( $page ); return ($size); } #------------------------------------------------------- =head3 getDiskPoolUsed Description : Get the used size of specified disk pool Arguments : User (root or non-root) zHCP Disk pool Returns : Used size of specified disk pool Example : my $out = xCAT::zvmCPUtils->getDiskPoolUsed($user, $hcp, $diskpool); =cut #------------------------------------------------------- sub getDiskPoolUsed { my ($class, $user, $hcp, $diskpool) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Volume_Space_Query_DM -q 3 -e 3 -n $diskpool -T $hcpUserId"`; my @lines = split('\n', $out); my @results; my $used = 0; foreach (@lines) { @results = split(' ', $_); if ($results[1] =~ '^9336') { # Change the format from blocks (512 byte) to cylinder (737280) my $cyls = ($results[3] * 512)/(737280); $used += $cyls; } elsif ($results[1] =~ '^3390') { $used += $results[3]; } } return ($used); } #------------------------------------------------------- =head3 getDiskPoolFree Description : Get the free size of specified disk pool Arguments : User (root or non-root) zHCP Disk pool Returns : Free size of specified disk pool Example : my $out = xCAT::zvmCPUtils->getDiskPoolFree($user, $hcp, $diskpool); =cut #------------------------------------------------------- sub getDiskPoolFree { my ($class, $user, $hcp, $diskpool) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Volume_Space_Query_DM -q 2 -e 3 -n $diskpool -T $hcpUserId"`; my @lines = split('\n', $out); my @results; my $free = 0; foreach (@lines) { @results = split(' ', $_); if ($results[1] =~ '^9336') { # Change the format from blocks (512 byte) to cylinder (737280) my $cyls = ( $results[3] * 512 ) / ( 737280 ); $free += $cyls; } elsif ($results[1] =~ '^3390') { $free += $results[3]; } } return ($free); } #------------------------------------------------------- =head3 getMaxMemory Description : Get the max memory of a given node Arguments : User (root or non-root) zHCP Node Returns : Max memory Example : my $maxMemory = xCAT::zvmCPUtils->getMaxMemory($user, $hcp, $node); =cut #------------------------------------------------------- sub getMaxMemory { my ($class, $user, $hcp , $node) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $userId = xCAT::zvmCPUtils->getUserId( $user, $node ); # Query the maximum memory allowed in user directory entry my $out = `ssh $user\@$hcp "$sudo /opt/zhcp/bin/smcli Image_Definition_Query_DM -T $userId -k STORAGE_MAXIMUM"`; my @results = split('=', $out); return ($results[1]); } #------------------------------------------------------- =head3 handlePowerUp Description : Handle power up of nodes whose IP information may change on power up. The routine will weed out non-s390x architectures and do processing for nodes whose status in the zvm table is "POWER_UP=1". Arguments : Callback Nodes that are to be handled. Returns : None Example : xCAT::zvmUtils->handlePowerUp( $callback, $nodes, \%args ); =cut #------------------------------------------------------- sub handlePowerUp { my ( $class, $callback, $nodes, $argsRef ) = @_; my %propHash; # Property hash used to fill in various tables my %sysInfo; # Hash to hold system information my %args = %$argsRef; # Command arguments, verbose is one such operand my $nodetypeTab = xCAT::Table->new('nodetype'); my $nodetypeHash = $nodetypeTab->getNodesAttribs( $nodes, ['arch'] ); my $zvmTab = xCAT::Table->new('zvm'); my $zvmHash = $zvmTab->getNodesAttribs( $nodes, ['hcp', 'status', 'userid'] ); foreach my $node ( keys %{$nodetypeHash} ) { next if ( $nodetypeHash->{$node}->[0]->{'arch'} !~ 's390x' ); my $status = $zvmHash->{$node}->[0]->{'status'}; if ( $status =~ 'POWER_UP=1' ) { my $userid = $zvmHash->{$node}->[0]->{'userid'}; my $rc = xCAT::zvmUtils->findAccessIP( $callback, $userid, $zvmHash->{$node}->[0]->{'hcp'}, \%sysInfo, \%args ); if ( $rc == 0 ) { # Got what we needed so we can update tables with the current information. if ( $args{'verbose'} == 1 ) { my $rsp; push @{$rsp->{data}}, "Updating xCAT tables with IP information for $node:\n" . " ip: $sysInfo{$userid}{'ipAddr'}, hostname: $sysInfo{$userid}{'hostname'}\n". " macAddr: $sysInfo{$userid}{'macAddr'}, switch: $sysInfo{$userid}{'switch'}\n". " Network Adapter VDEV: $sysInfo{$userid}{'adapterAddr'}"; xCAT::MsgUtils->message( "I", $rsp, $callback ); } substr( $sysInfo{$userid}{'macAddr'}, 10, 0 ) = ':'; substr( $sysInfo{$userid}{'macAddr'}, 8, 0 ) = ':'; substr( $sysInfo{$userid}{'macAddr'}, 6, 0 ) = ':'; substr( $sysInfo{$userid}{'macAddr'}, 4, 0 ) = ':'; substr( $sysInfo{$userid}{'macAddr'}, 2, 0 ) = ':'; %propHash = ( 'disable' => 0, 'interface' => $sysInfo{$userid}{'vdev'}, 'mac' => $sysInfo{$userid}{'macAddr'}, ); xCAT::zvmUtils->setNodeProps( 'mac', $node, \%propHash ); %propHash = ( 'disable' => 0, 'hostnames' => $sysInfo{$userid}{'hostname'}, 'ip' => $sysInfo{$userid}{'ipAddr'}, ); xCAT::zvmUtils->setNodeProps( 'hosts', $node, \%propHash ); $status =~ s/POWER_UP=1/POWER_UP=0/g; %propHash = ( 'status' => $status, ); xCAT::zvmUtils->setNodeProps( 'zvm', $node, \%propHash ); my $out = `/opt/xcat/sbin/makehosts $node 2>&1`; if ( $out ne '' ) { my $rsp; push @{$rsp->{data}}, "'makehosts' failed for $node. " . "'makehosts' response: $out"; xCAT::MsgUtils->message( "E", $rsp, $callback ); } # Inform OpenStack of the hostname. if ( -e $locOpenStackUpdateName ) { # Call the python change instance name command my $renamed = 0; my $args = "--nodename $node --hostname $sysInfo{$userid}{'hostname'}"; xCAT::MsgUtils->message( "S", "Invoking $locOpenStackUpdateName $args" ); my $out = `python $locOpenStackUpdateName $args`; xCAT::MsgUtils->message( "S", "Returned from OpenStack node name update for $node with $out" ); if ( $out ) { if ( $out =~ m/^Success!/ ) { $renamed = 1; if ( $args{'verbose'} == 1 ) { my $rsp; push @{$rsp->{data}}, "Renamed the OpenStack instance."; xCAT::MsgUtils->message("I", $rsp, $callback); } } } if ( !$renamed ) { # Return an information message but do not fail the nodeset with an error # message. This error is minor to the overall operation. my $rsp; push @{$rsp->{data}}, "Unable to update the OpenStack node name: $node"; xCAT::MsgUtils->message("I", $rsp, $callback); } } } else { my $rsp; push @{$rsp->{data}}, "Did not find sufficient IP information for $node."; xCAT::MsgUtils->message( "I", $rsp, $callback ); } } } } #------------------------------------------------------- =head3 findAccessIP Description : Obtain TCP/IP and hostname information about the virtual machine related to the node. Arguments : Callback in case we want to produce a message Virtual machine userid ZHCP node handling the z/VM host node Hash to contain information on the system Command invocation argument hash. 'ipfilter' and 'verbose' keys are used in this routine. SUDO issuer ($sudoer) or 'root' user for the SSH into ZHCP. This parameter avoids the call to getsudoer() to obtain the sudo and sudoer value. 'root' user does not use 'sudo'. Returns : 0 - No error non-zero - Error detected or filtered out as a usable IP address Example : $rc = findAccessIP( $callback, 'linux001', $hcp \%sysInfo, \%args ); =cut #------------------------------------------------------- sub findAccessIP { my $class = shift; # Perl class my $callback = shift; # Callback for messaging my $activeSystem = shift; # Userid of the system being queried my $hcp = shift; # HCP my $sysInfoRef = shift; # Hash reference for system IP information my $argsRef = shift; # Command arguments, verbose, ipFilter my %args = %$argsRef; # Access hash for easier reference my $sudoer = shift; # SUDO issuer my %adapter; # Adapter hash info, used mainly for verbose processing my @failureInfo; # Information on IP contact failures my $hostname = ''; # Hostname from the target node my @hostnameCmds = ( # List of host name resolution commands to be issued in a virtual OS. 'hostname --fqdn', 'hostname --long', 'hostname', ); my %ips; # Hash of IP info obtained from the various calls my $out; # Output buffer work area my $rc; # Return code my $rsp; # Message work buffer my $sudo = ''; # Assume we are not going to use SUDO on ZHCP call. # Use sudo or not if ( $sudoer eq '' ) { # Looks in the passwd table for a key = sudoer. ($sudoer, $sudo) = xCAT::zvmUtils->getSudoer(); } elsif ( $sudoer ne 'root' ) { # Non-root user specified so we will invoke 'sudo' on the SSH call to ZHCP. $sudo = 'sudo'; } # Get the list of IP addresses currently in use by the virtual machine. $out = `ssh -q $sudoer\@$hcp $sudo /opt/zhcp/bin/smcli "Virtual_Network_Adapter_Query_Extended -T '$activeSystem' -k 'image_device_number=*'"`; $rc = $? >> 8; if ($rc == 255) { push @{$rsp->{data}}, "Unable to communicate with the zhcp system: $hcp"; xCAT::MsgUtils->message("E", $rsp, $callback); goto FINISH_findAccessIP; } elsif ( $rc == 1 ) { my ( $smcliRC, $smcliRS, $smcliDesc ); my $errorOut = `echo "$out" | egrep -i '(^ Return Code: |^ Reason Code: |^ Description: )'`; my @errorLines = split( "\n", $errorOut ); foreach my $errorLine ( @errorLines ) { if ( $errorLine =~ /^ Return Code: / ) { ($smcliRC) = $errorLine =~ /^ Return Code: (.*)/; } if ( $errorLine =~ /^ Reason Code: / ) { ($smcliRS) = $errorLine =~ /^ Reason Code: (.*)/; } if ( $errorLine =~ /^ Description: / ) { ($smcliDesc) = $errorLine =~ /^ Description: (.*)/; } } if (( $smcliRC == 212 ) && ( $smcliRS == 8 )) { if ( $args{'verbose'} == 1 ) { push @{$rsp->{data}}, "For userid $activeSystem, the virtual machine does not have any network adapters."; xCAT::MsgUtils->message( "I", $rsp, $callback ); } push @failureInfo, "The virtual machine does not have any network adapters"; goto FINISH_findAccessIP; } else { push @{$rsp->{data}}, "An unexpected return code $smcliRC and reason code $smcliRS was received from " . "the zhcp server $hcp for an smcli Virtual_Network_Adapter_Query_Extended " . "request. Error description: $smcliDesc"; xCAT::MsgUtils->message("E", $rsp, $callback); goto FINISH_findAccessIP; } } elsif ( $rc != 0 ) { push @{$rsp->{data}}, "An unexpected return code $rc was received from " . "the zhcp server $hcp for an smcli Virtual_Network_Adapter_Query_Extended " . "request. SMAPI servers may be unavailable. " . "Received response: $out"; xCAT::MsgUtils->message("E", $rsp, $callback); goto FINISH_findAccessIP; } my $filteredOut = `echo "$out" | egrep -i '(adapter_address=|adapter_status=|mac_address=|mac_ip_address=|mac_ip_version=|lan_name=|lan_owner=)'`; my @ipOut = split( "\n", $filteredOut ); my ($adapterAddr, $adapterStatus, $ipAddr, $ipVersion, $lanName, $lanOwner, $macAddr ); foreach my $ipLine ( @ipOut ) { if ( $ipLine =~ /adapter_address=/ ) { ($adapterAddr) = $ipLine =~ /adapter_address=(.*)/; $adapter{$adapterAddr}{'ipCnt'} = 0; $adapter{$adapterAddr}{'macCnt'} = 0; ($lanName, $lanOwner) = ''; } elsif ( $ipLine =~ /adapter_status=/ ) { ($adapterStatus) = $ipLine =~ /adapter_status=(.*)/; $adapter{$adapterAddr}{'status'} = $adapterStatus; } elsif ( $ipLine =~ /^lan_name=/ ) { ($lanName) = $ipLine =~ /lan_name=(.*)/; #} elsif ( $ipLine =~ /^lan_owner=/ ) { # ($lanOwner) = $ipLine =~ /lan_owner=(.*)/; } elsif ( $ipLine =~ /^mac_address=/ ) { ($macAddr) = $ipLine =~ /mac_address=(.*)/; ($ipVersion, $ipAddr) = ''; $adapter{$adapterAddr}{'macCnt'} += 1; } elsif ( $ipLine =~ /^mac_ip_address=/ ) { ($ipAddr) = $ipLine =~ /mac_ip_address=(.*)/; } elsif ( $ipLine =~ /^mac_ip_version=/ ) { ($ipVersion) = $ipLine =~ /mac_ip_version=(.*)/; } if ( $ipVersion ne '' and $ipAddr ne '' ) { $ips{$ipAddr}{'adapterAddr'} = $adapterAddr; $ips{$ipAddr}{'ipVersion'} = $ipVersion; $ips{$ipAddr}{'lanName'} = $lanName if $lanName; #$ips{$ipAddr}{'lanOwner'} = $lanOwner if $lanOwner; $ips{$ipAddr}{'macAddr'} = $macAddr; $adapter{$adapterAddr}{'ipCnt'} += 1; } } my $adapterCnt = keys %adapter; push @{$rsp->{data}}, "For userid $activeSystem, $adapterCnt adapters were detected." if ( $args{'verbose'} == 1 ); foreach $adapterAddr ( keys %adapter ) { if ( $adapter{$adapterAddr}{'macCnt'} > 0 ) { if ( $adapter{$adapterAddr}{'ipCnt'} != 0 ) { push @{$rsp->{data}}, " Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs with $adapter{$adapterAddr}{'ipCnt'} associated IP address(es)" if ( $args{'verbose'} == 1 ); } else { push @{$rsp->{data}}, " Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs but no associated IP addresses" if ( $args{'verbose'} == 1 ); push @failureInfo, "Adapter $adapterAddr: $adapter{$adapterAddr}{'macCnt'} MACs but no associated IP addresses"; } } elsif ( $adapter{$adapterAddr}{'status'} eq '00' ) { push @{$rsp->{data}}, " Adapter $adapterAddr: Not coupled" if ( $args{'verbose'} == 1 ); push @failureInfo, "Adapter $adapterAddr: Not coupled"; } elsif ( $adapter{$adapterAddr}{'status'} eq '01' ) { push @{$rsp->{data}}, " Adapter $adapterAddr: Not active" if ( $args{'verbose'} == 1 ); push @failureInfo, "Adapter $adapterAddr: Not active"; } else { push @{$rsp->{data}}, " Adapter $adapterAddr: No MACs with associated IP addresses" if ( $args{'verbose'} == 1 ); push @failureInfo, "Adapter $adapterAddr: No MACs with associated IP addresses"; } } xCAT::MsgUtils->message( "I", $rsp, $callback ) if ( $args{'verbose'} == 1 ); if ( !%ips ) { if ( keys %adapter eq 0 ) { push @failureInfo, "No adapters were found"; } else { push @failureInfo, "No IP addresses were detected"; } goto FINISH_findAccessIP; } # Contact the IPs to see which one, if any, lets us in. foreach $ipAddr ( keys %ips ) { $rc = 0; if ( $ips{$ipAddr}{'ipVersion'} eq '6' ) { # IPv6 is not currently supported. next; } if ( $args{'ipfilter'} ) { if ( $ipAddr !~ m/$args{'ipfilter'}/i ) { if ( $args{'verbose'} == 1 ) { push @{$rsp->{data}}, "For userid $activeSystem, filtered out IP: $ipAddr"; xCAT::MsgUtils->message( "I", $rsp, $callback ); } push @failureInfo, "$ipAddr - filtered out by the specified IP filter"; next; } } # Ping the address to see if it is responsive. if ( $ips{$ipAddr}{'ipVersion'} eq '4' ) { $out = `ping -c1 $ipAddr`; $rc = $?; } elsif ( $ips{$ipAddr}{'ipVersion'} eq '6' ) { # IPv6 is not currently supported. $rc = 3; #$out = `ping6 -c1 $ipAddr`; #$rc = $?; next; } else { push @{$rsp->{data}}, "Userid $activeSystem, IP address: $ipAddr has an unsupported IP version: $ips{$ipAddr}"; xCAT::MsgUtils->message( "E", $rsp, $callback ); next; } if ( $rc != 0 or $out !~ / 0% packet loss,/ ) { if ( $args{'verbose'} == 1 ) { push @{$rsp->{data}}, "For userid $activeSystem, ping failed for $ipAddr"; xCAT::MsgUtils->message("I", $rsp, $callback); } push @failureInfo, "$ipAddr - Unable to ping, rc: $rc"; next; } # SSH into the system to verify access. my $line = `ssh -q $ipAddr pwd 2>/dev/null`; $rc = $? >> 8; if ( $rc == 255 ) { if ( $args{'verbose'} == 1 ) { push @{$rsp->{data}}, "For userid $activeSystem, Unable to ssh into: $ipAddr"; xCAT::MsgUtils->message("I", $rsp, $callback); } push @failureInfo, "$ipAddr - Unable to ssh into system"; next; } # Determine the fully qualified host name to use. my $fqdn = ''; # Attempt to get it from the system's OS using one of a number of commands. # If we can't get a fully qualified DNS names (with periods) then accept the short name. my $shortName = ''; foreach my $cmd ( @hostnameCmds ) { my $hostname = `ssh -q $ipAddr $cmd 2>/dev/null`; my $rc = $? >> 8; if ( $rc == 255 ) { if ( $args{'verbose'} == 1 ) { my $rsp; push @{$rsp->{data}}, "For userid $activeSystem, Unable to ssh into: $ipAddr"; xCAT::MsgUtils->message("I", $rsp, $callback); push @failureInfo, "$ipAddr - Unable to ssh into system"; last; } last; } elsif ( $rc == 0 ) { # verify the hostname is a fully qualified name. chomp $hostname; if ( $hostname =~ /\./ ) { $fqdn = $hostname; last; } else { $hostname =~ s/^\s+//; if (( $hostname ne '' ) && ( $hostname !~ /\s/ )) { # Single word returned without periods. Must be a short name. $shortName = $hostname; } # Keep looking for a long name but we will remember the short name # in case we can't find a long name. } } else { if ( $args{'verbose'} == 1 ) { my $rsp; push @{$rsp->{data}}, "For userid $activeSystem, \'$cmd\' returned, rc: $rc."; xCAT::MsgUtils->message("I", $rsp, $callback); } } } if (( $shortName eq '' ) && ( $rc != 255 )) { push @failureInfo, "$ipAddr - hostname query commands failed to return required information"; } if (( $fqdn eq '' ) && ( $shortName ne '' )) { if ( $args{'verbose'} == 1 ) { my $rsp; push @{$rsp->{data}}, "For userid $activeSystem, Unable to determine the fully qualified domain name but found a short name. The short name will be used."; xCAT::MsgUtils->message("I", $rsp, $callback); } $fqdn = $shortName; } # Found the last piece of info needed. # Note: If hostname is empty because we could not find it, the ultimate response will # be a failing return code. $sysInfoRef->{$activeSystem}{'adapterAddr'} = $ips{$ipAddr}{'adapterAddr'};; $sysInfoRef->{$activeSystem}{'ipAddr'} = $ipAddr; $sysInfoRef->{$activeSystem}{'ipVersion'} = $ips{$ipAddr}; $sysInfoRef->{$activeSystem}{'macAddr'} = $ips{$ipAddr}{'macAddr'}; $sysInfoRef->{$activeSystem}{'switch'} = $ips{$ipAddr}{'lanName'}; $sysInfoRef->{$activeSystem}{'hostname'} = $fqdn; last; } FINISH_findAccessIP: if ( $sysInfoRef->{$activeSystem}{'hostname'} ) { $rc = 0; } else { $rc = 1; if ( @failureInfo ) { my $rsp; my $failureString = join( ',\n', @failureInfo ); push @{$rsp->{data}}, "Unable to access $activeSystem for the following reasons:\n$failureString"; xCAT::MsgUtils->message("I", $rsp, $callback); $failureString = join( ', ', @failureInfo ); xCAT::zvmUtils->printSyslog( "findAccessIP() Unable to access $activeSystem for the following reasons: $failureString" ); } } return $rc; } #------------------------------------------------------- =head3 smapi4xcat Description : Verify if SMAPI EXEC (xCAT_Commands_IUO) exists Arguments : User (root or non-root) zHCP Returns : 0 EXEC not found 1 EXEC found Example : my $out = xCAT::zvmUtils->smapi4xcat($user, $hcp); =cut #------------------------------------------------------- sub smapi4xcat { my ( $class, $user, $hcp ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Get zHCP user ID my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); $hcpUserId =~ tr/a-z/A-Z/; # Check SMAPI level # Levels 621 and greater support SMAPI EXEC my $out = `ssh $user\@$hcp "$sudo $dir/smcli Query_API_Functional_Level -T $hcpUserId"`; $out = xCAT::zvmUtils->trimStr($out); if ( !($out =~ m/V6.2/i || $out =~ m/V6.1/i || $out =~ m/V5.4/i) ) { return 1; } # Check if SMAPI EXEC exists # EXEC found if RC = 8 and RS = 3002 $out = `ssh $user\@$hcp "$sudo $dir/smcli xCAT_Commands_IUO -T $hcpUserId -c ''"`; $out = xCAT::zvmUtils->trimStr($out); if ( $out =~ m/Return Code: 8/i && $out =~ m/Reason Code: 3002/i ) { return 1; } return 0; } #------------------------------------------------------- =head3 generateUserEntryFile Description : Generate a user entry file without Mdisk Arguments : UserId Password Memory Privilege Profile Cpu ipl logonby Returns : If successful, return file path. Otherwise, return -1 Example : my $out = xCAT::zvmUtils->generateUserEntryFile($userId, $password, $memorySize, $privilege, $profileName, $cpuCount, $ipl, $logonby); =cut #------------------------------------------------------- sub generateUserEntryFile { my ( $class, $userId, $password, $memorySize, $privilege, $profileName, $cpuCount, $ipl, $logonby) = @_; # If a file of this name already exists, just override it my $file = "/tmp/$userId.txt"; my $content = "USER $userId $password $memorySize $memorySize $privilege\nINCLUDE $profileName\nCPU 00 BASE\n"; # Add additional CPUs my $i; for ( $i = 1; $i < $cpuCount; $i++ ) { $content = $content.sprintf("CPU %02X\n", $i); } if ( $ipl ne "") { # the caller need validate this $ipl param $content = $content.sprintf("IPL %04s\n", $ipl); } if ( $logonby ne "") { # the caller need validate the $logonby param my $log = "LOGONBY ".$logonby."\n"; $content = $content.$log; } unless (open(FILE, ">$file")) { return -1; } print FILE $content; close(FILE); return $file; } #------------------------------------------------------- =head3 querySSI Description : Obtain the SSI and system status Arguments : User (root or non-root) zHCP Returns : SSI cluster name or string containing (Error)... Example : my $out = xCAT::zvmUtils->querySSI($user, $hcp); =cut #------------------------------------------------------- sub querySSI { my ( $class, $user, $hcp ) = @_; # Directory where executables are my $dir = '/opt/zhcp/bin'; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $outmsg; my $rc; my $ssi = `ssh -o ConnectTimeout=10 $user\@$hcp "$sudo $dir/smcli SSI_Query"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh -o ConnectTimeout=10 $user\@$hcp \"$sudo $dir/smcli SSI_Query\"", $hcp, "querySSI", $ssi ); if ($rc != 0) { return $outmsg; } $ssi = `echo "$ssi" | egrep -a -i "ssi_name"`; $ssi =~ s/ssi_name = //; $ssi =~ s/\s*$//; $ssi =~ s/^\s*//; return $ssi; } #------------------------------------------------------- =head3 rExecute Description : Execute a remote command Arguments : User (root or non-root) Node Command to execute Returns : Output returned from executing command Example : my $out = xCAT::zvmUtils->rExecute($user, $node, $cmd); =cut #------------------------------------------------------- sub rExecute { my ( $class, $user, $node, $cmd ) = @_; my $out; my $sudo = "sudo"; if ($user eq "root") { # Just execute the command if root #$out = `ssh $user\@$node "$cmd"`; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return $out; } # Encapsulate command in single quotes $cmd = "'" . $cmd . "'"; #$out = `ssh $user\@$node "$sudo sh -c $cmd"`; $cmd = "$sudo sh -c $cmd"; $out = xCAT::zvmUtils->execcmdonVM($user, $node, $cmd); # caller sets $user to $::SUDOER return $out; } #------------------------------------------------------- =head3 getUsedFcpDevices Description : Get a list of used FCP devices in the zFCP pools Arguments : User (root or non-root) zHCP Returns : List of known FCP devices, or hash with key "Error" containing error message Example : my %devices = xCAT::zvmUtils->getUsedFcpDevices($user, $zhcp); =cut #------------------------------------------------------- sub getUsedFcpDevices { my ( $class, $user, $hcp ) = @_; # Directory where zFCP pools are my $pool = "/var/opt/zhcp/zfcp"; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Grep the pools for used or allocated zFCP devices my %usedDevices; my @args; my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo cat $pool/*.conf"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $pool/*.conf\"", $hcp, "getUsedFcpDevices", $out ); if ($rc != 0) { $usedDevices{"Error"} = $outmsg; return %usedDevices; } $out = `echo "$out" | egrep -a -i "used|allocated"`; my @devices = split("\n", $out); foreach (@devices) { @args = split(",", $_); # 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,2000000000000112,,3B00-3B3F,,, $args[6] = xCAT::zvmUtils->trimStr($args[6]); # Push used or allocated devices into hash if ($args[6]) { $usedDevices{uc($args[6])} = 1; } } return %usedDevices; } #------------------------------------------------------- =head3 establishMount Description : Establish an NFS mount point on a zHCP system. Arguments : Sudoer user name Sudo keyword zHCP hostname Install root directory Local directory to remotely mount Mount access ('ro' for read only, 'rw' for read write) Directory as known to zHCP (out) Returns : 0 - Mounted, or zHCP and MN are on the same system 1 - Mount failed, errors returned in $callback Example : establishMount( $callback, $::SUDOER, $::SUDO, $hcp, $installRoot, $provMethod, "ro", \$remoteDeployDir ); =cut #------------------------------------------------------- sub establishMount { # Get inputs my ($class, $callback, $sudoer, $sudo, $hcp, $installRoot, $localDir, $access, $mountedPt) = @_; my $out; # If the target system is not on this system then establish the NFS mount point. my $hcpIP = xCAT::NetworkUtils->getipaddr( $hcp ); if (! defined $hcpIP) { xCAT::zvmUtils->printLn( $callback, "(Error) Unable to obtain the IP address of the hcp node" ); return 1; } # Get internal master IP if xcat and zhcp are on a 10. network my $masterIp = xCAT::TableUtils->get_site_attribute("internalmaster"); # Use "internalmaster", if it is set. Otherwise, look at "master" property. if (!defined $masterIp) { $masterIp = xCAT::TableUtils->get_site_attribute("master"); if (! defined $masterIp) { xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to obtain the management node IP address from the site table" ); return 1; } } if ($masterIp eq $hcpIP) { # xCAT MN and zHCP are on the same box and will use the same directory without the need for an NFS mount. $$mountedPt = "$installRoot/$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; } $$mountedPt = "/mnt/$masterHostname$installRoot/$localDir"; # If the mount point already exists then return because we are done. my $outmsg; my $rc; $out = `ssh $sudoer\@$hcp "$sudo mount"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $sudoer\@$hcp \"$sudo mount\"", $hcp, "establishMount", $out ); if ($rc != 0) { xCAT::zvmUtils->printLn( $callback, "$outmsg" ); return 1; } $rc = `echo "$out" | egrep -a -i $$mountedPt > /dev/null; echo \\\$?"`; if ($rc == 0) { return 0; } xCAT::zvmUtils->printSyslog( "establishMount() Preparing the NFS mount point on zHCP ($hcpIP) to xCAT MN $masterHostname($masterIp) for $localDir" ); # Prepare the staging mount point on zHCP, if they need to be established $rc = `ssh $sudoer\@$hcp "$sudo mkdir -p $$mountedPt && mount -t nfs -o $access $masterIp:/$localDir $$mountedPt; echo \\\$?"`; # Return code = 0 (mount succeeded) if ($rc != '0') { xCAT::zvmUtils->printLn( $callback, "$hcp: (Error) Unable to establish zHCP mount point: $$mountedPt" ); xCAT::zvmUtils->printSyslog( "establishMount() Unable to establish zHCP mount point: $$mountedPt, rc: $rc" ); return 1; } } return 0; } #------------------------------------------------------- =head3 getFreeRepoSpace Description : Get the free space of image repository under /install. Arguments : Node Returns : The available space for /install (e.g. "2.1G "). The value is returned as a perl string (e.g. "0 ") to avoid perl returning null instead of "0" in the case of no space available. Example : my $free = getFreeRepoSpace($callback, $node); =cut #------------------------------------------------------- sub getFreeRepoSpace { # Get inputs my ($class, $user, $node) = @_; 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`; # Comment out the horizontal whitespace escape, it was causing "Restarting xCATd Unrecognized escape" # $out =~ s/\h+/ /g; my @results = split(' ', $out); if ( $results[3] eq "0" ) { $results[3] = "0M"; } 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, errors returned in $callback) - zFCP device (if one is requested) - WWPN - LUN Example : my $resultsRef = xCAT::zvmUtils->findAndUpdatezFcpPool($callback, $header, $user, $hcp, $pool, $criteriaRef); =cut #------------------------------------------------------- sub findAndUpdatezFcpPool { # Get inputs my ($class, $callback, $header, $user, $hcp, $pool, $criteriaRef) = @_; my $outmsg; my $rc; my $out; # Determine if sudo is used my $sudo = "sudo"; if ($user eq "root") { $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; } # Owner must be specified if status is used if ($status =~ m/used/i && !$owner) { xCAT::zvmUtils->printLn( $callback, "$header: (Error) Owner must be specified if status is used." ); return \%results; } elsif ($status =~ m/free/i && $owner) { xCAT::zvmUtils->printLn( $callback, "$header: (Error) Owner must not be specified if status is free." ); 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", ""); # Check WWPN and LUN syntax if ( $wwpn && ($wwpn =~ /[^0-9a-f;]/i) ) { xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid world wide portname $wwpn." ); return \%results; } if ( $lun && ($lun =~ /[^0-9a-f]/i) ) { xCAT::zvmUtils->printLn( $callback, "$header: (Error) Invalid logical unit number $lun." ); return \%results; } } # Find disk pool (create one if non-existent) 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,,, $out = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $zfcpDir/$pool.conf\"", $hcp, "findAndUpdatezFcpPool", $out ); if ($rc != 0) { xCAT::zvmUtils->printLn($callback, "$outmsg"); return \%results; } $out = `echo "$out" | egrep -a -i ^free`; my @devices = split("\n", $out); $sizeFound = 0; foreach (@devices) { @info = split(',', $_); # Check if the size is sufficient. Convert size into MB. if ($info[3] =~ m/G/i) { # Convert to MegaBytes $info[3] =~ s/\D//g; $info[3] = int($info[3]) * 1024 } elsif ($info[3] =~ m/M/i) { # Do nothing $info[3] =~ s/\D//g; } else { 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"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $zfcpDir/$pool.conf\"", $hcp, "findAndUpdatezFcpPool", $select ); if ($rc != 0) { xCAT::zvmUtils->printLn($callback, "$outmsg"); return \%results; } $select = `echo "$select" | egrep -a -i "$wwpn,$lun"`; chomp($select); if (!$select) { xCAT::zvmUtils->printLn($callback, "$header: (Error) zFCP device 0x$wwpn/0x$lun could not be found in zFCP pool $pool"); return \%results; } @info = split(',', $select); if ($size) { if ($info[3] =~ m/G/i) { # Convert to MegaBytes $info[3] =~ s/\D//g; $info[3] = int($info[3]) * 1024 } else { # Do nothing $info[3] =~ s/\D//g; } # 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]; } xCAT::zvmUtils->printLn($callback, "$header: Found FCP device 0x$wwpn/0x$lun"); if ( ($status =~ m/used/i) && ($fcpDevice =~ /^auto/i) ) { # select an eligible FCP device $fcpDevice = xCAT::zvmUtils->selectFcpDevice($callback, $header, $user, $hcp, $fcpDevice, $range, $owner); if (!$fcpDevice) { return \%results; } } elsif ($status =~ m/free/i) { # Owner and FCP channel make no sense when status is free $fcpDevice = ""; $owner = ""; } # Mark WWPN and LUN as used, free, or reserved and set the owner/channel appropriately # This config file keeps track of the owner of each device, which is useful in nodeset $size = $size . "M"; my $select = `ssh $user\@$hcp "$sudo cat $zfcpDir/$pool.conf"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo cat $zfcpDir/$pool.conf\"", $hcp, "findAndUpdatezFcpPool", $select ); if ($rc != 0) { xCAT::zvmUtils->printLn($callback, "$outmsg"); return \%results; } $select = `echo "$select" | egrep -a -i "$lun"`; chomp($select); if ($select) { @info = split(',', $select); if (!$info[3]) { $info[3] = $size; } # Do not update if WWPN/LUN pair is specified but the pair does not exist if (!($info[1] =~ m/$wwpn/i)) { xCAT::zvmUtils->printLn($callback, "$header: (Error) FCP device $wwpn/$lun does not exists"); return \%results; } # Entry order: status,wwpn,lun,size,range,owner,channel,tag # The following are never updated: wwpn, lun, size, and range my $update = "$status,$info[1],$info[2],$info[3],$info[4],$owner,$fcpDevice,$tag"; my $expression = "'s#" . $select . "#" .$update . "#i'"; $out = `ssh $user\@$hcp "$sudo sed --in-place -e $expression $zfcpDir/$pool.conf"`; } else { # Insert device entry into file $out = `ssh $user\@$hcp "$sudo echo \"$status,$wwpn,$lun,$size,,$owner,$fcpDevice,$tag\" >> $zfcpDir/$pool.conf"`; } # Generate results hash %results = ( 'rc' => 0, 'fcp' => $fcpDevice, 'wwpn' => $wwpn, 'lun' => $lun ); return \%results; } #------------------------------------------------------- =head3 selectFcpDevice Description : Select an eligible FCP device for attaching a zFCP device to a node Arguments : Message header User (root or non-root) zHCP candidate FCP devices or auto FCP device range zFCP device owner Returns : selected FCP device or empty if no one is selected, errors returned in $callback Example : my $fcpDevice = xCAT::zvmUtils->selectFcpDevice($callback, $header, $user, $hcp, $fcpDevice, $range, $owner); =cut #------------------------------------------------------- sub selectFcpDevice { # Get inputs my ($class, $callback, $header, $user, $hcp, $fcpDevice, $range, $owner) = @_; # Determine if sudo is used my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Directory where executables are on zHCP my $dir = "/opt/zhcp/bin"; # Directory where FCP disk pools are on zHCP my $zfcpDir = "/var/opt/zhcp/zfcp"; my %results = ('rc' => -1); # Default to error # Check FCP device syntax if ($fcpDevice && ($fcpDevice !~ /^auto/i) && ($fcpDevice =~ /[^0-9a-f]/i)) { xCAT::zvmUtils->printLn($callback, "$header: (Error) Invalid FCP channel address $fcpDevice."); return; } # Find a free FCP device based on the given range if ($fcpDevice =~ m/^auto/i) { 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) my @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); if (exists $usedDevices{"Error"}) { xCAT::zvmUtils->printLn($callback, "$header: $usedDevices{Error}"); return; } my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); $hcpUserId =~ tr/a-z/A-Z/; # Find a free FCP channel my $outmsg; my $rc; my $out = `ssh $user\@$hcp "$sudo $dir/smcli System_WWPN_Query -T $hcpUserId"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo $dir/smcli System_WWPN_Query -T $hcpUserId\"", $hcp, "selectFcpDevice", $out ); if ($rc != 0) { xCAT::zvmUtils->printLn($callback, "$outmsg"); return; } $out = `echo "$out" | egrep -a -i "FCP device number|Status"`; my @devices = split( "\n", $out ); for (my $i = 0; $i < @devices; $i++) { # Extract the device number and status $fcpDevice = $devices[$i]; $fcpDevice =~ s/^FCP device number:(.*)/$1/; $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); # Use 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; } } # If there are multiple devices (multipathing), take the 1st one if ($fcpDevice) { if ($fcpDevice =~ m/;/i) { my @info = split(';', $fcpDevice); $fcpDevice = xCAT::zvmUtils->trimStr($info[0]); } # Make sure channel has a length of 4 while (length($fcpDevice) < 4) { $fcpDevice = "0" . $fcpDevice; } } return $fcpDevice; } #------------------------------------------------------- =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, or string containing (Error)... Example : my $pool = xCAT::zvmUtils->findzFcpDevicePool($user, $hcp, $wwpn, $lun); =cut #------------------------------------------------------- sub findzFcpDevicePool { # Get inputs my ( $class, $user, $hcp, $wwpn, $lun ) = @_; my $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 $out; my $outmsg; my $rc; $out = `ssh $user\@$hcp "$sudo grep -i -l \\\"$wwpn,$lun\\\" $zfcpDir/*.conf"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo grep -i -l \\\"$wwpn,$lun\\\" $zfcpDir/*.conf\"", $hcp, "findzFcpDevicePool", $out ); if ($rc != 0) { return $outmsg; } my @pools = split("\n", $out); my $pool = ""; if (scalar(@pools)) { $pool = basename($pools[0]); $pool =~ s/\.[^.]+$//; # Do not use extension } 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 or string containing (Error)... Example : my $deviceRef = xCAT::zvmUtils->findzFcpDeviceAttr($user, $hcp, $pool, $wwpn, $lun); =cut #------------------------------------------------------- sub findzFcpDeviceAttr { # Get inputs my ( $class, $user, $hcp, $pool, $wwpn, $lun ) = @_; my $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 $out; my $outmsg; my $rc; $out = `ssh $user\@$hcp "$sudo grep -i \"$wwpn,$lun\" $zfcpDir/$pool.conf"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "ssh $user\@$hcp \"$sudo grep -i \"$wwpn,$lun\" $zfcpDir/$pool.conf\"", $hcp, "findzFcpDeviceAttr", $out ); if ($rc != 0) { return $outmsg; } my @info = split("\n", $out); my $entry = $info[0]; chomp($entry); # 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; } #------------------------------------------------------- =head3 findUsablezHcpNetwork Description : Find a useable NIC shared with the zHCP for a given user Id Arguments : User (root or non-root) zHCP User Id to find a useable NIC on DHCP is used or not (0 or 1) Returns : NIC, device channel, and layer (2 or 3) Example : my ($nic, $channel, $layer) = xCAT::zvmUtils->findUsablezHcpNetwork($user, $hcp, $userId, $dhcp); =cut #------------------------------------------------------- sub findUsablezHcpNetwork { # Get inputs my ( $class, $user, $hcp, $userId, $dhcp ) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $nic = ''; # Usuable NIC on zHCP my $channel = ''; # Device channel where NIC is attached my $layer; my $i; my @words; # Get the networks used by the zHCP my @hcpNetworks = xCAT::zvmCPUtils->getNetworkNamesArray($user, $hcp); # Search directory entry for network name my $userEntry = `ssh $user\@$hcp "$sudo $::DIR/smcli Image_Query_DM -T $userId" | sed '\$d'`; xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() smcli Image_Query_DM -T $userId"); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() $userEntry"); my $out = `echo "$userEntry" | grep "NICDEF"`; my @lines = split('\n', $out); # Go through each line for ($i = 0; $i < @lines; $i++) { # Go through each network device attached to zHCP foreach (@hcpNetworks) { # If network device is found if ($lines[$i] =~ m/ $_/i) { # Get network layer $layer = xCAT::zvmCPUtils->getNetworkLayer($user, $hcp, $_); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() NIC:$_ layer:$layer"); # If template using DHCP, layer must be 2 if ((!$dhcp && $layer != 2) || (!$dhcp && $layer == 2) || ($dhcp && $layer == 2)) { # Save network name $nic = $_; # Get network virtual address @words = split(' ', $lines[$i]); # Get virtual address (channel) # Convert subchannel to decimal $channel = sprintf('%d', hex($words[1])); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() Candidate found NIC:$nic channel:$channel layer:$layer"); return ($nic, $channel, $layer); } else { # Go to next network available $nic = ''; } } } } # If network device is not found if (!$nic) { # Check for user profile my $profileName = `echo "$userEntry" | grep "INCLUDE"`; if ($profileName) { @words = split(' ', xCAT::zvmUtils->trimStr($profileName)); # Get user profile my $userProfile = xCAT::zvmUtils->getUserProfile($user, $hcp, $words[1]); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() $userProfile"); # Get the NICDEF statement $out = `echo "$userProfile" | grep "NICDEF"`; @lines = split('\n', $out); # Go through each line for ($i = 0; $i < @lines; $i++) { # Go through each network device attached to zHCP foreach (@hcpNetworks) { # If network device is found if ($lines[$i] =~ m/ $_/i) { # Get network layer $layer = xCAT::zvmCPUtils->getNetworkLayer($user, $hcp, $_); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() NIC:$_ layer:$layer"); # If template using DHCP, layer must be 2 if ((!$dhcp && $layer != 2) || (!$dhcp && $layer == 2) || ($dhcp && $layer == 2)) { # Save network name $nic = $_; # Get network virtual address @words = split(' ', $lines[$i]); # Get virtual address (channel) # Convert subchannel to decimal $channel = sprintf('%d', hex($words[1])); xCAT::zvmUtils->printSyslog("findUsablezHcpNetwork() Candidate found NIC:$nic channel:$channel layer:$layer"); return ($nic, $channel, $layer); } else { # Go to next network available $nic = ''; } } } # End of foreach } # End of for } # End of if } return; } #------------------------------------------------------- =head3 printInfo Description : Print a long string to stdout as information without checking anything Arguments : String Returns : Nothing Example : xCAT::zvmUtils->printInfo($callback, $str); =cut #------------------------------------------------------- sub printInfo { # Get inputs my ( $class, $callback, $str ) = @_; # Print string my $rsp; $rsp->{data}->[0] = "$str"; xCAT::MsgUtils->message( "I", $rsp, $callback ); return; } #------------------------------------------------------- =head3 getSpecialCloneInfo Description : Look in the /var/opt/xcat/doclone.txt file (if exists) and return a hash of the keys and values found that match the image name parameter Arguments : User friendly image name Returns : hash of keys and values found or empty hash Example : my %cloneinfo = xCAT::zvmUtils->getSpecialCloneInfo($callback, $user, $node); if (%cloneinfo) { %cloneinfo has at least one key } else { %cloneinfo empty, no keys } =cut #------------------------------------------------------- sub getSpecialCloneInfo { # Get inputs my ( $class, $imagename ) = @_; my %cloneInfoHash = (); # create empty hash # Directory where doclone.txt is my $dir = '/var/opt/xcat/'; my $clonefile = 'doclone.txt'; my $out; # Does the file exist? If so read and look for this image name if (-e "$dir$clonefile") { # look for this image name and ignore case $out = `cat $dir$clonefile | grep -v '^\\s*/[*]'| grep -v '^\\s*[*]'| grep -E -i -w "IMAGE_NAME[[:blank:]]*=[[:blank:]]*$imagename"`; my @lines = split( '\n', $out ); my $count = @lines; # loop for any lines found for (my $i=0; $i < $count; $i++) { # Break out each key=value; item my @parms = split( ';', $lines[$i]); my $parmcount = @parms; # get the key and value for this item, store in hash for (my $j=0; $j < $parmcount; $j++) { my @keyvalue = split('=', $parms[$j]); my $key = $keyvalue[0]; $key =~ s/^\s+|\s+$//g; # get rid of leading and trailing blanks next if ( length( $key ) == 0 ); # Skip incorrect key=value data my $value = $keyvalue[1]; $value =~ s/^\s+|\s+$//g; next if ( length( $value ) == 0 ); # Skip incorrect key=value data #uppercase both key and value; $key = uc $key; $value = uc $value; $cloneInfoHash{ $key } = $value; } } } return (%cloneInfoHash); } #------------------------------------------------------- =head3 pingNode Description : Execute a Perl ping for this node Arguments : Node name Returns : "ping" if found; or "noping" (if not found) Example : my $out = xCAT::zvmUtils->pingNode($node); =cut #------------------------------------------------------- sub pingNode { # Get input node my ( $class, $node ) = @_; my $timeout = 2; # how many seconds to wait for response. Default was 5 # call system ping and max count of pings 2 my $out = `ping -W $timeout -c 2 -q $node`; if ($? != 0) { # Ping failed, try to get result with execcmdonVM. my $result = xCAT::zvmUtils->execcmdonVM($::SUDOER, $node, 'date'); if (xCAT::zvmUtils->checkOutput( $result ) == -1) { return $result; } if ($result) { return ("ping"); } return ("noping"); } return ("ping"); } #------------------------------------------------------- =head3 onlineZhcpPunch Description : Online punch device and load VMCP module on zHCP Arguments : User (root or non-root) zHCP Returns : Operation results (Done/Failed) Example : my $out = xCAT::zvmUtils->onlineZhcpPunch($user, $hcp); =cut #------------------------------------------------------- sub onlineZhcpPunch { # Get input node my ( $class, $user, $hcp ) = @_; my $out = ""; my $subResp = ""; my $rc = ""; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } # Online zHCP's punch $out = `ssh $user\@$hcp "$sudo cat /sys/bus/ccw/drivers/vmur/0.0.000d/online" 2>&1`; $rc = $? >> 8; if ( $rc == 255 ) { # SSH failure to communicate with zHCP. $subResp = "Failed to communicate with the zHCP system to get the punch device status"; } elsif ( $rc != 0 ) { # Generic failure of the command. chomp( $out ); xCAT::zvmUtils->printSyslog( "onlineZhcpPunch() Failed to get the punch device status on zHCP rc: $rc, out: $out" ); $subResp = "Failed to online the punch device on zHCP rc: $rc, out: $out"; } if ( $subResp eq "" ) { if ($out != 1) { chomp( $out = `ssh $user\@$hcp "$sudo /sbin/cio_ignore -r 000d; /sbin/chccwdev -e 000d"`); $rc = $? >> 8; if ( $rc == 0 ) { $subResp = "Done"; } elsif ( $rc == 255 ) { # SSH failure to communicate with zHCP. $subResp = "Failed to communicate with the zHCP system to online the punch device"; } else { if ( !( $out =~ m/Done$/i ) ) { xCAT::zvmUtils->printSyslog("onlineZhcpPunch() failed to online the zHCP's punch, cmd output: $out."); $subResp = "Failed to online the zHCP's punch rc: $rc, out: $out"; } } `ssh $user\@$hcp "$sudo which udevadm &> /dev/null && udevadm settle || udevsettle"`; } else { $subResp = "Done"; } } return $subResp } #------------------------------------------------------- =head3 genCfgdrive Description : Generate a final config drive to punch Arguments : Configure file directory Returns : Generated config drive file path Example : my $out = xCAT::zvmUtils->genCfgdrive($path); =cut #------------------------------------------------------- sub genCfgdrive { # Get input node my ( $class, $cfgpath ) = @_; my $node = basename($cfgpath); my $out = xCAT::zvmUtils->injectMNKey($cfgpath); if ( $out =~ m/Failed/i ) { xCAT::zvmUtils->printSyslog("genCfgdrive() Failed to generate the final cfgdrive.tgz for target node: $node, out: $out"); return ""; } else { xCAT::zvmUtils->printSyslog("genCfgdrive() Successfully generated the final cfgdrive.tgz for target node: $node"); return "$cfgpath/cfgdrive.tgz"; } } #------------------------------------------------------- =head3 injectMNKey Description : Inject xCAT MN's public key to the meta_data.json for target vm Arguments : Configure file directory Returns : A message indicate whether the MN's key in injected success or not Example : my $out = xCAT::zvmUtils->injectMNKey($path); =cut #------------------------------------------------------- sub injectMNKey { # Get input node my ( $class, $cfgpath ) = @_; my $subResp = ""; if ( -e "$cfgpath/cfgdrive.tgz" ) { system("tar -zxf $cfgpath/cfgdrive.tgz -C $cfgpath "); } else { $subResp = "injectMNKey() Failed to find the cfgdrive.tgz under $cfgpath for target node"; return $subResp; } # Get xcat key, store it to a hash var for later use open(my $keyFile, '<', "/root/.ssh/id_rsa.pub"); my $mnKey = <$keyFile>; close($keyFile); my @set = ('0' ..'9', 'A' .. 'F'); my $mnKeyName = join '' => map $set[rand @set], 1 .. 8; my %mnKeyHash = ("name" => $mnKeyName, "type" => "ssh", "data" => $mnKey,); # Read the file content to a variable named md_json,and close the source file my $jsonText; my $MDfile; if(open($MDfile, '<', "$cfgpath/openstack/latest/meta_data.json")) { while(<$MDfile>) { $jsonText .= "$_"; } } else { $subResp = "injectMNKey() Failed to open the meta data file for processing"; close($MDfile); return $subResp; } close($MDfile); # Get the public_keys from meta_data.json, if it not exist, add xCAT's key to meta_data.json directly, # if already exist, compare if the xCAT's key is same or not with existing one, append xCAT key if not same my $md_json = decode_json($jsonText); if (exists $md_json->{"public_keys"}) { my $publicKeys = $md_json->{"public_keys"}; # Check if xCAT key already exist , append it if not exist. foreach my $pubkey ( keys %$publicKeys ) { if ( $publicKeys->{$pubkey} eq $mnKey ) { last; } $publicKeys->{$mnKeyName} = $mnKey; my @tkeys = $md_json->{"keys"}; push @tkeys, {%mnKeyHash}; #push $md_json->{"keys"}, {%mnKeyHash}; } } else { # Set the public_keys and keys with xCAT's key info in meta_data.json $md_json->{"public_keys"}->{$mnKeyName} = $mnKey; $md_json->{"keys"}[0] = {%mnKeyHash}; } # Save the changed meta_data.json to new file open my $fh, ">", "$cfgpath/meta_data.json"; print $fh encode_json($md_json); close $fh; # Replace the meta_data.json file in original config drive with the modified one system( "find $cfgpath/openstack -name meta_data.json -print | xargs -i cp $cfgpath/meta_data.json {}"); `rm -f $cfgpath/meta_data.json`; # Tar the file generate the final one my $oldpath=cwd(); chdir($cfgpath); system ( "tar -zcf cfgdrive.tgz openstack ec2"); chdir($oldpath); $subResp = "Done"; return $subResp; } #------------------------------------------------------- =head3 execcmdthroughIUCV Description : Execute a command to node with IUCV client. Arguments : User (root or non-root). zHCP (opencloud user) VM's userid command [parms..] the comands and parms with the command which need to execute. callback Returns : command result, if success. if an error: and $callback then $callback gets error message returns with string containing (Error) and message Example : my $out = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $command); =cut #------------------------------------------------------- sub execcmdthroughIUCV { my ($class, $user, $hcp, $userid, $commandwithparm, $callback) = @_; my $result = ''; my $rsp; my $msg; my $iucvpath = '/opt/zhcp/bin/IUCV'; my $isCallback = 0; if (defined $callback) { $isCallback = 1; } $result= `ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid "\'$commandwithparm\'" 2>&1`; my $rc = $? >> 8; $result = xCAT::zvmUtils->trimStr( $result ); if ( $isCallback || $rc == 0 ){ xCAT::zvmUtils->printSyslog("$userid: IUCV command: ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid $commandwithparm. return $rc\n $result"); } else { xCAT::zvmUtils->printSyslog("$userid: IUCV command: ssh $user\@$hcp $::SUDO $iucvpath/iucvclnt $userid $commandwithparm."); } if ( $rc == 0 ) { if ($result eq ''){ return "Done"; } return $result; } elsif ( $rc == 1 ) { $msg = "Issued command was not authorized or a generic Linux error occurred. error details $result"; push @{$rsp->{data}}, $msg; } elsif ( $rc == 2 ) { $msg = "parameter to iucvclient error, $result"; push @{$rsp->{data}}, $msg } elsif ( $rc == 4 ) { $msg = "IUCV socket error, error details $result"; push @{$rsp->{data}}, $msg; } elsif ( $rc == 8 ) { $msg = "Command executed failed, error details $result"; push @{$rsp->{data}}, $msg; } elsif ( $rc == 16 ) { $msg = "File Transport failed, error details $result"; push @{$rsp->{data}}, $msg; } elsif ( $rc == 32 ) { $msg = "File Transport failed, error details $result"; push @{$rsp->{data}}, $msg; } # Error occurred if ($isCallback){ xCAT::MsgUtils->message( "E", $rsp, $callback ); } return "(Error) $msg"; } #------------------------------------------------------- =head3 cleanIUCV Description : rollback IUCV to clean all the files that copy to it. Arguments : User (root or non-root). VM's node VM's system Returns : Nothing Example : xCAT::zvmUtils->cleanIUCV( $user, $hcp, $userid); =cut #------------------------------------------------------- sub cleanIUCV { my ($class, $user, $node, $os) = @_; my $sudo = "sudo"; if ($user eq "root") { $sudo = ""; } my $result = ''; my $outmsg = ''; my $cmd = ''; my $rc; my $trgtiucvpath = "/usr/bin/iucvserv"; my $trgtiucvservicepath_rh6_sl11 = "/etc/init.d/iucvserd"; my $trgtiucvservicepath_rh7 = "/lib/systemd/system/iucvserd.service"; my $trgtiucvservicepath_ubuntu16 = "/lib/systemd/system/iucvserd.service"; my $trgtiucvservicepath_sl12 = "/usr/lib/systemd/system/iucvserd.service"; #clean iucv server file $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $result, $node ); # Continue processing even if an error. #clean iucv server service file if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ) { $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh6_sl11 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh6_sl11 2>&1`; } elsif ( $os =~ m/sles12/i ) { $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_sl12 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_sl12 2>&1`; } elsif ( $os =~ m/rhel7/i){ $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh7 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_rh7 2>&1`; } elsif ( $os =~ m/ubuntu16/i){ $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_ubuntu16 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvservicepath_ubuntu16 2>&1`; } ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); # Continue processing even if an error. #clean iucv server authorized file $cmd = "ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node rm -rf $trgtiucvpath 2>&1`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); # Continue processing even if an error. #clean iucv server service start if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ){ $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"chkconfig --del iucvserd && service iucvserd stop 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node "chkconfig --del iucvserd && service iucvserd stop 2>&1"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); # Continue processing even if an error. }else{ $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"systemctl disable iucvserd.service && systemctl stop iucvserd.service 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node "systemctl disable iucvserd.service && systemctl stop iucvserd.service 2>&1"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "cleanIUCV", $result, $node ); # Continue processing even if an error. } } #------------------------------------------------------- =head3 setsshforvm Description : If IUCV communication failed, try to use ssh to make communication. Arguments : User (root or non-root). VM's node VM's linux system type command [parms..] the comands and parms with the command which need to execute. error message which is got in setup IUCV current VM's status in zvm table callback Returns : command result, if success. if an error: and $callback then $callback gets error message and routine returns with 1 if no $callback then routine returns with string containing Error: and message Example : my $out = xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); =cut #------------------------------------------------------- sub setsshforvm { my ($class, $user, $node, $os, $commandwithparm, $msg, $status, $callback) = @_; my $result =''; my $rsp; my $isCallback = 0; my $outmsg = ''; my $rc; if (defined $callback) { $isCallback = 1; } #clean IUCV server first. $result = xCAT::zvmUtils->cleanIUCV($user, $node, $os); if (xCAT::zvmUtils->checkOutput( $result ) == -1) { return $result; } # check whether the vm can be ping, if so then set ssh to zvm table, # to indicate that it use ssh. my $ping = `ping -W 2 -c 2 -q $node`; if ($? == 0) { my $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "setsshforvm", $result, $node ); if ($rc == 255) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } # Continue processing even if an error. } if ($status){ $status = "$status;SSH=1"; }else{ $status = "SSH=1"; } xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); xCAT::zvmUtils->printSyslog("$node: Set SSH=1 for node $node"); if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$node: Set SSH=1 for node $node."); } return $result; } # Error occurred on ping if ($callback){ push @{$rsp->{data}}, $msg; xCAT::MsgUtils->message( "E", $rsp, $callback ); } xCAT::zvmUtils->printSyslog("$node: $msg"); return "$msg"; } #------------------------------------------------------- =head3 execcmdonVM Description : Execute a command to node. Arguments : User (root or non-root). VM's node command [parms..] the comands and parms with the command which need to execute. callback Returns : command result, if success. if an error: and $callback then $callback gets error message routine returns with string containing Error: and message Example : my $out = xCAT::zvmUtils->execcmdonVM($user, $node, $commandwithparm, $callback); =cut #------------------------------------------------------- sub execcmdonVM { my ($class, $user, $node, $commandwithparm, $callback) = @_; # get HCP and z/VM userid my @propNames = ( 'hcp', 'userid', 'status' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); my $status = $propVals->{'status'}; if(!(defined($status))){ $status = ''; } my $hcp = $propVals->{'hcp'}; my $userid = $propVals->{'userid'}; my $isCallback = 0; if (defined $callback) { $isCallback = 1; } my $result = ''; my $outmsg = ''; my $rsp; my $rc; my $msg = ''; my $cmd = ''; my $opnclouduserid='OPNCLOUD'; my $simplecmd = 'date'; # Create path string my $dest = "$user\@$node"; my $srciucvpath = '/opt/zhcp/bin/IUCV'; my $trgtiucvpath = "/usr/bin/iucvserv"; my $trgtiucvservicepath_rh6_sl11 = "/etc/init.d/iucvserd"; my $trgtiucvservicepath_rh7 = "/lib/systemd/system/iucvserd.service"; my $trgtiucvservicepath_sl12 = "/usr/lib/systemd/system/iucvserd.service"; my $trgtiucvservicepath_ubuntu16 = "/lib/systemd/system/iucvserd.service"; my $authorizedfilepath = "/etc/iucv_authorized_userid"; my $xcatuserid = `vmcp q userid | awk '{print \$1}'`; chomp($xcatuserid); # Add escape for IUCV and SSH commands. if ($commandwithparm =~ '\\\"'){ $commandwithparm =~ s/"/\\"/g; } $commandwithparm =~ s/"/\\"/g; # For not xcat deployed node, use SSH to make communication. if (!(defined($userid)) || !(defined($hcp))){ $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); if ($rc == 255) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } } # Remove IUCV=1 if it has been set if ($status =~ /IUCV=1/){ $status =~ s/IUCV=1/SSH=1/g; xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); } return $result; } $userid =~ tr/a-z/A-Z/; # For normal managed nodes, ask zhcp to query the power state if (($userid ne $xcatuserid) && !($hcp =~ /$node/) && ($hcp ne '')){ # Get VM's power stat first, if power stat is off, return error. my $max = 0; while ( !$result && $max < 10 ) { $cmd = "ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q user $userid 2>/dev/null\" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/'"; $result = `ssh $::SUDOER\@$hcp "$::SUDO /sbin/vmcp q user $userid 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/'`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $hcp, "execcmdonVM", $result, $node ); if ($rc == 255) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } return $outmsg; } $max++; } #xCAT::zvmUtils->printSyslog("$node: ssh $::SUDOER\@$hcp \"$::SUDO /sbin/vmcp q user $userid 2>/dev/null\" | sed 's/HCPCQU045E.*/off/' | sed 's/$userid.*/on/' ##$result##"); if ("off" =~ $result) { my $msgText = "$node: (Error) VM $userid is powered off"; xCAT::zvmUtils->printSyslog("$msgText"); if ($isCallback) { xCAT::zvmUtils->printLn( $callback, "$msgText"); } return "$msgText"; } if (!($status =~ /SSH=1/) && !($status =~ /IUCV=1/)){ # if zhcp direct entry does set "IUCV ANY", will set to SSH directly. my $hcpUserId = xCAT::zvmCPUtils->getUserId($user, $hcp); @propNames = ( 'status' ); $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $hcp, @propNames ); my $iucvanystatus = $propVals->{'status'}; if (!($iucvanystatus =~ m/IUCVANY/i)){ xCAT::zvmUtils->printSyslog("$node: zhcp's IUCVANY status is not set"); my $out = `ssh $user\@$hcp "$::SUDO /opt/zhcp/bin/smcli Image_Query_DM -T $hcpUserId"| egrep -i "IUCV ANY"`; if ( $out =~ m/IUCV ANY/i){ if ($iucvanystatus){ $iucvanystatus = "$status;IUCVANY=1"; }else{ $iucvanystatus = "IUCVANY=1"; } }else{ if ($iucvanystatus){ $iucvanystatus = "$status;IUCVANY=0"; }else{ $iucvanystatus = "IUCVANY=0"; } } xCAT::zvmUtils->setNodeProp( 'zvm', $hcp, 'status', $iucvanystatus ); xCAT::zvmUtils->printSyslog("$node: zhcp's status is $iucvanystatus"); } if ($iucvanystatus =~ m/IUCVANY=0/i){ xCAT::zvmUtils->printSyslog("$node: zhcp doesn't support to make communication with IUCV, set SSH=1 for $node"); if ($status){ $status = "$status;SSH=1"; }else{ $status = "SSH=1"; } xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); } } } # If node userid is xcat or zhcp, only use SSH. if (($status =~ /SSH=1/) || ($userid eq $xcatuserid) || ($hcp =~ /$node/) || ($hcp eq '')){ # SSH=1, Use ssh to make communication. $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"$commandwithparm\""; $result = `ssh -o ConnectTimeout=5 $user\@$node "$commandwithparm"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); if ($rc == 255) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } } return $result; }elsif ($status =~ /IUCV=1/){ #xCAT::zvmUtils->printSyslog("$node: IUCV command: $commandwithparm"); # IUCV=1, Use IUCV to make communication. $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); return $result; } xCAT::zvmUtils->printSyslog("$node: VM $userid doesn't set communicate type, will set it first." ); my $releaseInfo = `ssh -qo ConnectTimeout=2 $user\@$node "$::SUDO ls /dev/null $locAllEtcVerFiles 2>/dev/null | xargs grep ''"`; if (xCAT::zvmUtils->checkOutput( $releaseInfo ) == -1) { return $releaseInfo; } my $os = buildOsVersion( $callback, $releaseInfo, 'all' ); # For the existed VMs which are deployed with SSH, will try to copy IUCV files to them. # These VMs are not set communication type, try IUCV first and set the type after communication. # if IUCV server doesn't exist on xcat /var/lib/sspmod, first to copy from OPNCLOUD. if ( not (-e "$srciucvpath/iucvserv" and -e "$srciucvpath/iucvserd" and -e "$srciucvpath/iucvserd.service" )) { $result= `mkdir -p $srciucvpath && scp -p $user\@$hcp:$srciucvpath/iucvser* $srciucvpath 2>&1`; $rc = $? >>8 ; xCAT::zvmUtils->printSyslog("$node: IUCV server files doesn't exist on xcat $srciucvpath, copy from OPNCLOUD.return $rc, $result"); if ($rc != 0) { $msg = "Failed to copy $user\@$hcp:$srciucvpath/iucvser* to $srciucvpath. $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } } # Check whether IUCV server is installed. $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); $rc = $? >> 8; xCAT::zvmUtils->printSyslog("$node: try to execute command through IUCV. $result return $?" ); if ( $rc != 0 ){ #IUCV server doesn't exist on node, copy file to it and restart service if ($result =~ "ERROR connecting socket") { xCAT::zvmUtils->printSyslog("$node: start to set iucv, first to copy iucv server files and start iucv server service" ); #copy IUCV server files. $result = `/usr/bin/scp -p $srciucvpath/iucvserv $dest:$trgtiucvpath 2>&1`; $rc = $? >> 8; xCAT::zvmUtils->printSyslog("$node: /usr/bin/scp -p $srciucvpath/iucvserv $dest:$trgtiucvpath 2>&1 return $rc\n $result"); if ($rc != 0) { $msg = "Failed to copy $srciucvpath/iucvserv to $dest:$trgtiucvpath. $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ) { $result = `/usr/bin/scp -p $srciucvpath/iucvserd $dest:$trgtiucvservicepath_rh6_sl11 2>&1`; } elsif ( $os =~ m/sles12/i ) { $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_sl12 2>&1`; } elsif ( $os =~ m/rhel7/i){ $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_rh7 2>&1`; } elsif ( $os =~ m/ubuntu16/i){ # Note: we should not encounter this line as we don't have ubuntu support before IUCV enablement $result = `/usr/bin/scp -p $srciucvpath/iucvserd.service $dest:$trgtiucvservicepath_ubuntu16 2>&1`; } $rc = $? >> 8; xCAT::zvmUtils->printSyslog("$node: /usr/bin/scp -p iucv service file return $rc $result"); if ($rc != 0) { $msg = "Failed to copy iucvservice file. $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } $opnclouduserid = xCAT::zvmCPUtils->getUserId($user, $hcp); $opnclouduserid =~ tr/a-z/A-Z/; if ($rc !=0) { $msg = "failed to get OPNCLOUD userid. return $? \n$result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"echo -n $opnclouduserid >$authorizedfilepath\" 2>&1"; $result = `ssh -o ConnectTimeout=5 $user\@$node "echo -n $opnclouduserid >$authorizedfilepath" 2>&1`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); if ($rc != 0) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } # Start service of IUCV server if ( $os =~ m/sles11/i or $os =~ m/rhel6/i ){ $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"chkconfig --add iucvserd && service iucvserd start 2>&1\""; $result = `ssh -o ConnectTimeout=5 $user\@$node "chkconfig --add iucvserd && service iucvserd start 2>&1"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); if ($rc != 0) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } }else{ $cmd = "ssh -o ConnectTimeout=5 $user\@$node \"systemctl enable iucvserd.service && systemctl start iucvserd.service 2>&1\""; $result = `ssh -o ConnectTimeout=5 $user\@$node "systemctl enable iucvserd.service && systemctl start iucvserd.service 2>&1"`; ($rc, $outmsg) = xCAT::zvmUtils->checkSSH_Rc( $?, "$cmd", $node, "execcmdonVM", $result, $node ); if ($rc != 0) { if ($isCallback){ xCAT::zvmUtils->printLn( $callback, "$outmsg"); } $msg = "echo -n $hcp >$authorizedfilepath, failed to create authorized userid for $node. return $rc $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } } $rc = $? >> 8; xCAT::zvmUtils->printSyslog("$node: start iucvserver service return $rc. $result"); # Now that the IUCV server has started successfully, send a simple command if ($rc == 0) { $result = xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $simplecmd, $callback); # The simple command worked! Update the zvm table so we always communicate via IUCV if ($? == 0){ xCAT::zvmUtils->printSyslog("$node: successfully initialized IUCV, Set IUCV=1 for $user"); if ($callback){ xCAT::zvmUtils->printLn( $callback, "$node: successfully initialized IUCV, Set IUCV=1 for $user"); } if ($status){ $status = "$status;IUCV=1"; }else{ $status = "IUCV=1"; } xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); return xCAT::zvmUtils->execcmdthroughIUCV($user, $hcp, $userid, $commandwithparm, $callback); }else{ $msg = "$node: Failed to start iucvserver, result is $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } } else { $msg = "$node: Failed to start iucvserver, result is $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } # If our command failed, just return the error } elsif ($result =~ /Command executed failed/) { return $result; } else { $msg = "$node: IUCV server on VM got another error that is not a socket error, result is $result"; return xCAT::zvmUtils->setsshforvm($user, $node, $os, $commandwithparm, $msg, $status, $callback); } } else { $msg = "IUCV has worked well, set IUCV=1 for $user ."; if ($status) { $status = "$status;IUCV=1"; } else { $status = "IUCV=1"; } xCAT::zvmUtils->setNodeProp( 'zvm', $node, 'status', $status ); return $result; } }