# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html #------------------------------------------------------- =head1 xCAT plugin to support z/VM =cut #------------------------------------------------------- package xCAT_plugin::zvm; use xCAT::Client; use xCAT::zvmUtils; use xCAT::zvmCPUtils; use xCAT::MsgUtils; use Sys::Hostname; use xCAT::Table; use xCAT::Utils; use Getopt::Long; use strict; # If the following line is not included, you get: # /opt/xcat/lib/perl/xCAT_plugin/zvm.pm did not return a true value 1; #------------------------------------------------------- =head3 handled_commands Return list of commands handled by this plugin =cut #------------------------------------------------------- sub handled_commands { return { rpower => 'nodehm:power,mgt', rinv => 'nodehm:mgt', mkvm => 'nodehm:mgt', rmvm => 'nodehm:mgt', lsvm => 'nodehm:mgt', chvm => 'nodehm:mgt', rscan => 'nodehm:mgt', nodeset => 'noderes:netboot', getmacs => 'nodehm:getmac,mgt', rnetboot => 'nodehm:mgt', updatenode => 'nodehm:mgt', }; } #------------------------------------------------------- =head3 preprocess_request Check and setup for hierarchy =cut #------------------------------------------------------- sub preprocess_request { my $req = shift; my $callback = shift; # Hash array my %sn; # Scalar variable my $sn; # Array my @requests; # If already preprocessed, go straight to request if ( $req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; } my $nodes = $req->{node}; my $service = "xcat"; # Find service nodes for requested nodes # Build an individual request for each service node if ($nodes) { $sn = xCAT::Utils->get_ServiceNode( $nodes, $service, "MN" ); # Build each request for each service node foreach my $snkey ( keys %$sn ) { my $n = $sn->{$snkey}; print "snkey=$snkey, nodes=@$n\n"; my $reqcopy = {%$req}; $reqcopy->{node} = $sn->{$snkey}; $reqcopy->{'_xcatdest'} = $snkey; $reqcopy->{_xcatpreprocessed}->[0] = 1; push @requests, $reqcopy; } return \@requests; } else { # Input error my %rsp; my $rsp; $rsp->{data}->[0] = "Input noderange missing. Useage: zvm \n"; xCAT::MsgUtils->message( "I", $rsp, $callback, 0 ); return 1; } } #------------------------------------------------------- =head3 process_request Process the command. This is the main call. =cut #------------------------------------------------------- sub process_request { my $request = shift; my $callback = shift; my $nodes = $request->{node}; my $command = $request->{command}->[0]; my $args = $request->{arg}; my $envs = $request->{env}; my %rsp; my $rsp; my @nodes = @$nodes; my $host = hostname(); # Directory where executables are $::DIR = "/opt/zhcp/bin"; # Process ID for xfork() my $pid; # Child process IDs my @children; # --- Power on or off a node --- if ( $command eq "rpower" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { powerVM( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Hardware and software inventory --- elsif ( $command eq "rinv" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { inventoryVM( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # ---- Create a virtual server ---- elsif ( $command eq "mkvm" ) { # Determine if the argument is a node my $clone = 'FALSE'; if ( $args->[0] ) { $clone = xCAT::zvmUtils->isZvmNode( $args->[0] ); } # --- Clone virtual server --- if ( $clone eq 'TRUE' ) { cloneVM( $callback, \@nodes, $args ); } # --- Create user entry --- # Create node based on directory entry # or create a NOLOG if no entry is provided else { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { makeVM( $callback, $_, $args ); # Exit process exit(0); } # End of elsif else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of else } # End of case # --- Remove a virtual server --- elsif ( $command eq "rmvm" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { removeVM( $callback, $_ ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Print the user entry --- elsif ( $command eq "lsvm" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { listVM( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Change the user entry --- elsif ( $command eq "chvm" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { changeVM( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Collect node information from hardware control point --- elsif ( $command eq "rscan" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { scanVM( $callback, $_ ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Set the boot state for a node --- elsif ( $command eq "nodeset" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { nodeSet( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Get the MAC address of a node --- elsif ( $command eq "getmacs" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { getMacs( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Boot from network --- elsif ( $command eq "rnetboot" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { netBoot( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # --- Update the node --- elsif ( $command eq "updatenode" ) { foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { updateNode( $callback, $_, $args ); # Exit process exit(0); } else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach } # End of case # Wait for all processes to end foreach (@children) { waitpid( $_, 0 ); } return; } #------------------------------------------------------- =head3 removeVM Description : Delete the user entry from user directory Arguments : Node to remove Returns : Nothing Example : removeVM($callback, $node); =cut #------------------------------------------------------- sub removeVM { # Get inputs my ( $callback, $node ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Power off userID my $out = `ssh $hcp "$::DIR/stopvs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Delete user entry $out = `ssh $hcp "$::DIR/deletevs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Check for errors my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { return; } # Remove node from 'zvm', 'nodelist', 'nodetype', 'noderes', and 'nodehm' tables # Save node entry in 'mac' table xCAT::zvmUtils->delTabEntry( 'zvm', 'node', $node ); xCAT::zvmUtils->delTabEntry( 'nodelist', 'node', $node ); xCAT::zvmUtils->delTabEntry( 'nodetype', 'node', $node ); xCAT::zvmUtils->delTabEntry( 'noderes', 'node', $node ); xCAT::zvmUtils->delTabEntry( 'nodehm', 'node', $node ); return; } #------------------------------------------------------- =head3 changeVM Description : Change a virtual server configuration Arguments : Node Option Options supported: add3390 [disk pool] [device address] [cylinders] [mode] [read password] [write password] [multi password] add3390active [device address] [mode] add9336 [disk pool] [virtual device] [block size] [mode] [blocks] [read password] [write password] [multi password] addnic [address] [type] [device count] addprocessor [address] addvdisk [userID] [device address] [size] connectnic2guestlan [address] [lan] [owner] connectnic2vswitch [address] [vswitch] copydisk [target address] [source node] [source address] dedicatedevice [virtual device] [real device] [mode] deleteipl formatdisk [disk address] [multi password] disconnectnic [address] grantvswitch [VSwitch] removedisk [virtual device] removenic [address] removeprocessor [address] replacevs [user directory entry] setipl [ipl target] [load parms] [parms] setpassword [password] Returns : Nothing Example : changeVM($callback, $node, $args); =cut #------------------------------------------------------- sub changeVM { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Output string my $out; # add3390 [disk pool] [device address] [cylinders] [mode] [read password] [write password] [multi password] if ( $args->[0] eq "--add3390" ) { my $pool = $args->[1]; my $addr = $args->[2]; my $cyl = $args->[3]; my $mode = $args->[4]; my $readPw = $args->[5]; my $writePw = $args->[6]; my $multiPw = $args->[7]; $out = `ssh $hcp "$::DIR/add3390 $userId $pool $addr $cyl $mode $readPw $writePw $multiPw"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # add3390active [device address] [mode] elsif ( $args->[0] eq "--add3390active" ) { my $addr = $args->[1]; my $mode = $args->[2]; $out = `ssh $hcp "$::DIR/add3390active $userId $addr $mode"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # add9336 [disk pool] [virtual device address] [block size] [blocks] [mode] [read password] [write password] [multi password] elsif ( $args->[0] eq "--add9336" ) { my $pool = $args->[1]; my $addr = $args->[2]; my $blksize = $args->[3]; my $blks = $args->[4]; my $mode = $args->[5]; my $readPw = $args->[6]; my $writePw = $args->[7]; my $multiPw = $args->[8]; $out = `ssh $hcp "$::DIR/add9336 $userId $pool $addr $blksize $blks $mode $readPw $writePw $multiPw"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addnic [address] [type] [device count] elsif ( $args->[0] eq "--addnic" ) { my $addr = $args->[1]; my $type = $args->[2]; my $devcount = $args->[3]; $out = `ssh $hcp "$::DIR/addnic $userId $addr $type $devcount"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addprocessor [type] address] elsif ( $args->[0] eq "--addprocessor" ) { my $type = $args->[1]; my $addr = $args->[2]; $out = `ssh $hcp "$::DIR/addprocessor $userId $type $addr"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addprocessoractive [address] [type] elsif ( $args->[0] eq "--addprocessoractive" ) { my $addr = $args->[1]; my $type = $args->[2]; $out = xCAT::zvmCPUtils->defineCpu( $node, $addr, $type ); $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # addvdisk [device address] [size] elsif ( $args->[0] eq "--addvdisk" ) { my $addr = $args->[1]; my $size = $args->[2]; $out = `ssh $hcp "$::DIR/addvdisk $userId $addr $size"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # connectnic2guestlan [address] [lan] [owner] elsif ( $args->[0] eq "--connectnic2guestlan" ) { my $addr = $args->[1]; my $lan = $args->[2]; my $owner = $args->[3]; $out = `ssh $hcp "$::DIR/connectnic2guestlan $userId $addr $lan $owner"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # connectnic2vswitch [address] [vswitch] elsif ( $args->[0] eq "--connectnic2vswitch" ) { my $addr = $args->[1]; my $vswitch = $args->[2]; # Connect to VSwitch $out = `ssh $hcp "$::DIR/connectnic2vswitch $userId $addr $vswitch"`; # Grant access to VSWITCH for Linux user $out .= "Granting access to VSWITCH for $userId\n "; $out .= `ssh $hcp "vmcp set vswitch $vswitch grant $userId"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # copydisk [target address] [source node] [source address] elsif ( $args->[0] eq "--copydisk" ) { my $tgtNode = $node; my $targetUserId = $userId; my $tgtAddr = $args->[1]; my $srcNode = $args->[2]; my $srcAddr = $args->[3]; # Get source userID @propNames = ( 'hcp', 'userid' ); $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $srcNode, @propNames ); my $sourceId = $propVals->{'userid'}; # --- Link and copy disk --- my $rc; my $try; my $srcDevNode; my $tgtDevNode; # Link target disk to HCP my $tgtLinkAddr; $try = 10; while ( $try > 0 ) { # New disk address $tgtLinkAddr = $tgtAddr + 1000; # Check if new disk address is used (target) $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtLinkAddr ); # If disk address is used (target) while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $tgtLinkAddr = $tgtLinkAddr + 1; $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtLinkAddr ); } # Link target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" ); $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $tgtAddr $tgtLinkAddr MR"`; # If link fails if ( $out =~ m/not linked/i ) { # Wait before trying again sleep(5); $try = $try - 1; } else { last; } } # End of while ( $try > 0 ) # If target disk is not linked if ( $out =~ m/not linked/i ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)" ); xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Exit return; } # Link source disk to HCP my $srcLinkAddr; $try = 10; while ( $try > 0 ) { # New disk address $srcLinkAddr = $srcAddr + 2000; # Check if new disk address is used (source) $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $srcLinkAddr ); # If disk address is used (source) while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $srcLinkAddr = $srcLinkAddr + 1; $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $srcLinkAddr ); } # Link source disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking source disk ($srcAddr) as ($srcLinkAddr)" ); $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $sourceId $srcAddr $srcLinkAddr MR"`; # If link fails if ( $out =~ m/not linked/i ) { # Wait before trying again sleep(5); $try = $try - 1; } else { last; } } # End of while ( $try > 0 ) # If source disk is not linked if ( $out =~ m/not linked/i ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link source disk ($srcAddr)" ); xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Exit return; } # --- Use FLASHCOPY --- xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcLinkAddr) to target disk ($tgtLinkAddr) using FLASHCOPY" ); $out = xCAT::zvmCPUtils->flashCopy( $hcp, $srcLinkAddr, $tgtLinkAddr ); $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); # --- Use Linux DD --- my @words; if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not supported. Using Linux DD" ); # Enable disks $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtLinkAddr ); $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $srcLinkAddr ); # Determine source device node $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$srcLinkAddr("`; @words = split( ' ', $out ); $srcDevNode = $words[6]; # Determine target device node $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtLinkAddr("`; @words = split( ' ', $out ); $tgtDevNode = $words[6]; # Format target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" ); $out = `ssh $hcp "dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; # Check for errors $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } # Sleep 2 seconds to let the system settle sleep(2); # Copy source disk to target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcDevNode) to target disk ($tgtDevNode)" ); $out = `ssh $hcp "dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096"`; # Check for error $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); # Disable disks $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtLinkAddr ); $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $srcLinkAddr ); # Detatch disks from HCP $out = `ssh $hcp "vmcp det $tgtLinkAddr"`; $out = `ssh $hcp "vmcp det $srcLinkAddr"`; return; } # Sleep 2 seconds to let the system settle sleep(2); } # Disable disks $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtLinkAddr ); $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $srcLinkAddr ); # Detatch disks from HCP xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" ); xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching source disk ($srcLinkAddr)" ); $out = `ssh $hcp "vmcp det $tgtLinkAddr"`; $out = `ssh $hcp "vmcp det $srcLinkAddr"`; $out = "$tgtNode: Done"; } # dedicatedevice [virtual device] [real device] [mode] elsif ( $args->[0] eq "--dedicatedevice" ) { my $vaddr = $args->[1]; my $raddr = $args->[2]; my $mode = $args->[3]; $out = `ssh $hcp "$::DIR/dedicatedevice $userId $vaddr $raddr $mode"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # deleteipl elsif ( $args->[0] eq "--deleteipl" ) { $out = `ssh $hcp "$::DIR/deleteipl $userId"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # exchangesshkeys [password] elsif ( $args->[0] eq "--exchangesshkeys" ) { # Get password my $password = $args->[1]; # Exchange SSH keys $out = `DSH_REMOTE_PASSWORD=$password xdsh $node -K`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # formatdisk [address] [multi password] elsif ( $args->[0] eq "--formatdisk" ) { my $tgtNode = $node; my $targetUserId = $userId; my $tgtAddr = $args->[1]; # --- Link and format disk --- my $rc; my $try; my $tgtDevNode; # Link target disk to HCP my $tgtLinkAddr; $try = 10; while ( $try > 0 ) { # New disk address $tgtLinkAddr = $tgtAddr + 1000; # Check if new disk address is used (target) $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtLinkAddr ); # If disk address is used (target) while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $tgtLinkAddr = $tgtLinkAddr + 1; $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtLinkAddr ); } # Link target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($tgtAddr) as ($tgtLinkAddr)" ); $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $tgtAddr $tgtLinkAddr MR"`; # If link fails if ( $out =~ m/not linked/i ) { # Wait before trying again sleep(5); $try = $try - 1; } else { last; } } # End of while ( $try > 0 ) # If target disk is not linked if ( $out =~ m/not linked/i ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Failed to link target disk ($tgtAddr)" ); xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Exit return; } # --- Format disk --- my @words; if ( $rc == -1 ) { # Enable disk $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtLinkAddr ); # Determine target device node $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtLinkAddr("`; @words = split( ' ', $out ); $tgtDevNode = $words[6]; # Format target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtDevNode)" ); $out = `ssh $hcp "dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; # Check for errors $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } # Sleep 2 seconds to let the system settle sleep(2); } # Disable disk $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtLinkAddr ); # Detatch disk from HCP xCAT::zvmUtils->printLn( $callback, "$tgtNode: Detatching target disk ($tgtLinkAddr)" ); $out = `ssh $hcp "vmcp det $tgtLinkAddr"`; $out = "$tgtNode: Done"; } # grantvswitch [VSwitch] elsif ( $args->[0] eq "--grantvswitch" ) { my $vsw = $args->[1]; $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $hcp, $userId, $vsw ); $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # disconnectnic [address] elsif ( $args->[0] eq "--disconnectnic" ) { my $addr = $args->[1]; $out = `ssh $hcp "$::DIR/disconnectnic $userId $addr"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # removedisk [virtual device address] elsif ( $args->[0] eq "--removedisk" ) { my $addr = $args->[1]; $out = `ssh $hcp "$::DIR/removemdisk $userId $addr"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # removenic [address] elsif ( $args->[0] eq "--removenic" ) { my $addr = $args->[1]; $out = `ssh $hcp "$::DIR/removenic $userId $addr"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # removeprocessor [address] elsif ( $args->[0] eq "--removeprocessor" ) { my $addr = $args->[1]; $out = `ssh $hcp "$::DIR/removeprocessor $userId $addr"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # replacevs [file] elsif ( $args->[0] eq "--replacevs" ) { my $file = $args->[1]; # Target system (HCP) -- root@gpok2.endicott.ibm.com my $target = "root@"; $target .= $hcp; if ($file) { # SCP file over to HCP $out = `scp $file $target:$file`; # Replace user directory entry $out = `ssh $hcp "$::DIR/replacevs $userId $file"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } else { $out = "$node: (Error) No user entry file specified"; xCAT::zvmUtils->printLn( $callback, "$out" ); return; } } # setipl [ipl target] [load parms] [parms] elsif ( $args->[0] eq "--setipl" ) { my $trgt = $args->[1]; my $loadparms = $args->[2]; my $parms = $args->[3]; $out = `ssh $hcp "$::DIR/setipl $userId $trgt $loadparms $parms"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # setpassword [password] elsif ( $args->[0] eq "--setpassword" ) { my $pw = $args->[1]; $out = `ssh $hcp "$::DIR/setpassword $userId $pw"`; $out = xCAT::zvmUtils->appendHostname( $node, $out ); } # Otherwise, print out error else { $out = "$node: (Error) Option not supported"; } xCAT::zvmUtils->printLn( $callback, "$out" ); return; } #------------------------------------------------------- =head3 powerVM Description : Power on or off a virtual server Arguments : Node Option [on|off|reset|stat] Returns : Nothing Example : powerVM($callback, $node, $args); =cut #------------------------------------------------------- sub powerVM { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Output string my $out; # Power on virtual server if ( $args->[0] eq 'on' ) { $out = `ssh $hcp "$::DIR/startvs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Power off virtual server elsif ( $args->[0] eq 'off' ) { $out = `ssh $hcp "$::DIR/stopvs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Get the status (on|off) elsif ( $args->[0] eq 'stat' ) { # Output is different on SLES 11 $out = `ssh $hcp "vmcp q user $userId 2>/dev/null" | sed 's/HCPCQU045E.*/off/' | sed 's/$userId.*/on/'`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # Reset a virtual server elsif ( $args->[0] eq 'reset' ) { $out = `ssh $hcp "$::DIR/stopvs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Wait for output while ( `vmcp q user $userId 2>/dev/null | sed 's/HCPCQU045E.*/Done/'` != "Done" ) { # Do nothing } $out = `ssh $hcp "$::DIR/startvs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } else { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); } return; } #------------------------------------------------------- =head3 scanVM Description : Get node information from HCP Arguments : HCP node Returns : Nothing Example : scanVM($callback, $node, $args); =cut #------------------------------------------------------- sub scanVM { # Get inputs my ( $callback, $node ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Print output string # [Node name]: # objtype=node # id=[userID] # os=[Operating system] # arch=[Architecture] # hcp=[HCP node name] # groups=[Group] # mgt=zvm # # gpok123: # objtype=node # id=LINUX123 # os=SUSE Linux Enterprise Server 10 (s390x) # arch=s390x # hcp=gpok456.endicott.ibm.com # groups=all # mgt=zvm # Output string my $str = ""; # Get nodes managed by this HCP # Look in 'zvm' table my $tab = xCAT::Table->new( 'zvm', -create => 1, -autocommit => 0 ); my @entries = $tab->getAllAttribsWhere( "hcp like '%" . $hcp . "%'", 'node', 'userid' ); my $out; my $managedNode; my $id; my $os; my $arch; my $groups; # Search for nodes managed by given HCP # Get 'node' and 'userid' properties foreach (@entries) { $managedNode = $_->{'node'}; # Get groups @propNames = ('groups'); $propVals = xCAT::zvmUtils->getNodeProps( 'nodelist', $managedNode, @propNames ); $groups = $propVals->{'groups'}; # Load VMCP module xCAT::zvmCPUtils->loadVmcp($managedNode); # Get userID $id = xCAT::zvmCPUtils->getUserId($managedNode); # Get operating system $os = xCAT::zvmUtils->getOs($managedNode); # Get architecture $arch = xCAT::zvmCPUtils->getArch($managedNode); # Create output string $str .= "$managedNode:\n"; $str .= " objtype=node\n"; $str .= " userid=$id\n"; $str .= " os=$os\n"; $str .= " arch=$arch\n"; $str .= " hcp=$hcp\n"; $str .= " groups=$groups\n"; $str .= " mgt=zvm\n\n"; } xCAT::zvmUtils->printLn( $callback, "$str" ); return; } #------------------------------------------------------- =head3 inventoryVM Description : Get virtual server hardware and software inventory Arguments : Node Type of inventory (config|all) Returns : Nothing Example : inventoryVM($callback, $node, $args); =cut #------------------------------------------------------- sub inventoryVM { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Output string my $str = ""; # Load VMCP module xCAT::zvmCPUtils->loadVmcp($node); # Get configuration if ( $args->[0] eq 'config' ) { # Get z/VM host for specified node my $host = xCAT::zvmCPUtils->getHost($node); # Get architecture my $arch = xCAT::zvmUtils->getArch($node); # Get operating system my $os = xCAT::zvmUtils->getOs($node); # Get privileges my $priv = xCAT::zvmCPUtils->getPrivileges($node); # Get memory configuration my $memory = xCAT::zvmCPUtils->getMemory($node); # Get processors configuration my $proc = xCAT::zvmCPUtils->getCpu($node); $str .= "z/VM UserID: $userId\n"; $str .= "z/VM Host: $host\n"; $str .= "Operating System: $os\n"; $str .= "Architecture: $arch\n"; $str .= "HCP: $hcp\n"; $str .= "Privileges: \n$priv\n"; $str .= "Total Memory: $memory\n"; $str .= "Processors: \n$proc\n"; } elsif ( $args->[0] eq 'all' ) { # Get z/VM host for specified node my $host = xCAT::zvmCPUtils->getHost($node); # Get architecture my $arch = xCAT::zvmUtils->getArch($node); # Get operating system my $os = xCAT::zvmUtils->getOs($node); # Get privileges my $priv = xCAT::zvmCPUtils->getPrivileges($node); # Get memory configuration my $memory = xCAT::zvmCPUtils->getMemory($node); # Get processors configuration my $proc = xCAT::zvmCPUtils->getCpu($node); # Get disks configuration my $storage = xCAT::zvmCPUtils->getDisks($node); # Get NICs configuration my $nic = xCAT::zvmCPUtils->getNic($node); # Create output string $str .= "z/VM UserID: $userId\n"; $str .= "z/VM Host: $host\n"; $str .= "Operating System: $os\n"; $str .= "Architecture: $arch\n"; $str .= "HCP: $hcp\n"; $str .= "Privileges: \n$priv\n"; $str .= "Total Memory: $memory\n"; $str .= "Processors: \n$proc\n"; $str .= "Disks: \n$storage\n"; $str .= "NICs: \n$nic\n"; } else { $str = "$node: (Error) Option not supported"; xCAT::zvmUtils->printLn( $callback, "$str" ); return; } # Append hostname (e.g. gpok3) in front $str = xCAT::zvmUtils->appendHostname( $node, $str ); xCAT::zvmUtils->printLn( $callback, "$str" ); return; } #------------------------------------------------------- =head3 listVM Description : Show node configuration Arguments : Node Option Options supported: getnetworknames getnetwork [networkname] diskpoolnames diskpool [pool name] [space (free or used)] Returns : Nothing Example : listVM($callback, $node); =cut #------------------------------------------------------- sub listVM { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } my $out; # Get disk pool names if ( $args->[0] eq "--diskpoolnames" ) { $out = `ssh $hcp "$::DIR/getdiskpoolnames $userId"`; } # Get disk pool configuration elsif ( $args->[0] eq "--diskpool" ) { my $pool = $args->[1]; my $space = $args->[2]; $out = `ssh $hcp "$::DIR/getdiskpool $userId $pool $space"`; } # Get network names elsif ( $args->[0] eq "--getnetworknames" ) { $out = xCAT::zvmCPUtils->getNetworkNames($hcp); } # Get network elsif ( $args->[0] eq "--getnetwork" ) { my $netName = $args->[1]; $out = xCAT::zvmCPUtils->getNetwork( $hcp, $netName ); } # Get user entry elsif ( !$args->[0] ) { $out = `ssh $hcp "$::DIR/getuserentry $userId"`; } else { $out = "$node: (Error) Option not supported"; } # Append hostname (e.g. gpok3) in front $out = xCAT::zvmUtils->appendHostname( $node, $out ); xCAT::zvmUtils->printLn( $callback, "$out" ); return; } #------------------------------------------------------- =head3 makeVM Description : Create a virtual server - A unique MAC address will be assigned to the virtual server Arguments : Node User entry text file (optional) Returns : Nothing Example : makeVM($callback, $node, $args); =cut #------------------------------------------------------- sub makeVM { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Get user entry file (if any) my $userEntry = $args->[0]; # Create virtual server my $out; my $target = "root@" . $hcp; if ($userEntry) { # Get MAC address in 'mac' table my $macId; my $generateNew = 0; @propNames = ('mac'); $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); if ($propVals) { # Get MAC suffix (MACID) $macId = $propVals->{'mac'}; $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); $macId = substr( $macId, 6 ); } else { # If no MACID is found, get one $macId = xCAT::zvmUtils->getMacID($hcp); if ( !$macId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Could not generate MACID" ); return; } # Set flag to generate new MACID after virtual server is created $generateNew = 1; } # Get the network name the HCP is on $out = `ssh $hcp "vmcp q v nic" | egrep -i "VSWITCH|LAN"`; my @lines = split( '\n', $out ); my $line = xCAT::zvmUtils->trimStr($lines[0]); my @words = split( ' ', $line ); my $netName = $words[4]; # Append MACID at the end of the NICDEF statement in user entry text file $out = `sed --in-place -e "s,$netName,$netName MACID $macId,g" $userEntry`; # SCP file over to HCP $out = `scp $userEntry $target:$userEntry`; # Create virtual server $out = `ssh $hcp "$::DIR/createvs $userId $userEntry"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Check output my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == 0 ) { # Get HCP MAC address # The HCP should only have (1) network -- (1) MAC address xCAT::zvmCPUtils->loadVmcp($hcp); $out = `ssh -o ConnectTimeout=5 $hcp "vmcp q nic" | grep "MAC:"`; my @lines = split( "\n", $out ); my @words = split( " ", $lines[0] ); # Extract MAC prefix my $prefix = $words[1]; $prefix = xCAT::zvmUtils->replaceStr( $prefix, "-", "" ); $prefix = substr( $prefix, 0, 6 ); # Generate MAC address my $mac = $prefix . $macId; # If length is less than 12, append a zero if ( length($mac) != 12 ) { $mac = "0" . $mac; } $mac = substr( $mac, 0, 2 ) . ":" . substr( $mac, 2, 2 ) . ":" . substr( $mac, 4, 2 ) . ":" . substr( $mac, 6, 2 ) . ":" . substr( $mac, 8, 2 ) . ":" . substr( $mac, 10, 2 ); # Save MAC address in 'mac' table xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac ); # Generate new MACID if ( $generateNew == 1 ) { $out = xCAT::zvmUtils->generateMacId($hcp); } } } else { # Create NOLOG virtual server $out = `ssh $hcp "$::DIR/createvs $userId"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } return; } #------------------------------------------------------- =head3 cloneVM Description : Clone a virtual server Arguments : Node Disk pool Disk password Returns : Nothing Example : cloneVM($callback, $targetNode, $args); =cut #------------------------------------------------------- sub cloneVM { # Get inputs my ( $callback, $nodes, $args ) = @_; # Get nodes my @nodes = @$nodes; # Return code for each command my $rc; my $out; # Child process IDs my @children; # Process ID for xfork() my $pid; # Get source node my $sourceNode = $args->[0]; my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); # Get HCP my $srcHcp = $propVals->{'hcp'}; # Get node userID my $sourceId = $propVals->{'userid'}; foreach (@nodes) { xCAT::zvmUtils->printLn( $callback, "$_: Cloning $sourceNode" ); # Exit if missing source node if ( !$sourceNode ) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node" ); return; } # Exit if missing source HCP if ( !$srcHcp ) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node HCP" ); return; } # Exit if missing source user ID if ( !$sourceId ) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing source node ID" ); return; } # Get target node @propNames = ( 'hcp', 'userid' ); $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $_, @propNames ); # Get target HCP my $tgtHcp = $propVals->{'hcp'}; # Get node userID my $tgtId = $propVals->{'userid'}; # Exit if missing target HCP if ( !$tgtHcp ) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing target node HCP" ); return; } # Exit if missing target user ID if ( !$tgtId ) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Missing target node ID" ); return; } # Exit if source and target HCP are not equal if ($srcHcp ne $tgtHcp) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Source and target HCP are not equal" ); return; } # --- Get MAC address --- my $targetMac; my $macId; my $generateNew = 0; # Flag to generate new MACID @propNames = ('mac'); $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $_, @propNames ); if (!$propVals) { # If no MACID is found, get one $macId = xCAT::zvmUtils->getMacID($tgtHcp); if ( !$macId ) { xCAT::zvmUtils->printLn( $callback, "$_: (Error) Could not generate MACID" ); return; } # Create MAC address (target) $targetMac = xCAT::zvmUtils->createMacAddr( $_, $macId ); # Save MAC address in 'mac' table xCAT::zvmUtils->setNodeProp( 'mac', $_, 'mac', $targetMac ); # Generate new MACID $out = xCAT::zvmUtils->generateMacId($tgtHcp); } } # --- Link source disks --- # Get MDisk statements of source node my @words; my $addr; my $srcMultiPw; my $linkAddr; # Hash table of source disk addresses # $srcLinkAddr[$addr] = $linkAddr my %srcLinkAddr; my @srcDisks = xCAT::zvmUtils->getMdisks( $callback, $sourceNode ); foreach (@srcDisks) { # Get disk address @words = split( ' ', $_ ); $addr = $words[1]; $srcMultiPw = $words[9]; # If source disk is not linked my $try = 10; while ( $try > 0 ) { # New disk address $linkAddr = $addr + 1000; # Check if new disk address is used (source) $rc = xCAT::zvmUtils->isAddressUsed( $srcHcp, $linkAddr ); # If disk address is used (source) while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $linkAddr = $linkAddr + 1; $rc = xCAT::zvmUtils->isAddressUsed( $srcHcp, $linkAddr ); } $srcLinkAddr { $addr } = $linkAddr; # Link source disk to HCP foreach (@nodes) { xCAT::zvmUtils->printLn( $callback, "$_: Linking source disk ($addr) as ($linkAddr)" ); } $out = `ssh -o ConnectTimeout=5 $srcHcp "vmcp link $sourceId $addr $linkAddr RR $srcMultiPw"`; if ( $out =~ m/not linked/i ) { # Do nothing } else { last; } $try = $try - 1; # Wait before next try sleep (5); } # End of while ( $try > 0 ) # If source disk is not linked if ( $out =~ m/not linked/i ) { foreach (@nodes) { xCAT::zvmUtils->printLn( $callback, "$_: Failed" ); } # Exit return; } # Enable source disk $out = xCAT::zvmUtils->disableEnableDisk( $callback, $srcHcp, "-e", $linkAddr ); } # End of foreach (@srcDisks) # Get user entry of source node my $srcUserEntry = "/tmp/$sourceNode.txt"; $out = `rm $srcUserEntry`; $out = xCAT::zvmUtils->getUserEntryWODisk( $callback, $sourceNode, $srcUserEntry ); # Check if user entry is valid $out = `cat $srcUserEntry`; # If output contains USER LINUX123, then user entry is good if ( $out =~ m/USER $sourceId/i ) { # --- Clone source node --- foreach (@nodes) { $pid = xCAT::Utils->xfork(); # Parent process if ($pid) { push( @children, $pid ); } # Child process elsif ( $pid == 0 ) { clone( $callback, $_, $args, \@srcDisks, \%srcLinkAddr ); # Exit process exit(0); } # End of elsif else { # Ran out of resources die "Error: Could not fork\n"; } } # End of foreach # Wait for all processes to end foreach (@children) { waitpid( $_, 0 ); } } # --- Detatch source disks --- for $addr ( keys %srcLinkAddr ) { $linkAddr = $srcLinkAddr { $addr }; # Disable source disk $out = xCAT::zvmUtils->disableEnableDisk( $callback, $srcHcp, "-d", $linkAddr ); $out = `ssh -o ConnectTimeout=5 $srcHcp "vmcp det $linkAddr"`; foreach (@nodes) { xCAT::zvmUtils->printLn( $callback, "$_: Detatching source disk ($addr) at ($linkAddr)" ); } } # --- Done --- foreach (@nodes) { xCAT::zvmUtils->printLn( $callback, "$_: Done" ); } return; } #------------------------------------------------------- =head3 clone Description : Clone a virtual server Arguments : Target node Disk pool Disk password (optional) Source disks Source disk link addresses Returns : Nothing Example : cloneVM($callback, $targetNode, $args); =cut #------------------------------------------------------- sub clone { # Get inputs my ( $callback, $tgtNode, $args, $srcDisksRef, $srcLinkAddrRef ) = @_; # Get source node properties from 'zvm' table my $sourceNode = $args->[0]; my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $sourceNode, @propNames ); # Get HCP my $srcHcp = $propVals->{'hcp'}; # Get node userID my $sourceId = $propVals->{'userid'}; # Get source disks my @srcDisks = @$srcDisksRef; my %srcLinkAddr = %$srcLinkAddrRef; # Return code for each command my $rc; # Get node properties from 'zvm' table @propNames = ( 'hcp', 'userid' ); $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $tgtNode, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node HCP" ); return; } # Get node userID my $targetUserId = $propVals->{'userid'}; if ( !$targetUserId ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing node ID" ); return; } # Exit if source node HCP is not the same as target node HCP if ( !( $srcHcp eq $hcp ) ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Source node HCP ($srcHcp) is not the same as target node HCP ($hcp)" ); return; } # Get target IP from /etc/hosts my $targetIp = xCAT::zvmUtils->getIp($tgtNode); if ( !$targetIp ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing IP for $tgtNode in /etc/hosts" ); return; } my $out; my @lines; my @words; # Get disk pool and multi password my $i; my %inputs; foreach $i ( 1 .. 2 ) { if ( $args->[$i] ) { # Split parameters by '=' @words = split( "=", $args->[$i] ); # Create hash array $inputs{ $words[0] } = $words[1]; } } # Get disk pool my $pool = $inputs{"pool"}; if ( !$pool ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing pool ID" ); return; } # Get multi password # It is Ok not have a password my $tgtPw = $inputs{"pw"}; # Get the network name the HCP is on $out = `ssh $hcp "vmcp q v nic" | egrep -i "VSWITCH|LAN"`; @lines = split( '\n', $out ); my $line = xCAT::zvmUtils->trimStr($lines[0]); @words = split( ' ', $line ); my $netName = $words[4]; # Get the NICDEF address of the network on the source node xCAT::zvmCPUtils->loadVmcp($sourceNode); $out = `ssh $sourceNode "vmcp q v nic"`; @lines = split( '\n', $out ); my $i; my $netAddr; my @tmp; for ( $i = 0 ; $i < @lines ; $i++ ) { if ( $lines[$i] =~ m/$netName/i ) { $line = xCAT::zvmUtils->trimStr( $lines[$i - 1] ); @words = split( ' ', $line ); @tmp = split( /\./, $words[1] ); $netAddr = $tmp[0]; last; } } # Exit if network address is not found if (!$netAddr) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Node is not on the same network ($netName) as the hardware control point" ); return; } $netAddr = "0.0." . $netAddr; # Set IP address my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); # Save user directory entry as /tmp/hostname.txt # The source user entry is retrieved in cloneVM() my $userEntry = "/tmp/$tgtNode.txt"; my $srcUserEntry = "/tmp/$sourceNode.txt"; # Remove existing user entry if any $out = `rm $userEntry`; $out = `ssh -o ConnectTimeout=5 $hcp "rm $userEntry"`; # Copy user entry of source node $out = `cp $srcUserEntry $userEntry`; # Replace source userID with target userID $out = `sed --in-place -e "s,$sourceId,$targetUserId,g" $userEntry`; # Get target MAC address in 'mac' table my $targetMac; my $macId; my $generateNew = 0; # Flag to generate new MACID @propNames = ('mac'); $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $tgtNode, @propNames ); if ($propVals) { # Get MACID $targetMac = $propVals->{'mac'}; $macId = $propVals->{'mac'}; $macId = xCAT::zvmUtils->replaceStr( $macId, ":", "" ); $macId = substr( $macId, 6 ); } else { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing target MAC address" ); return; } # Get source MAC address in 'mac' table my $srcMacId; @propNames = ('mac'); $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $sourceNode, @propNames ); if ($propVals) { # Get MACID $srcMacId = $propVals->{'mac'}; $srcMacId = xCAT::zvmUtils->replaceStr( $srcMacId, ":", "" ); $srcMacId = substr( $srcMacId, 6 ); } else { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Missing source MAC address" ); return; } # Append MACID at the end of the NICDEF statement in user entry text file $out = `sed --in-place -e "s,$srcMacId,$macId,g" $userEntry`; # SCP user entry file over to HCP xCAT::zvmUtils->sendFile( $hcp, $userEntry, $userEntry ); # --- Create new virtual server --- my $try = 10; while ($try > 0) { if ($try > 9) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Creating user directory entry" ); } else { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to create user directory entry" ); } $out = `ssh $hcp "$::DIR/createvs $targetUserId $userEntry"`; # Check if user entry is created $out = `ssh $hcp "$::DIR/getuserentry $targetUserId"`; $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ($rc == -1) { # Wait before trying again sleep(5); $try = $try - 1; } else { last; } } # Exit on bad output if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry" ); return; } # Load VMCP module on HCP and source node xCAT::zvmCPUtils->loadVmcp($hcp); xCAT::zvmCPUtils->loadVmcp($sourceNode); # Get VSwitch of master node my @vswitchId = xCAT::zvmCPUtils->getVswitchId($sourceNode); # Grant access to VSwitch for Linux user # GuestLan do not need permissions foreach (@vswitchId) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Granting VSwitch ($_) access for $targetUserId" ); $out = xCAT::zvmCPUtils->grantVSwitch( $callback, $hcp, $targetUserId, $_ ); # Check for errors $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { # Exit on bad output xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } } # End of foreach (@vswitchId) # --- Add MDisk to target user entry --- my $addr; my @tgtDisks; my $type; my $mode; my $cyl; my $srcMultiPw; foreach (@srcDisks) { # Get disk address @words = split( ' ', $_ ); $addr = $words[1]; push( @tgtDisks, $addr ); $type = $words[2]; $mode = $words[6]; $srcMultiPw = $words[9]; # Add ECKD disk if ( $type eq '3390' ) { # Get disk size (cylinders) $out = `ssh -o ConnectTimeout=5 $sourceNode "vmcp q v dasd" | grep "DASD $addr"`; @words = split( ' ', $out ); $cyl = xCAT::zvmUtils->trimStr( $words[5] ); $try = 10; while ( $try > 0 ) { # Add ECKD disk if ($try > 9) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Adding minidisk ($addr)" ); } else { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Trying again ($try) to add minidisk ($addr)" ); } $out = `ssh $hcp "$::DIR/add3390 $targetUserId $pool $addr $cyl $mode $tgtPw $tgtPw $tgtPw"`; # Check output $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { # Wait before trying again sleep(5); # One less try $try = $try - 1; } else { # If output is good, exit loop last; } } # End of while ( $try > 0 ) # Exit on bad output if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Could not create user entry" ); return; } } # End of if ( $type eq '3390' ) # Add FBA disk elsif ( $type eq '9336' ) { # -- To be supported -- # Get disk size (blocks) # Add disk } } # Check if the number of disks in target user entry # is equal to the number of disks added my @disks; $try = 10; while ( $try > 0 ) { # Get disks within user entry $out = `ssh $hcp "$::DIR/getuserentry $targetUserId" | grep "MDISK"`; @disks = split( '\n', $out ); xCAT::zvmUtils->printLn( $callback, "$tgtNode: Disks added (" . @tgtDisks . "). Disks in user entry (" . @disks . ")" ); if (@disks != @tgtDisks) { $try = $try - 1; # Wait before trying again sleep(5); } else { last; } } # Exit if all disks are not present if (@disks != @tgtDisks) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: (Error) Disks not present in user entry"); return; } # --- Link, format, and copy source disks --- my $srcAddr; my $tgtAddr; my $srcDevNode; my $tgtDevNode; foreach (@tgtDisks) { # --- Link target disk --- $try = 10; while ( $try > 0 ) { # New disk address $srcAddr = $srcLinkAddr { $_ }; $tgtAddr = $_ + 2000; # Check if new disk address is used (target) $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtAddr ); # If disk address is used (target) while ( $rc == 0 ) { # Generate a new disk address # Sleep 5 seconds to let existing disk appear sleep(5); $tgtAddr = $tgtAddr + 1; $rc = xCAT::zvmUtils->isAddressUsed( $hcp, $tgtAddr ); } # Link target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Linking target disk ($_) as ($tgtAddr)" ); $out = `ssh -o ConnectTimeout=5 $hcp "vmcp link $targetUserId $_ $tgtAddr MR $tgtPw"`; # If link fails if ( $out =~ m/not linked/i ) { # Wait before trying again sleep(5); $try = $try - 1; } else { last; } } # End of while ( $try > 0 ) # If target disk is not linked if ( $out =~ m/not linked/i ) { xCAT::zvmUtils->printLn( $callback, "$$tgtNode: (Error) Failed to link target disk ($_)" ); xCAT::zvmUtils->printLn( $callback, "$tgtNode: Failed" ); # Exit return; } # --- Use FLASHCOPY --- xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr) using FLASHCOPY" ); $out = xCAT::zvmCPUtils->flashCopy( $hcp, $srcAddr, $tgtAddr ); $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); # --- Use Linux DD --- if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: FLASHCOPY not supported. Using Linux DD" ); # Enable target disk $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtAddr ); # Determine source device node $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$srcAddr("`; @words = split( ' ', $out ); $srcDevNode = $words[6]; # Determine target device node $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtAddr("`; @words = split( ' ', $out ); $tgtDevNode = $words[6]; # Format target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Formating target disk ($tgtAddr)" ); $out = `ssh $hcp "dasdfmt -b 4096 -y -f /dev/$tgtDevNode"`; # Check for errors $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } # Sleep 2 seconds to let the system settle sleep(2); # Copy source disk to target disk xCAT::zvmUtils->printLn( $callback, "$tgtNode: Copying source disk ($srcAddr) to target disk ($tgtAddr)" ); $out = `ssh $hcp "dd if=/dev/$srcDevNode of=/dev/$tgtDevNode bs=4096"`; # Check for error $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } # Sleep 2 seconds to let the system settle sleep(2); } # Disable and enable target disk $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", $tgtAddr ); # Determine target device node (it might have changed) $out = `ssh $hcp "cat /proc/dasd/devices" | grep ".$tgtAddr("`; @words = split( ' ', $out ); $tgtDevNode = $words[6]; # Get disk address that is the root partition (/) my $rootPartitionAddr = xCAT::zvmUtils->getRootDiskAddr($sourceNode); if ( $_ eq $rootPartitionAddr ) { # --- Set network configuration --- xCAT::zvmUtils->printLn( $callback, "$tgtNode: Setting network configuration" ); # Mount target disk my $cloneMntPt = "/mnt/$targetUserId"; $tgtDevNode .= "1"; $out = `ssh $hcp "mkdir $cloneMntPt"`; $out = `ssh $hcp "mount /dev/$tgtDevNode $cloneMntPt"`; # Set hostname $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" $cloneMntPt/etc/HOSTNAME`; # If Red Hat -- Set hostname in /etc/sysconfig/network my $os = xCAT::zvmUtils->getOs($sourceNode); if ( $os =~ m/Red Hat/i ) { $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" $cloneMntPt/etc/sysconfig/network`; } # Set IP address my $sourceIp = xCAT::zvmUtils->getIp($sourceNode); # Get network configuration file # Location of this file depends on the OS my $ifcfg = xCAT::zvmUtils->getIfcfgByNic( $sourceNode, $netAddr ); my $ifcfgPath = $cloneMntPt; $ifcfgPath .= $ifcfg; $out = `ssh $hcp sed --in-place -e "s/$sourceNode/$tgtNode/g" \ -e "s/$sourceIp/$targetIp/g" $cloneMntPt/etc/hosts`; $out = `ssh $hcp sed --in-place -e "s/$sourceIp/$targetIp/g" \ -e "s/$sourceNode/$tgtNode/g" $ifcfgPath`; # Set MAC address my $networkFile = $tgtNode . "_network_config"; if ( $os =~ m/Red Hat/i ) { # Red Hat only $out = `ssh $hcp "cat $ifcfgPath" | grep -v "MACADDR" > /tmp/$networkFile`; $out = `echo "MACADDR='$targetMac'" >> /tmp/$networkFile`; } else { # SuSE only $out = `ssh $hcp "cat $ifcfgPath" | grep -v "LLADDR" | grep -v "UNIQUE" > /tmp/$networkFile`; $out = `echo "LLADDR='$targetMac'" >> /tmp/$networkFile`; $out = `echo "UNIQUE=''" >> /tmp/$networkFile`; } xCAT::zvmUtils->sendFile( $hcp, "/tmp/$networkFile", $ifcfgPath ); # Set to hardware configuration -- Only for layer 2 my $layer = xCAT::zvmCPUtils->getNetworkLayer($sourceNode); if ( $layer == 2 ) { # --- Red Hat --- if ( $os =~ m/Red Hat/i ) { my $srcMac; # Get source MAC address in 'mac' table @propNames = ('mac'); $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $sourceNode, @propNames ); if ($propVals) { # Get MAC address $srcMac = $propVals->{'mac'}; } else { xCAT::zvmUtils->printLn( $callback, "$tgtNode: Could not find MAC address of $sourceNode" ); # Unmount disk $out = `ssh $hcp "umount $cloneMntPt"`; # Disable disks $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); # Detatch disks from HCP $out = `ssh $hcp "vmcp det $tgtAddr"`; return; } # Set MAC address $out = `ssh $hcp sed --in-place -e "s/$srcMac/$targetMac/g" $ifcfgPath`; } # --- SuSE --- else { # Get hardware configuration my $hwcfg = xCAT::zvmUtils->getHwcfg($sourceNode); my $hwcfgPath = $cloneMntPt; # Set layer 2 support $hwcfgPath .= $hwcfg; my $hardwareFile = $tgtNode . "_hardware_config"; $out = `ssh $hcp "cat $hwcfgPath" | grep -v "QETH_LAYER2_SUPPORT" > /tmp/$hardwareFile`; $out = `echo "QETH_LAYER2_SUPPORT='1'" >> /tmp/$hardwareFile`; xCAT::zvmUtils->sendFile( $hcp, "/tmp/$hardwareFile", $hwcfgPath ); } } # End of if ( $layer == 2 ) # Flush disk $out = `ssh $hcp "sync"`; # Unmount disk $out = `ssh $hcp "umount $cloneMntPt"`; } # Disable disks $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-d", $tgtAddr ); # Detatch disks from HCP $out = `ssh $hcp "vmcp det $tgtAddr"`; sleep(5); } # End of foreach (@tgtDisks) # Add node to DHCP $out = `makedhcp -a`; # Power on target virtual server xCAT::zvmUtils->printLn( $callback, "$tgtNode: Powering on" ); $out = `ssh $hcp "$::DIR/startvs $targetUserId"`; # Check for error $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$tgtNode: $out" ); return; } } #------------------------------------------------------- =head3 nodeSet Description : Set the boot state for a node - Punch initrd, kernel, and parmfile to node reader - Layer 2 and 3 VSwitch/Lan supported Arguments : Node Returns : Nothing Example : nodeSet($callback, $node, $args); =cut #------------------------------------------------------- sub nodeSet { # Get inputs my ( $callback, $node, $args ) = @_; # Get action my $action = $args->[0]; if ( !( $action eq "install" ) ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Option not supported" ); return; } # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Get node root password @propNames = ('password'); $propVals = xCAT::zvmUtils->getTabPropsByKey( 'passwd', 'key', 'system', @propNames ); my $passwd = $propVals->{'password'}; if ( !$passwd ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing root password for this node" ); return; } # Get Linux distribution @propNames = ('current_osimage'); $propVals = xCAT::zvmUtils->getNodeProps( 'noderes', $node, @propNames ); my $distr = $propVals->{'current_osimage'}; if ( !$distr ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system to be deployed on this node" ); return; } # Get autoyast/kickstart template @propNames = ('profile'); $propVals = xCAT::zvmUtils->getTabPropsByKey( 'osimage', 'imagename', $distr, @propNames ); my $profile = $propVals->{'profile'}; if ( !$distr ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing profile for this node" ); return; } # Get host IP and hostname from /etc/hosts my $out = `cat /etc/hosts | grep $node`; my @words = split( ' ', $out ); my $hostIP = $words[0]; my $hostname = $words[2]; if ( !$hostIP || !$hostname ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); return; } # Get NIC address from user entry $out = `ssh $hcp "$::DIR/getuserentry $userId" | grep "NICDEF"`; if ( !$out ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing NICDEF statement in user entry of node" ); return; } # Grab first NICDEF address my @lines = split( '\n', $out ); @words = split( ' ', $lines[0] ); my $readChannel = "0.0.0" . ( $words[1] + 0 ); my $writeChannel = "0.0.0" . ( $words[1] + 1 ); my $dataChannel = "0.0.0" . ( $words[1] + 2 ); # Get network type (Layer 2 or 3) my $netName = $words[6]; $out = `ssh $hcp "vmcp q lan $netName"`; if ( !$out ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing NICDEF statement in user entry of node" ); return; } # Go through each line my $layer = 3; # Default to layer 3 @lines = split( '\n', $out ); foreach (@lines) { # If the line contains ETHERNET, then it is a layer 2 network if ( $_ =~ m/ETHERNET/i ) { $layer = 2; } } # Get MAC address -- Only for layer 2 my $mac = ""; my @propNames; my $propVals; if ( $layer == 2 ) { # Search 'mac' table for node @propNames = ('mac'); $propVals = xCAT::zvmUtils->getTabPropsByKey( 'mac', 'node', $node, @propNames ); $mac = $propVals->{'mac'}; # If no MAC address is found, exit # MAC address should have been assigned to the node upon creation if ( !$mac ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing MAC address of node" ); return; } } # Get domain from site table my $siteTab = xCAT::Table->new('site'); my $domainHash = $siteTab->getAttribs( { key => "domain" }, 'value' ); my $domain = $domainHash->{'value'}; # Get first 3 octets of node IP (IPv4) @words = split( /\./, $hostIP ); my $octets = "$words[0].$words[1].$words[2]"; # Get networks in 'networks' table my $entries = xCAT::zvmUtils->getAllTabEntries('networks'); # Go through each network my $network; foreach (@$entries) { # Get network $network = $_->{'net'}; # If networks contains the first 3 octets of the node IP if ( $network =~ m/$octets/i ) { # Exit loop last; } else { $network = ""; } } # If no network found if ( !$network ) { # Exit xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any networks in the networks table" ); return; } @propNames = ( 'mask', 'gateway', 'tftpserver', 'nameservers' ); $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); my $mask = $propVals->{'mask'}; my $gateway = $propVals->{'gateway'}; my $ftp = $propVals->{'tftpserver'}; my $nameserver = $propVals->{'nameservers'}; if ( !$network || !$mask || !$ftp || !$nameserver ) { # It is acceptable to not have a gateway xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing network information" ); return; } # Get broadcast address of NIC my $ifcfg = xCAT::zvmUtils->getIfcfgByNic( $hcp, $readChannel ); $out = `cat $ifcfg | grep "BROADCAST"`; @words = split( '=', $out ); my $broadcast = $words[1]; $broadcast = xCAT::zvmUtils->trimStr($broadcast); $broadcast = xCAT::zvmUtils->replaceStr( $broadcast, "'", "" ); # Load VMCP module on HCP xCAT::zvmCPUtils->loadVmcp($hcp); # Sample paramter file exists in installation CD -- Use that as a guide my $sampleParm; my $parmHeader; my $parms; my $parmFile; my $kernelFile; my $initFile; # If punch is successful -- Look for this string my $searchStr = "created and transferred"; # Default parameters -- SLES my $instNetDev = "osa"; # Only OSA interface type is supported my $osaInterface = "qdio"; # OSA interface = qdio or lcs my $osaMedium = "eth"; # OSA medium = eth (ethernet) or tr (token ring) # Default parameters -- RHEL my $netType = "qeth"; my $portName = "FOOBAR"; my $portNo = "0"; # SUSE installation my $template; if ( $distr =~ m/sles/i ) { # Create directory in FTP root (/install) to hold template $out = `mkdir -p /install/custom/install/sles`; # Copy autoyast template $template = "/install/custom/install/sles/" . $node . ".sles10.s390x.tmpl"; $out = `cp /opt/xcat/share/xcat/install/sles/$profile $template`; # Edit template my $device = "qeth-bus-ccw-$readChannel"; my $chanIds = "$readChannel $writeChannel $dataChannel"; $out = `sed --in-place -e "s,replace_host_address,$hostIP,g" \ -e "s,replace_long_name,$hostname,g" \ -e "s,replace_short_name,$node,g" \ -e "s,replace_domain,$domain,g" \ -e "s,replace_hostname,$node,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_broadcast,$broadcast,g" \ -e "s,replace_device,$device,g" \ -e "s,replace_ipaddr,$hostIP,g" \ -e "s,replace_lladdr,$mac,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_network,$network,g" \ -e "s,replace_ccw_chan_ids,$chanIds,g" \ -e "s,replace_ccw_chan_mode,FOOBAR,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_root_password,$passwd,g" $template`; # Read sample parmfile in /install/sles10.2/s390x/1/boot/s390x/ $sampleParm = "/install/$distr/s390x/1/boot/s390x/parmfile"; open( SAMPLEPARM, "<$sampleParm" ); # Search parmfile for -- ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb while () { # If the line contains 'ramdisk_size' if ( $_ =~ m/ramdisk_size/i ) { $parmHeader = xCAT::zvmUtils->trimStr($_); } } # Close sample parmfile close(SAMPLEPARM); # Create parmfile -- Limited to 10 lines # End result should be: # ramdisk_size=65536 root=/dev/ram1 ro init=/linuxrc TERM=dumb # HostIP=10.0.0.5 Hostname=gpok5.endicott.ibm.com # Gateway=10.0.0.1 Netmask=255.255.255.0 # Broadcast=10.0.0.0 Layer2=1 OSAHWaddr=02:00:01:FF:FF:FF # ReadChannel=0.0.0800 WriteChannel=0.0.0801 DataChannel=0.0.0802 # Nameserver=9.0.2.11 Portname=OSAPORT Portno=0 # Install=ftp://10.0.0.1/sles10.2/s390x/1/ # UseVNC=1 VNCPassword=123456 # InstNetDev=osa OsaInterface=qdio OsaMedium=eth Manual=0 my $ay = "ftp://$ftp/custom/install/sles/" . $node . ".sles10.s390x.tmpl"; $parms = $parmHeader . "\n"; $parms = $parms . "AutoYaST=$ay\n"; $parms = $parms . "HostIP=$hostIP Hostname=$hostname\n"; $parms = $parms . "Gateway=$gateway Netmask=$mask\n"; # Set layer in autoyast profile if ( $layer == 2 ) { $parms = $parms . "Broadcast=$network Layer2=1 OSAHWaddr=$mac\n"; } else { $parms = $parms . "Broadcast=$network Layer2=0\n"; } $parms = $parms . "ReadChannel=$readChannel WriteChannel=$writeChannel DataChannel=$dataChannel\n"; $parms = $parms . "Nameserver=$nameserver Portname=$portName Portno=0\n"; $parms = $parms . "Install=ftp://$ftp/$distr/s390x/1/\n"; $parms = $parms . "UseVNC=1 VNCPassword=123456\n"; $parms = $parms . "InstNetDev=$instNetDev OsaInterface=$osaInterface OsaMedium=$osaMedium Manual=0\n"; # Write to parmfile $parmFile = "/tmp/" . $node . "Parm"; open( PARMFILE, ">$parmFile" ); print PARMFILE "$parms"; close(PARMFILE); # Send kernel, parmfile, and initrd to reader to HCP $kernelFile = "/tmp/" . $node . "Kernel"; $initFile = "/tmp/" . $node . "Initrd"; $out = `cp /install/$distr/s390x/1/boot/s390x/vmrdr.ikr $kernelFile`; $out = `cp /install/$distr/s390x/1/boot/s390x/initrd $initFile`; xCAT::zvmUtils->sendFile( $hcp, $kernelFile, $kernelFile ); xCAT::zvmUtils->sendFile( $hcp, $parmFile, $parmFile ); xCAT::zvmUtils->sendFile( $hcp, $initFile, $initFile ); # Set the virtual unit record devices online on HCP $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "c" ); $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "d" ); # Purge reader $out = xCAT::zvmCPUtils->purgeReader( $hcp, $userId ); xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); # Punch kernel to reader on HCP $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, $kernelFile, "sles.kernel", "" ); xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" ); if ( $out =~ m/Failed/i ) { return; } # Punch parm to reader on HCP $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, $parmFile, "sles.parm", "-t" ); xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" ); if ( $out =~ m/Failed/i ) { return; } # Punch initrd to reader on HCP $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, $initFile, "sles.initrd", "" ); xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); if ( $out =~ m/Failed/i ) { return; } xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot." ); } # RHEL installation elsif ( $distr =~ m/rhel/i ) { # Create directory in FTP root (/install) to hold template $out = `mkdir -p /install/custom/install/rh`; # Copy kickstart template $template = "/install/custom/install/rh/" . $node . ".rhel5.s390x.tmpl"; $out = `cp /opt/xcat/share/xcat/install/rh/$profile $template`; # Edit template my $url = "ftp://$ftp/$distr/s390x/"; $out = `sed --in-place -e "s,replace_url,$url,g" \ -e "s,replace_ip,$hostIP,g" \ -e "s,replace_netmask,$mask,g" \ -e "s,replace_gateway,$gateway,g" \ -e "s,replace_nameserver,$nameserver,g" \ -e "s,replace_hostname,$hostname,g" \ -e "s,replace_rootpw,$passwd,g" $template`; # Read sample parmfile in /install/rhel5.3/s390x/images $sampleParm = "/install/$distr/s390x/images/generic.prm"; open( SAMPLEPARM, "<$sampleParm" ); # Search parmfile for -- root=/dev/ram0 ro ip=off ramdisk_size=40000 while () { # If the line contains 'ramdisk_size' if ( $_ =~ m/ramdisk_size/i ) { $parmHeader = xCAT::zvmUtils->trimStr($_); } } # Close sample parmfile close(SAMPLEPARM); # Get mdisk address my @mdisks = xCAT::zvmUtils->getMdisks( $callback, $node ); my $dasd = ""; my $i = 0; foreach (@mdisks) { $i = $i + 1; @words = split( ' ', $_ ); # Do not put a comma at the end of the last disk address if ( $i == @mdisks ) { $dasd = $dasd . "0.0.$words[1]"; } else { $dasd = $dasd . "0.0.$words[1],"; } } # Create parmfile -- Limited to 80 characters/line, maximum of 11 lines # End result should be: # ramdisk_size=40000 root=/dev/ram0 ro ip=off # ks=ftp://10.0.0.1/rhel5.3/s390x/compute.rhel5.s390x.tmpl # RUNKS=1 cmdline # DASD=0.0.0100 HOSTNAME=gpok4.endicott.ibm.com # NETTYPE=qeth IPADDR=10.0.0.4 # SUBCHANNELS=0.0.0800,0.0.0801,0.0.0800 # NETWORK=10.0.0.0 NETMASK=255.255.255.0 # SEARCHDNS=endicott.ibm.com BROADCAST=10.0.0.255 # GATEWAY=10.0.0.1 DNS=9.0.2.11 MTU=1500 # PORTNAME=UNASSIGNED PORTNO=0 LAYER2=0 # vnc vncpassword=123456 my $ks = "ftp://$ftp/custom/install/rh/" . $node . ".rhel5.s390x.tmpl"; $parms = $parmHeader . "\n"; $parms = $parms . "ks=$ks\n"; $parms = $parms . "RUNKS=1 cmdline\n"; $parms = $parms . "DASD=$dasd HOSTNAME=$hostname\n"; $parms = $parms . "NETTYPE=$netType IPADDR=$hostIP\n"; $parms = $parms . "SUBCHANNELS=$readChannel,$writeChannel,$dataChannel\n"; $parms = $parms . "NETWORK=$network NETMASK=$mask\n"; $parms = $parms . "SEARCHDNS=$domain BROADCAST=$broadcast\n"; $parms = $parms . "GATEWAY=$gateway DNS=$nameserver MTU=1500\n"; # Set layer in kickstart profile if ( $layer == 2 ) { $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=1 MACADDR=$mac\n"; } else { $parms = $parms . "PORTNAME=$portName PORTNO=$portNo LAYER2=0\n"; } $parms = $parms . "vnc vncpassword=123456\n"; # Write to parmfile $parmFile = "/tmp/" . $node . "Parm"; open( PARMFILE, ">$parmFile" ); print PARMFILE "$parms"; close(PARMFILE); # Send kernel, parmfile, conf, and initrd to reader to HCP $kernelFile = "/tmp/" . $node . "Kernel"; $initFile = "/tmp/" . $node . "Initrd"; $out = `cp /install/$distr/s390x/images/kernel.img $kernelFile`; $out = `cp /install/$distr/s390x/images/initrd.img $initFile`; xCAT::zvmUtils->sendFile( $hcp, $kernelFile, $kernelFile ); xCAT::zvmUtils->sendFile( $hcp, $parmFile, $parmFile ); xCAT::zvmUtils->sendFile( $hcp, $initFile, $initFile ); # Set the virtual unit record devices online $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "c" ); $out = xCAT::zvmUtils->disableEnableDisk( $callback, $hcp, "-e", "d" ); # Purge reader $out = xCAT::zvmCPUtils->purgeReader( $hcp, $userId ); xCAT::zvmUtils->printLn( $callback, "$node: Purging reader... Done" ); # Punch kernel to reader on HCP $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, $kernelFile, "rhel.kernel", "" ); xCAT::zvmUtils->printLn( $callback, "$node: Punching kernel to reader... $out" ); if ( $out =~ m/Failed/i ) { return; } # Punch parm to reader on HCP $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, $parmFile, "rhel.parm", "-t" ); xCAT::zvmUtils->printLn( $callback, "$node: Punching parm to reader... $out" ); if ( $out =~ m/Failed/i ) { return; } # Punch initrd to reader on HCP $out = xCAT::zvmCPUtils->punch2Reader( $hcp, $userId, $initFile, "rhel.initrd", "" ); xCAT::zvmUtils->printLn( $callback, "$node: Punching initrd to reader... $out" ); if ( $out =~ m/Failed/i ) { return; } xCAT::zvmUtils->printLn( $callback, "$node: Kernel, parm, and initrd punched to reader. Ready for boot." ); } return; } #------------------------------------------------------- =head3 getMacs Description : Get the MAC address of a given node - Requires the node be online - Saves MAC address in 'mac' table Arguments : Node Returns : Nothing Example : getMacs($callback, $node, $args); =cut #------------------------------------------------------- sub getMacs { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Get MAC address in 'mac' table my @propNames = ('mac'); my $propVals = xCAT::zvmUtils->getNodeProps( 'mac', $node, @propNames ); my $mac; if ($propVals) { # Get MAC address $mac = $propVals->{'mac'}; xCAT::zvmUtils->printLn( $callback, "$node: $mac" ); return; } # If MAC address is not in the 'mac' table, get it using VMCP xCAT::zvmCPUtils->loadVmcp($node); # Get xCat MN Lan/VSwitch name my $out = `vmcp q v nic | egrep -i "VSWITCH|LAN"`; my @lines = split( '\n', $out ); my @words; # Go through each line and extract VSwitch and Lan names # and create search string my $searchStr = ""; my $i; for ( $i = 0 ; $i < @lines ; $i++ ) { # Extract VSwitch name if ( $lines[$i] =~ m/VSWITCH/i ) { @words = split( ' ', $lines[$i] ); $searchStr = $searchStr . "$words[4]"; } # Extract Lan name elsif ( $lines[$i] =~ m/LAN/i ) { @words = split( ' ', $lines[$i] ); $searchStr = $searchStr . "$words[4]"; } if ( $i != ( @lines - 1 ) ) { $searchStr = $searchStr . "|"; } } # Get MAC address of node # This node should be on only 1 of the networks that the xCat MN is on $out = `ssh -o ConnectTimeout=5 $node "vmcp q v nic" | egrep -i "$searchStr"`; if ( !$out ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Failed to find MAC address" ); return; } @lines = split( '\n', $out ); @words = split( ' ', $lines[0] ); my $mac = $words[1]; # Replace - with : $mac = xCAT::zvmUtils->replaceStr( $mac, "-", ":" ); xCAT::zvmUtils->printLn( $callback, "$node: $mac" ); # Save MAC address and network interface into 'mac' table xCAT::zvmUtils->setNodeProp( 'mac', $node, 'mac', $mac ); return; } #------------------------------------------------------- =head3 netBoot Description : Boot from network Arguments : Node Address to IPL from Returns : Nothing Example : netBoot($callback, $node, $args); =cut #------------------------------------------------------- sub netBoot { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Get IPL my @ipl = split( '=', $args->[0] ); if ( !( $ipl[0] eq "ipl" ) ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IPL" ); return; } # Boot node my $out = `ssh $hcp "$::DIR/startvs $userId"`; my $rc = xCAT::zvmUtils->checkOutput( $callback, $out ); if ( $rc == -1 ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Boot failed" ); return; } else { xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } # IPL when virtual server is online sleep(5); $out = xCAT::zvmCPUtils->sendCPCmd( $hcp, $userId, "IPL $ipl[1]" ); xCAT::zvmUtils->printLn( $callback, "$node: Booting from $ipl[1]... Done" ); return; } #------------------------------------------------------- =head3 updateNode Description : Update node Arguments : Node Option Options supported: release [updated version] Returns : Nothing Example : updateNode($callback, $node, $args); =cut #------------------------------------------------------- sub updateNode { # Get inputs my ( $callback, $node, $args ) = @_; # Get node properties from 'zvm' table my @propNames = ( 'hcp', 'userid' ); my $propVals = xCAT::zvmUtils->getNodeProps( 'zvm', $node, @propNames ); # Get HCP my $hcp = $propVals->{'hcp'}; if ( !$hcp ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node HCP" ); return; } # Get node userID my $userId = $propVals->{'userid'}; if ( !$userId ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing node ID" ); return; } # Get host IP and hostname from /etc/hosts my $out = `cat /etc/hosts | grep $node`; my @words = split( ' ', $out ); my $hostIP = $words[0]; my $hostname = $words[2]; if ( !$hostIP || !$hostname ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing IP for $node in /etc/hosts" ); return; } # Get first 3 octets of node IP (IPv4) @words = split( /\./, $hostIP ); my $octets = "$words[0].$words[1].$words[2]"; # Get networks in 'networks' table my $entries = xCAT::zvmUtils->getAllTabEntries('networks'); # Go through each network my $network; foreach (@$entries) { # Get network $network = $_->{'net'}; # If networks contains the first 3 octets of the node IP if ( $network =~ m/$octets/i ) { # Exit loop last; } else { $network = ""; } } # If no network found if ( !$network ) { # Exit xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node does not belong to any networks in the networks table" ); return; } # Get FTP server @propNames = ('tftpserver'); $propVals = xCAT::zvmUtils->getTabPropsByKey( 'networks', 'net', $network, @propNames ); my $ftp = $propVals->{'tftpserver'}; if ( !$ftp ) { # It is acceptable to not have a gateway xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing FTP server" ); return; } # Update node operating system if ( $args->[0] eq "--release" ) { my $version = $args->[1]; if ( !$version ) { xCAT::zvmUtils->printLn( $callback, "$node: (Error) Missing operating system release" ); return; } # Get node operating system my $os = xCAT::zvmUtils->getOs($node); # Check node OS is the same as the version OS given # You do not want to update a SLES with a RHEL if ( (($os =~ m/SUSE/i) && !($version =~ m/sles/i)) || (($os =~ m/Red Hat/i) && !($version =~ m/rhel/i)) ){ xCAT::zvmUtils->printLn( $callback, "$node: (Error) Node operating system is different from the operating system given to upgrade to" ); return; } # Generate FTP path to operating system image my $path; if ($version =~ m/sles/i) { # SuSE Enterprise Linux path - ftp://10.0.0.1/sles10.3/s390x/1/ $path = "ftp://$ftp/$version/s390x/1/"; # Add installation source using rug $out = `ssh $node "rug sa -t zypp $path $version"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Subscribe to catalog $out = `ssh $node "rug sub $version"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Refresh services $out = `ssh $node "rug ref"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); # Update $out = `ssh $node "rug up -y"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } else { # Red Hat Enterprise Linux path - ftp://10.0.0.1/rhel5.4/s390x/Server/ $path = "ftp://$ftp/$version/s390x/Server/"; # Check if file.repo already has this repository location $out = `ssh $node "cat /etc/yum.repos.d/file.repo"`; if ($out =~ m/[$version]/i) { # Send over release key my $key = "/install/rhel5.4/s390x/RPM-GPG-KEY-redhat-release"; my $tmp = "/tmp/RPM-GPG-KEY-redhat-release"; xCAT::zvmUtils->sendFile($node, $key, $tmp); # Import key $out = `ssh $node "rpm --import /tmp/$key"`; # Upgrade $out = `ssh $node "yum upgrade -y"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } else { # Create repository $out = `ssh $node "echo [$version] >> /etc/yum.repos.d/file.repo"`; $out = `ssh $node "echo baseurl=$path >> /etc/yum.repos.d/file.repo"`; $out = `ssh $node "echo enabled=1 >> /etc/yum.repos.d/file.repo"`; # Send over release key my $key = "/install/rhel5.4/s390x/RPM-GPG-KEY-redhat-release"; my $tmp = "/tmp/RPM-GPG-KEY-redhat-release"; xCAT::zvmUtils->sendFile($node, $key, $tmp); # Import key $out = `ssh $node "rpm --import $tmp"`; # Upgrade $out = `ssh $node "yum upgrade -y"`; xCAT::zvmUtils->printLn( $callback, "$node: $out" ); } } } # Otherwise, print out error else { $out = "$node: (Error) Option not supported"; } xCAT::zvmUtils->printLn( $callback, "$out" ); return; }