| #!/usr/bin/expect -- |
| # |
| # blade -- console and management control for IBM blade servers. |
| # |
| # Obtain consoles for and hard reset support for blades in |
| # IBM blade centres. Blades are identified by their blade centre ids. |
| # |
| # usage: |
| # blade reboot|console <blade id> <user> <password> <connect command> ... |
| # |
| # example: |
| # blade reboot blade[4] FOO BAR telnet 1.2.3.4 |
| # blade console blade[4] FOO BAR telnet 1.2.3.4 |
| # |
| # (C) Copyright IBM Corp. 2004, 2005, 2006 |
| # Author: Andy Whitcroft <andyw@uk.ibm.com> |
| # |
| # The Console Multiplexor is released under the GNU Public License V2 |
| # |
| set P "blade" |
| |
| if { [llength $argv] < 5 } { |
| puts stderr "Usage: $P <command> <blade> <login> <password> <connect command ...>" |
| exit 1 |
| } |
| |
| log_user 0 |
| |
| set cmd [lindex $argv 0] |
| set system [lindex $argv 1] |
| set username [lindex $argv 2] |
| set password [lindex $argv 3] |
| set command [lrange $argv 4 end] |
| |
| # Validate the command. |
| switch -- $cmd { |
| console {} |
| reboot {} |
| default { |
| puts stderr "$P: $cmd: unrecognised command"; |
| exit 1 |
| } |
| } |
| # Ensure the blade name is fully qualified. This ensures that |
| # the console prompt detection as reliable as possible. Add the system |
| # prefix if it was not specified. |
| switch -re -- "$system" { |
| system:.* { } |
| .* { set system "system:$system" } |
| } |
| |
| #log_file -a "$logfile" |
| |
| set elapsed_time 0 |
| set timeout 30 |
| |
| proc note {m} { |
| global P |
| puts "$P: $m" |
| } |
| proc warn {m} { |
| global P |
| puts "$P: WARNING: $m" |
| } |
| proc winge {m} { |
| global P |
| puts "$P: ERROR: $m" |
| } |
| |
| note "Logging into blade centre with command \"$command\" to restart it"; |
| |
| # CONNECT: connect to the remote console. |
| eval spawn $command |
| expect { |
| default { |
| winge "login prompt not issued" |
| exit 2 |
| } |
| "Connection closed by foreign host." { |
| winge "Telnet connection closed." |
| exit 1 |
| } |
| "Unable to connect to remote host:" { |
| winge "Connection to remote console failed" |
| exit 2 |
| } |
| "username:" { |
| #note "saw login prompt" |
| } |
| } |
| |
| # AUTHENTICATE: send username and password at the relevant prompts |
| note "sending login ..." |
| send -- "$username\r" |
| expect { |
| default { |
| winge "password prompt not issued" |
| exit 2 |
| } |
| "password:" { |
| #note "password prompt found" |
| } |
| } |
| |
| note "sending password ..." |
| send -- "$password\r" |
| expect { |
| default { |
| winge "command prompt not issued" |
| exit 2 |
| } |
| "Invalid login!" { |
| winge "login/password incorrect ... aborting" |
| exit 1 |
| } |
| -- "system>" { |
| #note "command prompt found" |
| } |
| } |
| |
| # SYSTEM: first validate the system specifier, if it does not |
| # exist get a listing and print that out whilst we are connected. |
| note "selecting system '$system' ..." |
| send "env -T $system\r" |
| set found 0 |
| set prompt "$system>" |
| expect { |
| default { |
| winge "command prompt not issued" |
| exit 2 |
| } |
| OK { |
| set found 1 |
| exp_continue |
| } |
| -- "system>" { |
| #note "command prompt found" |
| } |
| -ex $prompt { |
| #note "command prompt found" |
| } |
| } |
| |
| # The system the user specified was not found, give them a hand by |
| # getting a list of systems. |
| if {$found == 0} { |
| note "Defined systems:" |
| send "list -l 2\r" |
| expect { |
| default { |
| winge "command prompt not issued" |
| exit 2 |
| } |
| -- "system>" { |
| #note "command prompt found" |
| } |
| "\n$" { |
| puts -nonewline "$expect_out(buffer)" |
| exp_continue |
| } |
| } |
| note "complete ... exiting" |
| send "exit\r" |
| exit 1 |
| } |
| |
| # CONSOLE: open a console channel |
| if {[string compare $cmd "console"] == 0} { |
| set ts_start 1 |
| set ts_cur 1 |
| set retry 0; |
| note "connecting to console ..." |
| |
| # Unless console session lasts for more than two seconds, retry every |
| # thirty seconds for up to a day. This prevents repeated login events |
| # which fill logs and cause loss of useful event log information. |
| while {($ts_cur - $ts_start < 2) && ($retry < 2880)} { |
| # 30 second delay between attempts, except for the first one |
| if {$ts_start != 1} { |
| note "connection failed, retrying after 30 second delay" |
| after 30000 |
| } |
| set ts_start [timestamp]; |
| send "console -o\r" |
| interact { |
| -o -ex $prompt { |
| return |
| } |
| } |
| set ts_cur [timestamp]; |
| incr retry |
| } |
| winge "console lost" |
| exit 0 |
| } |
| |
| # Function to wait until the issued power command has taken effect. |
| proc powerWait {s} { |
| global prompt |
| set ok 0 |
| for {set retry 1} {$ok == 0 && $retry < 30} {incr retry} { |
| note "checking for power state $s ..." |
| after 2000 |
| send "power -state\r" |
| expect { |
| default { |
| winge "command prompt not issued" |
| exit 2 |
| } |
| -ex $s { |
| set ok 1 |
| exp_continue |
| } |
| -ex "$prompt" { |
| #note "command prompt found" |
| } |
| } |
| } |
| if {$ok == 0} { |
| winge "system did not enter power $s state ... aborting" |
| exit 2 |
| } |
| } |
| |
| # DOWN: shut the system down ... hard. Expect to OK before the |
| # prompt in the case of success. |
| note "powering cycling the system ..." |
| send "power -cycle\r" |
| set fail 1 |
| expect { |
| default { |
| winge "command prompt not issued" |
| exit 2 |
| } |
| "OK" { |
| set fail 0 |
| exp_continue |
| } |
| -ex "$prompt" { |
| #note "command prompt found" |
| } |
| } |
| if {$fail == 1} { |
| winge "power cycle failed ... system not rebooted" |
| exit 2 |
| } |
| powerWait On |
| |
| note "complete ... exiting" |
| send "exit" |
| exit 0 |