diff --git a/perl-xCAT/xCAT/zvmCPUtils.pm b/perl-xCAT/xCAT/zvmCPUtils.pm new file mode 100644 index 000000000..c0cbac58e --- /dev/null +++ b/perl-xCAT/xCAT/zvmCPUtils.pm @@ -0,0 +1,335 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + This is a CP utility plugin for the z/VM. + +=cut + +#------------------------------------------------------- +package xCAT::zvmCPUtils; +use xCAT::zvmUtils; +use strict; +use warnings; +1; + +#------------------------------------------------------- + +=head3 getUserId + + Description : Get userID for specified node + Arguments : Node name + Returns : UserID + Example : my $userID = xCAT::zvmCPUtils->getUserId($node); + +=cut + +#------------------------------------------------------- +sub getUserId { + + # Get inputs + my ( $class, $node ) = @_; + + # Get userId using VMCP + my $out = `ssh -o ConnectTimeout=5 $node vmcp q userid`; + my @results = split( ' ', $out ); + return ( $results[0] ); +} + +#------------------------------------------------------- + +=head3 getSn + + Description : Get serial number for specified node + Arguments : Node name + Returns : Serial number + Example : my $sn = xCAT::zvmCPUtils->getSn($node); + +=cut + +#------------------------------------------------------- +sub getSn { + + # Get inputs + my ( $class, $node ) = @_; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + + # Look in /proc/sysinfo to get serial number + my $out = `ssh $hcp cat /proc/sysinfo | egrep -i "manufacturer|type|model|sequence code|plant"`; + my @props = split( '\n', $out ); + my $man = $props[0]; + my $type = $props[1]; + my $model = $props[2]; + my $sn = $props[3]; + my $plant = $props[4]; + + # Trim and get property value + # Get manufacturer + @props = split( ':', $man ); + $man = xCAT::zvmUtils->trim( $props[1] ); + + # Get machine type + @props = split( ':', $type ); + $type = xCAT::zvmUtils->trim( $props[1] ); + + # Get model + @props = split( ': ', $model ); + $model = xCAT::zvmUtils->trim( $props[1] ); + @props = split( ' ', $model ); + $model = xCAT::zvmUtils->trim( $props[0] ); + + # Get sequence number + @props = split( ':', $sn ); + $sn = xCAT::zvmUtils->trim( $props[1] ); + + # Get plant + @props = split( ':', $plant ); + $plant = xCAT::zvmUtils->trim( $props[1] ); + + return ("$man-$type-$model-$plant-$sn"); +} + +#------------------------------------------------------- + +=head3 getHost + + Description : Get z/VM host for specified node + Arguments : Node name + Returns : z/VM host + Example : my $host = xCAT::zvmCPUtils->getHost($node); + +=cut + +#------------------------------------------------------- +sub getHost { + + # Get inputs + my ( $class, $node ) = @_; + + # Get host using VMCP + my $out = `ssh -o ConnectTimeout=5 $node vmcp q userid`; + my @results = split( ' ', $out ); + my $host = $results[2]; + + return ($host); +} + +#------------------------------------------------------- + +=head3 getOs + + Description : Get operating system name of specified node + Arguments : Node name + Returns : Operating system name + Example : my $osName = xCAT::zvmCPUtils->getOs($node); + +=cut + +#------------------------------------------------------- +sub getOs { + + # Get inputs + my ( $class, $node ) = @_; + + # Get operating system + my $out = `ssh -o ConnectTimeout=5 $node cat /etc/*release`; + my @results = split( '\n', $out ); + return ( xCAT::zvmUtils->trim( $results[0] ) ); +} + +#------------------------------------------------------- + +=head3 getArch + + Description : Get architecture of specified node + Arguments : Node name + Returns : Architecture of node + Example : my $arch = xCAT::zvmCPUtils->getArch($node); + +=cut + +#------------------------------------------------------- +sub getArch { + + # Get inputs + my ( $class, $node ) = @_; + + # Get host using VMCP + my $arch = `ssh $node uname -p`; + + return ( xCAT::zvmUtils->trim($arch) ); +} + +#------------------------------------------------------- + +=head3 getPrivileges + + Description : Get privilege classes of specified node + Arguments : Node name + Returns : Privilege classes + Example : my $memory = xCAT::zvmCPUtils->getPrivileges($node); + +=cut + +#------------------------------------------------------- +sub getPrivileges { + + # Get inputs + my ( $class, $node ) = @_; + + # Get memory configuration + my $out = `ssh -o ConnectTimeout=5 $node vmcp q priv`; + my @out = split( '\n', $out ); + $out[1] = xCAT::zvmUtils->trim( $out[1] ); + $out[2] = xCAT::zvmUtils->trim( $out[2] ); + my $str = " $out[1]\n $out[2]\n"; + return ($str); +} + +#------------------------------------------------------- + +=head3 getMemory + + Description : Get memory configuration of specified node + Arguments : Node name + Returns : Memory configuration + Example : my $memory = xCAT::zvmCPUtils->getMemory($node); + +=cut + +#------------------------------------------------------- +sub getMemory { + + # Get inputs + my ( $class, $node ) = @_; + + # Get memory configuration + my $out = `ssh -o ConnectTimeout=5 $node vmcp q virtual storage`; + my @out = split( '=', $out ); + return ( xCAT::zvmUtils->trim( $out[1] ) ); +} + +#------------------------------------------------------- + +=head3 getCpu + + Description : Get processor configuration of specified node + Arguments : Node name + Returns : Processor configuration + Example : my $proc = xCAT::zvmCPUtils->getCpu($node); + +=cut + +#------------------------------------------------------- +sub getCpu { + + # Get inputs + my ( $class, $node ) = @_; + + # Get processors configuration + my $out = `ssh -o ConnectTimeout=5 $node vmcp q virtual cpus`; + my $str = xCAT::zvmUtils->tabStr($out); + + return ($str); +} + +#------------------------------------------------------- + +=head3 getNic + + Description : Get network interface card (NIC) configuration of specified node + Arguments : Node name + Returns : NIC configuration + Example : my $nic = xCAT::zvmCPUtils->getNic($node); + +=cut + +#------------------------------------------------------- +sub getNic { + + # Get inputs + my ( $class, $node ) = @_; + + # Get NIC configuration + my $out = `ssh -o ConnectTimeout=5 $node vmcp q virtual nic`; + my $str = xCAT::zvmUtils->tabStr($out); + return ($str); +} + +#------------------------------------------------------- + +=head3 getDisks + + Description : Get disk configuration of specified node + Arguments : Node name + Returns : Disk configuration + Example : my $storage = xCAT::zvmCPUtils->getDisks($node); + +=cut + +#------------------------------------------------------- +sub getDisks { + + # Get inputs + my ( $class, $node ) = @_; + + # Get disks configuration + my $out = `ssh -o ConnectTimeout=5 $node vmcp q virtual dasd`; + my $str = xCAT::zvmUtils->tabStr($out); + return ($str); +} + +#------------------------------------------------------- + +=head3 loadVmcp + + Description : Load VMCP module for specified node + Arguments : Node name + Returns : Nothing + Example : my $out = xCAT::zvmCPUtils->loadVmcp($node); + +=cut + +#------------------------------------------------------- +sub loadVmcp { + + # Get inputs + my ( $class, $node ) = @_; + + # Get VSWITCH of master node + my $out = `ssh -o ConnectTimeout=5 $node modprobe vmcp`; + return; +} + +#------------------------------------------------------- + +=head3 getVswitchId + + Description : Get VSWITCH ID for specified node + Arguments : Node name + Returns : VSWITCH IDs + Example : my @vswitch = xCAT::zvmCPUtils->getVswitchId($node); + +=cut + +#------------------------------------------------------- +sub getVswitchId { + + # Get inputs + my ( $class, $node ) = @_; + + # Get VSWITCH of master node + my $out = `ssh -o ConnectTimeout=5 $node vmcp q nic | grep "VSWITCH"`; + my @lines = split( '\n', $out ); + my @parms; + my @vswitch; + foreach (@lines) { + @parms = split( ' ', $_ ); + push( @vswitch, $parms[4] ); + } + + return @vswitch; +} diff --git a/perl-xCAT/xCAT/zvmUtils.pm b/perl-xCAT/xCAT/zvmUtils.pm new file mode 100644 index 000000000..a046706d9 --- /dev/null +++ b/perl-xCAT/xCAT/zvmUtils.pm @@ -0,0 +1,729 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + This is a utility plugin for the z/VM. + +=cut + +#------------------------------------------------------- +package xCAT::zvmUtils; +use xCAT::MsgUtils; +use xCAT::Utils; +use xCAT::Table; +use strict; +use warnings; +1; + +#------------------------------------------------------- + +=head3 getTabProp + Description : Get node property from specified table + Arguments : Table + Node + Property + Returns : Property from specifed table + Example : my $prop = xCAT::zvmUtils->getTabProp($tabName, $node, $propName); + +=cut + +#------------------------------------------------------- +sub getTabProp { + + # Get inputs + my ( $class, $tabName, $node, $propName ) = @_; + + # Get specified table + my $tab = xCAT::Table->new($tabName); + + # Get value from column + my $entry = $tab->getNodeAttribs( $node, [$propName] ); + my $propVal = $entry->{$propName}; + return ($propVal); +} + +#------------------------------------------------------- + +=head3 setTabProp + + Description : Set node property from specified table + Arguments : Table + Node + Property + Returns : Nothing + Example : xCAT::zvmUtils->setTabProp($tabName, $node, $propName, $propVal); + +=cut + +#------------------------------------------------------- +sub setTabProp { + + # Get inputs + my ( $class, $tabName, $node, $propName, $propVal ) = @_; + + # Get specified 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 delTabNode + + Description : Delete node from specified table + Arguments : Table + Node + Returns : Nothing + Example : xCAT::zvmUtils->delTabNode($tabName, $node); + +=cut + +#------------------------------------------------------- +sub delTabNode { + + # Get inputs + my ( $class, $tabName, $node ) = @_; + + # Get specified table + my $tab = xCAT::Table->new( $tabName, -create => 1, -autocommit => 0 ); + + # Delete node from table + my %key = ( 'node' => $node ); + $tab->delEntries( \%key ); + + # Save table + $tab->commit; + + return; +} + +#------------------------------------------------------- + +=head3 tabStr + + Description : Tab 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 trim + + Description : Trim whitespaces in string + Arguments : String + Returns : Trimmed string + Example : my $str = xCAT::zvmUtils->trim($str); + +=cut + +#------------------------------------------------------- +sub trim { + + # Get string + my ( $class, $str ) = @_; + + # Trim right + $str =~ s/\s*$//; + + # Trim left + $str =~ s/^\s*//; + + return ($str); +} + +#------------------------------------------------------- + +=head3 printLn + + Description : Print string + Arguments : String + Returns : Nothing + Example : xCAT::zvmUtils->printLn($callback, $str); + +=cut + +#------------------------------------------------------- +sub printLn { + + # Get inputs + my ( $class, $callback, $str ) = @_; + + # Print string + my $rsp; + $rsp->{data}->[0] = "$str"; + xCAT::MsgUtils->message( "I", $rsp, $callback ); + + return; +} + +#------------------------------------------------------- + +=head3 isZvmNode + + Description : Determine if specified node is in '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 ) = @_; + + # zVM guest ID + my $id; + + # Look in 'zvm' table + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); + + my @results = $tab->getAllAttribsWhere( "node like '%" . $node . "%'", 'userid' ); + foreach (@results) { + $id = $_->{'userid'}; + + # Get userID if one is not in the table + if ($id) { + return ('TRUE'); + } + } + + return ('FALSE'); +} + +#------------------------------------------------------- + +=head3 getIp + + Description : Get IP address of specified node + Arguments : Node + Returns : IP address + Example : my $ip = xCAT::zvmUtils->getIp($node); + +=cut + +#------------------------------------------------------- +sub getIp { + + # Get inputs + my ( $class, $node ) = @_; + + # Get IP address + my $out = `ssh $node ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2`; + my @parms = split( ' ', $out ); + + return ( $parms[0] ); +} + +#------------------------------------------------------- + +=head3 getIfcfg + + Description : Get /etc/sysconfig/network/ifcfg-qeth file name for specified node + Arguments : Node + Returns : /etc/sysconfig/network/ifcfg-qeth file name + Example : my $ifcfg = xCAT::zvmUtils->getIfcfg($node); + +=cut + +#------------------------------------------------------- +sub getIfcfg { + + # Get inputs + my ( $class, $node ) = @_; + + # Get /etc/sysconfig/network/ifcfg-qeth file name + my $out = `ssh $node ls /etc/sysconfig/network/ifcfg-qeth*`; + my @parms = split( '\n', $out ); + + return ( $parms[0] ); +} + +#------------------------------------------------------- + +=head3 getIpBroadcast + + Description : Get IP broadcast of specified node + Arguments : Node + Returns : IP broadcast + Example : my $broadcast = xCAT::zvmUtils->getIpBroadcast($node); + +=cut + +#------------------------------------------------------- +sub getIpBroadcast { + + # Get inputs + my ( $class, $node ) = @_; + + # Get IP address + my $out = `ssh $node ifconfig | grep 'Bcast:'| cut -d: -f3`; + my @parms = split( ' ', $out ); + + return ( $parms[0] ); +} + +#------------------------------------------------------- + +=head3 getDns + + Description : Get DNS server of specified node + Arguments : Node + Returns : DNS server + Example : my $dns = xCAT::zvmUtils->getDns($node); + +=cut + +#------------------------------------------------------- +sub getDns { + + # Get inputs + my ( $class, $node ) = @_; + + # Get IP address + my $out = `ssh $node cat /etc/resolv.conf | grep 'nameserver'`; + my @parms = split( ' ', $out ); + + return ( $parms[1] ); +} + +#------------------------------------------------------- + +=head3 getGateway + + Description : Get default gateway of specified node + Arguments : Node + Returns : Default gateway + Example : my $gw = xCAT::zvmUtils->getGateway($node); + +=cut + +#------------------------------------------------------- +sub getGateway { + + # Get inputs + my ( $class, $node ) = @_; + + # Get IP address + my $out = `ssh $node cat /etc/sysconfig/network/routes`; + my @parms = split( ' ', $out ); + return ( $parms[1] ); +} + +#------------------------------------------------------- + +=head3 sendFile + + Description : SCP file to specified node + Arguments : Node + File + Returns : Nothing + Example : my $out = xCAT::zvmUtils->sendFile($node, $file); + +=cut + +#------------------------------------------------------- +sub sendFile { + + # Get inputs + my ( $class, $node, $file ) = @_; + + # Create destination string + my $dest = "root@"; + $dest .= $node; + + # SCP directory entry file over to HCP + my $out = `scp $file $dest:$file`; + + return; +} + +#------------------------------------------------------- + +=head3 getRootNode + + Description : Get device node mounted on (/) + Arguments : Node name + Returns : Device node mounted on (/) + Example : my $deviceNode = xCAT::zvmUtils->getRootNode($node); + +=cut + +#------------------------------------------------------- +sub getRootNode { + + # Get inputs + my ( $class, $node ) = @_; + + # Get device node mounted on (/) + my $out = `ssh $node mount | grep "/ type"`; + my @parms = split( ' ', $out ); + $parms[0] = xCAT::zvmUtils->trim( $parms[0] ); + + return ( $parms[0] ); +} + +#------------------------------------------------------- + +=head3 disableEnableDisk + + Description : Disable or enable disk for specified node + Arguments : Node + Device address + Option [-d | -e] + Returns : Nothing + Example : my $out = xCAT::zvmUtils->disableEnableDisk($callback, $node, $option, $devAddr); + +=cut + +#------------------------------------------------------- +sub disableEnableDisk { + + # Get inputs + my ( $class, $callback, $node, $option, $devAddr ) = @_; + + # --- Disable or enable disk --- + if ( $option eq "-d" || $option eq "-e" ) { + + # Do nothing + } + else { + return; + } + + my $out = `ssh $node chccwdev $option $devAddr`; + + return; +} + +#------------------------------------------------------- + +=head3 getMdisks + + Description : Get MDisk statements in user directory entry + Arguments : Node + Returns : MDisk statements + Example : my @mdisks = xCAT::zvmUtils->getMdisks($callback, $node); + +=cut + +#------------------------------------------------------- +sub getMdisks { + + # Get inputs + my ( $class, $callback, $node ) = @_; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: No HCP defined for this node" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + my $out = `ssh $hcp $dir/getimagerecords $userId`; + + # Get MDISK statements + my @lines = split( '\n', $out ); + my @disks; + foreach (@lines) { + $_ = xCAT::zvmUtils->trim($_); + + if ( $_ =~ m/MDISK/i ) { + + # Save MDISK statements + push( @disks, $_ ); + } + } + return (@disks); +} + +#------------------------------------------------------- + +=head3 grantVSwitch + + Description : Grant access to VSwitch for specified user ID + Arguments : zHCP node + User ID + Vswitch ID + Returns : Output string + Example : my $out = xCAT::zvmUtils->grantVswitch($callback, $hcp, $userId, $vswitchId); + +=cut + +#------------------------------------------------------- +sub grantVSwitch { + + # Get inputs + my ( $class, $callback, $hcp, $userId, $vswitchId ) = @_; + + my $out = `ssh $hcp vmcp set vswitch $vswitchId grant $userId`; + $out = xCAT::zvmUtils->trim($out); + my $retStr; + if ( $out eq "Command complete" ) { + $retStr = " Operation Successful\n"; + } + else { + $retStr = " Operation Failed\n"; + return ($retStr); + } + + return ($retStr); +} + +#------------------------------------------------------- + +=head3 readConfigFile + + Description : Read in configuration file + Arguments : Node + Configuration file + Returns : Hash arrary containing node configuration + Example : my %nodeConfig = xCAT::zvmUtils->readConfigFile($callback, $node, $file); + +=cut + +#------------------------------------------------------- +sub readConfigFile { + + # Get inputs + my ( $class, $callback, $node, $file ) = @_; + + # Hash array containing new node configuration + my %target; + + # Get configuration file + if ( !$file ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing configuration file" ); + return; + } + else { + + # Open configuration file + open( CONFIG, $file ) || die("Error: Could not open file"); + my @configFile = ; + close(CONFIG); + + # Read configuration file + my @parms; + my $pattern = $node . ":"; + my $save = 0; + foreach (@configFile) { + $_ = xCAT::zvmUtils->trim($_); + + # If the line contains specified node + if ( $_ =~ m/$pattern/i ) { + + # Save configuration + $save = 1; + + # Find ':' and replace with '' + $_ =~ s/://g; + $target{"Hostname"} = $_; + } + + # Stop saving at next line containing ':' + if ( $save == 1 && $_ =~ m/:/i ) { + $save = 0; + } + + # Save configuration + if ( $save == 1 ) { + + # Create hash array + @parms = split( "=", $_ ); + $target{"$parms[0]"} = "$parms[1]"; + } + + } # End of foreach + } # End of else + + # If there is not a new node configuration + if ( !$target{"Hostname"} ) { + xCAT::zvmUtils->printLn( $callback, "Error: Node configuration not found" ); + return; + } + + return %target; +} + +#------------------------------------------------------- + +=head3 saveDirEntryNoDisk + + Description : Get user directory entry for specified node, + remove the MDISK statments, and save it to a file + Arguments : Node name + File name + Returns : Nothing + Example : my $out = xCAT::zvmUtils->saveDirEntryNoDisk($callback, $node, $file); + +=cut + +#------------------------------------------------------- +sub saveDirEntryNoDisk { + + # Get inputs + my ( $class, $callback, $node, $file ) = @_; + + # Directory where executables are + my $dir = '/opt/zhcp/bin'; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: No HCP defined for this node" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + my $out = `ssh $hcp $dir/getimagerecords $userId`; + + # Create a file to save output + open( DIRENTRY, ">$file" ); + + # Remove MDISK statement + my @lines = split( '\n', $out ); + foreach (@lines) { + + # Trim line + $_ = xCAT::zvmUtils->trim($_); + + if ( $_ =~ m/MDISK/i ) { + + # Do nothing + } + else { + + # Write directory entry into file + print DIRENTRY "$_\n"; + } + } + close(DIRENTRY); + + return; +} + +#------------------------------------------------------- + +=head3 appendHostname + + Description : Append specified hostname in front of every line + Arguments : Hostname + String + Returns : String with hostname in front + 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 isOutputGood + + Description : Check output + Arguments : Output string + Returns : 0 Good output + -1 Bad output + Example : my $ans = xCAT::zvmUtils->isOutputGood($callback, $out); + +=cut + +#------------------------------------------------------- +sub isOutputGood { + my ( $class, $callback, $out ) = @_; + + # Check output string + my @outLn = split( "\n", $out ); + foreach (@outLn) { + + # If output contains 'Operation Failed' return -1 + if ( $_ =~ m/Operation Failed/i ) { + return -1; + } + } + + return 0; +} + +#------------------------------------------------------- + +=head3 isAddressUsed + + Description : Check if specified address is used + Arguments : Disk address + Returns : 0 Address used + -1 Address not used + Example : my $ans = xCAT::zvmUtils->isAddressUsed($node, $address); + +=cut + +#------------------------------------------------------- +sub isAddressUsed { + my ( $class, $node, $address ) = @_; + + # Search for disk address + my $out = `ssh $node vmcp q v dasd | grep "DASD $address"`; + if ($out) { + return 0; + } + + return -1; +} diff --git a/xCAT-server/lib/xcat/plugins/zvm.pm b/xCAT-server/lib/xcat/plugins/zvm.pm new file mode 100644 index 000000000..e7d7c6890 --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/zvm.pm @@ -0,0 +1,1336 @@ +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +#------------------------------------------------------- + +=head1 + + xCAT plugin package to handle z/VM + Supported command: + zvm->zvm +=cut + +#------------------------------------------------------- +package xCAT_plugin::zvm; +use xCAT::zvmUtils; +use xCAT::zvmCPUtils; +use xCAT::MsgUtils; +use Sys::Hostname; +use xCAT::Table; +use xCAT::Utils; +use Getopt::Long; +use Switch; +use strict; +use warnings; + +# If this line is not included, you get: +# /opt/xcat/lib/perl/xCAT_plugin/zvm.pm did not return a true value +1; + +#------------------------------------------------------- + +=head3 handled_commands + + Return list of commands handled by this plugin + +=cut + +#------------------------------------------------------- + +sub handled_commands { + return { zvm => "zvm" }; +} + +#------------------------------------------------------- + +=head3 preprocess_request + + Check and setup for hierarchy + +=cut + +#------------------------------------------------------- +sub preprocess_request { + my $req = shift; + my $callback = shift; + + # Hash array + my %sn; + + # Scalar variable + my $sn; + + # Array + my @requests; + + # If already preprocessed, go straight to request + if ( $req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; } + my $nodes = $req->{node}; + my $service = "xcat"; + + # Find service nodes for requested nodes + # Build an individual request for each service node + if ($nodes) { + $sn = xCAT::Utils->get_ServiceNode( $nodes, $service, "MN" ); + + # Build each request for each service node + foreach my $snkey ( keys %$sn ) { + my $n = $sn->{$snkey}; + print "snkey=$snkey, nodes=@$n\n"; + my $reqcopy = {%$req}; + $reqcopy->{node} = $sn->{$snkey}; + $reqcopy->{'_xcatdest'} = $snkey; + $reqcopy->{_xcatpreprocessed}->[0] = 1; + push @requests, $reqcopy; + } + + return \@requests; + } + else { + + # Input error + my %rsp; + my $rsp; + $rsp->{data}->[0] = "Input noderange missing. Useage: zvm \n"; + xCAT::MsgUtils->message( "I", $rsp, $callback, 0 ); + return 1; + } +} + +#------------------------------------------------------- + +=head3 process_request + + Process the command. This is the main call. + +=cut + +#------------------------------------------------------- +sub process_request { + my $request = shift; + my $callback = shift; + my $nodes = $request->{node}; + my $command = $request->{command}->[0]; + my $args = $request->{arg}; + my $envs = $request->{env}; + my %rsp; + my $rsp; + my @nodes = @$nodes; + my $host = hostname(); + + # In order for any SSH command to be sent, you will need to run + # xdsh [group | node] -K + + # Directory where executables are + $::DIR = "/opt/zhcp/bin"; + + # Process ID for fork() + my $pid; + + # Child process IDs + my @children; + + # Determine command sent + switch ($command) { + + # Controls the power for a single or range of nodes + case "rpower" { + foreach (@nodes) { + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + powerVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Remote hardware inventory + case "rinv" { + foreach (@nodes) { + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + inventoryVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Creates zVM virtual server + case "mkvm" { + foreach (@nodes) { + + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + + # Determine if the argument is a node + my $ans = 'FALSE'; + if ( $args->[0] ) { + $ans = xCAT::zvmUtils->isZvmNode( $args->[0] ); + } + + # If it is a node -- then clone specified node + if ( $ans eq 'TRUE' ) { + cloneVM( $callback, $_, $args ); + } + + # If it is not a node -- then create node based on directory entry + # Or create a NOLOG if no entry is provided + else { + makeVM( $callback, $_, $args ); + } + + # Exit process + exit(0); + } # End of elsif + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Removes zVM virtual server + case "rmvm" { + foreach (@nodes) { + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + removeVM( $callback, $_ ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Lists zVM user directory entry + case "lsvm" { + foreach (@nodes) { + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + listVM( $callback, $_ ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Changes zVM user directory entry + case "chvm" { + foreach (@nodes) { + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + changeVM( $callback, $_, $args ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + + # Collects node information from one or more hardware control points. + case "rscan" { + foreach (@nodes) { + $pid = fork(); + + # Parent process + if ($pid) { + push( @children, $pid ); + } + + # Child process + elsif ( $pid == 0 ) { + scanVM( $callback, $_ ); + + # Exit process + exit(0); + } + else { + + # Ran out of resources + die "Error: Could not fork\n"; + } + + } # End of foreach + } # End of case + } # End of switch + + # Wait for all processes to end + foreach (@children) { + waitpid( $_, 0 ); + } + + return; +} + +#------------------------------------------------------- + +=head3 removeVM + + Description : Removes server + Arguments : Node + Returns : Nothing + Example : removeVM($node); + +=cut + +#------------------------------------------------------- +sub removeVM { + + # Get inputs + my ( $callback, $node ) = @_; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + # Power off userID + my $out = `ssh $hcp $::DIR/stopvs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + + # Get device address for each MDISK + my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $node ); + my @parms; + my $addr; + foreach (@mdisks) { + @parms = split( ' ', $_ ); + $addr = $parms[1]; + + # Remove MDISK + # This cleans up the disks before it is put back in the pool + $out = `ssh $hcp $::DIR/removemdisk $userId $addr`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + } + + # Sleep 5 seconds + # To let the z/VM user directory settle + sleep(5); + + # Delete user directory entry + $out = `ssh $hcp $::DIR/deletevs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + + # Remove node from 'zvm', 'nodelist', 'nodehm', and 'hosts' table + xCAT::zvmUtils->delTabNode( 'zvm', $node ); + xCAT::zvmUtils->delTabNode( 'nodelist', $node ); + xCAT::zvmUtils->delTabNode( 'nodetype', $node ); + xCAT::zvmUtils->delTabNode( 'nodehm', $node ); + xCAT::zvmUtils->delTabNode( 'hosts', $node ); + + return; +} + +#------------------------------------------------------- + +=head3 changeVM + + Description : Changes server configuration + Arguments : Node + Option + + Options supported: + add3390 [disk pool] [device address] [mode] [cylinders] + add9336 [disk pool] [virtual device] [mode] [block size] [blocks] + addnic [address] [type] [device count] + addprocessor [address] + addvdisk [userID] [device address] [size] + connectnic2guestlan [address] [lan] [owner] + connectnic2vswitch [address] [vswitch] + dedicatedevice [virtual device] [real device] [mode] + deleteipl + disconnectnic [address] + removemdisk [virtual device] + removenic [address] + removeprocessor [address] + replacevs [file] + setipl [ipl target] [load parms] [parms] + setpassword [password] + + Returns : Nothing + Example : changeVM($node, $args); + +=cut + +#------------------------------------------------------- +sub changeVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + my $out; + + # add3390 [disk pool] [device address] [mode] [cylinders] + if ( $args->[0] eq "--add3390" ) { + my $pool = $args->[1]; + my $addr = $args->[2]; + my $mode = $args->[3]; + my $cyl = $args->[4]; + my $readPw = $args->[5]; + my $writePw = $args->[6]; + my $multiPw = $args->[7]; + + $out = `ssh $hcp $::DIR/add3390 $userId $pool $addr $mode $cyl $readPw $writePw $multiPw`; + } + + # add9336 [disk pool] [virtual device address] [mode] [block size] [blocks] + elsif ( $args->[0] eq "--add9336" ) { + my $pool = $args->[1]; + my $addr = $args->[2]; + my $mode = $args->[3]; + my $blksize = $args->[4]; + my $blks = $args->[5]; + $out = `ssh $hcp $::DIR/add9336 $userId $pool $addr $mode $blksize $blks`; + } + + # addnic [address] [type] [device count] + elsif ( $args->[0] eq "--addnic" ) { + my $addr = $args->[1]; + my $type = $args->[2]; + my $devcount = $args->[3]; + + $out = `ssh $hcp $::DIR/addnic $userId $addr $type $devcount`; + } + + # addprocessor [address] + elsif ( $args->[0] eq "--addprocessor" ) { + my $addr = $args->[1]; + + $out = `ssh $hcp $::DIR/addprocessor $userId $addr`; + } + + # addvdisk [device address] [size] + elsif ( $args->[0] eq "--addvdisk" ) { + my $addr = $args->[1]; + my $size = $args->[2]; + + $out = `ssh $hcp $::DIR/addvdisk $userId $addr $size`; + } + + # connectnic2guestlan [address] [lan] [owner] + elsif ( $args->[0] eq "--connectnic2guestlan" ) { + my $addr = $args->[1]; + my $lan = $args->[2]; + my $owner = $args->[3]; + + $out = `ssh $hcp $::DIR/connectnic2guestlan $userId $addr $lan $owner`; + } + + # connectnic2vswitch [address] [vswitch] + elsif ( $args->[0] eq "--connectnic2vswitch" ) { + my $addr = $args->[1]; + my $vswitch = $args->[2]; + + # Connect to VSwitch + $out = `ssh $hcp $::DIR/connectnic2vswitch $userId $addr $vswitch`; + + # Grant access to VSWITCH for Linux user + $out .= "Granting access to VSWITCH for $userId...\n "; + $out .= `ssh $hcp vmcp set vswitch $vswitch grant $userId`; + } + + # dedicatedevice [virtual device] [real device] [mode] + elsif ( $args->[0] eq "--dedicatedevice" ) { + my $vaddr = $args->[1]; + my $raddr = $args->[2]; + my $mode = $args->[3]; + + $out = `ssh $hcp $::DIR/dedicatedevice $userId $vaddr $raddr $mode`; + } + + # deleteipl + elsif ( $args->[0] eq "--deleteipl" ) { + $out = `ssh $hcp $::DIR/deleteipl $userId`; + } + + # disconnectnic [address] + elsif ( $args->[0] eq "--disconnectnic" ) { + my $addr = $args->[1]; + $out = `ssh $hcp $::DIR/disconnectnic $userId $addr`; + } + + # removemdisk [virtual device address] + elsif ( $args->[0] eq "--removemdisk" ) { + my $addr = $args->[1]; + $out = `ssh $hcp $::DIR/removemdisk $userId $addr`; + } + + # removenic [address] + elsif ( $args->[0] eq "--removenic" ) { + my $addr = $args->[1]; + $out = `ssh $hcp $::DIR/removenic $userId $addr`; + } + + # removeprocessor [address] + elsif ( $args->[0] eq "--removeprocessor" ) { + my $addr = $args->[1]; + $out = `ssh $hcp $::DIR/removeprocessor $userId $addr`; + } + + # replacevs [file] + elsif ( $args->[0] eq "--replacevs" ) { + my $file = $args->[1]; + + my $dest = "root@"; + $dest .= $hcp; + if ($file) { + + # SCP file over to HCP + $out = `scp $file $dest:$file`; + + # Replace user directory entry + $out = `ssh $hcp $::DIR/replacevs $userId $file`; + } + else { + $out = "Error: No directory entry file specified"; + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + } + + # setipl [ipl target] [load parms] [parms] + elsif ( $args->[0] eq "--setipl" ) { + my $trgt = $args->[1]; + my $loadparms = $args->[2]; + my $parms = $args->[3]; + $out = `ssh $hcp $::DIR/setipl $userId $trgt $loadparms $parms`; + } + + # setpassword [password] + elsif ( $args->[0] eq "--setpassword" ) { + my $pw = $args->[1]; + $out = `ssh $hcp $::DIR/setpassword $userId $pw`; + } + + # Print out error + else { + $out = "Error: Option not supported"; + } + + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; +} + +#------------------------------------------------------- + +=head3 powerVM + + Description : Powers on/off a server + Arguments : Node + Option [on|off|reset|stat] + Returns : Nothing + Example : powerVM($node, $args); + +=cut + +#------------------------------------------------------- +sub powerVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + my $out; + if ( $args->[0] eq 'on' ) { + $out = `ssh $hcp $::DIR/startvs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + } + elsif ( $args->[0] eq 'off' ) { + + $out = `ssh $hcp $::DIR/stopvs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + } + elsif ( $args->[0] eq 'stat' ) { + + # Output is different on SLES 11 + $out = `vmcp q user $userId 2>/dev/null | sed 's/HCPCQU045E.*/off/' | sed 's/$userId.*/on/'`; + xCAT::zvmUtils->printLn( $callback, "$node: $out" ); + } + elsif ( $args->[0] eq 'reset' ) { + + $out = `ssh $hcp $::DIR/stopvs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + + # Wait for output + while ( `vmcp q user $node 2>/dev/null | sed 's/HCPCQU045E.*/proceed/'` != "proceed" ) { + } + $out = `ssh $hcp $::DIR/startvs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + } + else { + xCAT::zvmUtils->printLn( $callback, "Error: Option not supported" ); + } + return; +} + +#------------------------------------------------------- + +=head3 scanVM + + Description : Collects node information from one or more hardware control points. + Arguments : zHCP node + Returns : Nothing + Example : scanVM($node); + +=cut + +#------------------------------------------------------- +sub scanVM { + + # Get inputs + my ( $callback, $node ) = @_; + + # Get HCP DNS host name + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + # Print output string + # [Node name]: + # objtype=node + # id=[userID] + # os=[Operating system] + # arch=[Architecture] + # hcp=[HCP node name] + # groups=[Group] + # mgt=zvm + # + # gpok123: + # objtype=node + # id=LINUX123 + # os=SUSE Linux Enterprise Server 10 (s390x) + # arch=s390x + # hcp=gpok456.endicott.ibm.com + # groups=all + # mgt=zvm + + my $str = ""; + + # Get nodes managed by this HCP + # Look in 'zvm' table + my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); + + my $out; + my $managedNode; + my $id; + my $os; + my $arch; + my $groups; + + # Get 'node' and 'userid' properties + my @results = $tab->getAllAttribsWhere( "hcp like '%" . $hcp . "%'", 'node', 'userid' ); + foreach (@results) { + $managedNode = $_->{'node'}; + $id = $_->{'userid'}; + $groups = xCAT::zvmUtils->getTabProp( 'nodelist', $managedNode, 'groups' ); + + # Get userID if one is not in the table + if ( !$id ) { + + # Load VMCP module + $out = xCAT::zvmCPUtils->loadVmcp($managedNode); + + # Get userID + $id = xCAT::zvmCPUtils->getUserId($managedNode); + } + + # Get operating system + $os = xCAT::zvmCPUtils->getOs($managedNode); + + # Get architecture + $arch = xCAT::zvmCPUtils->getArch($managedNode); + + # Create output string + $str .= "$managedNode:\n"; + $str .= " objtype=node\n"; + $str .= " id=$id\n"; + $str .= " os=$os\n"; + $str .= " arch=$arch\n"; + $str .= " hcp=$hcp\n"; + $str .= " groups=$groups\n"; + $str .= " mgt=zvm\n\n"; + } + + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; +} + +#------------------------------------------------------- + +=head3 inventoryVM + + Description : Get server hardware and software inventory + Arguments : Node and arguments + Returns : Nothing + Example : inventoryVM($node, $args); + +=cut + +#------------------------------------------------------- +sub inventoryVM { + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Output string + my $str = ""; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + # Load VMCP module + my $out = xCAT::zvmCPUtils->loadVmcp($node); + + # Get configuration + if ( $args->[0] eq 'config' ) { + + # Get z/VM host for specified node + my $host = xCAT::zvmCPUtils->getHost($node); + + # Get architecture + my $arch = xCAT::zvmCPUtils->getArch($node); + + # Get operating system + my $os = xCAT::zvmCPUtils->getOs($node); + + # Get privileges + my $priv = xCAT::zvmCPUtils->getPrivileges($node); + + # Get memory configuration + my $memory = xCAT::zvmCPUtils->getMemory($node); + + # Get processors configuration + my $proc = xCAT::zvmCPUtils->getCpu($node); + + $str .= "z/VM UserID: $userId\n"; + $str .= "z/VM Host: $host\n"; + $str .= "Operating System: $os\n"; + $str .= "Architecture: $arch\n"; + $str .= "HCP: $hcp\n"; + $str .= "Privileges: \n$priv\n"; + $str .= "Total Memory: $memory\n"; + $str .= "Processors: \n$proc\n"; + } + elsif ( $args->[0] eq 'all' ) { + + # Get z/VM host for specified node + my $host = xCAT::zvmCPUtils->getHost($node); + + # Get architecture + my $arch = xCAT::zvmCPUtils->getArch($node); + + # Get operating system + my $os = xCAT::zvmCPUtils->getOs($node); + + # Get privileges + my $priv = xCAT::zvmCPUtils->getPrivileges($node); + + # Get memory configuration + my $memory = xCAT::zvmCPUtils->getMemory($node); + + # Get processors configuration + my $proc = xCAT::zvmCPUtils->getCpu($node); + + # Get disks configuration + my $storage = xCAT::zvmCPUtils->getDisks($node); + + # Get NICs configuration + my $nic = xCAT::zvmCPUtils->getNic($node); + + # Create output string + $str .= "z/VM UserID: $userId\n"; + $str .= "z/VM Host: $host\n"; + $str .= "Operating System: $os\n"; + $str .= "Architecture: $arch\n"; + $str .= "HCP: $hcp\n"; + $str .= "Privileges: \n$priv\n"; + $str .= "Total Memory: $memory\n"; + $str .= "Processors: \n$proc\n"; + $str .= "Disks: \n$storage\n"; + $str .= "NICs: \n$nic\n"; + } + else { + $str = "Error: Invalid argument"; + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; + } + + # Append hostname in front + $str = xCAT::zvmUtils->appendHostname( $node, $str ); + + xCAT::zvmUtils->printLn( $callback, "$str" ); + return; +} + +#------------------------------------------------------- + +=head3 listVM + + Description : Get user directory entry + Arguments : Node + Returns : Nothing + Example : listVM($node); + +=cut + +#------------------------------------------------------- +sub listVM { + + # Get inputs + my ( $callback, $node ) = @_; + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node HCP" ); + return; + } + + # Get node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + # Get virtual server directory entry + my $out = `ssh $hcp $::DIR/getimagerecords $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + + return; +} + +#------------------------------------------------------- + +=head3 makeVM + + Description : Create a server + Arguments : Node + User directory entry as text file + Returns : Nothing + Example : makeVM($node, $args); + +=cut + +#------------------------------------------------------- +sub makeVM { + + # Before a virtual server can be created + # You need to add the virtual server into the tables: + # mkdef -t node -o gpok123 userid=linux123 hcp=gpok456.endicott.ibm.com mgt=zvm groups=all + # This will add the node into the 'nodelist', 'nodehm', and 'zvm' tables + + # Get inputs + my ( $callback, $node, $args ) = @_; + + # Get HCP + # It was defined in mkdef command + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: No HCP defined for this node" ); + return; + } + + # Get new node userID + my $userId = xCAT::zvmUtils->getTabProp( 'zvm', $node, 'userid' ); + if ( !$userId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + # Get file (if any) + my $file = $args->[0]; + my $out; + + # Create destination string, e.g. + # root@gpok123.endicott.ibm.com + my $dest = "root@"; + $dest .= $hcp; + if ($file) { + + # SCP file over to HCP + $out = `scp $file $dest:$file`; + + # Create virtual server + $out = `ssh $hcp $::DIR/createvs $userId $file`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + } + else { + + # Create NOLOG virtual server + $out = `ssh $hcp $::DIR/createvs $userId`; + xCAT::zvmUtils->printLn( $callback, "$out" ); + } + + return; + +} + +#------------------------------------------------------- + +=head3 cloneVM + + Description : Clone a server + Arguments : Node and configuration + Returns : Nothing + Example : cloneVM($node, $args); + +=cut + +#------------------------------------------------------- +sub cloneVM { + + # Before a virtual server can be created + # You need to add the virtual server into the tables: + # mkdef -t node -o gpok123 userid=LINUX123 ip=9.60.18.123 hcp=gpok456.endicott.ibm.com mgt=zvm groups=all + # This will add the node into the 'nodelist', 'nodehm', 'zvm', and 'hosts' tables + + # Get inputs + my ( $callback, $targetNode, $args ) = @_; + + # Return code for each command + my $rtn; + + xCAT::zvmUtils->printLn( $callback, "$targetNode: Cloning" ); + + # Get HCP + my $hcp = xCAT::zvmUtils->getTabProp( 'zvm', $targetNode, 'hcp' ); + if ( !$hcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: No HCP defined for this node" ); + return; + } + + # Get target node userID + my $targetUserId = xCAT::zvmUtils->getTabProp( 'zvm', $targetNode, 'userid' ); + if ( !$targetUserId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing node ID" ); + return; + } + + # Get source node + my $sourceNode = $args->[0]; + if ( !$sourceNode ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing master node" ); + return; + } + + # Get source node userID + my $sourceId = xCAT::zvmUtils->getTabProp( 'zvm', $sourceNode, 'userid' ); + if ( !$sourceId ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing master node ID" ); + return; + } + + # Get source node HCP + my $sourceHcp = xCAT::zvmUtils->getTabProp( 'zvm', $sourceNode, 'hcp' ); + if ( !$sourceHcp ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing master node ID" ); + return; + } + + # Exit if source node HCP is not the same as target node HCP + elsif ( !( $sourceHcp eq $hcp ) ) { + xCAT::zvmUtils->printLn( $callback, + "Error: Source node HCP ($sourceHcp) is not the same as target node HCP ($hcp)" ); + return; + } + + # Get other inputs (4 in total) + # Disk pool, read, write, and multi passwords + my $i; + my @parms; + my %inputs; + foreach $i ( 1 .. 4 ) { + if ( $args->[$i] ) { + + # Split parameters by '=' + @parms = split( "=", $args->[$i] ); + + # Create hash array + $inputs{ $parms[0] } = $parms[1]; + } + } + + # Get disk pool + my $pool = $inputs{"pool"}; + if ( !$pool ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing pool ID" ); + return; + } + + # Get read, write, and multi password + my $readPw = $inputs{"readpw"}; + if ( !$readPw ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing read password" ); + return; + } + my $writePw = $inputs{"writepw"}; + if ( !$writePw ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing write password" ); + return; + } + my $multiPw = $inputs{"multipw"}; + if ( !$multiPw ) { + xCAT::zvmUtils->printLn( $callback, "Error: Missing multi password" ); + return; + } + + # Get MDisk statements of source node + my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sourceNode ); + + # Get directory entry of source node + # Save user directory entry as /tmp/dirEntry.txt + my $dirFile = "/tmp/dirEntry.txt"; + my $out = xCAT::zvmUtils->saveDirEntryNoDisk( $callback, $sourceNode, $dirFile ); + + # SCP directory entry file over to HCP + $out = xCAT::zvmUtils->sendFile( $hcp, $dirFile ); + + # Create new virtual server + xCAT::zvmUtils->printLn( $callback, "$targetNode: Creating user directory entry" ); + $out = `ssh $hcp $::DIR/createvs $targetUserId $dirFile`; + + # Check for errors + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + + # Exit on bad output + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + + # Load VMCP module + $out = xCAT::zvmCPUtils->loadVmcp($sourceNode); + + # Get VSwitch of master node + my @vswitchId = xCAT::zvmCPUtils->getVswitchId($sourceNode); + + # Grant access to VSwitch for Linux user + # GuestLan do not need permissions + xCAT::zvmUtils->printLn( $callback, "$targetNode: Granting VSwitch access" ); + foreach (@vswitchId) { + $out = xCAT::zvmUtils->grantVSwitch( $callback, $hcp, $targetUserId, $_ ); + + # Check for errors + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + + # Exit on bad output + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + } + + # Add MDisk to target user directory entry + my @trgtDisks; + my $addr; + my $type; + my $mode; + my $cyl; + foreach (@srcDisks) { + + # Get disk device address + @parms = split( ' ', $_ ); + $addr = $parms[1]; + push( @trgtDisks, $addr ); + $type = $parms[2]; + $mode = $parms[6]; + + # Add ECKD disk + if ( $type eq '3390' ) { + + # Get disk size (cylinders) + $out = `ssh -o ConnectTimeout=5 $sourceNode vmcp q v dasd | grep "DASD $addr"`; + @parms = split( ' ', $out ); + $cyl = xCAT::zvmUtils->trim( $parms[5] ); + + # Add disk + xCAT::zvmUtils->printLn( $callback, "$targetNode: Adding minidisk" ); + $out = `ssh $hcp $::DIR/add3390 $targetUserId $pool $addr $mode $cyl $readPw $writePw $multiPw`; + + # Check for errors + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$out" ); + + # Exit on bad output + return; + } + } + + # Add FBA disk + elsif ( $type eq '9336' ) { + + # Get disk size (blocks) + } + + } + + # Format and copy source disks + my $targetAddr; + my $targetDevNode; + my $sourceDevNode; + foreach (@trgtDisks) { + $targetAddr = $_ + 1000; + + # Check if there is an existing address + $out = xCAT::zvmUtils->isAddressUsed( $sourceNode, $targetAddr ); + + # If there is an existing address + while ( $out == 0 ) { + + # Generate a new address + # Sleep 2 seconds to let existing disk appear + sleep(2); + $targetAddr = $targetAddr + 1; + $out = xCAT::zvmUtils->isAddressUsed( $sourceNode, $targetAddr ); + } + + # Link target disk to source disk + $out = `ssh $sourceNode vmcp link $targetUserId $_ $targetAddr MW $multiPw`; + + # Get for errors + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + + # Enable target disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $sourceNode, "-e", $targetAddr ); + + # Determine target device node + $out = `ssh $sourceNode cat /proc/dasd/devices | grep ".$targetAddr("`; + @parms = split( ' ', $out ); + $targetDevNode = $parms[6]; + + # Determine source device node + $out = `ssh $sourceNode cat /proc/dasd/devices | grep "$_"`; + @parms = split( ' ', $out ); + $sourceDevNode = $parms[6]; + + # Format disk + xCAT::zvmUtils->printLn( $callback, "$targetNode: Formating disk" ); + $out = `ssh $sourceNode dasdfmt -b 4096 -y -f /dev/$targetDevNode`; + + # Check for errors + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + + # Exit on bad output + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + + # Copy source disk to target disk + xCAT::zvmUtils->printLn( $callback, "$targetNode: Copying source disk" ); + $out = `ssh $sourceNode dd if=/dev/$sourceDevNode of=/dev/$targetDevNode bs=4096`; + + # Check for error + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + + # Sleep 2 seconds to let the system settle + sleep(2); + + # Disable and enable target disk + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $sourceNode, "-d", $targetAddr ); + $out = xCAT::zvmUtils->disableEnableDisk( $callback, $sourceNode, "-e", $targetAddr ); + + # Determine target device node + $out = `ssh $sourceNode cat /proc/dasd/devices | grep ".$targetAddr("`; + @parms = split( ' ', $out ); + $targetDevNode = $parms[6]; + + # Get source device node that is mounted on (/) + my $sourceRootDevNode = xCAT::zvmUtils->getRootNode($sourceNode); + if ( $sourceRootDevNode =~ m/$sourceDevNode/i ) { + + # Set network configuration + xCAT::zvmUtils->printLn( $callback, "$targetNode: Setting network configuration" ); + + # Mount target disk + my $cloneMntPt = "/mnt/$targetUserId"; + $targetDevNode .= "1"; + + # xCAT::zvmUtils->printLn( $callback, "Mounting $cloneMntPt..." ); + $out = `ssh $sourceNode mkdir $cloneMntPt`; + $out = `ssh $sourceNode mount /dev/$targetDevNode $cloneMntPt`; + + # Set hostname + $out = `ssh $sourceNode sed --in-place -e "s/$sourceNode/$targetNode/g" $cloneMntPt/etc/HOSTNAME`; + $out = `ssh $sourceNode cat $cloneMntPt/etc/HOSTNAME`; + + # Set IP address + my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); + my $targetIp = xCAT::zvmUtils->getTabProp( "hosts", $targetNode, "ip" ); + my $ifcfg = xCAT::zvmUtils->getIfcfg($sourceNode); + my $ifcfgPath = $cloneMntPt; + $ifcfgPath .= $ifcfg; + $out = +`ssh $sourceNode sed --in-place -e "s/$sourceNode/$targetNode/g" \ -e "s/$sourceIp/$targetIp/g" $cloneMntPt/etc/hosts`; + $out = +`ssh $sourceNode sed --in-place -e "s/$sourceIp/$targetIp/g" \ -e "s/$sourceNode/$targetNode/g" $ifcfgPath`; + + # Flush disk + $out = `ssh $sourceNode sync`; + + # Unmount disk + $out = `ssh $sourceNode umount $cloneMntPt`; + } + + # Detatch disk + $out = `ssh $sourceNode vmcp det $targetAddr`; + } + + # Power on target virtual server + xCAT::zvmUtils->printLn( $callback, "$targetNode: Powering on" ); + $out = `ssh $hcp $::DIR/startvs $targetUserId`; + + # Check for error + $rtn = xCAT::zvmUtils->isOutputGood( $callback, $out ); + if ( $rtn == -1 ) { + + # Exit on bad output + xCAT::zvmUtils->printLn( $callback, "$out" ); + return; + } + + xCAT::zvmUtils->printLn( $callback, "$targetNode: Done" ); + + return; +}