#!/usr/bin/expect -- # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html log_user 0 set stm 1 # # 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. # proc nc_msg { msg } { global verbose if { $verbose == 1 } { send_user $msg } } # # PROCEDURE # # Define the procedure to connect to the IVM # proc connect {} { global hcp global ssh_spawn_id global prompt global userid global passwd global PROGRAM if [ catch { spawn ssh $userid@$hcp} ssh_pid ] { send_user "$PROGRAM: Unable to spawn ssh connection to $hcp\n" exit 1 } set ssh_spawn_id $spawn_id set timeout 10 set pwd_sent 0 # # Look for password prompt, and reply # expect { -i $ssh_spawn_id \ -re $prompt { } -re "ast login.*" { return } -re "assword: $" { if { $pwd_sent != 1 } { set pwd_sent 1 send -i $ssh_spawn_id "$passwd\r" exp_continue } send_user "$PROGRAM: Incorrect login\n" exit 1 } timeout { send_user "$PROGRAM: Timeout waiting for password prompt\n" exit 1 } eof { send_user "$PROGRAM: ssh connection terminated unexpectedly\n" exit 1 } } } # # PROCEDURE # # Procedure to run the rpower command # test its return code and capture its output # proc run_lssyscfg {} { global DSPMSG global PROGRAM global ssh_spawn_id global env global manage global msg global node global profile global rc global prompt global tmp global BINPATH set timeout 20 set runcmd [list exec $BINPATH/rpower $node state] set rc [catch $runcmd msg] nc_msg "$PROGRAM Status: run_lssyscg : partition status : $msg\n" regexp "$node:\[ ]*(.*)" $msg tmp1 tmp if { $tmp != "" } { set msg $tmp } nc_msg "####msg:$msg#########\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 if { $rc } { send_user "$PROGRAM: Unable to determine machine state\n" nc_msg "$PROGRAM Status: error from rpower command\n" send_user "Error : $msg\n" exit 1 } } # # PROCEDURE # # Declare procedure to write usage message and exit # proc usage {} { global PROGRAM send_user "Usage: Install partition \ \n\t$PROGRAM \[-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$PROGRAM -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" exit 1 } # # PROCEDURE # # Check command line arguments # proc ck_args {} { global adap_speed global adap_duplex global client_ip global server_ip global gateway_ip global node global manage global profile global hcp global lparid global PROGRAM global discover_macaddr global extra_args global macaddress global phys_loc global discover_all global discovery global noboot global filename if { $discovery && ( $adap_speed == "" || $adap_duplex == "" ) } { send_user "$PROGRAM: Speed and duplex required\n" usage } if { $discovery && $client_ip == "" } { send_user "$PROGRAM: Client IP is required\n" usage } if { $discovery && $server_ip == "" } { send_user "$PROGRAM: Server IP is required\n" usage } if { $discovery && $gateway_ip == "" } { send_user "$PROGRAM: Gateway IP is required\n" usage } if { $node == "" } { send_user "$PROGRAM: Node is required\n" usage } else { nc_msg "$PROGRAM Status: node $node\n" } if { $manage == "" } { send_user "$PROGRAM: Managed system is required\n" usage } else { nc_msg "$PROGRAM Status: managed system $manage\n" } if { $hcp == "" } { send_user "$PROGRAM: Hardware control point address is required\n" usage } else { nc_msg "$PROGRAM Status: Hardware control point $hcp\n" } if { $lparid == "" } { send_user "$PROGRAM: LPAR Id is required\n" usage } else { nc_msg "$PROGRAM Status: LPAR Id is $lparid\n" } if { $profile == "" } { send_user "$PROGRAM: Profile is required\n" usage } else { nc_msg "$PROGRAM Status: profile $profile\n" } if { $discover_macaddr && $extra_args != "" } { send_user "$PROGRAM: Can not specify -M and -g flags together.\n" usage } if { $discover_macaddr && ( $macaddress != "" || $phys_loc != "" ) } { send_user "$PROGRAM: Can not specify -M and -l or -m flags together.\n" usage } if { $macaddress != "" && $phys_loc != "" } { send_user "$PROGRAM: Can not specify -l and -m flags together.\n" usage } if { $discover_all && ( $macaddress != "" || $phys_loc != "" ) } { send_user "$PROGRAM: Can not specify -A and -m or -l flags together.\n" usage } if { $discover_all && !$discovery && !$noboot } { send_user "$PROGRAM: Flag -A must be specify with flag -D for booting.\n" usage } if { $discover_macaddr && $discovery && ( $server_ip == "" || $gateway_ip == "" || $client_ip == "" || $adap_speed == "" || $adap_duplex == "" ) } { send_user "$PROGRAM: Flag -M with -D require arguments for -C, -S, -G, -s and -d.\n" usage } if { $discover_macaddr && !$discovery && ( $server_ip != "" || $gateway_ip != "" || $client_ip != "" || $adap_speed != "" || $adap_duplex != "" ) } { send_user "$PROGRAM: Flag -M with arguments for -C, -S, -G, -s and -d require -D flag.\n" usage } if { $discover_macaddr && !$noboot } { send_user "$PROGRAM: -M flag requires -n.\n" usage } if { [regexp "(\[ ]+)-" $node ] } { send_user "$PROGRAM: Error : $node\n" send_user "$PROGRAM: Node is required\n" exit 1 } if { [regexp "(\[ ]+)-" $manage ] } { send_user "$PROGRAM: Error : $managed\n" send_user "$PROGRAM: Managed system is required\n" exit 1 } if { [regexp "(\[ ]+)-" $profile ] } { send_user "$PROGRAM: Error : $profile\n" send_user "$PROGRAM: Profile is required\n" exit 1 } } # # 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. # proc send_command {} { global command global spawn_id_rconsole # sleep so that any error messages from previous command are cleared up exec sleep 1 send -i $spawn_id_rconsole -- $command } # # 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. # proc get_phandle {} { global spawn_id_rconsole global expect_out global phandle_array global full_path_name_array global rc global command global adapter_found global adap_type global dev_count global dev_pat global dev_type global DSPMSG global PROGRAM global NODENAME nc_msg "$PROGRAM Status: Starting to get full device name and phandle\n" # 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. # set timeout 30 ;# shouldn't take long nc_msg "$PROGRAM Status: sending dev / command\n" set command "dev /\r" send_command expect { -i $spawn_id_rconsole -re "dev(.*)ok(.*)0 >" { nc_msg "$PROGRAM Status: at root\n" } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } # 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. # nc_msg "$PROGRAM Status: sending ls command\n" set command "ls \r" send_command set adapter_found 0 set done 0 set timeout 60 ;# shouldn't take more than a few minutes 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. # expect { -re "(\n)(\[^\r]*)(\r)" { if { [regexp "\n(\[^\r]*):(\[ ]+)/(\[^\r]*)\r" $expect_out(0,string) x1 x2 x3 x4] } { # Each level is inspected for a match set level [expr ([string length $x3]-1)/2] set path($level) $x4 for {set j 0} {$j < $dev_count} {incr j 1} { if {[regexp $dev_pat($j) $x4]} { if {[regexp "hfi-ethernet" $x4] && $dev_pat($j) == "ethernet"} { continue } incr adapter_found 1 for {set i 0} {$i <= $level} {incr i 1} { append full_path_name_array($adapter_found) "/$path($i)" } set phandle_array($adapter_found) $x2 set adap_type($adapter_found) $dev_type($j) break } } } } -re ">" { set done 1 } timeout { send_user "$PROGRAM: Timeout isolating single line of ls output\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } # Did we find one or more adapters? if { $adapter_found > 0 } { set rc 0 } else { send_user "$PROGRAM: No network adapters found\n" set rc -1 } } # # 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. # proc get_adap_prop { phandle } { global rc global spawn_id_rconsole global expect_out global command global adap_prop_list global PROGRAM global NODENAME set adap_prop_list {} set rc 0 set timeout 120 set state 0 nc_msg "$PROGRAM Status: get_adap_prop start\n" # state 0, stack count 0 set done(0) 0 set cmd(0) "\" supported-network-types\" $phandle get-package-property\r" set msg(0) "$PROGRAM Status: rc and all supported network types now on stack\n" set pattern(0) "(.*)3 >(.*)" set newstate(0) 1 # state 1, return code and string on stack set done(1) 0 set cmd(1) ".\r" set msg(1) "$PROGRAM Status: All supported network types now on stack\n" set pattern(1) "(.*)2 >(.*)" set newstate(1) 2 # state 2, data ready to decode set done(2) 0 set cmd(2) "decode-string\r" set msg(2) "$PROGRAM Status: supported network type isolated on stack\n" set pattern(2) "(.*)ok(.*)4 >(.*)" set newstate(2) 3 # state 3, decoded string on stack set done(3) 0 set cmd(3) "dump\r" set msg(3) "$PROGRAM Status: supported network type off stack\n" set pattern(3) ".*:.*:(.*):.*:.*:(.*):.*(2 >)(.*)" set newstate(3) 4 # state 4, need to check for more data to decode set done(4) 0 set cmd(4) ".s\r" set msg(4) "$PROGRAM Status: checking for more supported network types\n" set pattern(4) ".s (\[0-9a-f]* )(.*)>" set newstate(4) 5 # state 5, done decoding string, clear stack set done(5) 0 set cmd(5) ".\r" set msg(5) "$PROGRAM Status: one entry on stack cleared\n" set pattern(5) "(.*)ok(.*)1 >(.*)" set newstate(5) 6 # state 6, finish clearing stack, choose correct adapter type set done(6) 0 set cmd(6) ".\r" set msg(6) "$PROGRAM Status: finished clearing stack\n" set pattern(6) "(.*)ok(.*)0 >(.*)" set newstate(6) 7 # state 7, done set done(7) 1 while { $done($state) == 0 } { nc_msg "$PROGRAM Status: command is $cmd($state)\n" set command $cmd($state) send_command expect { -i $spawn_id_rconsole -re $pattern($state) { nc_msg $msg($state) set state $newstate($state) # 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 == 4 } { set nw_type $expect_out(1,string)$expect_out(2,string) # 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 "$PROGRAM Status: Adapter properties are $adap_prop\n" # if it's not in the list, add it, otherwise continue if { [ lsearch adap_prop_list $adap_prop ] == -1 } { lappend adap_prop_list $adap_prop nc_msg "$PROGRAM 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 == 5 } { if { [ string index $expect_out(2,string) 0] != 0 } { set state 2 } } } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout in getting adapter properpties\n" nc_msg "timeout state is $state\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } set rc 0 return } # # 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. proc get_mac_addr { phandle } { global rc global spawn_id_rconsole global expect_out global command global PROGRAM global NODENAME set rc 0 nc_msg "$PROGRAM 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 set done(0) 0 set cmd(0) "\" local-mac-address\" $phandle get-package-property\r" set msg(0) "$PROGRAM Status: return code and mac-address now on stack\n" set pattern(0) "(.*)3 >(.*)" set 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 set done(1) 0 set cmd(1) ".\r" set msg(1) "$PROGRAM Status: mac-address now on stack\n" set pattern(1) "(.*)2 >(.*)" set 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 set done(2) 0 set 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" set msg(2) "$PROGRAM Status: set command\n" set pattern(2) "(.*)ok" set newstate(2) 3 set done(3) 0 set cmd(3) "dump-mac\r" set msg(3) "$PROGRAM Status: mac-address displayed, stack empty\n" set pattern(3) "dump-mac(.*)ok" set newstate(3) 4 # state 4, all done set done(4) 1 set state 0 set timeout 60 ;# shouldn't take long while { $done($state) == 0 } { nc_msg "$PROGRAM Status: command is $cmd($state)\n" set command $cmd($state) send_command expect { -i $spawn_id_rconsole -re $pattern($state) { nc_msg $msg($state) set state $newstate($state) } "1 > " { if { $state == 0 } { # An error occurred while obtaining the mac address. Log the error, # but don't quit nodecond. instead, return NA for the address # set command ".\r" send_command expect { -i $spawn_id_rconsole -re "(-*\[0-9\]*) ok(.*)0 >(.*)" { set mac_rc $expect_out(1,string) nc_msg "$PROGRAM Status: Error getting MAC address for phandle=$phandle. RC=$mac_rc.\n" send_user "# Could not obtain MAC address; setting MAX to NA\n" set rc 0 set mac_address "NA" return $mac_address } timeout { send_user "$PROGRAM: Timeout when getting mac address\n" nc_msg "timeout state is $state\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout in getting mac address\n" nc_msg "timeout state is $state\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } # if the state is 0, 1, or 2, an error occurred and the join will fail if { $state == 4 } { set mac_address [ join $expect_out(1,string) "" ] set rc 0 return $mac_address } else { set rc -1 } } # # 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. proc get_adaptr_loc { phandle } { global rc global spawn_id_rconsole global expect_out global command global PROGRAM global NODENAME set rc 0 nc_msg "$PROGRAM 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 set done(0) 0 set cmd(0) "\" ibm,loc-code\" $phandle get-package-property\r" set msg(0) "$PROGRAM Status: return code and loc-code now on stack\n" set pattern(0) "(.*)3 >(.*)" set 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 set done(1) 0 set cmd(1) ".\r" set msg(1) "$PROGRAM Status: loc-code now on stack\n" set pattern(1) "(.*)2 >(.*)" set newstate(1) 2 # state 2, loc-code on stack set done(2) 0 set cmd(2) "dump\r" set msg(2) "$PROGRAM Status: loc-code displayed, stack empty\n" set pattern(2) "(.*)(: )(.*)( :)(.*)(\.: ok)" set newstate(2) 3 # state 3, all done set done(3) 1 set state 0 set timeout 60 ;# shouldn't take long while { $done($state) == 0 } { nc_msg "$PROGRAM Status: command is $cmd($state)\n" set command $cmd($state) send_command expect { -i $spawn_id_rconsole -re $pattern($state) { nc_msg $msg($state) set state $newstate($state) } "1 > " { if { $state == 0 } { set command ".\r" send_command expect { -i $spawn_id_rconsole -re "(-*\[0-9\]*) ok(.*)0 >(.*)" { set loc_rc $expect_out(1,string) send_user "$PROGRAM: Error getting adapter physical location.\n" nc_msg "$PROGRAM Status: Error getting physical location for phandle=$phandle. RC=$loc_rc.\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout when openning console\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout in openning console\n" nc_msg "$PROGRAM Status: timeout state is $state\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } if { $state == 3 } { set rc 0 if { [regexp "(.*)(: )(.*)( :)(.*)(:)" $expect_out(1,string) v1 v2 v3 v4 v5 v6 ] } { return $v6$expect_out(5,string) } else { return $expect_out(5,string) } } else { set rc -1 } } # # PROCEDURE # # Declare procedure to ping the given server through the specified LAN adapter. # The procedure will return 0 if the ping is successful, and 1 if the ping # was unsuccessful. The global rc variable will be set if there's an error. # proc ping_server { full_path_name phandle } { global spawn_id_rconsole global expect_out global server_ip global client_ip global gateway_ip global list_type global adap_speed global adap_duplex global command global adap_prop_list global rc global PROGRAM global NODENAME global env global mac_address nc_msg "$PROGRAM Status: ping_server start\n" if {[info exists env(FIRMWARE_DUMP)]} { Firmware_Dump $full_path_name $phandle } set j 0 set tty_do_ping 0 set stack_level 0 set properties_matched 0 set adap_conn "" set speed_list {} set duplex_list {} set adap_conn_list {} # 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 set done(0) 0 set cmd(0) "dev $full_path_name\r" set msg(0) "$PROGRAM Status: selected $full_path_name as the active adapter\n" set pattern(0) ".*dev(.*)0 >(.*)" set newstate(0) 1 # state 1, send property command to set selected type set done(1) 0 set cmd(1) "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r" set msg(1) "$PROGRAM Status: chosen network type set\n" set pattern(1) ".*ethernet(.*)0 >(.*)" set newstate(1) 2 # state 2, activate /packages/net set done(2) 0 set cmd(2) "dev /packages/net\r" set msg(2) "$PROGRAM Status: selected the /packages/net node as the active package\n" set pattern(2) ".*dev(.*)ok(.*)0 >(.*)" set newstate(2) 3 # state 3, ping the server set done(3) 0 set msg(3) "$PROGRAM Status: ping return code now on stack\n" set newstate(3) 4 #IPv6 if { [regexp ":" $server_ip]!=0 } { #::1, calculate link local address if { [string compare "$client_ip" "::1"] == 0 } { #set macaddr [get_mac_addr $phandle] set linklocal_ip [exec /opt/xcat/share/xcat/tools/mac2linklocal -m $mac_address] } else { set linklocal_ip $client_ip } set cmd(3) "ping $full_path_name:ipv6,$server_ip,$linklocal_ip,$gateway_ip\r" } else { set cmd(3) "ping $full_path_name:$server_ip,$client_ip,$gateway_ip\r" } set pattern(3) ".*ping(.*)ok(.*)0 >(.*)" # state 4, all done set done(4) 0 set cmd(4) "0 to my-self\r" set msg(4) "$PROGRAM Status: resetting pointer\n" set pattern(4) "(.*)ok(.*)0 >(.*)" set newstate(4) 5 # state 5, all done set done(5) 1 # for ping, only need to set speed and duplex for ethernet adapters # if { $list_type == "ent" } { set state 0 # Get the list of properties for this adapter # get_adap_prop $phandle if { $rc != 0 } { exit 1 } if { [ llength $adap_prop_list ] == 0 } { send_user "$PROGRAM: No properties found for adapter '$full_path_name'\n" set rc 0 return 1 } # Now need to verify that the network params we were passed are valid for # the given adapter # foreach prop $adap_prop_list { regexp (.*),(.*),(.*) $prop dummy a_speed a_conn a_duplex if { ( $a_speed == $adap_speed ) && ( $a_duplex == $adap_duplex ) } { set properties_matched 1 if { [ lsearch $adap_conn_list $a_conn ] == -1 } { lappend adap_conn_list $a_conn } } } if { $properties_matched == 0 } { set adap_speed $a_speed set adap_duplex $a_duplex set properties_matched 1 lappend adap_conn_list $a_conn } set i [ llength $adap_conn_list ] if { $properties_matched == 0 } { send_user "$PROGRAM: '$adap_speed/$adap_duplex' settings are not supported on this adapter\n" set rc 0 return 1 } } else { set state 2 } set timeout 300 while { $done($state) == 0 } { nc_msg "$PROGRAM Status: command is $cmd($state)\n" set command $cmd($state) send_command expect { -i $spawn_id_rconsole -re $pattern($state) { nc_msg $msg($state) set state $newstate($state) if { $state == 1 } { set adap_conn [ lindex $adap_conn_list $j ] set cmd(1) "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r" nc_msg "$PROGRAM Status: Trying connector type $adap_conn\n" incr j 1 } if { ( ($tty_do_ping == 1) && ($state == 4) ) || ($tty_do_ping != 1) && ($state == 3) } { set ping_debug $expect_out(buffer) } if { ( ($tty_do_ping == 1) && ($state == 5) ) || ($tty_do_ping != 1) && ($state == 4) } { if { ($tty_do_ping == 1) && ($state == 5) } { set ping_rc $expect_out(1,string) set stack_level $expect_out(3,string) } elseif { ($state == 4) && ($tty_do_ping != 1) && ([regexp "PING SUCCESS" $expect_out(buffer)]) } { set ping_rc 0 } elseif { [regexp "unknown word" $expect_out(buffer)] } { nc_msg "$PROGRAM Status: try tty-do-ping.\n" set ping_rc 1 set tty_do_ping 1 set state 3 set cmd(3) "\" $full_path_name:$client_ip,$server_ip,$gateway_ip\" tty-do-ping\r" set pattern(3) "(.*)ok(.*)(\[1-2\]) >(.*)" # state 4, get the return code off the stack set done(4) 0 set cmd(4) ".\r" set msg(4) "$PROGRAM Status: return code displayed, stack empty\n" set pattern(4) "(\[0-9\]*) ok(.*)(\[0-1\]) >(.*)" set 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, reset pointer set done(5) 0 set cmd(5) "0 to my-self\r" set msg(5) "$PROGRAM Status: resetting pointer\n" set pattern(5) "(.*)ok(.*)0 >(.*)" set newstate(5) 6 # state 6, all done set done(6) 1 } else { set ping_rc 1 } if { $ping_rc == 0 } { send_user "# $full_path_name ping successful.\n" } elseif { $ping_rc == 1 } { send_user "# $full_path_name ping unsuccessful.\n" nc_msg "# $full_path_name ping unsuccessful.\n" nc_msg "$ping_debug\n" # An unsuccessful return may leave another item on the stack to # be removed. Check for it, and remove if necessary while { $stack_level != 0 } { set command ".\r" send_command expect { -i $spawn_id_rconsole -re "(\[0-9\]*) ok(.*)(\[0-1\]) >(.*)" { set stack_level $expect_out(3,string) nc_msg "$PROGRAM Status: stack_level is <$stack_level>\n" } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set 1 return } timeout { send_user "$PROGRAM: Timeout in ping server\n" set 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set 1 return } } } # Check if there are any more adapter connector types # to try # if { ( $list_type == "ent" ) && ( $j < $i ) } { set adap_conn [ lindex $adap_conn_list $j ] nc_msg "$PROGRAM Status: Trying connector type $adap_conn\n" incr j 1 # 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. # set done(0) 0 set cmd(0) "0 to my-self\r" set msg(0) "$PROGRAM Status: resetting pointer\n" set pattern(0) "(.*)ok(.*)0 >(.*)" set newstate(0) 1 set state 0 } } else { send_user "$PROGRAM: Unexpected ping return code\n" set rc 1 return } } } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout when pinging server\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } set rc 0 return $ping_rc } # # PROCEDURE # proc set_disk_boot {} { global rc global expect_out global spawn_id_rconsole global command global PROGRAM global NODENAME global phandle_array global full_path_name_array global adapter_found global adap_type global sms_dev_array_loc global sms_dev_array_name global sms_dev_array_os global sms_dev_array_os_ver global colon global list_physical global boot_device # state 0, get SMS screen set done(0) 0 regexp "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Select Boot Options(\r)" $expect_out(0,string) x0 x1 x2 command set cmd(0) "$command\r" set msg(0) "$PROGRAM Status: sending return to repaint SMS screen\n" set pattern(0) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Configure Boot Device Order(\r)" set newstate(0) 1 # state 1, Multiboot set done(1) 0 set msg(1) "$PROGRAM Status: Multiboot\n" set pattern(1) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Select 1st Boot Device(\r)" set newstate(1) 2 # state 2, Configure Boot Device Order set done(2) 0 set msg(2) "$PROGRAM Status: Configure Boot Device Order" set pattern(2) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Hard Drive(.*)" set newstate(2) 3 # state 3, Select Device Type set done(3) 0 set msg(3) "$PROGRAM Status: Select Device Type" set pattern(3) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)SCSI(.*)" set newstate(3) 4 # state 4, Select Media Type set done(4) 0 set msg(4) "$PROGRAM Status: Select Media Type" set pattern(4) "(\n)(\[ ])(\[1])(\[.])(\[ ]+)(\\S+)(.*)" set newstate(4) 5 # state 5, Select Media Adapter set done(5) 0 set msg(5) "$PROGRAM Status: Select Media Adapter" set pattern(5) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)(\\S)(\[ ]+)SCSI (\[0-9]+) MB Harddisk(.*)loc=(.*)\[)]" set newstate(5) 6 # state 6, Select Device set done(6) 0 set msg(6) "$PROGRAM Status: Select Device" set pattern(6) "(\n)(\[ ])(\[0-9])(\[.])(\[ ]+)Set Boot Sequence(.*)" set newstate(6) 7 # state 7, Select Task set done(7) 0 set msg(7) "$PROGRAM Status: Select Task" set pattern(7) "(.*)Current Boot Sequence(.*)" set newstate(7) 8 # state 8, Return to Main Menu set done(8) 0 set cmd(8) "M" set msg(8) "$PROGRAM Status: Restored Default Setting.\n" set pattern(8) "(.*)Navigation key(.*)" set newstate(8) 9 # state 9, Getting to SMS Main Menu set done(9) 0 set cmd(9) "0\r" set msg(9) "$PROGRAM Status: Getting to SMS Main Menu.\n" set pattern(9) "(.*)Exit SMS(.*)Prompt?(.*)" set newstate(9) 10 # state 10, Exiting SMS set done(10) 0 set cmd(10) "Y" set msg(10) "$PROGRAM Status: Exiting SMS.\n" set pattern(10) "(.*)ok(.*)0 >(.*)" set newstate(10) 11 # state 11, all done set done(11) 1 set timeout 30 set state 0 while { $done($state) == 0 } { set command $cmd($state) send_command expect { -i $spawn_id_rconsole -re $pattern($state) { if { $state == 4 } { if { $expect_out(6,string) == "None" } { set state 8 } } set state $newstate($state) if { ($state != 8) && ($state != 9) && ($state != 10) } { set cmd($state) "$expect_out(3,string)\r" } } -re "THE SELECTED DEVICES WERE NOT DETECTED IN THE SYSTEM" { send_user "$PROGRAM Status: THE hard disk WERE NOT DETECTED IN THE SYSTEM!\n" set rc 1 #return exit 1 } timeout { send_user "$PROGRAM: Timeout in settin boot order\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } } ################################################################### # # 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. ################################################################### proc boot_network {} { global rc global spawn_id_rconsole global full_path_name global command global speed global duplex global chosen_adap_type global server_ip global client_ip global gateway_ip global netmask global dump_target global dump_lun global dump_port global extra_args global PROGRAM global NODENAME global set_boot_order set rc 0 set i 0 nc_msg "$PROGRAM 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 set done(0) 0 if {$dump_target != ""} { set 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" set pattern(0) "iSCSI" } else { if {$extra_args != "" } { if { [regexp ":" $server_ip]!=0 } { #ipv6 set net_device(0) "$full_path_name:ipv6,speed=$speed,duplex=$duplex,siaddr=$server_ip,ciaddr=$client_ip,giaddr=$gateway_ip,filename=$NODENAME,$extra_args" } else { set net_device(0) "$full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip $extra_args" } } else { if { [regexp ":" $server_ip]!=0 } { #ipv6 set net_device(0) "$full_path_name:ipv6,speed=$speed,duplex=$duplex,siaddr=$server_ip,ciaddr=$client_ip,giaddr=$gateway_ip,filename=$NODENAME" } else { set net_device(0) "$full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip" } } set pattern(0) "BOOTP" } set cmd(0) "boot $net_device(0)\r" set msg(0) "$PROGRAM Status: network boot initiated\n" set newstate(0) 99 # If the install adapter is FDDI, don't set the speed and duplex # state 1 set done(1) 0 set net_device(1) "$full_path_name:bootp,$server_ip,,$client_ip,$gateway_ip" set cmd(1) "boot $net_device(1)\r" set msg(1) "$PROGRAM Status: network boot initiated\n" set pattern(1) "BOOTP" set newstate(1) 99 # state 99, all done set done(99) 1 # state -1, all done set done(-1) 1 if { $chosen_adap_type == "fddi" } { set state 1 } else { if { $speed == "" || $duplex == "" } { send_user "$PROGRAM: Cannot set speed or duplex for network boot\n" set rc 1 return } set state 0 } ################################################################## # Set the boot device order. ################################################################## if { $set_boot_order > 0 } { set done(2) 0 set msg(2) "$PROGRAM Status: read original boot-device\n" set cmd(2) "printenv boot-device\r" set pattern(2) ".*boot-device\\s+(\\S+)(.*)ok(.*)" set newstate(2) 3 set done(3) 0 set msg(3) "$PROGRAM Status: set the environment variable boot-device\n" set pattern(3) "(.*)ok(.*)(\[0-9]) >(.*)" if { $state == 0 } { set newstate(3) 0 } else { set newstate(3) 1 } set state 2 } set timeout 30 ;# shouldn't take long while { $done($state) == 0 } { set command $cmd($state) send_command expect { -i $spawn_id_rconsole -nocase -re $pattern($state) { if { $state == 2 } { if { $set_boot_order == 1 } { ######################################## # Set network as boot device ######################################## set cmd(3) "setenv boot-device $net_device($newstate(3))\r" } elseif { $set_boot_order == 2 } { ######################################## # Set network as 1st boot device,disk as 2nd boot device ######################################## set boot_device_bk $expect_out(1,string) set cmd(3) "setenv boot-device $net_device($newstate(3)) $boot_device_bk\r" } elseif { $set_boot_order == 3 } { ######################################## # Set disk as 1st boot device,network as 2nd boot device ######################################## set boot_device_bk $expect_out(1,string) set cmd(3) "setenv boot-device $boot_device_bk $net_device($newstate(3))\r" } elseif { $set_boot_order == 4 } { ######################################## # set disk as boot device ######################################## set boot_device_bk $expect_out(1,string) set cmd(3) "setenv boot-device $boot_device_bk\r" } } nc_msg $msg($state) set state $newstate($state) } -re "----" { nc_msg $msg($state) set state $newstate($state) } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout in boot network\n" nc_msg "timeout state is $state\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } } # # PROCEDURE # # proc Boot {} { global rc global expect_out global spawn_id_rconsole global PROGRAM global NODENAME set rc 0 nc_msg "$PROGRAM Status: waiting for the boot image to boot up.\n" set timeout 1200 ;# could take a while depending on configuration # check for bootp response error expect { -i $spawn_id_rconsole -re "RESTART-CMD" { # 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 send_user "$PROGRAM: The network boot ended in an error.\n" send_user "$PROGRAM: Error : $RESTART-CMD\n" set rc 5 return } -nocase -re {!([0-9A-F]+)} { send_user "$PROGRAM: The network boot ended in an error.\n" nc_msg $expect_out(buffer) set rc 1 return } -ex {BOOTP} { nc_msg "$PROGRAM: # Network boot proceeding, exiting.\n" } -ex {------} { nc_msg "$PROGRAM: # Network boot proceeding, exiting.\n" } -ex {iSCSI} { nc_msg "$PROGRAM: # Network boot proceeding, exiting.\n" } timeout { set mins [expr ($timeout/60)] send_user "$PROGRAM: Timeout waiting for the boot image to boot up. \ \n$PROGRAM 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 $expect_out(buffer) set rc 1 return } eof { send_user "$PROGRAM: Port closed waiting for boot image to boot.\n" set rc 1 return } } } ################################################################### # # 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. # ################################################################### proc multiple_open-dev {} { global rc global expect_out global spawn_id_rconsole global PROGRAM global NODENAME global comman global env global command set command "dev /packages/net \r" send_command set command "FALSE value OPEN-DEV_DEBUG \r" send_command if {[info exists env(OPEN_DEV_DEBUG)]} { set command "TRUE to OPEN-DEV_DEBUG \r" send_command } set 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 set command " patch new-open-dev open-dev net-ping \r" send_command set timeout 30 expect { -i $spawn_id_rconsole -re "patch new-open-dev(.*)>" { nc_msg "$PROGRAM Status: at End of multiple_open-dev \n" } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } ################################################################### # # PROCEDURE # # Declare procedure to get additional firmware debug statement. # ################################################################### proc Firmware_Dump { device_path phandle } { global rc global expect_out global spawn_id_rconsole global PROGRAM global NODENAME global command nc_msg "$PROGRAM Status: Firmware_Dump start\n" # state 0 set done(0) 0 set cmd(0) "dev /packages/obp-tftp\r" set msg(0) "$PROGRAM Status: selected /packages/obp_tftp\n" set pattern(0) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(0) 1 # state 1 set done(1) 0 set cmd(1) ": testing1 .\" OBP-TFTP entry\" cr init-nvram-adptr-parms ;\r" set msg(1) "$PROGRAM Status: running test - OBP-TFTP entry\n" set pattern(1) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(1) 2 # state 2 set done(2) 0 set cmd(2) ": testing2 .\" OBP-TFTP exit, TRUE\" cr true ;\r" set msg(2) "$PROGRAM Status: running test - OBP-TFTP exit\n" set pattern(2) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(2) 3 # state 3 set done(3) 0 set cmd(3) "patch testing1 init-nvram-adptr-parms open\r" set msg(3) "$PROGRAM Status: running test - patch testing1\n" set pattern(3) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(3) 4 # state 4 set done(4) 0 set cmd(4) "patch testing2 true open\r" set msg(4) "$PROGRAM Status: running test - patch testing2\n" set pattern(4) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(4) 5 # state 5 set done(5) 0 set cmd(5) "dev $device_path\r" set msg(5) "$PROGRAM Status: running test - dev $device_path\n" set pattern(5) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(5) 6 # state 6 set done(6) 0 set cmd(6) "true to debug-init\r" set msg(6) "$PROGRAM Status: running test - true to debug-init\n" set pattern(6) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(6) 7 # state 7 set done(7) 0 set cmd(7) "true to debug-error\r" set msg(7) "$PROGRAM Status: running test - true to debug-error\n" set pattern(7) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(7) 8 # state 8 set done(8) 0 set cmd(8) "$phandle to active-package\r" set msg(8) "$PROGRAM Status: running $phandle to active-package\n" set pattern(8) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(8) 9 # state 9 set done(9) 0 set cmd(9) ".properties\r" set msg(9) "$PROGRAM Status: running .properies\n" set pattern(9) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(9) 10 # state 10 set done(10) 0 set cmd(10) "clear\r" set msg(10) "$PROGRAM Status: running clear\n" set pattern(10) "(.*)ok(.*)(\[0-9]) >(.*)" set newstate(10) 11 # state 11, all done set done(11) 1 set state 0 set timeout 30 ;# shouldn't take long while { $done($state) == 0 } { set command $cmd($state) nc_msg "$PROGRAM Status: command is $cmd($state)\n" send_command expect { -i $spawn_id_rconsole -re $pattern($state) { nc_msg $msg($state) set state $newstate($state) } -re "]" { send_user "$PROGRAM: Unexpected prompt\n" set rc 1 return } -re "(.*)DEFAULT(.*)" { send_user "$PROGRAM: Default catch error\n" set rc 1 return } timeout { send_user "$PROGRAM: Timeout\n" nc_msg "$PROGRAM Status: timeout state is $state\n" set rc 1 return } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" set rc 1 return } } } } # # Main MAIN main # set BINPATH "/opt/xcat/bin" set PROGRAM "lpar_netboot" set CONSOLEBIN "/opt/xcat/bin/rcons" set noboot 0 ;# default is to boot set Boot_timeout 3000 global expect_out global loc_code # Flags and initial variable set discovery 0 set discover_all 0 set hdr_printed 0 set verbose 0 set discover_macaddr 0 set rc 0 set debug_flag 0 set rmvterm_flag 0 set immed_flag 0 set from_of 0 set dev_type_found 0 set list_physical 0 set set_boot_order 0 set colon 0 set choice 0 set full_path_name "" set adap_speed "" set adap_duplex "" set client_ip "" set server_ip "" set gateway_ip "" set extra_args "" set macaddress "" set netmask "" set dump_target "" set dump_lun "" set dump_port "" set phys_loc "" set userid "" set passwd "" set prompt "\\\$ \$" set ssh_spawn_id 0 set mac_address "" # 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. set dev_pat(0) "ethernet" set dev_type(0) "ent" set dev_pat(1) "token-ring" set dev_type(1) "tok" set dev_pat(2) "fddi" set dev_type(2) "fddi" set dev_count [array size dev_pat] # # Log the process id # set proc_id [ eval pid ] nc_msg "$PROGRAM Status: process id is $proc_id\n" # # # Process command line options and parameters # # while { [llength $argv] > 0} { set flag [lindex $argv 0] switch -glob -- $flag { "-A" { set discover_all 1 set argv [lrange $argv 1 end] } "-C" { set client_ip [lindex $argv 1] set argv [lrange $argv 2 end] } "-D" { set discovery 1 set argv [lrange $argv 1 end] } "-G" { set gateway_ip [lindex $argv 1] set argv [lrange $argv 2 end] } "-P" { set list_physical 1 set argv [lrange $argv 1 end] } "-M" { set discover_macaddr 1 set argv [lrange $argv 1 end] } "-N" { set netmask [lindex $argv 1] set argv [lrange $argv 2 end] } "-S" { set server_ip [lindex $argv 1] set argv [lrange $argv 2 end] } "-c" { set colon 1 set argv [lrange $argv 1 end] } "-d" { set adap_duplex [lindex $argv 1] set argv [lrange $argv 2 end] } "-f" { set rmvterm_flag 1 set argv [lrange $argv 1 end] } "-g" { set extra_args [lindex $argv 1] set argv [lrange $argv 2 end] } "-i" { set immed_flag 1 set argv [lrange $argv 1 end] } "-o" { set from_of 1 set argv [lrange $argv 1 end] } "-w" { set set_boot_order [lindex $argv 1] set argv [lrange $argv 2 end] } "-L" { set dump_lun [lindex $argv 1] set argv [lrange $argv 2 end] } "-l" { set phys_loc [lindex $argv 1] set argv [lrange $argv 2 end] } "-m" { set macaddress [lindex $argv 1] set argv [lrange $argv 2 end] } "-n" { set noboot 1 set argv [lrange $argv 1 end] } "-p" { set dump_port [lindex $argv 1] set argv [lrange $argv 2 end] } "-s" { set adap_speed [lindex $argv 1] set argv [lrange $argv 2 end] } "-T" { set dump_target [lindex $argv 1] set argv [lrange $argv 2 end] } "-t" { set list_type [lindex $argv 1] set argv [lrange $argv 2 end] if { $list_type == "hfi-ent" } { set dev_pat(0) "hfi-ethernet" set dev_type(0) "hfi-ent" } # # Validate the argument # set dev_type_found 0 foreach dev [array names dev_type] { if {$dev_type($dev) == $list_type} { set dev_type_found 1 break } } if {$dev_type_found == 0 } { send_user "$PROGRAM:$dev_type_found, '$list_type' is not a valid adapter choice\n" exit 1 } } "-v" { set verbose 1 set argv [lrange $argv 1 end] } "-x" { set debug_flag 1 set argv [lrange $argv 1 end] } "--help" { usage } "-*" { send_user "$PROGRAM: Illegal option: $flag\n" usage } default {break} } } if { [llength $argv] < 1 } { send_user "$PROGRAM: Missing parameter\n" usage } set arg0 [lindex $argv 0] set arg1 [lindex $argv 1] if { [llength $argv] > 10 } { send_user "$PROGRAM: Extraneous parameter(s)\n" usage } if { $list_physical == 0 } { set dev_pat(3) "l-lan" set dev_type(3) "ent" set dev_count [array size dev_pat] } else { set dev_pat(3) "" set dev_type(3) "" set dev_count [array size dev_pat] } if { $set_boot_order > 1 } { set dev_pat(4) "scsi" set dev_type(4) "disk" set dev_count [array size dev_pat] } set node [lindex $argv 0] set profile [lindex $argv 1] set manage [lindex $argv 2] set lparid [lindex $argv 3] set hcp [lindex $argv 4] set name [lindex $argv 5] if {$dev_type_found} { nc_msg "$PROGRAM Status: List only $list_type adapters\n" } if {$noboot} { nc_msg "$PROGRAM Status: -n (no boot) flag detected\n" } if {$discovery} { nc_msg "$PROGRAM Status: -D (discovery) flag detected\n" } if {$discover_all} { nc_msg "$PROGRAM Status: -A (discover all) flag detected\n" } if {$verbose} { nc_msg "$PROGRAM Status: -v (verbose debug) flag detected\n" } if {$discover_macaddr} { nc_msg "$PROGRAM Status: -M (discover mac address) flag detected\n" } if {$immed_flag} { nc_msg "$PROGRAM Status: -i (force immediate shutdown) flag detected\n" } if {$list_physical} { nc_msg "$PROGRAM Status: -P (list only phsical network) flag detected\n"} if {$colon} { nc_msg "$PROGRAM Status: -c (list colon separated ) flag detected\n" } if {$debug_flag} { nc_msg "$PROGRAM Status: -d (debug) flag detected\n" exp_internal 1 log_user 1 } if {$discovery && $adap_speed != ""} { nc_msg "$PROGRAM Status: using adapter speed of $adap_speed\n" } if {$discovery && $adap_duplex != ""} { nc_msg "$PROGRAM Status: using adapter duplex of $adap_duplex\n" } if {$discovery && $server_ip != ""} { nc_msg "$PROGRAM Status: using server IP address of $server_ip\n" } if {$discovery && $client_ip != ""} { nc_msg "$PROGRAM Status: using client IP address of $client_ip\n" } if {$discovery && $gateway_ip != ""} { nc_msg "$PROGRAM Status: using gateway IP address of $gateway_ip\n" } if {$discovery && $macaddress != ""} { nc_msg "$PROGRAM Status: using macaddress of $macaddress\n" } if {$discovery && $phys_loc != ""} { nc_msg "$PROGRAM Status: using physical location code of $phys_loc\n" } nc_msg "node: $node\n" nc_msg "name: $name\n" set node $name set NODENAME $node ck_args if {$extra_args != "" } { nc_msg "$PROGRAM Status: extra arguments sent to booting: $extra_args\n" } if { ! $noboot } { set hdw_addr "" set speed $adap_speed set duplex $adap_duplex } # Get userid and password set userid $env(HCP_USERID) set passwd $env(HCP_PASSWD) # # Connect to the remote server # nc_msg "$PROGRAM Status: connecting to $hcp\n" #connect nc_msg "$PROGRAM Status: finished connecting to $hcp\n" # # open the S1 serial port # nc_msg "$PROGRAM Status: open port\n" if [ catch { spawn $CONSOLEBIN $name -f } rsconole_pid ] { send_user "Unable to spawn console connection to $hcp\n" exit 1 } set spawn_id_rconsole $spawn_id nc_msg "$PROGRAM Status: spawn_id is $spawn_id\n" # # Signal handling stuff # trap { global PROGRAM set signal SIG[trap -name] send_user "$PROGRAM: Received signal named '$signal'\n" exec kill -KILL $spawn_id_rconsole $ssh_spawn_id exit -1 } {INT HUP QUIT TERM} set timeout 30 set pwd_sent 0 send_user "# Connecting to $name\n" expect { -i $spawn_id_rconsole \ -re "Enter.* for help.*" { send_user "# Connected\n" } timeout { send_user "$PROGRAM: Timeout waiting for console connection\n" exit 1 } eof { send_user "$PROGRAM: Please make sure rcons $name works\n" exit 1 } } send_user "# Checking for power off.\n" run_lssyscfg if { [string compare "$msg" "Not Available"] == 0 } { send_user "$PROGRAM: LPAR is Not Available. Please make sure the CEC's state\n" set rc 1 exit 1 } if { $from_of } { if { ([string compare "$msg" "open-firmware"] != 0) && ([string compare "$msg" "Open Firmware"] != 0) } { send_user "$PROGRAM: You used the -o option. Please make sure the LPAR's initial state is open firmware\n" set rc 1 exit 1 } } if { $from_of != 1 } { if { ([string compare "$msg" "Off"] == 0) || ([string compare "$msg" "off"] == 0) || ([string compare "$msg" "Not Activated"] == 0) } { send_user "# Power off complete.\n" } else { # # power off the node # send_user "# Power off the node.\n" set cmd "0" if { $immed_flag } { #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" --immed -n \"$node\"" set cmd [list exec $BINPATH/rpower $node off] } else { #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" -n \"$node\"" set cmd [list exec $BINPATH/rpower $node off] } set timeout 120 set rc [catch $cmd msg] if { $rc } { send_user "$PROGRAM: Cannot power off $NODENAME\n" nc_msg "$PROGRAM Status: error from rpower command\n" send_user "$PROGRAM: Error : $msg\n" exit 1 } send_user "# Wait for power off.\n" # # chsysstate will return a successful result if it was able to pass the power # command from the IVM to the node, but the node may not be powered off yet. Need # to query to make sure the node is off before we continue # set done 0 set query_count 0 while { ! $done } { run_lssyscfg # # separate the nodename from the query status # if { ([string compare "$msg" "Off"] == 0) || ([string compare "$msg" "off"] == 0) || ([string compare "$msg" "Not Activated"] == 0) } { send_user "# Power off complete.\n" set done 1 continue } incr query_count if { $query_count > 300 } { send_user "$PROGRAM: Timed out waiting for power off of $NODENAME\n" nc_msg "$PROGRAM Status: error from rpower command : \"$msg\"\n" exit 1 } sleep 1 } } set done 0 set retry_count 0 if { $set_boot_order > 1 } { send_user "$PROGRAM Status: Power on $NODENAME to SMS.\n" set timeout 120 while { ! $done } { set cmd [list exec $BINPATH/rpower $node sms] set rc [catch $cmd msg] nc_msg "$PROGRAM Status: wait for power on\n" if { $rc } { if { $retry_count == 3 } { nc_msg "$PROGRAM Status: error from rpower command: $msg\n" send_user "$PROGRAM Status: error from rpower command: $msg\n" exit 1 } sleep 1 incr retry_count } else { set done 1 } } } else { send_user "# Power on $NODENAME to Open Firmware.\n" set timeout 120 while { ! $done } { #send -i $ssh_spawn_id "chsysstate -r lpar -o on -b of -m \"$manage\" -n \"$node\" -f \"$profile\"; echo Rc=\$\?\r" set cmd [list exec $BINPATH/rpower $node of] set rc [catch $cmd msg] nc_msg "$PROGRAM Status: wait for power on\n" if { $rc } { if { $retry_count == 3 } { nc_msg "$PROGRAM Status: error from rpower command: $msg\n" send_user "$PROGRAM Status: error from rpower command: $msg\n" exit 1 } sleep 1 incr retry_count } else { set done 1 } } } nc_msg "$PROGRAM Status: wait for power on\n" if { $rc } { send_user "$PROGRAM: Cannot power on $NODENAME\n" nc_msg "$PROGRAM Status: error from rpower command\n" send_user "$PROGRAM: Error : $msg\n" exit 1 } # # Verify node is powered on before continuing # set done 0 set query_count 0 while { ! $done } { run_lssyscfg # # separate the nodename from the query status # if { ([string compare "$msg" "Open Firmware"] == 0) || ([string compare "$msg" "Open firmware"] == 0) || ([string compare "$msg" "open-firmware"] == 0) } { send_user "# Power on complete.\n" set done 1 continue } set timeout 1 expect { -i $spawn_id_rconsole -re "(.*)elect this consol(.*)" { nc_msg "$PROGRAM Status: selecting active console\n" set command "0" send_command } } incr query_count if { $query_count > 300 } { send_user "$PROGRAM: Timed out waiting for power on of $NODENAME\n" nc_msg "$PROGRAM Status: error from rpower command : \"$msg\" \n" exit 1 } sleep 1 } } set retry 0 set done 0 set timeout 10 nc_msg "$PROGRAM Status: Check for active console.\n" while { ! $done } { expect { -i $spawn_id_rconsole -re "(.*)elect this consol(.*)" { nc_msg "$PROGRAM Status: selecting active console\n" set command "0" send_command } -re "English|French|German|Italian|Spanish|Portuguese|Chinese|Japanese|Korean" { nc_msg "$PROGRAM Status: Languagae Selection Panel received\n" set command "2\r" send_command } -re "'admin'" { nc_msg "$PROGRAM Status: No password specified\n" send_user "$PROGRAM: No password specified\n" exit 1 } -re "Invalid Password" { nc_msg "$PROGRAM Status: FSP password is invalid.\n" send_user "$PROGRAM Status: FSP password is invalid.\n" exit 1 } -re "ok(.*)0 >" { nc_msg "$PROGRAM Status: at ok prompt\n" set done 1 } -re "SMS(.*)Navigation Keys" { nc_msg "$PROGRAM Status: SMS\n" set done 1 } timeout { set command "\r" send_command incr retry if { $retry == 9 } { send_user "$PROGRAM: Timeout waiting for ok prompt; exiting.\n" exit 1 } } eof { send_user "$PROGRAM: Cannot connect to $NODENAME\n" exit 1 } } } if { $set_boot_order > 1 } { set_disk_boot } global phandle set done 0 set retry 0 exec sleep 1 while { ! $done } { # # Call get_phandle to gather information for all the supported network adapters # in the device tree. # get_phandle if { $rc } { incr retry set command "\r" send_command if { $retry == 3 } { send_user "$PROGRAM: Unable to obtain network adapter information. Quitting.\n" exit 1 } } else { set done 1 } } # Call multiple_open-dev to circumvent firmware OPEN-DEV failure multiple_open-dev if { $rc } { nc_msg "Unable to create multiple_open-dev.\n" send_user "$PROGRAM: Unable to obtain network adapter information. Quitting.\n" exit 1 } if { $discovery } { send_user "# Client IP address is $client_ip\n" send_user "# Server IP address is $server_ip\n" send_user "# Gateway IP address is $gateway_ip\n" } if { $noboot } { # Display information for all supported adapters if {[info exists list_type]} { set match_pat $list_type } else { set match_pat ".*" ;# match anything } if { $colon } { send_user "#Type:Location_Code:MAC_Address:Full_Path_Name:Ping_Result:Device_Type:Size_MB:OS:OS_Version:\n" } else { send_user "# Type \tLocation Code \tMAC Address\t Full Path Name\t Ping Result\n" } if { $discover_all } { for {set i 1} {$i <= $adapter_found} {incr i 1} { if {[regexp $match_pat $adap_type($i)] != 0 } { if { $adap_type($i) != "hfi-ent" } { set mac_address [get_mac_addr $phandle_array($i)] set loc_code [get_adaptr_loc $phandle_array($i)] } set ping_result "" if { $discovery } { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] nc_msg "$PROGRAM Status: ping_server returns $ping_rc\n" if { $ping_rc != 0 } { set ping_result "unsuccessful" } else { set ping_result "successful" } } if { $adap_type($i) == "hfi-ent" } { set mac_address [get_mac_addr $phandle_array($i)] set loc_code [get_adaptr_loc $phandle_array($i)] } if { [string first "vdevice" "$full_path_name_array($i)"] != -1 } { set device_type "virtual" } else { set device_type "physical" } if { $colon } { send_user "$adap_type($i)\:$loc_code\:$mac_address\:$full_path_name_array($i)\:$ping_result\:$device_type\:\:\:\:\n" } else { send_user "$adap_type($i) $loc_code $mac_address $full_path_name_array($i) $ping_result $device_type\n" } } } } else { for {set i 1} {$i <= $adapter_found} {incr i 1} { if {[regexp $match_pat $adap_type($i)] != 0 } { if { $adap_type($i) == "hfi-ent" } { set mac_address [get_mac_addr $phandle_array($i)] set loc_code [get_adaptr_loc $phandle_array($i)] } set ping_result "" if { $discovery } { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] nc_msg "$PROGRAM Status: ping_server returns $ping_rc\n" if { $ping_rc != 0 } { set ping_result "unsuccessful" } else { set ping_result "successful" } } if { $adap_type($i) == "hfi-ent" } { set mac_address [get_mac_addr $phandle_array($i)] set loc_code [get_adaptr_loc $phandle_array($i)] } if { [string first "vdevice" "$full_path_name_array($i)"] != -1 } { set device_type "virtual" } else { set device_type "physical" } if { $colon } { send_user "$adap_type($i)\:$loc_code\:$mac_address\:$full_path_name_array($i)\:$ping_result\:$device_type\:\:\:\:\n" } else { send_user "$adap_type($i) $loc_code $mac_address $full_path_name_array($i) $ping_result $device_type\n" } break; } } } if { $from_of != 1 } { nc_msg "$PROGRAM Status: power off the node after noboot == 1\n" set cmd "0" if { $immed_flag } { #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" --immed -n \"$node\"" set cmd [list exec $BINPATH/rpower $node off] } else { #set cmd "chsysstate -r lpar -o shutdown -m \"$manage\" -n \"$node\"" set cmd [list exec $BINPATH/rpower $node off] } set timeout 120 set rc [catch $cmd msg] if { $rc } { send_user "$PROGRAM: Cannot power off $NODENAME\n" nc_msg "$PROGRAM Status: error from rpower command\n" send_user "$PROGRAM: Error : $msg\n" exit 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 == 1 } { for {set i 1} {$i <= $adapter_found} {incr i 1} { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] # if ping was successful, then use that adapter for lpar_netboot if { $ping_rc == 0 } { set phandle $phandle_array($i) set full_path_name $full_path_name_array($i) set chosen_adap_type $adap_type($i) break } } } elseif { $macaddress != "" } { set match 0 for {set i 1} {$i <= $adapter_found} {incr i 1} { if {[regexp "hfi-ent" $adap_type($i)] } { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] } set mac_address [get_mac_addr $phandle_array($i)] if { [string first "$mac_address" "$macaddress"] != -1 } { if { $discovery == 1 } { if { $adap_type($i) != "hfi-ent" } { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] } if { $ping_rc != 0 } { send_user "$PROGRAM: Unable to boot network adapter.\n" exit 1 } } set phandle $phandle_array($i) set full_path_name $full_path_name_array($i) set chosen_adap_type $adap_type($i) set match 1 break } } if { !$match } { send_user "$PROGRAM: Can not find mac address '$macaddress'\n" exit 1 } } elseif { $phys_loc != "" } { set match 0 for {set i 1} {$i <= $adapter_found} {incr i 1} { set loc_code [get_adaptr_loc $phandle_array($i)] if { [string first "$phys_loc" "$loc_code"] != -1 } { if { $discovery == 1 } { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] if { $ping_rc != 0 } { send_user "$PROGRAM: Unable to boot network adapter.\n" exit 1 } } set phandle $phandle_array($i) set full_path_name $full_path_name_array($i) set chosen_adap_type $adap_type($i) set match 1 break } } if { !$match } { send_user "$PROGRAM: Can not find physical location '$phys_loc'\n" exit 1 } } else { # # Use the first ethernet adapter in the # device tree. # for {set i 1} {$i <= $adapter_found} {incr i 1} { if { $adap_type($i) == $list_type } { if { $discovery == 1 } { set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)] if { $ping_rc != 0 } { exit 1 } } set phandle $phandle_array($i) set full_path_name $full_path_name_array($i) set chosen_adap_type $adap_type($i) break } } } if { $full_path_name == "" } { send_user "$PROGRAM: Unable to boot network adapter.\n" exit 1 } else { send_user "# Network booting install adapter.\n" boot_network } if { $rc == 0 } { send_user "# bootp sent over network.\n" Boot } else { nc_msg "return code $rc from boot_network\n" } if { $rc == 5} { # Need to retry network boot because of intermittant network failure # after partition reboot. Force partition to stop at Open Firmware prompt. expect { -i $spawn_id_rconsole -re "keyboard" { set command "8" send_command sleep 10 } timeout { send_user "$PROGRAM: Timeout; exiting\n" exit 1 } eof { send_user "$PROGRAM: cannot connect to $NODENAME" exit 1 } send_user "# Network booting install adapter.\n" nc_msg "Retrying network-boot from RESTART-CMD error.\n" set done 0 while { ! $done } { boot_network if { $rc == 0 } { set done 1 } else { # try again in 10 seconds sleep 10 } } } send_user "# bootp sent over network.\n" Boot } } # # mission accomplished, beam me up scotty. # if { !$noboot } { if { $rc == 0 } { send_user "# Finished.\n" } else { send_user "# Finished in an error.\n" } } else { set done 0 set query_count 0 while { ! $done } { run_lssyscfg # # separate the nodename from the query status # if { $from_of != 1 } { if { ([string compare "$msg" "Off"] == 0) || ([string compare "$msg" "off"] == 0) || ([string compare "$msg" "Not Activated"] == 0) } { set done 1 } } else { if { ([string compare "$msg" "open-firmware"] == 0) || ([string compare "$msg" "Open Firmware"] == 0) } { set done 1 } } incr query_count if { $query_count > 60 } { set done 1 } sleep 1 } } set command "~." send_command exit $rc