diff --git a/perl-xCAT/xCAT/LparNetbootExp.pm b/perl-xCAT/xCAT/LparNetbootExp.pm new file mode 100644 index 000000000..268a6f84a --- /dev/null +++ b/perl-xCAT/xCAT/LparNetbootExp.pm @@ -0,0 +1,3086 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html +# + +package xCAT::LparNetbootExp; + +#----------------------------------------------------------------------------- + +=head1 LparNetbootExp +Usage: Install partition + lpar_netboot [-v] [-x] [-f] [-w set_boot_order] [-A -D | [-D] | [-D] -m macaddress] -t ent -s speed -d duplex + -S server -G gateway -C client hostname profile managed_system lparid remote_host + +Usage: Return macaddress + lpar_netboot -M -n [-v] -t ent [-f] [-x] [-D -s speed -d duplex -S server -G gateway -C client] hostname profile managed_system lparid remote_host + + -n Do not boot partition + -t Specifies network type ent + -D Perform ping test, use adapter that successfully ping the server + -s Network adapter speed + -d Network adapter duplex + -S Server IP address + -G Gateway IP address + -C Client IP address + -m MAC Address + -v Verbose output + -x Debug output + -f Force close virtual terminal session + -w Set boot device order + 0: Don't set boot device order + 1: Set network as boot device + 2: Set network as 1st boot device, disk as 2nd boot device + 3: Set disk as 1st boot device, network as 2nd boot device + 4: set disk as boot device + -M Discovery ethernet adapter mac address and location code + --help Prints this help + + +=cut + +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; + $::XCATDIR = $ENV{'XCATDIR'} ? $ENV{'XCATDIR'} : '/etc/xcat'; +} + + +use lib "$::XCATROOT/lib/perl"; +use xCAT::Utils; +use Getopt::Long; +use xCAT::MsgUtils; +use Expect; +use strict; +use Data::Dumper; +my $adapter_found = 0; +my @adap_type; +my @full_path_name_array; +my @phandle_array; +my $macaddress; +my $phys_loc; +my $client_ip; +my $gateway_ip; +my $device_type; +my $server_ip; + +# List supported network adapters here. dev_pat is an array of regexp patterns +# the script searches for in the device tree listing. dev_type is the type +# of device displayed in the output. +my @dev_pat = ( + "ethernet", + "token-ring", + "fddi" + ); +my @dev_type = ( + "ent", + "tok", + "fddi" + ); +my $dev_count = scalar(@dev_type); #number of supported device type + + +#------------------------------------------------------------------------------- + +=head3 nc_msg + + PROCEDURE + + Declare procedure to write status/error messages + We do it this way so that if /var is full (or some other error occurs) + we can trap it in a common section of code. + +=cut + +#----------------------------------------------------------------------------- + +sub nc_msg +{ + my $verbose = shift; + my $msg = shift; + my $rsp; + + if ( $verbose eq 1 ) { + $rsp->{data}->[0] = $msg; + xCAT::MsgUtils->message("I", $rsp ,$::CALLBACK); + } + + if ( $verbose eq 2 ) { + $rsp->{data}->[0] = $msg; + xCAT::MsgUtils->message("E", $rsp ,$::CALLBACK); + } +} +#------------------------------------------------------------------------------- + +=head3 run_lssyscfg + + Procedure to run the rpower command + test its return code and capture its output + +=cut + +#----------------------------------------------------------------------------- + +sub run_lssyscfg +{ + my $req = shift; + if (($req) && ($req =~ /xCAT::/)) + { + $req = shift; + } + my $verbose = shift; + my $node = shift; + my $cmd; + my $out; + + $out = xCAT::Utils->runxcmd( + { + command => ['rpower'], + node => [$node], + arg => ['state'] + }, + $req, 0, 1); + if ($::RUNCMD_RC != 0) { + nc_msg(2, "Unable to run rpower $node state.\n"); + return undef; + } + my $output = join ',', @$out; + nc_msg($verbose, "Status: run_lssyscg : partition status : $output\n"); + + nc_msg($verbose, "####msg:$output#########\n" ); + + + ################################################################### + # Slow down the requests speed to hcp, so that hcp will not busy on + # query. Instead, hcp should put more time on other activities. + # Another reason to set this sleep is giving some time to the lpars + # to be more stable. + ################################################################### + sleep 4; + + return $output; +} + +#------------------------------------------------------------------------------- + +=head3 usage + + + PROCEDURE + + Declare procedure to write usage message and exit + + +=cut + +#----------------------------------------------------------------------------- + +sub usage { + + my $msg = "Usage: Install partition \ + \n\t \[-v\] \[-x\] \[-f\] \[-w set_boot_order\] \[-A -D | \[-D\] | \[-D\] -m macaddress\] -t ent -s speed -d duplex \ + \n\t\t-S server -G gateway -C client hostname profile managed_system lparid remote_host\ + \n \ + \nUsage: Return macaddress \ + \n\t -M -n \[-v\] -t ent \[-f] \[-x] \[-D -s speed -d duplex -S server -G gateway -C client\] hostname profile managed_system lparid remote_host\ + \n \ + \n\t-n\tDo not boot partition \ + \n\t-t\tSpecifies network type ent \ + \n\t-D\tPerform ping test, use adapter that successfully ping the server \ + \n\t-s\tNetwork adapter speed \ + \n\t-d\tNetwork adapter duplex \ + \n\t-S\tServer IP address \ + \n\t-G\tGateway IP address \ + \n\t-C\tClient IP address \ + \n\t-m\tMAC Address \ + \n\t-v\tVerbose output \ + \n\t-x\tDebug output \ + \n\t-f\tForce close virtual terminal session \ + \n\t-w\tSet boot device order \ + \n\t\t\t0: Don't set boot device order \ + \n\t\t\t1: Set network as boot device \ + \n\t\t\t2: Set network as 1st boot device, disk as 2nd boot device \ + \n\t\t\t3: Set disk as 1st boot device, network as 2nd boot device \ + \n\t\t\t4: set disk as boot device \ + \n\t-M\tDiscovery ethernet adapter mac address and location code \ + \n\t--help\tPrints this help\n"; + nc_msg(1, $msg); + + return 1; +} +#------------------------------------------------------------------------------- + +=head3 ck_args + + PROCEDURE + + Check command line arguments + + +=cut + +#----------------------------------------------------------------------------- +sub ck_args { + + my $opt = shift; + if (($opt) && ($opt =~ /xCAT::/)) + { + $opt = shift; + } + my $verbose = shift; + my $node = $opt->{node}; + my $mtms = $opt->{fsp}; + my $hcp = $opt->{hcp}; + my $lparid = $opt->{id}; + my $profile = $opt->{pprofile}; + + if (exists( $opt->{D}) and (!exists ($opt->{s}) or !exists ($opt->{d} ))) { + nc_msg(2, "Speed and duplex required\n"); + usage; + return 1; + } + + if (exists ($opt->{D}) and !exists ($opt->{C})) { + nc_msg(2, "Client IP is required\n"); + usage; + return 1; + } + if (exists( $opt->{D}) and !exists($opt->{S})) { + nc_msg(2, "Server IP is required\n"); + usage; + return 1; + } + + if (exists( $opt->{D}) and !exists($opt->{G})) { + nc_msg(2, "Gateway IP is required\n"); + usage; + return 1; + } + + unless($node) { + nc_msg(2, "Node is required\n"); + usage; + return 1; + } else { + nc_msg($verbose, "Node is $node\n"); + } + + unless($mtms) { + nc_msg(2, "Managed system is required\n"); + usage; + return 1; + } else { + nc_msg($verbose, "Managed system is $mtms.\n"); + } + + unless ($hcp) { + nc_msg(2, "Hardware control point address is required\n"); + usage; + return 1; + } else { + nc_msg($verbose, "Hardware control point address is $hcp.\n"); + } + + unless ($lparid) { + nc_msg(2, "Lpar Id is required.\n"); + usage; + return 1; + } else { + nc_msg($verbose, "LPAR Id is $lparid.\n"); + } + + unless ($profile) { + nc_msg(2, "Profile is required.\n"); + usage; + return 1; + } else { + nc_msg($verbose, "profile $profile.\n"); + } + + if ($opt->{M} and $opt->{g}) { + nc_msg(2, "Can not specify -M and -g flags together.\n"); + usage; + return 1; + } + if ($opt->{M} and ($opt->{m} or $opt->{l})) { + nc_msg(2, "Can not specify -M and -l or -m flags together.\n"); + usage; + return 1; + } + + if ($opt->{m} and $opt->{l}) { + nc_msg(2, "Can not specify -l and -m flags together.\n"); + usage; + return 1; + } + + if ($opt->{A} and ($opt->{m} or $opt->{l})) { + nc_msg(2, "Can not specify -A and -m or -l flags together.\n"); + usage; + return 1; + } + + if ($opt->{A} and !exists($opt->{D}) and !exists($opt->{n})) { + nc_msg(2, "Flag -A must be specify with flag -D for booting.\n"); + usage; + return 1; + } + + if ($opt->{M} and $opt->{D} and (!exists($opt->{S}) or !exists($opt->{G}) or !exists($opt->{C}) or !exists( $opt->{s}) or !exists($opt->{d}))) { + nc_msg(2, "Flag -M with -D require arguments for -C, -S, -G, -s and -d.\n"); + usage; + return 1; + } + + if ($opt->{M} and !exists($opt->{D}) and (!exists($opt->{S}) or !exists($opt->{G}) or !exists($opt->{C}) or !exists($opt->{s}) or !exists($opt->{d}))){ + nc_msg(2, "Flag -M with arguments for -C, -S, -G, -s and -d require -D flag.\n"); + usage; + return 1; + } + + if ($opt->{M} and !exists($opt->{n})) { + nc_msg(2, "-M flag requires -n.\n"); + usage; + return 1; + } + + if ($node =~ /(\[ ]+)-/) { + nc_msg(2, "Error : $node, node is required\n"); + return 1; + } + + if ($mtms =~ /(\[ ]+)-/) { + nc_msg(2, "Error : $mtms, Managed system is required\n"); + return 1; + } + + if ($profile =~ /(\[ ]+)-/) { + nc_msg(2, "Error : $profile, profile is required\n"); + return 1; + } +} + + + + +#------------------------------------------------------------------------------- + +=head3 send_command + + + PROCEDURE + + Declare procedure to send commands slowly. This is needed because + some bytes are missed by the service processor when sent at top speed. + The sleep was needed because a command was sent sometimes before the + results of the previous command had been received. + + The Open Firmware is constrained on how quickly it can process input streams. + The following code causes expect to send 10 characters and then wait 1 second + before sending another 10 bytes. + + +=cut + +#----------------------------------------------------------------------------- + +sub send_command { + my $verbose = shift; + my $rconsole = shift; + my $cmd = shift; + nc_msg($verbose, "sending commands $cmd to expect \n"); + my $msg; + + $msg = $rconsole->send($cmd); + sleep 1; + return $msg; + +} + + +#------------------------------------------------------------------------------- + +=head3 get_phandle + + PROCEDURE + + Declare procedure to parse the full device tree that is displayed as a result + of an ls command. The information needed is the phandle and full device name + of a supported network card found in the device tree. The phandle is used in + other procedures to get and change properties of the network card. The full + device name is used to network boot from the network adapter. + +=cut + +#----------------------------------------------------------------------------- + +sub get_phandle { + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + my $timeout = 30; + my $done = 0; + my @result; + my $expect_out; + my $retry_count; + my %path; + + + # This is the first procedure entered after getting to the ok prompt. On entry + # the current device is not root. The command 'dev /' is sent to get to the + # root of the device tree. There is no output from the dev command. The expected + # output is the ok prompt ('>'). + # + # The pwd command can be used to determine what the current device is. + # + + send_command($verbose, $rconsole, "dev /\r"); + + @result = $rconsole->expect( + $timeout, + [qr/ok/=> + sub { + nc_msg($verbose, "Status: at root\n"); + $rconsole->clear_accum(); + } + ], + [qr/]/=> + sub { + nc_msg($verbose, "Unexpected prompt\n"); + return 1; + } + ], + [timeout => + sub { + $rconsole->send("\r"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node"); + return 1; + } + ], + ); + # Next, the 'ls' command is sent. The result is a display of the entire + # device tree. The code then looks at the + # output from the ls command one line at a time, trying to match it with the + # regexp pattern in dev_pat, an array that contains all the supported network + # adapters. When found, the adapter type, the phandle and path name are saved + # in array variables. + # + # The complicated part is that the full path name may be spread over more than + # one line. Each line contains information about a node. If the supported + # network adapter is found on an nth level node, the full path name is the + # concatenation of the node information from the 0th level to the nth level. + # Hence, the path name from each level of the device tree needs to be saved. + # + # The pattern "\n(\[^\r]*)\r" is worth a second look. It took + # many hours of debug and reading the expect book to get it right. When more + # than one line of data is returned to expect at once, it is tricky getting + # exactly one line of data to look at. This pattern works because it looks + # for a newline(\n), any character other than a carriage return(\[^\r]*), and + # then for a carriage return. This causes expect to match a single line. + # If (.*) is used instead of (\[^\r]*), multiple lines are matched. (that was + # attempt number 1) + # + # Once a single line is found, it tries to determine what level in the device + # tree this line is. + # searching through subsequent lines and subsequent levels until an + # adapter is found. + # The level of the current line, which + # is calculated based on the assumption of "Level = (Leading Spaces - 1)/2". + # Leading Spaces is the number of spaces between the first colon ':' and the + # first non-space character of each line. + # + # Using the -d flag helped a lot in finding the correct pattern. + # + + send_command($verbose, $rconsole, "ls \r"); + + $timeout = 60; + $done = 0; + while (!$done) { + # this expect call isolates single lines + # This code uses the tcl regexp to parse the single line + # isolated by expect. + # + # When the ok prompt ('>') is matched, this indicates the end of + # the ls output, at which point the done variable is set, to break + # out of the loop. + # + # All other lines are ignored. + # + @result = $rconsole->expect( + $timeout, + [qr/(\n)([^\r]*)(\r)/=> + sub { + nc_msg($verbose, "begin to parse network adapters \n"); + } + ], + [qr/>/=> + sub { + nc_msg($verbose, "finished \n"); + $done = 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout isolating single line of ls output\n"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node"); + return 1; + } + ], + ); + if ($result[2] =~ /(\w*)\:(\s*)\/(\S*)/) { + + my $x1 = $1; + my $x2 = $2; #number of space + my $x3 = $3; #device + # Each level is inspected for a match + my $level = (length($x2)-1)/2; + $path{$level} = $x3; + my $j = 0; + + for ($j = 0; $j < $dev_count; $j++) { + if ($x3 =~ /$dev_pat[$j]/) { + if ( $x3 =~ /hfi-ethernet/ and $dev_pat[$j] eq "ethernet" ){ + next; + } + my $i = 0; + for ($i = 0; $i <= $level; $i++) + { + $full_path_name_array[$adapter_found] .= "/" . $path{$i}; + } + $phandle_array[$adapter_found] = $x1; + $adap_type[$adapter_found] = $dev_type[$j]; + $adapter_found ++; + last; + } + } + } + } + # Did we find one or more adapters? + if ( $adapter_found > 0 ) { + return 0; + } else { + nc_msg(2, "No network adapters found\n" ); + return 1; + } + +} +#------------------------------------------------------------------------------- + +=head3 get_adap_prop + + PROCEDURE + + Declare procedure to obtain the list of valid adapter connector properties + from the adapter card. Connector types can be rj45, sc, 9pin, aui, + bnc, or mic. Speeds can be 10, 100, or 1000. Duplex can be half or + full. This procedure will use the "supported-network-types" + argument to the get-package-property command to get the list of + properties for the given adapter. + +=cut + +#----------------------------------------------------------------------------- +sub get_adap_prop { + my $phandle = shift; + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + my $timeout = 120; + my $rc = 0; + my $state = 0; + my @cmd; + my @done; + my @msg; + my @pattern; + my @newstate; + my @result; + my @adap_prop_array; + my $nw_type; + my $nw_speed; + my $nw_conn; + my $nw_duplex; + + nc_msg($verbose, " Status: get_adap_prop start\n"); + + # state 0, stack count 0 + $done[0] = 0; + $cmd[0] = "\" supported-network-types\" " . $phandle . " get-package-property\r"; + $msg[0] = "Status: rc and all supported network types now on stack\n"; + #$pattern[0] = "(.*)3 >(.*)"; + $pattern[0] = "3 >"; + $newstate[0] = 1; + + # state 1, return code and string on stack + $done[1] = 0; + $cmd[1] = ".\r"; + $msg[1] = "Status: All supported network types now on stack\n"; + #$pattern[1] = "(.*)2 >(.*)"; + $pattern[1] = "2 >"; + $newstate[1] = 2; + + # state 2, data ready to decode + $done[2] = 0; + $cmd[2] = "decode-string\r"; + $msg[2] = "Status: supported network type isolated on stack\n"; + #$pattern[2] = "(.*)ok(.*)4 >(.*)"; + $pattern[2] = "4 >"; + $newstate[2] = 3; + + # state 3, decoded string on stack + $done[3]= 0; + $cmd[3] = "dump\r"; + $msg[3] = "Status: supported network type off stack\n"; + #$pattern[3] = ".*:.*:(.*):.*:.*:(.*):.*(2 >)(.*)"; + $pattern[3] = "ok"; + $newstate[3] = 4; + + # state 4, need to check for more data to decode + $done[4] = 0; + $cmd[4] = ".s\r"; + $msg[4] = "Status: checking for more supported network types\n"; + #$pattern[4] = ".s (\[0-9a-f]* )(.*)>"; + $pattern[4] = "ok"; + $newstate[4]= 5; + + # state 5, done decoding string, clear stack + $done[5] = 0; + $cmd[5] = ".\r"; + $msg[5] = "Status: one entry on stack cleared\n"; + #$pattern[5] = "(.*)ok(.*)1 >(.*)"; + $pattern[5] = "ok"; + $newstate[5] = 6; + + # state 6, finish clearing stack, choose correct adapter type + $done[6]= 0; + $cmd[6] = ".\r"; + $msg[6] = "Status: finished clearing stack\n"; + #$pattern[6] = "(.*)ok(.*)0 >(.*)"; + $pattern[6] = "ok"; + $newstate[6]= 7; + + # state 7, done + $done[7] = 1; + + while($done[$state] eq 0) { + nc_msg($verbose, "Status: command is $cmd[$state]\n"); + send_command($verbose, $rconsole, $cmd[$state]); + @result = $rconsole->expect( + $timeout, + [ qr/$pattern[$state]/i, + sub { + nc_msg($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [ qr/]/, + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [ qr/(.*)DEFAULT(.*)/, + sub { + nc_msg(2, " Default catch error\n"); + return 1; + } + ], + [ timeout=> + sub { + nc_msg(2, "Timeout in getting adapter properpties\n"); + return 1; + } + ], + [ eof => + sub { + nc_msg(2, "Cannot connect to $node\n"); + return 1; + } + ] + ); + # After state 3, the network type is parsed and the connector + # type extracted. If the type hasn't been found, add it to + # the list of supported connector types. + if ( $state eq 4 ) { + # Build the adapter properties from the string + #regexp .*,(.*),(.*),(.*) $nw_type dummy nw_speed nw_conn nw_duplex + #set adap_prop "$nw_speed,$nw_conn,$nw_duplex" + #nc_msg "Status: Adapter properties are $adap_prop\n" + + # if it's not in the list, add it, otherwise continue + if ( $result[3] =~ /(\w*):(.*):(\w*)\,(\w*)\,(\w*):/) { + $nw_type = $3; + $nw_speed = $4; + $nw_conn = $5; + nc_msg($verbose, "nwtype is $3, nwspeed is $4, nwconn is $5\n"); + } + if ( $result[3] =~ /(\w*):(.*):(\w*)\,(\w*):/) { + $nw_duplex = $4; + nc_msg($verbose, "nwduplex is $4\n"); + } + } + + #push @adap_prop_array, $nw_type.",".$nw_speed.",".$nw_conn.",".$nw_duplex; + push @adap_prop_array, $nw_speed.",".$nw_conn.",".$nw_duplex; + nc_msg($verbose, "Status: Adding adapter properties to list\n"); + + # After state 4, a test is done to see if all of the supported + # network types have been decoded. If they have been, the + # state variable is left alone. if not, the state variable is + # set to 2, causing a loop back to the step where the + # decode-string command is sent. + if ( $state eq 5 ) { + if ($result[3] =~/2 > \.s \w+ (\w*)/) { + $state = 2 if ($1 ne 0); + } + } + } + if (scalar(@adap_prop_array) ne 0) { + return \@adap_prop_array; + } else { + return 0; + } +} + +#------------------------------------------------------------------------------- + +=head3 get_mac_addr + + + PROCEDURE + + Declare procedure to obtain the ethernet or mac address property from the + ethernet card. + + 3 commands lines containing a total of 6 commands are used. + + The get-package-property command is an example of a command + that takes it's arguments off the stack and puts the results back onto the + stack. Because of this, the arguments for the get-package-property command + are in front of the command verb. + + + The only reason this procedure is implemented in a loop is to avoid coding + 3 expect commands. + +=cut + +#----------------------------------------------------------------------------- +sub get_mac_addr { + my $phandle = shift; + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + my $timeout = 60; + my $state = 0; + my @result; + my $mac_rc; + my @cmd; + my @done; + my @msg; + my @pattern; + my @newstate; + my $mac_address; + + + nc_msg($verbose, "Status: get_mac_addr start\n"); + + # cmd(0) could have been sent as 3 commands. " mac-address" (tcl forces + # the use of \") is the first command on this line. The result of entering + # " mac-address" is that 2 stack entries are created, and address and a length. + # + # The next command in cmd(0) is the phandle with no quotes. This results in + # one stack entry because the phandle is an address. + # + # the third command in cmd(0) is get-package-property. After this command, there + # are 3 stack entries (return code, address and length of mac-address). + # state 0, stack count 0, send command + $done[0] = 0; + $cmd[0] = "\" local-mac-address\" ". $phandle . " get-package-property\r"; + $msg[0] = "Status: return code and mac-address now on stack\n"; + $pattern[0] = "ok";#"\s*3 >"; + $newstate[0] = 1; + + # cmd(1) is a dot (.). This is a stack manipulation command that removes one + # thing from the stack. pattern(1) is looking for a prompt with the 2 indicating + # that there are 2 things left on the stack. + # state 1, return code and mac-address on stack + $done[1]= 0; + $cmd[1] = ".\r"; + $msg[1] = "Status: mac-address now on stack\n"; + #$pattern[1] = "(.*)2 >(.*)"; + $pattern[1] = "ok"; #"2 >"; + $newstate[1]= 2; + + # cmd(2) is the dump command. This takes an address and a length off the stack + # and displays the contents of that storage in ascii and hex. The long pattern + # puts the hex into the variable expect_out(3,string). The tcl verb 'join' is + # used to eliminate the spaces put in by the dump command. + # state 2, mac-address on stack + $done[2] = 0; + $cmd[2] = ": dump-mac ( prop-addr prop-len -- ) \ + cr \ + dup decode-bytes 2swap 2drop ( data-addr data-len ) \ + ( data-len ) 0 ?do \ + dup c@ 2 u.r ( data-addr ) \ + char+ ( data-addr' ) \ + loop \ + drop \ + cr \ + ; \r"; + $msg[2] = "Status: set command\n"; + $pattern[2] = "ok"; + $newstate[2]= 3; + + $done[3]= 0; + $cmd[3] = "dump-mac\r"; + $msg[3] = "Status: mac-address displayed, stack empty\n"; + $pattern[3] = "dump-mac(\\s*)(\\w*)(\\s*)ok"; + $newstate[3] = 4 ; + + + # state 4, all done + $done[4] = 1; + + while($done[$state] eq 0) { + send_command($verbose, $rconsole, $cmd[$state]); + @result = $rconsole->expect( + $timeout, + [qr/$pattern[$state]/=> + sub { + nc_msg($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [qr/1 > /=> + sub { + if( $state eq 0 ) { + # An error occurred while obtaining the mac address. Log the error, + # but don't quit nodecond. instead, return NA for the address + # + send_command($verbose, $rconsole, ".\r"); + $rconsole->expect( + $timeout, + #[ qr/(-*\[0-9\]*) ok(.*)0 >(.*)/i, + [ qr/0 >/i, + sub { + #$mac_rc = $expect_out; + nc_msg($verbose, "Status: Error getting MAC address for phandle=$phandle. RC=$mac_rc.\n"); + nc_msg(2, "Could not obtain MAC address; setting MAX to NA\n" ); + return "NA"; + } + ], + [ timeout=> + sub { + nc_msg(2, "Timeout when getting mac address\n"); + return 1; + } + ], + [ eof => + sub { + nc_msg(2, " Cannot connect to $node\n"); + return 1; + } + ] + ); + } + } + ], + [qr/]/=> + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [qr/(.*)DEFAULT(.*)/=> + sub { + nc_msg(2, "Default catch error\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout in getting mac address\n"); + nc_msg($verbose, "timeout state is $state\n"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node"); + return 1; + } + ], + ); + } + # if the state is 0, 1, or 2, an error occurred and the join will fail + if ($state eq 4) { + if ($result[2] =~ /dump-mac\s*(\w*)\s*ok/) { + $mac_address = $1; + } + return $mac_address; + } else { + return 1; + } + +} + + +#------------------------------------------------------------------------------- + +=head3 get_adaptr_loc + + + PROCEDURE + + Declare procedure to obtain the list of ethernet adapters, their physical + location codes and MAC addresses. + + The get-package-property command is an example of a command + that takes it's arguments off the stack and puts the results back onto the + stack. Because of this, the arguments for the get-package-property command + are in front of the command verb. + + The only reason this procedure is implemented in a loop is to avoid coding + 3 expect commands. + +=cut + +#----------------------------------------------------------------------------- + sub get_adaptr_loc { + my $phandle = shift; + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + my @cmd; + my @done; + my @msg; + my @pattern; + my @newstate; + my $state = 0; + my $timeout = 60; # shouldn't take long + my @result; + my @path; + my $loc_code; + + nc_msg($verbose, "Status: get_adaptr_loc start\n"); + # cmd(0) could have been sent as 3 commands. " ibm,loc-code" (tcl forces + # the use of \") is the first command on this line. The result of entering + # " ibm,loc-code" is that 2 stack entries are created, and address and a length. + # + # The next command in cmd(0) is the phandle with no quotes. This results in + # one stack entry because the phandle is an address. + # + # the third command in cmd(0) is get-package-property. After this command, there + # are 3 stack entries (return code, address and length of mac-address). + # state 0, stack count 0, send command + $done[0] = 0; + $cmd[0] = "\" ibm,loc-code\" $phandle get-package-property\r"; + $msg[0] = "Status: return code and loc-code now on stack\n"; + #$pattern[0] = "(.*)3 >(.*)"; + $pattern[0] = "3 >"; + $newstate[0] = 1; + + # cmd(1) is a dot (.). This is a stack manipulation command that removes one + # thing from the stack. pattern(1) is looking for a prompt with the 2 indicating + # that there are 2 things left on the stack. + # state 1, return code and loc-code on stack + $done[1]= 0; + $cmd[1] = ".\r"; + $msg[1] = "Status: loc-code now on stack\n"; + #$pattern[1] = "(.*)2 >(.*)"; + $pattern[1] = "ok"; #"2 >"; + $newstate[1]= 2; + + # state 2, loc-code on stack + $done[2]= 0; + $cmd[2] = "dump\r"; + $msg[2] = "Status: loc-code displayed, stack empty\n"; + #$pattern[2] = "(.*)(: )(.*)( :)(.*)(\.: ok)"; + $pattern[2] = "ok"; + $newstate[2]= 3; + + # state 3, all done + $done[3] = 1; + + while($done[$state] eq 0) { + nc_msg($verbose, "PROGRAM Status: command is $cmd[$state]\n"); + send_command($verbose, $rconsole, $cmd[$state]); + @result = $rconsole->expect( + $timeout, + [qr/$pattern[$state]/=> + sub { + nc_msg($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [qr/1 >/=> + sub { + my $exp = shift; + if($state eq 0) { + send_command($verbose, $rconsole, ".\r"); + $exp->expect( + #[qr/(-*\[0-9\]*) ok(.*)0 >(.*)/=> + [qr/0 >/=> + sub { + my $loc_rc = shift; + nc_msg(2, "Error getting adapter physical location.\n"); + nc_msg($verbose, "Status: Error getting physical location for phandle=$phandle. RC=$loc_rc.\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout when openning console\n"); + return 1; + } + ], + [eof=> + sub { + nc_msg(2, "Cannot connect to the $node\n"); + return 1; + } + ], + ); + + } + } + ], + [qr/]/=> + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [qr/(.*)DEFAULT(.*)/=> + sub { + nc_msg(2, "Default catch error\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout when openning console\n"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to the $node\n"); + return 1; + } + ], + ); + } + # Did we find one or more adapters? + + if ($result[3] =~ /(\w*):(.*):(\w*\.\w*\.\w*):/) { + $loc_code = $3; + }else { + return undef; + } +} + +#------------------------------------------------------------------------------- + +=head3 ping_server + + + + PROCEDURE + + Declare procedure to obtain the list of valid adapter connector properties + from the adapter card. Connector types can be rj45, sc, 9pin, aui, + bnc, or mic. Speeds can be 10, 100, or 1000. Duplex can be half or + full. This procedure will use the "supported-network-types" + argument to the get-package-property command to get the list of + properties for the given adapter. + + +=cut + +#----------------------------------------------------------------------------- +sub ping_server{ + my $phandle = shift; + my $full_path_name = shift; + my $rconsole = shift; + my $node = shift; + my $mac_address = shift; + my $verbose = shift; + my $adap_speed = shift; + my $adap_duplex = shift; + my $list_type = shift; + my $server_ip = shift; + my $client_ip = shift; + my $gateway_ip = shift; + my $adap_prop_list_array; + #my %env = shift; + my $command; + my $linklocal_ip; + my @result; + my @done; + my @cmd; + my @msg; + my @pattern; + my @newstate; + my $state = 0; + my $timeout; + + nc_msg($verbose, "Status: ping_server start\n"); + + #if (exists($env{'FIRMWARE_DUMP'})) { + # $full_path_name = Firmware_Dump($phandle); + #} + my $j = 0; + my $tty_do_ping = 0; + my $stack_level = 0; + my $properties_matched = 0; + my $adap_conn; + my $speed_list; + my $duplex_list; + my @adap_conn_list; + my $i; + my $ping_debug; + my $ping_rc; + + # If the adapter type chosen is ethernet, need to set the speed and duplex + # of the adapter before we perform the ping. If token ring or fddi, + # this is not required, so begin with state 2. + # + # cmd(0) sets the given adapter as active, to allow setting of speed + # and duplex + # + # cmd(1) writes the settings to the current adapter + # + # cmd(2) selects the /packages/net node as the active package to access the + # ping command. + # + # The next command in cmd(3) is the ping command. This places the return code + # on the stack. A return code of 0 indicates success. + # + # state 0, set the current adapter + $done[0] = 0; + $cmd[0] = "dev $full_path_name\r"; + $msg[0] = "Status: selected $full_path_name as the active adapter\n"; + #$pattern[0] = ".*dev(.*)0 >(.*)"; + $pattern[0] = "0 >"; + $newstate[0] = 1; + + # state 1, send property command to $selected type; + $done[1] = 0; + $cmd[1] = "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r"; + $msg[1] = "Status: chosen network type set\n"; + #$pattern[1] =".*ethernet(.*)0 >(.*)"; + $pattern[1] ="0 >"; + $newstate[1]= 2; + + # state 2, activate /packages/net + $done[2] = 0; + $cmd[2] = "dev /packages/net\r"; + $msg[2] = "Status: selected the /packages/net node as the active package\n"; + #$pattern[2] = ".*dev(.*)ok(.*)0 >(.*)"; + $pattern[2] = "ok"; + $newstate[2]= 3; + + # state 3, ping the server + $done[3] = 0; + $msg[3] = "Status: ping return code now on stack\n"; + $newstate[3] = 4; + + #IPv6 + if ( $server_ip =~ /:/ ) { + #::1, calculate link local address + if ($client_ip eq "::1") { + my $command = "/opt/xcat/share/xcat/tools/mac2linklocal -m $mac_address"; + $linklocal_ip = $rconsole->send($command); + } else { + $linklocal_ip = $client_ip; + } + $cmd[3] = "ping $full_path_name:ipv6,$server_ip,$linklocal_ip,$gateway_ip\r"; + } else { + $cmd[3] = "ping $full_path_name:$server_ip,$client_ip,$gateway_ip\r"; + } + $pattern[3] = ".*ping(.*)ok(.*)0 >(.*)"; + + # state 4, all done + $done[4] = 0; + $cmd[4] = "0 to my-self\r"; + $msg[4] = "Status: resetting pointer\n"; + #$pattern[4] = "(.*)ok(.*)0 >(.*)"; + $pattern[4] = "ok"; + $newstate[4] = 5; + + # state 5, all done + $done[5] = 1; + + # for ping, only need to set speed and duplex for ethernet adapters + # + if ( $list_type eq "ent" ) { + $state = 0; + + # Get the list of properties for this adapter + # + $adap_prop_list_array = get_adap_prop($phandle, $rconsole, $node, $verbose); + if ( $adap_prop_list_array eq 1 ) { + nc_msg(2, "ERROR return from get_adap_prop\n"); + return 1; + } + + if ( $adap_prop_list_array eq undef ) { + nc_msg(2, "No properties found for adapter '$full_path_name'\n"); + return 1; + } + + # Now need to verify that the network params we were passed are valid for + # the given adapter + # + my $a_speed; + my $a_conn; + my $a_duplex; + for my $prop (@$adap_prop_list_array) { + if( $prop =~ /(.*),(.*),(.*)/) { + $a_speed = $1; + $a_conn = $2; + $a_duplex = $3; + if ( ( $a_speed eq $adap_speed ) && ( $a_duplex eq $adap_duplex ) ) { + $properties_matched = 1; + if ( grep {$_ eq $a_conn } @adap_conn_list) { + push @adap_conn_list, $a_conn; + } + } + } + } + + if ( $properties_matched eq 0 ) { + $adap_speed = $a_speed; + $adap_duplex = $a_duplex; + $properties_matched = 1; + push @adap_conn_list, $a_conn; + } + + $i = scalar(@adap_conn_list); + + if ( $properties_matched eq 0 ) { + nc_msg(2, "'$adap_speed/$adap_duplex' settings are not supported on this adapter\n"); + return 1; + } + } else { + $state = 2; + } + + $timeout = 300; + while ( $done[$state] eq 0 ) { + send_command($verbose, $rconsole, $cmd[$state]); + @result = $rconsole->expect( + $timeout, + [qr/$pattern[$state]/s=> + sub { + nc_msg($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [qr/]/=> + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [qr/(.*)DEFAULT(.*)/=> + sub { + nc_msg(2, "Default catch error\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout when openning console\n"); + return 1; + } + ], + [eof=> + sub { + nc_msg(2, "Cannot connect to the $node\n"); + return 1; + } + ], + ); + if ( $state eq 1 ) { + $adap_conn = $adap_conn_list[$j]; + $cmd[1] = "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r"; + nc_msg($verbose, "Status: Trying connector type $adap_conn\n"); + $j++; + } + if ((($tty_do_ping eq 1) && ($state eq 4)) || ($tty_do_ping ne 1) && ($state eq 3) ) { + $ping_debug = $result[2]; + } + if ( ( ($tty_do_ping eq 1) && ($state eq 5) ) || ($tty_do_ping ne 1) && ($state eq 4) ) { + if ( ($tty_do_ping eq 1) && ($state eq 5) ) { + #$ping_rc = $result[2]; + $stack_level = length($result[4]); + } elsif ( ($state eq 4) && ($tty_do_ping ne 1) && ($result[2] =~ /PING SUCCESS/)) { + $ping_rc = 0; + } elsif ( $result[2] =~ /unknown word/ ) { + nc_msg($verbose, "Status: try tty-do-ping.\n"); + $ping_rc = 1; + $tty_do_ping = 1; + $state = 3 ; + $cmd[3] = "\" $full_path_name:$client_ip,$server_ip,$gateway_ip\" tty-do-ping\r"; + $pattern[3] = "(.*)ok(.*)(\[1-2\]) >(.*)"; + + # state 4, get the return code off the stack + $done[4] = 0; + $cmd[4] = ".\r"; + $msg[4] = "Status: return code displayed, stack empty\n"; + $pattern[4] = "(\[0-9\]*) ok(.*)(\[0-1\]) >(.*)"; + $newstate[4] = 5; + + # this command is used to work around a default catch problem in open + # firmware. Without it, a default catch occurs if we try to set + # adapter properties again after a ping + # + # state 5, re$pointer + $done[5] = 0; + $cmd[5] = "0 to my-self\r"; + $msg[5] = "Status: resetting pointer\n" ; + $pattern[5] = "(.*)ok(.*)0 >(.*)"; + $newstate[5] = 6 ; + + # state 6, all done + $done[6] = 1; + } else { + $ping_rc = 1; + } + + if ( $ping_rc eq 0 ) { + nc_msg($verbose, "# $full_path_name ping successful.\n"); + } elsif ( $ping_rc eq 1 ) { + nc_msg($verbose, "# $full_path_name ping unsuccessful.\n"); + nc_msg($verbose, "# $full_path_name ping unsuccessful.\n"); + nc_msg($verbose, "$ping_debug\n"); + + # An unsuccessful return may leave another item on the stack to + # be removed. Check for it, and remove if necessary + my $matchexp = 0; + my @exp_out; + while ( $stack_level ne 0 ) { + send_command($verbose, $rconsole, ".\r"); + @exp_out = $rconsole->expect( + [qr/(\[0-9\]*) ok(.*)(\[0-1\]) >(.*)/s=> + sub { + $matchexp = 1; + } + ], + [qr/]/=> + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [qr/(.*)DEFAULT(.*)/=> + sub { + nc_msg(2, "Default catch error\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout in ping server\n"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node\n"); + return 1; + } + ], + ); + if ($matchexp) { + $matchexp = 0; + $stack_level = length($exp_out[4]); + nc_msg($verbose, "Status: stack_level is <$stack_level>\n"); + } + } + # Check if there are any more adapter connector types + # to try + # + if ( ( $list_type eq "ent" ) && ( $j < $i ) ) { + $adap_conn = $adap_conn_list[$j]; + nc_msg($verbose, "Status: Trying connector type $adap_conn\n"); + $j++; + + # Need to work around a default catch problem in open + # firmware by sending a "0 to my-self" instruction + # following the ping. To make sure this happens in + # this rare case where we have an adapter with multiple connectors, + # we have to force the instruction into the 0th slot in + # the array. This is OK, since we only set the current + # adapter once, upon entering this procedure. + # + $done[0] = 0; + $cmd[0] = "0 to my-self\r"; + $msg[0] = "Status: resetting pointer\n"; + $pattern[0] = "(.*)ok(.*)0 >(.*)"; + $newstate[0] = 1; + + $state = 0; + } + } else { + nc_msg(2, "Unexpected ping return code\n"); + return 1; + } + } + } + return $ping_rc; +} + + +#------------------------------------------------------------------------------- + +=head3 set_disk_boot + + + + PROCEDURE + + Declare procedure to obtain the list of valid adapter connector properties + from the adapter card. Connector types can be rj45, sc, 9pin, aui, + bnc, or mic. Speeds can be 10, 100, or 1000. Duplex can be half or + full. This procedure will use the "supported-network-types" + argument to the get-package-property command to get the list of + properties for the given adapter. + + +=cut + +#----------------------------------------------------------------------------- +sub set_disk_boot { + my @expect_out = shift; + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + my $x0; + my $x1; + my $x2; + my $command; + my @done; + my @cmd; + my @msg; + my @pattern; + my @newstate; + my $timeout; + my $state; + + # state 0, get SMS screen + $done[0] = 0; + if($expect_out[0] =~ /(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Select Boot Options(\r)/){ + $x0 = $1; + $x1 = $2; + $x2 = $3; + $command = $4; + } + $cmd[0] = "$command\r"; + $msg[0] = "Status: sending return to repaint SMS screen\n"; + $pattern[0] = "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Configure Boot Device Order(\r)"; + $newstate[0] = 1; + + # state 1, Multiboot + $done[1] = 0; + $msg[1] = "Status: Multiboot\n"; + $pattern[1] = "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Select 1st Boot Device(\r)"; + $newstate[1] = 2; + + # state 2, Configure Boot Device Order + $done[2] = 0; + $msg[2] = "Status: Configure Boot Device Order"; + $pattern[2] = "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Hard Drive(.*)"; + $newstate[2] = 3; + + # state 3, Select Device Type + $done[3] = 0; + $msg[3] = "Status: Select Device Type"; + $pattern[3] = "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)SCSI(.*)"; + $newstate[3] = 4; + + # state 4, Select Media Type + $done[4] = 0; + $msg[4] = "Status: Select Media Type"; + $pattern[4] = "(\n)(\[ ])(\[1])(\[.])(\[ ]+)(\\S+)(.*)"; + $newstate[4] = 5 ; + + # state 5, Select Media Adapter + $done[5] = 0; + $msg[5] = "Status: Select Media Adapter"; + $pattern[5] = "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)(\\S)(\[ ]+)SCSI (\[0-9]+) MB Harddisk(.*)loc=(.*)\[)]"; + $newstate[5] = 6; + + # state 6, Select Device + $done[6] = 0; + $msg[6] = "Status: Select Device"; + $pattern[6] = "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Set Boot Sequence(.*)"; + $newstate[6] = 7; + + # state 7, Select Task + $done[7] = 0; + $msg[7] = "Status: Select Task"; + $pattern[7] = "(.*)Current Boot Sequence(.*)"; + $newstate[7] = 8 ; + + # state 8, Return to Main Menu + $done[8] = 0; + $cmd[8] = "M"; + $msg[8] = "Status: Restored Default Setting.\n" ; + $pattern[8] = "(.*)Navigation key(.*)"; + $newstate[8] = 9; + + # state 9, Getting to SMS Main Menu + $done[9] = 0; + $cmd[9] = "0\r"; + $msg[9] = "Status: Getting to SMS Main Menu.\n"; + $pattern[9] = "(.*)Exit SMS(.*)Prompt?(.*)"; + $newstate[9] = 10; + + # state 10, Exiting SMS + $done[10] = 0; + $cmd[10] = "Y"; + $msg[10] = "Status: Exiting SMS.\n"; + $pattern[10] = "(.*)ok(.*)0 >(.*)"; + $newstate[10] = 11; + + # state 11, all done + $done[11] = 1; + + $timeout = 30; + $state = 0; + + while ( $done[$state] eq 0 ) { + send_command($verbose, $rconsole, $cmd[$state]); + $rconsole->expect( + [qr/$pattern[$state]/=> + sub { + if ( $state eq 4 ) { + if ( $expect_out[6] eq "None" ) { + $state = 8; + } + } + $state = $newstate[$state]; + if ( ($state != 8) && ($state != 9) && ($state != 10) ) { + $cmd[$state] = "$expect_out[3]\r"; + } + } + ], + [qr/THE SELECTED DEVICES WERE NOT DETECTED IN THE SYSTEM/=> + sub { + nc_msg(2, " Status: THE hard disk WERE NOT DETECTED IN THE SYSTEM!\n"); + return 1; + } + ], + [timeout => + sub { + nc_msg(2, "Timeout in settin boot order\n"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node\n"); + return 1; + } + ], + ); + } + +} + + +################################################################### +# +# PROCEDURE +# +# Declare procedure to boot the system from the selected ethernet card. +# +# This routine does the following: +# 1. Initiates the boot across the network. (state 0 or 1) +# +# state 99 is normal exit and state -1 is error exit. +################################################################### +sub boot_network { + my $rconsole = shift; + if (($rconsole) && ($rconsole =~ /xCAT::/)) + { + $rconsole = shift; + } + my $full_path_name = shift; + my $speed = shift; + my $duplex = shift; + my $chosen_adap_type = shift; + my $server_ip = shift; + my $client_ip = shift; + my $gateway_ip = shift; + my $netmask = shift; + my $dump_target = shift; + my $dump_lun = shift; + my $dump_port = shift; + my $verbose = shift; + my $extra_args = shift; + my $node = shift; + my $set_boot_order = shift; + my @net_device; + my @pattern; + my @cmd; + my @msg; + my @newstate; + my @done; + my $state = 0; + my $timeout; + my $boot_device_bk; + + + nc_msg($verbose, "Status: boot_network start\n"); + ################################################################### + # Variables associated with each of the commands sent by this routine + # are defined below. + # + # The done variable is flag that is set to 1 to break out of the loop + # + # The cmd variable is the command to be sent to the chrp interface. + # In one case it set in the special processing code because the + # ihandle is not available then this code is executes. + # + # The msg variable contains the message sent after a successful pattern match + # + # The pattern variable is the pattern passed to expect + # + # The newstate variable indicates what command is to be issued next + ################################################################### + + # If the install adapter is Ethernet or Token Ring, set the speed and + # duplex during boot. + # state 0, stack count 0 + $done[0] = 0; + if ($dump_target != "") { + $net_device[0] = "$full_path_name:iscsi,ciaddr=$client_ip,subnet-mask=$netmask,itname=dummy,iport=$dump_port,ilun=$dump_lun,iname=$dump_target,siaddr=$server_ip,2"; + $pattern[0] = "iSCSI"; + } else { + if ($extra_args != "" ) { + if ( $server_ip =~ /:/ ) { #ipv6 + $net_device[0] = "$full_path_name:ipv6,speed=$speed,duplex=$duplex,siaddr=$server_ip,ciaddr=$client_ip,giaddr=$gateway_ip,filename=$node,$extra_args"; + } else { + $net_device[0] = "$full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip $extra_args"; + } + } else { + if ( $server_ip =~ /:/ ) { #ipv6 + $net_device[0] = "$full_path_name:ipv6,speed=$speed,duplex=$duplex,siaddr=$server_ip,ciaddr=$client_ip,giaddr=$gateway_ip,filename=$node"; + } else { + $net_device[0] = "$full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip"; + } + } + $pattern[0] = "BOOTP"; + } + + $cmd[0] = "boot $net_device[0]\r"; + $msg[0] = "Status: network boot initiated\n"; + $newstate[0] = 99; + + # If the install adapter is FDDI, don't set the speed and duplex + # state 1 + $done[1] = 0; + $net_device[1] = "$full_path_name:bootp,$server_ip,,$client_ip,$gateway_ip"; + $cmd[1] = "boot $net_device[1]\r"; + $msg[1] = "Status: network boot initiated\n"; + $pattern[1] = "BOOTP"; + $newstate[1] = 99; + + # state 99, all done + $done[99] = 1; + + # state -1, all done + $done[100] = 1; #-1??? + + if ($chosen_adap_type eq "fddi" ) { + $state = 1; + } else { + if ($speed eq "" || $duplex eq "" ) { + nc_msg(2, "Cannot set speed or duplex for network boot\n"); + return 1; + } + $state = 0; + } + ################################################################## + # Set the boot device order. + ################################################################## + if ( $set_boot_order > 0 ) { + $done[2] = 0; + $msg[2] = "Status: read original boot-device\n"; + $cmd[2] = "printenv boot-device\r"; + $pattern[2] = ".*boot-device\\s+(\\S+)(.*)ok(.*)"; + $newstate[2] = 3; + + $done[3] = 0; + $msg[3] = "Status: set the environment variable boot-device\n"; + $pattern[3] = "(.*)ok(.*)(\[0-9]) >(.*)"; + if ( $state eq 0 ) { + $newstate[3] = 0; + } else { + $newstate[3] = 1; + } + $state = 2; + } + + $timeout = 30; # shouldn't take long + while ( $done[$state] eq 0 ) { + send_command($verbose, $rconsole, $cmd[$state]); + $rconsole->expect( + [qr/$pattern[$state]/=> + sub { + my @expect_out = shift; + if ( $state eq 2 ) { + if ( $set_boot_order eq 1 ) { + ######################################## + # Set network as boot device + ######################################## + $cmd[3] = "setenv boot-device $net_device[$newstate[3]]\r"; + } elsif ( $set_boot_order eq 2 ) { + ######################################## + # Set network as 1st boot device,disk as 2nd boot device + ######################################## + $boot_device_bk = $expect_out[1]; + $cmd[3] = "setenv boot-device $net_device[$newstate[3]] $boot_device_bk\r"; + } elsif ( $set_boot_order eq 3 ) { + ######################################## + # Set disk as 1st boot device,network as 2nd boot device + ######################################## + $boot_device_bk = $expect_out[1]; + $cmd[3] = "setenv boot-device $boot_device_bk $net_device[$newstate[3]]\r"; + } elsif ( $set_boot_order eq 4 ) { + ######################################## + # set disk as boot device + ######################################## + $boot_device_bk = $expect_out[1]; + $cmd[3] = "setenv boot-device $boot_device_bk\r"; + } + } + nc_msg($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [qr/----/=> + sub { + nc_msg ($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [qr/]/=> + sub { + nc_msg (2, "Unexpected prompt\n"); + return 1; + } + ], + [qr/(.*)DEFAULT(.*)/=> + sub { + nc_msg(2, "Default catch error\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout when openning console\n"); + return 1; + } + ], + [eof=> + sub { + nc_msg(2, "Cannot connect to the $node\n"); + return 1; + } + ], + ); + } +} +# +# PROCEDURE +# +# +sub Boot { + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + #my @expect_out = shift; + my $rc = 0; + my $timeout; + my $state; + + + nc_msg($verbose, "Status: waiting for the boot image to boot up.\n"); + + $timeout = 1200; # could take a while depending on configuration + $rconsole->expect( + [qr/RESTART-CMD/=> + sub { + # If we see a "problem doing RESTART-CMD" message, we re-hit the OPEN-DEV + # issue after firmware rebooted itself and we need to retry the netboot once more + nc_msg(2, "The network boot ended in an error.\nError : RESTART-CMD\n"); + return 1; + } + ], + #[!(qr/[0-9A-F]+/)=> + # sub { + # nc_msg(2, "The network boot ended in an error.\n"); + # #nc_msg($verbose, $expect_out[buffer]); + # return 1; + # } + #], + [qr/BOOTP/=> #-ex + sub { + nc_msg($verbose, "# Network boot proceeding, exiting.\n"); + } + ], + [qr/------/=> #-ex + sub { + nc_msg($verbose, "# Network boot proceeding, exiting.\n"); + } + ], + [timeout=> + sub { + my $mins = $timeout/60; + nc_msg(2, "Timeout waiting for the boot image to boot up. \ + \n waited '$mins' minutes for the boot image to boot. \ + \nEither the boot up has taken longer than expected or \ + \nthere is a problem with system boot. Check the boot \ + \nof the node to determine if there is a problem.\n"); + #nc_msg($verbose, $expect_out[buffer]); + return 1; + } + ], + [eof=> + sub { + nc_msg(2, "Port closed waiting for boot image to boot.\n"); + return 1; + } + ], + ); + return 0; +} + + +################################################################### +# +# PROCEDURE +# +# Create multiple open-dev function in Open Firmware to try open +# a device. The original problem is a firmware issue which fails +# to open a device. This procedure will create multiple sub +# function in firmware to circumvent the problem. +# +################################################################### +sub multiple_open_dev { + my $rconsole = shift; + if (($rconsole) && ($rconsole =~ /xCAT::/)) + { + $rconsole = shift; + } + my $node = shift; + my $verbose = shift; + my $expect_out; + my $command; + my $timeout; + + send_command($verbose, $rconsole, "dev /packages/net \r"); + send_command($verbose, $rconsole, "FALSE value OPEN-DEV_DEBUG \r"); + + if (exists $ENV{'OPEN_DEV_DEBUG'}) { + send_command($verbose, $rconsole, "TRUE to OPEN-DEV_DEBUG \r"); + } + + $command = ": new-open-dev ( str len -- true|false ) \ + open-dev_debug if cr .\" NEW-OPEN-DEV: Entering, Device : \" 2dup type cr then \ + { _str _len ; _n } \ + 0 -> _n \ + get-msecs dup d# 60000 + ( start timeout ) \ + begin \ + ( start timeout ) get-msecs over > if \ + open-dev_debug if \ + ( start timeout ) drop get-msecs swap - \ + cr .\" FAILED TO OPEN DEVICE\" \ + cr .\" NUMBER OF TRIES \" _n .d \ + cr .\" TIME ELAPSED \" ( time ) .d .\" MSECONDS\" cr \ + else \ + ( start timout ) 2drop \ + then \ + false exit \ + else \ + true \ + then \ + while \ + ( start timeout ) \ + _n 1 + -> _n \ + _str _len open-dev ( ihandle|false ) ?dup if \ + -rot ( ihandle start timeout ) \ + open-dev_debug if \ + ( start timeout ) drop get-msecs swap - \ + cr .\" SUCCESSFULLY OPENED DEVICE\" \ + cr .\" NUMBER OF TRIES \" _n .d \ + cr .\" TIME ELAPSED \" ( time ) .d .\" MSECONDS\" cr \ + else \ + ( start timeout ) 2drop \ + then \ + ( ihandle ) exit \ + then \ + ( start timeout ) \ + repeat \ + ; \r"; + send_command($verbose, $rconsole, $command); + + $command = "patch new-open-dev open-dev net-ping \r"; + send_command($verbose, $rconsole, $command); + + $timeout = 30; + $rconsole->expect( + $timeout, + #[qr/patch new-open-dev(.*)>/=> + [qr/>/=> + sub { + nc_msg($verbose, "Status: at End of multiple_open_dev \n"); + return 0; + } + ], + [qr/]/=> + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [timeout => + sub { + send_user(2, "Timeout\n"); + return 1; + } + ], + [eof => + sub { + send_user(2, "Cannot connect to $node\n"); + return 1; + } + ], + ); +} +################################################################### +# +# PROCEDURE +# +# Declare procedure to get additional firmware debug statement. +# +################################################################### +sub Firmware_Dump { + my $rconsole = shift; + my $node = shift; + my $verbose = shift; + my $device_path = shift; + my $phandle = shift; + my $rc; + my $expect_out; + my @done; + my @cmd; + my @msg; + my @pattern; + my @newstate; + my $timeout; + my $state = 0; + + nc_msg($verbose,"Status: Firmware_Dump start\n"); + + # state 0 + $done[0] = 0; + $cmd[0] = "dev /packages/obp-tftp\r"; + $msg[0] = "Status: selected /packages/obp_tftp\n"; + $pattern[0] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[0] = 1; + + # state 1 + $done[1] = 0; + $cmd[1] = ": testing1 .\" OBP-TFTP entry\" cr init-nvram-adptr-parms ;\r"; + $msg[1] = "Status: running test - OBP-TFTP entry\n"; + $pattern[1] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[1] = 2; + + # state 2 + $done[2] = 0; + $cmd[2] = ": testing2 .\" OBP-TFTP exit, TRUE\" cr true ;\r"; + $msg[2] = "Status: running test - OBP-TFTP exit\n"; + $pattern[2] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[2] = 3; + + # state 3 + $done[3] = 0; + $cmd[3] = "patch testing1 init-nvram-adptr-parms open\r"; + $msg[3] = "Status: running test - patch testing1\n"; + $pattern[3] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[3] = 4; + + # state 4 + $done[4] = 0; + $cmd[4] = "patch testing2 true open\r"; + $msg[4] = "Status: running test - patch testing2\n"; + $pattern[4] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[4] = 5; + + # state 5 + $done[5] = 0; + $cmd[5] = "dev $device_path\r"; + $msg[5] = "Status: running test - dev $device_path\n"; + $pattern[5] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[5] = 6; + + # state 6 + $done[6] = 0; + $cmd[6] = "true to debug-init\r"; + $msg[6] = "Status: running test - true to debug-init\n"; + $pattern[6] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[6] = 7; + + # state 7 + $done[7] = 0; + $cmd[7] = "true to debug-error\r"; + $msg[7] = "Status: running test - true to debug-error\n"; + $pattern[7] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[7] = 8; + + # state 8 + $done[8] = 0; + $cmd[8] = "$phandle to active-package\r"; + $msg[8] = "Status: running $phandle to active-package\n"; + $pattern[8] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[8] = 9; + + # state 9 + $done[9] = 0; + $cmd[9] = ".properties\r"; + $msg[9] = "Status: running .properies\n"; + $pattern[9] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[9] = 10; + + # state 10 + $done[10] = 0; + $cmd[10] = "clear\r"; + $msg[10] = "Status: running clear\n"; + $pattern[10] = "(.*)ok(.*)(\[0-9]) >(.*)"; + $newstate[10] = 11; + + # state 11, all done + $done[11] = 1; + + $state = 0; + $timeout = 30; # shouldn't take long + while ($done[$state] == 0) { + send_command($verbose, $rconsole, $cmd[$state]); + $rconsole->expect( + [qr/$pattern[$state]/ => + sub { + nc_msg($verbose, $msg[$state]); + $state = $newstate[$state]; + } + ], + [qr/]/=> + sub { + nc_msg(2, "Unexpected prompt\n"); + return 1; + } + ], + [qr/(.*)DEFAULT(.*)/=> + sub { + nc_msg(2, "Default catch error\n"); + return 1; + } + ], + [timeout=> + sub { + nc_msg(2, "Timeout\n"); + nc_msg(2, "Status: timeout state is $state\n"); + return 1; + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node\n"); + return 1; + } + ], + ); + } +} + +#----------------------------------------------------------------------------- + +=head3 lparnetbootexp + + Function: + Same function as the lpar_netboot.exp + + Return: + 0 - good + 1 - abort + 2 - usage error + +=cut +#----------------------------------------------------------------------------- +sub lparnetbootexp +{ + my $opt = shift; + if (($opt) && ($opt =~ /xCAT::/)) + { + $opt = shift; + } + my $CONSOLEBIN = "/opt/xcat/bin/rcons"; + my $PROGRAM = "lpar_netboot"; + my $noboot = 0; #default is to boot + my $Boot_timeout = 3000; + my @expect_out; + my $req = shift; + my $cmd; + my $timeout; + my $output; + my $done; + my $retry_count; + my $query_count; + my $match_pat; + my $loc_code; + my $ping_rc; + my $ping_result; + my $chosen_adap_type; + my $i; + my $phandle; + my $match; + my $list_type; + my $sysoutput; + my $exp_internal; +# Flags and initial variable + my $discovery = 0; + my $discover_all = 0; + my $verbose = 0; + my $discover_macaddr = 0; + my $rc = 0; + my $debug_flag = 0; + my $rmvterm_flag = 0; + my $immed_flag = 0; + my $from_of = 0; + my $dev_type_found = 0; + my $list_physical = 0; + my $set_boot_order = 0; + my $colon = 0; + my $choice = 0; + my $full_path_name; + my $adap_speed; + my $adap_duplex; + my $client_ip; + my $server_ip; + my $gateway_ip; + my $extra_args; + my $macaddress; + my $netmask; + my $dump_target; + my $dump_lun; + my $dump_port; + my $phys_loc; + my $userid; + my $passwd; + my $prompt = "\\\$ \$"; + my $ssh_spawn_id = 0; + my $mac_address; + + + $::CALLBACK = $req->{callback}; +# +# Log the process id +# + my $proc_id = getppid; + nc_msg($verbose, "$PROGRAM Status: process id is $proc_id\n"); + +# +# +# Process command line options and parameters +# +# +#done== + + if ( exists ($opt->{A})) { + $discover_all = 1; + } + + if ( exists ($opt->{C})) { + $client_ip = $opt->{C}; + } + + if ( exists ($opt->{D})) { + $discovery = 1; + } + + if ( exists ($opt->{G})) { + $gateway_ip = $opt->{G}; + } + + if ( exists ($opt->{P})) { + $list_physical = 1; + } + + if ( exists ($opt->{M})) { + $discover_macaddr = 1; + } + + if ( exists ($opt->{N})) { + $netmask = $opt->{N}; + } + + if ( exists ($opt->{S})) { + $server_ip = $opt->{S}; + } + + if ( exists ($opt->{c})) { + $colon = 1; + } + + if ( exists ($opt->{d})) { + $adap_duplex = $opt->{d}; + } + + if ( exists ($opt->{f})) { + $rmvterm_flag = 1; + } + + if ( exists ($opt->{g})) { + $extra_args = $opt->{g}; + } + + if ( exists ($opt->{i})) { + $immed_flag = 1; + } + + if ( exists ($opt->{o})) { + $from_of = 1; + } + + if ( exists ($opt->{w})) { + $set_boot_order = $opt->{w}; + } + if ( exists ($opt->{L})) { + $dump_lun = $opt->{L}; + } + + if ( exists ($opt->{l})) { + $phys_loc = $opt->{l}; + } + + if ( exists ($opt->{m})) { + $macaddress = $opt->{m}; + } + + if ( exists ($opt->{n})) { + $noboot = 1; + } + + if ( exists ($opt->{p})) { + $dump_port = $opt->{p}; + } + + if ( exists ($opt->{s})) { + $adap_speed = $opt->{s}; + } + + if ( exists ($opt->{T})) { + $dump_target = $opt->{T}; + } + + if ( exists ($opt->{t})) { + $list_type = $opt->{t}; + if ( $list_type eq "hfi-ent" ) { + $dev_pat[0] = "hfi-ethernet"; + $dev_type[0] = "hfi-ent"; + } + # + # Validate the argument + # + $dev_type_found = 0; + foreach my $dev (@dev_type) { + if ( $dev eq $list_type) { + $dev_type_found = 1; + last; + } + } + + if ( $dev_type_found eq 0 ) { + nc_msg($verbose, "$PROGRAM:$dev_type_found, '$list_type' is not a valid adapter choice\n"); + return 1; + } + } + + if ( exists ($opt->{v})) { + $verbose = 1; + } else { + $verbose = 0; + } + if ( exists ($opt->{x})) { + $debug_flag = 1; + } + if ( exists ($opt->{help})) { + usage; + } + #set arg0 [lindex $argv 0] + #set arg1 [lindex $argv 1] + #if ( scalar(%$opt) > 10 ) { + # nc_msg($verbose, "$PROGRAM: Extraneous parameter(s)\n"); + # usage; + #} + + if ( $list_physical eq 0 ) { + $dev_pat[3] = "l-lan"; + $dev_type[3] = "ent"; + $dev_count = scalar(@dev_pat); + } else { + $dev_pat[3] = ""; + $dev_type[3] = ""; + $dev_count = scalar(@dev_pat); + } + + if ( $set_boot_order > 1 ) { + $dev_pat[4] = "scsi"; + $dev_type[4] = "disk"; + $dev_count = scalar(@dev_pat); + } + my $node = $opt->{node}; + my $profile = $opt->{pprofile}; + my $manage = $opt->{fsp}; + my $lparid = $opt->{id}; + my $hcp = $opt->{hcp}; + + + + if ($dev_type_found) { nc_msg($verbose, "$PROGRAM Status: List only $list_type adapters\n"); } + if ($noboot) { nc_msg($verbose, "$PROGRAM Status: -n (no boot) flag detected\n"); } + if ($discovery) { nc_msg($verbose, "$PROGRAM Status: -D (discovery) flag detected\n"); } + if ($discover_all) { nc_msg($verbose, "$PROGRAM Status: -A (discover all) flag detected\n" ); } + if ($verbose) { nc_msg($verbose, "$PROGRAM Status: -v (verbose debug) flag detected\n"); } + if ($discover_macaddr) { nc_msg($verbose, "$PROGRAM Status: -M (discover mac address) flag detected\n"); } + if ($immed_flag) { nc_msg($verbose, "$PROGRAM Status: -i (force immediate shutdown) flag detected\n"); } + if ($list_physical) { nc_msg($verbose, "$PROGRAM Status: -P (list only phsical network) flag detected\n");} + if ($colon) { nc_msg($verbose, "$PROGRAM Status: -c (list colon separated ) flag detected\n" ); } + if ($debug_flag) { + nc_msg($verbose, "$PROGRAM Status: -d (debug) flag detected\n"); + #$exp_internal = 1; + #$log_user = 1; + } + if ($discovery and $adap_speed ) { nc_msg($verbose, "$PROGRAM Status: using adapter speed of $adap_speed\n" ); } + if ($discovery and $adap_duplex ) { nc_msg($verbose, "$PROGRAM Status: using adapter duplex of $adap_duplex\n" ); } + if ($discovery and $server_ip ) { nc_msg($verbose, "$PROGRAM Status: using server IP address of $server_ip\n"); } + if ($discovery and $client_ip) { nc_msg($verbose, "$PROGRAM Status: using client IP address of $client_ip\n" ); } + if ($discovery and $gateway_ip) { nc_msg($verbose, "$PROGRAM Status: using gateway IP address of $gateway_ip\n"); } + if ($discovery and $macaddress) { nc_msg($verbose, "$PROGRAM Status: using macaddress of $macaddress\n" ); } + if ($discovery and $phys_loc ) { nc_msg($verbose, "$PROGRAM Status: using physical location code of $phys_loc\n"); } + nc_msg($verbose, "node:".$node); + #################################### + # process the arguments + #################################### + $rc = xCAT::LparNetbootExp->ck_args($opt, $node, $verbose); + if ($rc != 0) { + nc_msg(2, "ck_args failed. \n"); + return 1; + } + + #################################### + # decide if need to do the connect + #################################### + + + #################################### + # open the console + #################################### + nc_msg($verbose, "open port\n"); + $cmd = $::XCATROOT . '/bin/rcons ' . $node . ' -f'; + my $rconsole = Expect->new; + ################################################## + # raw_pty() disables command echoing and CRLF + # translation and gives a more pipe-like behaviour. + # Note that this must be set before spawning + # the process. Unfortunately, this does not work + # with AIX (IVM). stty(qw(-echo)) will at least + # disable command echoing on all platforms but + # will not suppress CRLF translation. + ################################################## + #$rconsole->raw_pty(1); + #$rconsole->slave->stty(qw(sane -echo)); #hidden information return from rcons + + ################################################## + # exp_internal(1) sets exp_internal debugging + # to STDERR. + ################################################## + #$rconsole->exp_internal( $verbose ); #hidden information return from rcons + + ################################################## + # log_stdout(0) disables logging to STDOUT. + # This corresponds to the Tcl log_user variable. + ################################################## + #$rconsole->log_stdout( $verbose ); #hidden information return from rcons + $rconsole->log_stdout(0); #hidden information return from rcons + + my $consolefork = $rconsole->spawn($cmd); +#$rconsole->log_file("/tmp/consolelog"); + my $console_pid = $rconsole->pid; + unless ($console_pid) { + nc_msg(2, "Unable to open console.\n"); + return 1; + } + nc_msg($verbose, "spawn_id is $console_pid.\n"); + #################################### + #kill the process after received the signal + #################################### + + #################################### + # check the result of rcons + #################################### + nc_msg($verbose, "Connecting to the $node.\n"); + sleep 3; + $timeout = 2; + $rconsole->expect( + $timeout, + [ qr/Enter.* for help.*/i => + sub { + $rc = 0; + nc_msg($verbose, "Connected.\n"); + } + ], + [ timeout => + sub { + $rc = 1; + nc_msg(2, "Timeout waiting for console connection.\n"); + } + ], + [ eof => + sub { + $rc = 2; + nc_msg(2, "Please make sure rcons $node works.\n"); + } + ], + ); + + unless ($rc eq 0) { + return 1; + } + #################################### + # check the node state + #################################### + nc_msg($verbose, "Checking for power off.\n"); + my $subreq = $req->{subreq}; + $output = xCAT::LparNetbootExp->run_lssyscfg($subreq, $verbose, $node); + if ($output =~ /Not Available/) { + nc_msg(2, "LPAR is Not Available. Please make sure the CEC's state.\n"); + return 1; + } else { + nc_msg($verbose, "The lpar state is $output.\n"); + } + + if ($from_of) { + #unless($output =~ /open-firmware/i){ + unless($output =~ /open firmware/i){ + nc_msg(2, "You are using the -o option. Please make sure the LPAR's initial state is open firmware.\n"); + return 1; + } + + } + + #################################### + # if -o is not used, power node of + #################################### + unless ($from_of) { + if (($output =~ /off/i) or ($output =~ /Not Activated/i) ) { + nc_msg(1, "# Power off complete.\n"); + } else { + nc_msg(1, "# Begin to Power off the node.\n"); + $sysoutput = xCAT::Utils->runxcmd( + { + command => ['rpower'], + node => [$node], + arg => ['off'] + }, + $subreq, 0, 1); + $output = join ',', @$sysoutput; + if ($::RUNCMD_RC != 0) { #$::RUNCMD_RC will get its value from runxcmd_output + nc_msg(2, "Unable to run rpower $node off.\n"); + return 1; + } + + unless ($output =~ /Success/) { + nc_msg(2, "Power off failed.\n"); + return 1; + } else { + nc_msg($verbose, "Wait for power off.\n"); + } + + $done = 0; + $query_count = 0; + while (!$done) { + $output = xCAT::LparNetbootExp->run_lssyscfg($subreq, $verbose, $node); + if (($output =~ /off/i) or ($output =~ /Not Activated/)) { + nc_msg($verbose, "Power off complete.\n"); + $done = 1; + next; + } + $query_count++; + if ($query_count > 300) { + nc_msg(2, "Power off failed.\n"); + return 1; + } + } + sleep 1; + } + } + + + ################################################# + # if set_boot_order is set, set the boot order + # if not set, power the node to open firmware + ################################################# + $done = 0; + $retry_count = 0; + if ($set_boot_order) { + nc_msg($verbose, "Power on $node to SMS.\n"); + while (!$done) { + $sysoutput = xCAT::Utils->runxcmd( + { + command => ['rpower'], + node => [$node], + arg => ['sms'] + }, + $subreq, 0, 1); + $output = join ',', @$sysoutput; + nc_msg($verbose, "Waiting for power on...\n"); + + if ($::RUNCMD_RC != 0) { + nc_msg(2, "Unable to run rpower $node sms\n"); + return 1; + } + unless ($output =~ /Success/) { + if ($retry_count eq 3) { + nc_msg(2, "Power off failed, msg is $output.\n"); + return 1; + } + sleep 1; + $retry_count ++; + } else { + $done = 1; + } + } + } else { + nc_msg($verbose, "Power on the $node to the Open Firmware.\n"); + while (!$done) { + $sysoutput = xCAT::Utils->runxcmd( + { + command => ['rpower'], + node => [$node], + arg => ['of'] + }, + $subreq, 0, 1); + $output = join ',', @$sysoutput; + nc_msg($verbose, "Waiting for power on...\n"); + + if ($::RUNCMD_RC != 0) { + nc_msg(2, "Unable to run rpower $node open firmware.\n"); + return 1; + } + unless ($output =~ /Success/) { + if ($retry_count eq 3) { + nc_msg(2, "Power off failed, msg is $output.\n"); + return 1; + } + sleep 1; + $retry_count ++; + } else { + $done = 1; + } + } + } + + + ########################### + # Check the node state + ########################### + $done = 0; + $query_count = 0; + $timeout = 1; + nc_msg($verbose, "Check the node state again;"); + while(!$done) { + $output = xCAT::LparNetbootExp->run_lssyscfg($subreq, $verbose, $node); + nc_msg($verbose, "The node state is $output.\n"); + if ($output =~ /Open Firmware/i) { + nc_msg($verbose, "Power on complete.\n"); + $done = 1; + next; + } + + $query_count++; + # if the node is not in openfirmware state, just wait for it + my @result = $rconsole->expect( + $timeout, + [ qr/(.*)elect this consol(.*)/=> + sub { + $rconsole->send("0\r"); + #$rconsole->clear_accum(); + #$rconsole->exp_continue(); + } + ], + ); + + if ($query_count > 300 ) { + nc_msg(2, "Timed out waiting for power on of $node"); + nc_msg($verbose, "error from rpower command : \"$output\" \n"); + return 1; + } + sleep 1; + } + + + ############################## + # Check for active console + ############################## + nc_msg($verbose, "Check for active console.\n"); + $done = 0; + $retry_count = 0; + + while (!$done) { + my @result = $rconsole->expect( + $timeout, + #[qr/ok(.*)0 >/=> + [qr/0(.*)ok/=> + sub { + nc_msg($verbose, " at ok prompt\n"); + $done = 1; + } + ], + [qr/(.*)elect this consol(.*)/=> + sub { + nc_msg($verbose, " selecting active console\n"); + $rconsole->send("0\r"); + } + ], + [qr/English|French|German|Italian|Spanish|Portuguese|Chinese|Japanese|Korean/=> + sub { + nc_msg($verbose, "Languagae Selection Panel received\n"); + $rconsole->send("2\r"); + } + ], + [qr/admin/=> + sub { + nc_msg($verbose, "No password specified\n"); + $rconsole->soft_close(); + return 1; + } + ], + [qr/Invalid Password/=> + sub { + nc_msg($verbose, "FSP password is invalid.\n"); + $rconsole->soft_close(); + return 1; + } + ], + [qr/SMS(.*)Navigation Keys/=> + sub { + nc_msg($verbose, "SMS\n"); + $done = 1; + } + ], + [timeout=> + sub { + $rconsole->send("\r"); + $retry_count++; + if ($retry_count eq 9) { + nc_msg(2, "Timeout waiting for ok prompt; exiting.\n"); + $rconsole->soft_close(); + return 1; + } + } + ], + [eof => + sub { + nc_msg(2, "Cannot connect to $node"); + $rconsole->soft_close(); + return 1; + } + ], + ); + } + + + + + ############################## + # Set the node boot order + ############################## + if ($set_boot_order) { #rnetboot node will not go here + nc_msg($verbose, "begin to set disk boot"); + my $result = set_disk_boot( $rconsole, $node, $verbose);#@expect_out, $rconsole, $node, $verbose); + unless( $result ) { + nc_msg(2, "Unable to set $node boot order"); + } + } + + sleep 1; + + ############################## + # Call get_phandle to gather + # information for all the + # supported network adapters + # in the device tree. + ############################## + $done = 0; + $retry_count = 0; + nc_msg($verbose, "begin to run get_phandle"); + while (!$done) { + my $result = get_phandle($rconsole, $node, $verbose); + unless (!$result) { + $retry_count++; + $rconsole->send("\r"); + if ( $retry_count eq 3) { + nc_msg(2, "Unable to obtain network adapter information. Quitting.\n"); + return 1; + } + } else { + $done = 1; + } + } + + ############################## + # Call multiple_open_dev to + # circumvent firmware OPEN-DEV + # failure + ############################## + nc_msg($verbose, "begin to run multiple_open_dev"); + my $result = xCAT::LparNetbootExp->multiple_open_dev($rconsole, $node, $verbose); + unless ( $result) { + nc_msg(2, "Unable to obtain network adapter information. Quitting.\n"); + return 1; + } + + ############################## + # + ############################## + nc_msg($verbose, "begin to process opt-discovery"); + if ($discovery) { #rnetboot node will not go here + nc_msg(1, "# Client IP address is $client_ip\n"); + nc_msg(1, "# Server IP address is $server_ip\n"); + nc_msg(1, "# Gateway IP address is $gateway_ip\n"); + } + + + ############################## + # Display information for all + # supported adapters + ############################## + if ($noboot) { + if ($list_type) { + $match_pat = $list_type; + } else { + $match_pat = ".*"; + } + + if($colon) { + nc_msg(1, "#Type:Location_Code:MAC_Address:Full_Path_Name:Ping_Result:Device_Type:Size_MB:OS:OS_Version:\n"); + } else { + nc_msg(1, "# Type \tLocation Code \tMAC Address\t Full Path Name\t Ping Result\n"); + } + + if ( $discover_all ) { + for( $i = 0; $i <= $adapter_found; $i++) { + if ($adap_type[$i] =~ /$match_pat/) { + if (!($adap_type[$i] eq "hfi-ent")) { + $mac_address = get_mac_addr($phandle_array[$i], $rconsole, $node, $verbose); + $loc_code = get_adaptr_loc($phandle_array[$i], $rconsole, $node, $verbose); + } + $ping_result = ""; + if ($discovery) { + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + nc_msg($verbose, "ping_server returns $ping_rc\n"); + unless( $ping_rc eq 0) { + $ping_result = "unsuccessful"; + } else { + $ping_result = "successful"; + } + } + + if ( $adap_type[$i] eq "hfi-ent") { + $mac_address = get_mac_addr($phandle_array[$i], $rconsole, $node, $verbose); + $loc_code = get_adaptr_loc($phandle_array[$i], $rconsole, $node, $verbose); + } + + if ($full_path_name_array[$i] =~ /vdevice/) { + $device_type = "virtual"; + } else { + $device_type = "physical"; + } + + if($colon) { + nc_msg(1, "$adap_type[$i]\:$loc_code\:$mac_address\:$full_path_name_array[$i]\:$ping_result\:$device_type\:\:\:\:\n"); + } else { + nc_msg(1, "$adap_type[$i] $loc_code $mac_address $full_path_name_array[$i] $ping_result $device_type\n"); + } + } + } + } else { + for( $i = 0; $i <= $adapter_found; $i++) { + if ($adap_type[$i] =~ /$match_pat/) { + if ($adap_type[$i] eq "hfi-ent") { + $mac_address = get_mac_addr($phandle_array[$i], $rconsole, $node, $verbose); + $loc_code = get_adaptr_loc($phandle_array[$i], $rconsole, $node, $verbose); + } + $ping_result = ""; + if ($discovery) { + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + nc_msg($verbose, "ping_server returns $ping_rc\n"); + unless( $ping_rc eq 0) { + $ping_result = "unsuccessful"; + } else { + $ping_result = "successful"; + } + } + + if ( $adap_type[$i] eq "hfi-ent") { + $mac_address = get_mac_addr($phandle_array[$i], $rconsole, $node, $verbose); + $loc_code = get_adaptr_loc($phandle_array[$i], $rconsole, $node, $verbose); + } + + if ($full_path_name_array[$i] =~ /vdevice/) { + $device_type = "virtual"; + } else { + $device_type = "physical"; + } + + if($colon) { + nc_msg($verbose, "$adap_type[$i]\:$loc_code\:$mac_address\:$full_path_name_array[$i]\:$ping_result\:$device_type\:\:\:\:\n"); + } else { + nc_msg($verbose, "$adap_type[$i] $loc_code $mac_address $full_path_name_array[$i] $ping_result $device_type\n"); + } + last; + } + } + } + unless($from_of) { + nc_msg($verbose, "power off the node after noboot eq 1\n"); + $sysoutput = xCAT::Utils->runxcmd( + { + command => ['rpower'], + node => [$node], + arg => ['off'] + }, + $subreq, 0, 1); + $output = join ',', @$sysoutput; + if ($::RUNCMD_RC != 0) { + nc_msg(2, "Unable to run rpower $node sms.\n"); + nc_msg($verbose, "Status: error from rpower command\n"); + nc_msg(2, "Error : $output\n"); + return 1; + } + } + } else { # Do a network boot + # Loop throught the adapters and perform a ping test to discover an + # adapter that pings successfully, then use that adapter to network boot. + if ($discover_all) { + for ($i = 0; $i <= $adapter_found; $i ++) { + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + + + if ( $ping_rc eq 0) { + $phandle = $phandle_array[$i]; + $full_path_name = $full_path_name_array[$i]; + $chosen_adap_type = $adap_type[$i]; + last; + } + } + } elsif ( $macaddress != "" ) { + $match = 0; + for ($i = 0; $i <= $adapter_found; $i++) { + if ($adap_type[$i] =~ /hfi-ent/) { + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + } + $mac_address = get_mac_addr($phandle_array[$i], $rconsole, $node, $verbose); + if ( $macaddress =~ /$mac_address/ ) { + if ($discovery eq 1) { + unless ( $adap_type[$i] eq "hfi-ent" ) { + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + } + unless ( $ping_rc eq 0) { + nc_msg(2, "Unable to boot network adapter.\n" ); + return 1; + } + } + $phandle = $phandle_array[$i]; + $full_path_name = $full_path_name_array[$i]; + $chosen_adap_type = $adap_type[$i]; + $match = 1; + last; + } + } + unless($match) { + nc_msg(2, "Can not find mac address '$macaddress'\n"); + return 1; + } + } elsif ( $phys_loc != "") { + $match = 0; + for ($i = 0; $i <= $adapter_found; $i++) { + $loc_code = get_adaptr_loc($phandle_array[$i], $rconsole, $node, $verbose); + if ($loc_code =~ /$phys_loc/) { + if ($discovery) { + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + unless ($ping_rc eq 0) { + nc_msg(2, "Unable to boot network adapter.\n"); + return 1; + } + } + $phandle = $phandle_array[$i]; + $full_path_name = $full_path_name_array[$i]; + $chosen_adap_type = $adap_type[$i]; + $match = 1; + last; + } + } + if (!$match) { + nc_msg(2, "Can not find physical location '$phys_loc'\n"); + return 1; + } + } else { + # + # Use the first ethernet adapter in the + # device tree. + # + for ($i = 0; $i < $adapter_found; $i++) { + nc_msg($verbose, " begint to boot from first adapter in the device tree \n"); + if ($adap_type[$i] eq $list_type ) { + if ( $discovery eq 1 ){ + $ping_rc = ping_server($phandle_array[$i], $full_path_name_array[$i], $rconsole, $node, $mac_address, $verbose, $adap_speed, $adap_duplex, $list_type, $server_ip, $client_ip, $gateway_ip); + unless ($ping_rc eq 0) { + return 1; + } + } + $phandle = $phandle_array[$i]; + $full_path_name = $full_path_name_array[$i]; + $chosen_adap_type = $adap_type[$i]; + last; + } + } + } + my $result; + if ($full_path_name eq "") { + nc_msg(2, "Unable to boot network adapter.\n"); + return 1; + } else { + nc_msg(1, "# Network booting install adapter.\n"); + $result = xCAT::LparNetbootExp->boot_network($rconsole, $full_path_name, $adap_speed, $adap_duplex , $chosen_adap_type, $server_ip, $client_ip, $gateway_ip, $netmask, $dump_target, $dump_lun, $dump_port, $verbose, $extra_args, $node, $set_boot_order ); + } + + + if ($result eq 0) { + nc_msg($verbose, "bootp sent over netowrk.\n"); + my $res = Boot($rconsole, $node, $verbose);#, @expect_out); + unless ($res eq 0) { + nc_msg(2, "Can not boot, result = $res. \n"); + } + } else { + nc_msg($verbose, "return code $result from boot_network\n"); + } + + ########################################################################### + # Need to retry network boot because of intermittant network failure + # after partition reboot. Force partition to stop at Open Firmware prompt. + ########################################################################### + if ($result eq 5) { + + $timeout = 300; + $rconsole->expect( + $timeout, + [ qr/keyboard/i, + sub { + $rconsole->send("8\r"); + sleep 10; + } + ], + [ qr/timeout/i, + sub { + nc_msg(2, "Timeout; exiting.\n"); + return 1; + } + ], + [ eof => + sub { + nc_msg(2, "cannot connect to $node.\n"); + return 1; + } + ], + [ + sub { + nc_msg(1, "# Network booting install adapter.\n"); + nc_msg($verbose, "Retrying network-boot from RESTART-CMD error.\n"); + $done = 0; + while (! $done ) { + my $res = xCAT::LparNetbootExp->boot_network($node,); + if ($res eq 0) { + $done = 1; + } else { + sleep 10; + } + } + } + ], + ); + } + nc_msg(1, "# bootp sent over network.\n"); + $rc = Boot($rconsole, $node, $verbose);#, @expect_out); + unless ($rc eq 0) { + nc_msg(2, "Can't boot here. \n"); + } + } + + ################################################ + # mission accomplished, beam me up scotty. + ################################################# + unless ($noboot) { + if ( $rc eq 0) { + nc_msg(1, "# Finished.\n" ); + } else { + nc_msg(1, "# Finished in an error.\n"); + } + } else { + $done = 0; + $query_count = 0; + while(!$done) { + $output = xCAT::LparNetbootExp->run_lssyscfg($subreq, $verbose, $node); + + ############################################## + # separate the nodename from the query status + ############################################## + if ($from_of ne 1) { + if (( $output =~ /off/i ) or ($output =~ /Not Activated/i)) { + $done = 1; + } + } else { + if ( $output =~ /open-firmware/i ) { + $done = 1; + } + } + $query_count++; + if ( $query_count > 60 ){ + $done = 1; + } + sleep 1; + } + } + $cmd = "~."; + send_command($verbose, $rconsole, $cmd); + + return 0; + +} +