#!/usr/bin/expect --
################################################################################
#
# File:         libilo
# Description:  Library routines to interact with Integrated Lights Out interface
#               and the Onboard Adminstrator
#
# Routines:   
#             ConnectFW    -- Log into iLO or the OA
#             ConnectVSP   -- get into the system console from the iLO prompt
#             ILOlogout    -- log out of the iLO console
#             OAlogout     -- log out of the OA console
#             ILOReset     -- Reset the system from ilo
#             ReadPwrState -- Read the power state of a system (on or off) 
#             DumpLog      -- Read iLO logs
#             ClearLogs    -- Clear iLO logs
#             OAGetPowerState
#                          -- Get power state for blades from the OA
#
#      -*- OpenSAF  -*-
#
# (C) Copyright 2008 The OpenSAF Foundation
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed
# under the GNU Lesser General Public License Version 2.1, February 1999.
# The complete license can be accessed from the following location:
# http://opensource.org/licenses/lgpl-license.php
# See the Copying file included with the OpenSAF distribution for full
# licensing terms.
#
# Author(s):
#           Hewlett-Packard Company
#
#
################################################################################
if {![info exists __LIBILO_SOURCED] || !$__LIBILO_SOURCED } {
   set __LIBILO_SOURCED 1
} \
else {
   return 0
}

#********************************** Globals ************************************
#                                 and Defaults                          
#*******************************************************************************
source $basepath/defaults

#********************************* Libraries ***********************************
source "$basepath/libsm"
source "$basepath/libssh"
source "$basepath/stdlib"

#******************************** Connect FW  **********************************
# ConnectFW: Connect to iLO or the OA
# Input:
#        fwhost  -- The hostname/IP address of the system's
#                   ilo or oa interface
#        user		 -- User to log in as
#        passwd  -- Password for that user
#        (global) ilo_prompt
#        (global) oa_prompt
#				 (global) domain
#
# Output: Sets atILOprompt or atOAprompt depending
#         on what we connect to.
# Return: 1 => success
#         0 => failure
# Notes:  This is just a wrapper around ConnectSsh
#         fwhost is NOT a fully qualified domain name, so
#         the domain will be appended.
#
################################################################################
proc ConnectFW {fwhost user passwd} {
	global domain
	global VERBOSE DRYRUN
    global spawn_id

    set retVal 0
    if {$VERBOSE} { send_user "\n\[libilo] ConnectFW\n" }
    
		  # Add the domain to the hostname, if appropriate
    if {[info exists domain] && ![regexp "$domain" $fwhost]} {
        # Check if $fwhost is an IPv4 or IPv6 address:
		if {![regexp {[0-9]+.[0-9]+.[0-9]+.[0-9]+} $fwhost] && \
			![regexp {[0-9a-fA-F:]+:[0-9a-fA-F]} $fwhost] } {
			# Add the domain to the hostname
			set fwhost "$fwhost.$domain"
		}
	}

	set retVal [ConnectSsh $user $passwd $fwhost rnull]
    if {$DRYRUN} {
       upvar type type
       upvar atOAprompt atOAprompt
       upvar atILOprompt atILOprompt
       if {[info exists type]} {
           if {$type == "oa"} { set atOAprompt 1 }
           if {$type == "ilo"} { set atILOprompt 1 }
       }
    } 

	return $retVal
}  ;# End ConnectFW

#**************************** Connect iLO from OA  *****************************
# ConnectILOfromOA: Connect to iLO from the OA
# Input:
#        bay -- bay number
#
# Output: Sets atILOprompt or atOAprompt depending
#         on what we connect to.
# Return: 1 => success
#         0 => failure
# Notes:  Must already be logged into the OA
#
################################################################################
proc ConnectILOfromOA {bay} {
   global VERBOSE DRYRUN
   global ilo_prompt oa_prompt
   global endl send_slow
   global atOAprompt atILOprompt
   set ret 0

   if {$VERBOSE} {
      send_user "\n\[SM] ConnectILOfromOA:\n"
      send_user "\tbay = $bay\n"
   }

   if {! $atOAprompt } {
      send_user "ERROR: Can't connect to iLO from the OA if not already logged into the OA\n"
      return 0
   }
     # Do a little error checking
   if {![regexp {^[0-9]+$} $bay]} {
      ErrorMessage "Bad value for OA bay, $bay.  Should be a number 1 - 16" 1
   } 
   set cmd "connect server $bay"
   if {$DRYRUN} {
      send_user "$cmd\n"
      return 1
   }
   send -s "$cmd\r"
   expect {
       timeout {
           send_user "ERROR: Timed out trying to connect to iLO of server $bay from the OA\n"
           return 0
       }
       -re "$cmd" {
           # Command echoed
           exp_continue
       }
       -re "$ilo_prompt" {
           # Success!
           set ret 1
       }

       -re "$oa_prompt" {
           send_user "\nERROR: something's amiss -- saw OA prompt\n"
       }

       -re "Invalid Arguments" {
		  # OA error message
          ErrorMessage "Invalid bay $bay. OA can't connect to console!\n" 1
	   }

       -re "$endl" {
           exp_continue
       }
   }   ;# End expect

   if {$VERBOSE} { send_user "\tConnectILOfromOA returning $ret\n" }
   return $ret
} ;# End ConnectILOfromOA

#*********************** Connect VSP  **********************
# ConnectVSP: Send the series of commands to get to the system
#           console from the iLO propmpt
# Input:
#        (global) atILOprompt   - Flag indicating iLO prompt
#        (global) atConsole     - Flag indicating at system console
#
# Output: Sets global variable atBmcPrompt to indicate that
#         we are not at the BMC prompt.
# Return: 1 => success
#         0 => failure
############################################################
proc ConnectVSP {} {
    global atILOprompt atConsole
    global atOAprompt oa_prompt ilo_prompt
    global prompt login_prompt root_prompt user_prompt
    global endl tetExit
    global send_slow timeout_setting
    global VERBOSE DRYRUN

    if {$VERBOSE} {
       send_user "\n\[SM] Getting system console from iLO\n"
    }

    # Initialize the flag that indicates whether we're logged into
    #  the system console.
    set atConsole 0

    # For retries
    set try 0
    set maxTry 3

    # Empty the buffer to get rid of any previous state...
    expect *

    if {![info exists atILOprompt] || !$atILOprompt} {
        send_user "ERROR: ConnectVSP -- not at iLO prompt to get console\n"
        return 0
    }
    set resend 1
    set cmd "vsp"
    if {$DRYRUN} {
        send_user "$cmd\n"
        return 1
    }

	  # Flag that the server is powered off
	set serverOff 0
    send -s "$cmd\r"
    expect {
        timeout {
			if {$serverOff} {
				# The logon showed that the system is off
				send_user "Timeout: Explicitly turning system on\n"
				send "power on\r"
				set serverOff 0
				exp_continue
			} \
			else {
				send_user "\[SM] Timed out after $timeout_setting seconds to get to system console from iLO\n"
				return 0
			}
        }
        
        eof {
			Error "Detected EOF when trying to connect to the system console\n"
			return 0
        }

        -re "$cmd" {
             #Command was echoed
             exp_continue
        }

		-re "Server Power: +Off" {
			# The server is currently off.
			set serverOff 1
			# If the system is in the process of cold reset, it will
			# transition to "on".  We'll wait a couple of seconds to
			# let it complete the transition.
			sleep 2
			exp_continue
		}
        -re "Starting virtual serial port" {
             # Good, iLO is trying to connect...
            set resend 0
            exp_continue
        }
        -re "Virtual Serial Port active:" {
            # Got to the serial port: 
            if {$VERBOSE} { send_user "\n\tAt the live console\n" }
            set atConsole 1
            set resend 0
            # We _could_ do this and detect if we're logged in
            #  or not, but what if the system is rebooting?
            #  Just return and let the caller decide what to do...
#            send "\r"
#            exp_continue
            return 1
        }

		-re "bash: vsp: command not found" {
			# Some how we got to a state where we sent "vsp" twice
            set atConsole 1
            set resend 0
			return 1
		}

        -re "no address associated with name" {
            send_user "\nERROR: invalid console\n"
            return 0
        }

        -re "\[Ii]nvalid \[Aa]rguments" {
            send_user "\nERROR: invalid console\n"
            return 0
        }

        -re "\[Cc]onnection \[Rr]efused" {
            Error "Failed to connect with $console\n"
            return 0
        }

        -re "\[Cc]onnection \[Cc]losed" {
            Error "Failed to connect with $console\n"
            return 0
        }

        -re "The server is not powered on" {
             # Wait a bit, in case the system is being reset
             sleep .5
             send "\r"
             exp_continue
        }

        -re "it is already in use" {
             # The serial console is being used by someone else.
             # For the moment, we'll exit.  We could change the behavior
             #  to print a message and ask if the user wants just to be
             #  connected at the ilo prompt
            send_user "\nERROR: Serial console in use!!\n\n"
            return 0
        }
    
        -re "$ilo_prompt\[\n\r]+" {
            # Some times the prompt is printed twice -- just ignore the first one
            exp_continue
        }

        -re "$ilo_prompt" {
            # Hmm, command didn't take...
            if {$resend} {
               incr try
               if {[expr $try >= $maxTry]} {
                  send_user "\nERROR: Couldn't attach to system console from iLO\n" 1
                  return 0
               }
               send -s "$cmd\r"
               exp_continue
            }
        }

	    -re "COMMAND NOT RECOGNIZED" {
             # Bummer, something's wrong
             ErrorMessage "Couldn't attach to system console from iLO using '$cmd'\n" $tetExit(NORESULT)
		    }

	    -re "error_tag" {
             # Bummer, something's wrong
             ErrorMessage "Couldn't attach to system console from iLO using '$cmd'\n" $tetExit(NORESULT)
		    }

        -re "\[Ll]ogin incorrect" {
             # Connected, and for some reason seeing OS login message
            set connected 1
            set atConsole 1
        }

        -re "$login_prompt" {
             # At the login prompt
            set atConsole 1
        }

        -re "$root_prompt" {
             # Already logged in and ready to go
            set atConsole 1
        }

        -re "$user_prompt" {
             # Already logged in and ready to go
            set atConsole 1
        }
    }

    return $atConsole
}   ;# End ConnectVSP

#********************** ILO logout  *********************
# ILOlogout: Send the series of commands to get to the system
#           console from the iLO propmpt
# Input:
#        (global) atILOprompt   - Flag indicating iLO prompt
#        (global) atOAprompt    - Flag indicating iLO prompt
#        (global) atConsole     - Flag indicating at system console
#        (global) VERBOSE 		- debug flags
#
# Output: Sets global variable atBmcPrompt to indicate that
#         we are not at the BMC prompt.
# Return: 1 => success
#         0 => failure
#########################################################
proc ILOlogout {} {
    global atILOprompt atConsole
    global atOAprompt
	global oa_prompt ilo_prompt
    global endl
    global send_slow
    global login_prompt root_prompt
    global VERBOSE DRYRUN

    # send command to get to get back to the iLO prompt, then exit
    if {$VERBOSE} {
       send_user "\n\[SM] ILOlogout: logging out from iLO\n"
    }

    if {$DRYRUN} {
        send_user "\t<esc>-(\n"
        # match open paren )
        send_user "exit\n"
        return 1
    }

    # Clear buffer...
    expect *
   
    # Flag if we've sent "exit"
    set exitSent 0
    # For retries
    set try 0
    set maxTry 3
    # Return value
    set retVal 0
   
    set cmd "\x1b("
    # Syntax matching, )
    set old_send_slow $send_slow
    set send_slow {1 .1}
    send -s "$cmd"
    expect {
        timeout {
           send_user "\[SM] ILOlogout: failed to exit from the virtual serial console\n"
           set send_slow $old_send_slow
           return 0
        }
        
        -re "exit" {
            # Command echoed
            exp_continue
        }
        -re "$ilo_prompt\[\r\n]+" {
            # Sometimes iLO prints the prompt multiple times, so
            #  just keep watching.
            exp_continue
        }

        -re "$ilo_prompt" {
            set atConsole 0
            set atILOprompt 1
            incr try
            if {$try > $maxTry} {
               send_user "\[SM] ILOlogout: failed to exit from the iLO prompt\n"
            } \
            else {
		       send -s "exit\r"
               exp_continue
            }
        }

        -re "$oa_prompt" {
            set atConsole 0
            set atOAprompt 1
		    set retVal 1
            send "exit\r"
            exp_continue
        }

		-re "CLI session stopped" {
            set atILOprompt 0
            set connected 0
            if {$atOAprompt} {
               if {$VERBOSE} {
                  send_user "\nLogged out from iLO. Continuing to log out from OA\n"
               } 
               exp_continue
            } \
            else {
		       set retVal 1
            }
		}

        -re "Connection to .* closed" {
            # Seen when ssh to iLO or OA is closed
            set atConsole 0
            set connected 0
            # If we've exited from iLO, but atOAprompt
            #  is set, we've got to exit the OA, continue
            #  looking for FW messages to see the OA prompt,
            #  send exit.  If not, we've successfully exited
            #  from the console
            if {$atOAprompt} {
               set atOAprompt 0
            }
            set retVal 1
        }

        -re "$login_prompt" {
            incr try
            if {[expr $try < $maxTry]} { 
                send -s "$cmd"
                exp_continue
            } \
            else {
                send_user "Found login prompt -- failed to get back to the iLO prompt.\n"
            }
        }
        
        -re "$root_prompt" {
            incr try
            if {[expr $try < $maxTry]} { 
                send -s "$cmd"
                exp_continue
            } \
            else {
                send_user "Found root prompt -- failed to get back to the iLO prompt.\n"
            }
        }
        -re "$endl" {
            exp_continue
        }

    }
    set send_slow $old_send_slow
    return $retVal
}  ;# End ILOlogout

#********************** OA logout  *********************
# OAlogout: Send the series of commands to get to the system
#           console from the OA propmpt
# Input:
#        (global) atOAprompt	- Flag indicating iLO prompt
#        (global) VERBOSE		- debug flags
#
# Output: Sets global variable atBmcPrompt to indicate that
#         we are not at the BMC prompt.
# Return: 1 => success
#         0 => failure
#########################################################
proc OAlogout {} {
    global atOAprompt atILOprompt
	global oa_prompt
    global ilo_prompt
    global endl
    global send_slow
    global VERBOSE DRYRUN

    # send command to exit from the OA
    if {$VERBOSE} {
       send_user "\n\[SM] OAlogout: Logging out from the OA\n"
    }

    if {$DRYRUN} {
        send_user "exit\n"
        return 1
    }

    # Clear buffer...
    expect *
   
    # For retries
    set try 0
    set maxTry 3
    # Return value
    set retVal 0
   
    set old_send_slow $send_slow
    set send_slow {1 .1}
      # Keep track if we've already sent exit command and seen it echoed.
    set exitSent 0
    send -s "exit\r"
    set atOAprompt 0
    expect {
        timeout {
           send_user "\[SM] OAlogout: Timed out trying to return from the OA prompt\n"
           set send_slow $old_send_slow
           set connected 0
           return 0
        }
        
        eof {
           if {$VERBOSE} {send_user "\[sm] OAlogout: detected EOF from OA\n"}
           set send_slow $old_send_slow
           set connected 0
           return 0
        }
        
        -re "exit" {
            # Command echoed
            set exitSent 1
            exp_continue
        }

        -re "$ilo_prompt\[\r\n]+" {
            # For some reason, there is an empty line with the ilo prompt
            exp_continue
        }

        -re "$ilo_prompt" {
            set atILOprompt 1
            incr try
            if {$try >= $maxTry} {
               send_user "\[SM] OAlogout: Stuck in iLO trying to exit from the OA.\n"
               set send_slow $old_send_slow
            } \
            else {
               if {$VERBOSE } {send_user "\[OAlogout] Exiting from the iLO prompt\n"}
		       send -s "exit\r"
               exp_continue
            }
        }

        -re "$oa_prompt" {
            set exitSent 1
            set atOAprompt 1
            set atILOprompt 0
            incr try
            if {$try >= $maxTry} {
               send_user "\[SM] OAlogout: Failed to exit from the OA prompt\n"
               set send_slow $old_send_slow
            } \
            else {
               if {$VERBOSE} {send_user "\[SM] OAlogout: Detected OA prompt.\n" }
		       send -s "exit\r"
               exp_continue
            }
            set atConsole 0
        }

		-re "CLI session stopped" {
            if {$VERBOSE} { send_user "\tOAlogout: Detected exit from iLO\n" }
            set atConsole 0
            set atILOprompt 0
            send -s "exit\r"
            exp_continue
		}

		-re "Connection to .* closed" {
            if {$VERBOSE} { send_user "\tOAlogout: Detected ssh disconnect\n" }
            set send_slow $old_send_slow
            set atConsole 0
            set atILOprompt 0
            if {$atOAprompt} { set atOAprompt 0 }
            set connected 0
		    set retVal 1
		}

        -re "$endl" {
            exp_continue
        }

    }

    return $retVal
}  ;# End OAlogout

#********************** ILO Reset  **********************
# ILOReset: Reset the system from ilo
# Input:
#        (global) atILOprompt   - Flag indicating iLO prompt
#        (global) atConsole     - Flag indicating at system console
#        (global) VERBOSE 		- debug flags
#
# Output: Sets global variable atILOprompt to indicate that
#         we are not at the BMC prompt.
# Return: 1 => success
#         0 => failure
#########################################################
proc ILOReset {type} {
    global atILOprompt atConsole
    global prompt login_prompt root_prompt ilo_prompt
    global endl
    global send_slow
    global VERBOSE DRYRUN

    if {$VERBOSE} {
         send_user "\nILOReset:\n"
         send_user "\ttype = $type\n"
         send_user "\tatILOprompt = $atILOprompt\n"
         send_user "\tatConsole = $atConsole\n"
    }

     # If we're not at the iLO prompt, get there.
    if {! $atILOprompt } {
       if {! $atConsole} { 
          send_user "\nERROR: Not at iLO prompt, but not at console... doing nothing\n"
          return 0
       }
       # we're logged in to iLO, and have the console
       set cmd "\x1b("
       # Syntax matching, )
       if {$DRYRUN} {
          send_user "<esc>(\n"
         # syntax matching, )
       } \
       else {
          send -s "$cmd"
          set atILOprompt 1
          set atConsole 0
       }
    }

    # OK, now we're ready to rock and roll
    if {$type == warm } {
       set resetCmd "power $type"
    } \
    else {
       set resetCmd "power reset"
    }

    if {$DRYRUN} {
       send_user "resetCmd\n"
       return 1
    }

    set RetVal 0
    set sawResetEcho 0

    expect {
       timeout {
           send_user "\nERROR: Didn't get iLO prompt to send reset\n\n"
           return 0
       }

       -re "power " {
           # Command echoed
           incr sawResetEcho 1
           exp_continue
       }

       -re "$ilo_prompt" {
          
           if {!$sawResetEcho} {
             # Try it again...
               sleep .5
               send -s "$resetCmd\r"
               exp_continue
           } \
           else {
              set RetVal 1
           }
       }

       -re "unknown command" {
           send_user "\nERROR: iLO didn't recognize the command\n\n"
           return 0
       }

       -re "error_tag" {
           # Hmm, didn't get it.
           send_user "\nERROR: iLO didn't recognize the command\n\n"
           return 0
       }
    }  ;# End expect{}
    return $RetVal
}  ;# End ILOReset


#*********************** ReadPwrState *********************
# Inputs: none
#
# Return: 1 if system is on
#         0 if system is off
#        -1 failed to get power state
#***********************************************************
proc ReadPwrState {} {
   global ilo_prompt prompt
   global atILOprompt endl
   global VERBOSE DRYRUN

   if {$atILOprompt} {
       set prompt $ilo_prompt
   } \
   else {
       send_user "\nERROR: You must be logged into iLO to call ReadPwrState\n"
       return -1
   }

   set cmd "power"
   if {$VERBOSE} {
       send_user "$cmd\n"
       return 0
   }
   send "$cmd\r"
   expect {
      timeout {
          send_user  "ERROR: Timed out checking power state from iLO"
          return -1
      }
      -re "server power is currently:\[ \t]*(\[A-Za-z]+)$endl" {
          set state $expect_out(1,string)
          if {$VERBOSE} {
             send_user "System power is currently $state\n"
          }
          switch -re -- {
              "\[Oo]\[Ff]+" { return 0 }
              "\[Oo]\[Nn]" { return 1 }
              default {
                 send_user "ERROR: Don't understand power state!\n"
                 return -1
              }
          }

      }
   }
   # Should never get here, so return -1 to indicate something broke
   return -1
}        ;# End ReadPwrState


#************************ Clear Logs **********************
# Inputs: Clear iLO logs
#
# Return: 1 if log was successfully dumped
#         0 if not
#
# Notes:
#         Must already be logged into iLO before
#         calling this routine
#***********************************************************
proc ClearLogs {} {
    global ilo_prompt atILOprompt
    global endl send_slow
    global VERBOSE DRYRUN

    set retVal 0
    if {![info exists atILOprompt] || !$atILOprompt} {
       send_user "\nERROR: Must be logged into iLO _before_ calling ClearLogs\n"
       return 0
    }
    
    if {![info exists send_slow]} {set send_slow {1 .005}}
    send -s "delete /map1/log1\r"
    expect {
       timeout {
           send_user "\nERROR: Timed out trying to clear iLO logs\n"
           return 0
       }

       -re "delete /map1/log1" {
           # Echoed command
           exp_continue
       }

       -re "event log cleared" {
           if {$VERBOSE} { send_user "\tINFO: event logs successfully cleared\n"}
           set retVal 1
           exp_continue
       }
             
       -re "$ilo_prompt" {
          if {$VERBOSE} { send_user "\tINFO: returning $retVal\n"}
       }

       -re "$endl" {
           # Staying alive
           exp_continue
       }
    }
    return $retVal
} ;# End ClearLogs

#************************** DumpLog ************************
# Inputs: None
#
# Output: 1 if log was successfully dumped
#         0 if not
# Notes:
#         Must already be logged into the OA or iLO before
#         calling this routine
#***********************************************************
proc DumpLog {} {
   global ilo_prompt prompt
   global atILOprompt endl
   global VERBOSE DRYRUN

   if {$VERBOSE} {
      send_user "\n\[SM] DumpLog:\n"
      send_user "\tnumber = $number\n"
      send_user "\tatILOprompt = $atILOprompt\n"
      send_user "\tatOAprompt = $atOAprompt\n"
   }

   if {![info exists atILOprompt] || !$atILOprompt} {
      send_user "\nERROR: You must be logged into iLO before calling DumpLog\n"
      return 0
   }

   set retVal 0

    # Clear the buffer
   expect *

   set try 0
   set maxTry 3

   if {$DRYRUN} {
      send_user "show /map1/log1\n"
      send_user "show /map1/log1/record3\n"
      return 1
   }
    # Not in dryrun mode, really send the command
   send "show /map1/log1\r"
   expect {
       timeout {
          send_user "\nERROR: Timed out trying to dump iLO logs\n"
          return 0
       }
       -re "show /map1" {
           # command echoed
           exp_continue
       }
       -re "(record\[0-9]+)" {
           # Got another log event
           lappend logs "$expect_out(1,string)"
           exp_continue
       }
       -re "$endl" {
           exp_continue
       }
       -re "$ilo_prompt" {
           if {$VERBOSE} {
              if {[info exists logs]} {
                 set numLogs [llength $logs]
              } else {
                 set numLogs 0
                 set retVal 1
              }
              send_user "At the end of record listing found $numLogs logs\n"
           }
        }
   }
   if [info exists logs] {
      log_user 1
      foreach record $logs {
         send "show /map1/log1/$record\r"
         expect {
            timeout {
                send_user "\nERROR: Timed out displaying iLO logs\n"
                return 0
            }
            -re "show /map1" {
               # command echoed
               exp_continue
            }

            -re "$endl" {
               exp_continue
            }
            -re "$ilo_prompt" {
               if {$VERBOSE} { "\nFinished printing iLO logs\n" }
               set retVal 1
            }
         }  ;# End expect{}
      }     ;# End foreach
  }         ;# End if info exists logs
  return $retVal
}        ;# End DumpLog


#************************** OACheckPowerStatus *************************
# Input:  rPwrArray - Reference to an array that will be populated with
#                     power status
#         PwrArray($bay) = "ON" | "OFF"
#
# Output: An array of system with ON if blade is on
#         OFF if blade is off
# Return: 1 => success
#         0 => failure
#
# Notes:
#         Must already be logged into the OA before calling this routine
#
#         Example of OA output that will be used to generate results:
# 
# OA-001CC41415EF> show server list
# 
# Bay iLO Name                      iLO IP Address  Status   Pwr UID Partner
# --- ----------------------------- --------------- -------- --- --- -------
#   1 ILOUSE7355YE9                 10.105.1.51     OK       On  Off
#   2 ILOUSE7355YE4                 10.105.1.54     OK       On  Off
#   3 ILOUSE7103JCE                 10.105.1.57     OK       On  Off
#   4 ILOUSE7103JC9                 10.105.1.60     OK       On  Off
#   5 ILOUSE8109CBE                 10.105.1.63     OK       Off Off
#   6 ILOUSE8109AN2                 10.105.1.66     OK       Off Off
#   7 ILOUSE8109CBD                 10.105.1.69     OK       Off Off
#   8 ILOUSE81099ST                 10.105.1.72     OK       On  Off
#   9 ILOUSM80200V7                 10.105.1.75     OK       On  Off
#  10 ILOUSM751003H                 10.105.1.78     OK       On  Off
#  11 ILOUSM7510036                 10.105.1.81     OK       On  Off
#  12 ILOUSM751003X                 10.105.1.84     OK       On  Off
#  13 [Absent]
#  14 [Absent]
#  15 [Absent]
#  16 [Absent]
# Totals: 12 server blades installed, 9 powered on.
#
# PwrArray elements:
#   PwrArray(1) = "ON"
#   PwrArray(2) = "ON"
#    ...
#   PwrArray(5) = "OFF"
#    ...
#   PwrArray(9) = "ON"
#    ...
#   PwrArray(12) = "ON"
# 
#***********************************************************************
proc OACheckPowerStatus { rPwrArray } {
   global oa_prompt prompt
   global atOAprompt endl ws
   global send_slow
   global VERBOSE DRYRUN
   global spawn_id tetExit

   upvar $rPwrArray PwrArray

   if {$VERBOSE} {
       send_user "\nOACheckPowerStatus\n"
       send_user "\tatOAprompt = $atOAprompt\n"
   }
   if {!$atOAprompt} {
      ErrorMessage "You must be at the OA prompt to check the power status for a blade\n" $tetExit(NORESULT)
   }

   set retVal 0
   set cmd "show server list"

   if {$DRYRUN && ![info exists spawn_id]} {
       send_user "$cmd\n"
       return 1
   }

   set sawEcho 0
   send -s "$cmd\r"
   expect {
       timeout {
           send_user "\nERROR: Timed out trying to read power states from the OA\n"
           return UNKNOWN
       }

       -re "$cmd" {
           # Command was echoed
           incr sawEcho
           exp_continue
       }
      
       -re "${ws}(\[0-9]+)${ws}\[A-Z0-9]+${ws}\[0-9.]+${ws}\[A-Z]+${ws}(\[Oo]\[nf]+)${ws}" {
           if {$VERBOSE} {
              send_user "INFO: blade $expect_out(1,string) is $expect_out(2,string)\n"
           }
           set retVal 1
             # Explicitly set PwrArray entry with "ON" or "OFF" so
             #  the case is known.
           if {[regexp {[Oo][Nn]} $expect_out(2,string)]} {
              set PwrArray($expect_out(1,string)) "ON"
           } \
           elseif {[regexp {[Oo][Ff]+} $expect_out(2,string)]} {
              set PwrArray($expect_out(1,string)) "OFF"
           }
            # Continue until we see a prompt again.
           exp_continue
       }

       -re "$oa_prompt" {
          if {$VERBOSE} {  send_user "\tINFO: sawEcho is $sawEcho\n" }
          if {!$sawEcho} { exp_continue }
          # The command has returned, so we're done
       }

       -re "$endl" {
            exp_continue
       }

   } 

   return $retVal
}  ;# End OACheckPowerStatus

#*******************************************************************************
#  SetPowerState: Set the system's power state from the OA or iLO
#  Input: state  -> the power state (if at the ilo prompt, used with "power" command)
#		  (global) WaitForPower - how long to wait before verifing the power state.
#  Return: 1 => success
#          0 => failure
#  Notes: Must already be logged into FW.
#*******************************************************************************
proc SetPowerState {state checkState rblade} {
    global VERBOSE DRYRUN
    global atOAprompt oa_prompt
    global atILOprompt ilo_prompt
    global send_slow
	global WaitForPower
    global endl ws

    upvar $rblade blade
	set success 0	;# The return value
    if {![info exists blade]} { set blade "" }

      
    if {!$atILOprompt && $atOAprompt} {
       if {$blade == ""} {
          ErrorMessage "SetPowerState: at OA prompt, but no information for which blade to set power on" 1
       }
       if {[regexp -nocase {on} $state]} {
          set cmd "poweron server $blade"
          set postResetState "on"
       } \
       elseif {[regexp -nocase {off} $state]} {
          set cmd "poweroff server $blade"
          set postResetState "off"
       } \
       elseif {[regexp -nocase {reset} $state]} {
          set cmd "reset server $blade"
          set postResetState "on"
       } \
       elseif {[regexp -nocase {warm} $state]} {
          set cmd "reboot server $blade"
          set postResetState "on"
       } \
       else {
          ErrorMessage "Request to set power to unknown power state $state\n" 1
       }
    } \
    elseif {$atILOprompt} { 
       set cmd "power $state"
       if {[regexp -nocase {reset} $state] || [regexp -nocase {warm} $state]} {
          set postResetState "on"
       } \
       else {
          set postResetState $state
       }
    } \
    else {
        ErrorMessage "You must be logged into either iLO or the OA before calling SetPowerState\n" 1
    }

    if {$DRYRUN} {
        send_user "$cmd\n"
        send_user "\tVerify power state:\n"
        if {$atILOprompt} {
           send_user "\tpower\n"
        } \
        else {
           send_user "show server list\n"
        }
        return 1
    }

      # How long we'll wait for the system to transition to the new power state
	if {![info exists WaitForPower]} { set WaitForPower 45 }
	set waited 0
	  # Some times it seems that when querying after settint the power state
      #  the set failed, but actually, the system hasn't transitioned
	  #  its power state.  Try the query multiple times.
	set maxTry 3
	set try 0
    send -s "$cmd\r"
    expect {
        timeout {
            ErrorMessage "Timed out running '$cmd'\n" 1
        }

        -re "$cmd" {
			# Command echoed
 			exp_continue
		}
     
          # These are iLO responses
		-re "power: server power is currently:\[ \t]*(.*)" {
			if {[regexp -nocase "$postResetState" $expect_out(1,string)]} {
               send_user "Successfully set power to $state\n"
               return 1
            } \
            else {
			 	if {[expr $try < $maxTry]} {
					incr try
					send_user "\nTry $try... Waiting $WaitForPower seconds to check power again\n"
					sleep $WaitForPower
					send -s "power\r"
					exp_continue
				} \
				else {
              		ErrorMessage "Tried to set system to $state, but it's $expect_out(1,string)\n" 1
				}
           }
        }

		-re "Server power already" {
            if {$VERBOSE} { send_user "\nINFO: System is already $state\n" }
            return 1
        }

        -re "power\r" {
			# Verify power state command echoed
 			exp_continue
		}

         # These are OA replies
        -re "${ws}(\[0-9]+)${ws}\[A-Z0-9]+${ws}\[0-9.]+${ws}\[A-Z]+${ws}(\[Oo]\[nf]+)${ws}" {
             if {$VERBOSE} {
                send_user "INFO: blade $expect_out(1,string) is $expect_out(2,string)\n"
             }
             if {$blade != ""} {
                if {$expect_out(1,string) == $blade} {
                   if {[regexp -nocase "^${postResetState}$" $expect_out(2,string)]} {
                      send_user "Successfully set power to $state\n"
                      return 1
                   } \
                   else {
					  if {[expr $try < $maxTry] } {
						incr try
						send_user "\nTry $try... Waiting $WaitForPower seconds to check power again\n"
						sleep $WaitForPower
						send -s "show server list\r"
						exp_continue
					  } \
					  else {
                      	ErrorMessage "Tried to set system to $state, but it's $expect_out(2,string)\n" 1
					  }
                   }
                }
             }
             exp_continue
        }

        -re "\[Bb]ay \[0-9]+ is empty" {
             if {[info exists blade]} {
                send_user "\nERROR: Bay $blade does not have a system in it.\n"
             } \
             else {
                send_user "\nERROR: SetPowerState BROKEN, should not have seen this message!\n"
             }
             return 0
        }

        -re "show server list" {
            # Command echoed
            exp_continue
        }

        -re "\[Ii]nvalid \[Aa]rguments" {
            ErrorMessage "Bad arguments to OA, '$cmd'\n" 1
        }

        -re "Resetting the server" {
             exp_continue
        }

        -re "Do you want to continue\?" {
            send "yes\r"
            exp_continue
        }

        -re "Successfully reset the E-Fuse" {
			set success 1
            if {$VERBOSE} { 
               send_user "\n\tSuccessfully set $state"
               if [info exists blade] {
                    send_user " on $blade\n"
               } \
               else {
                    send_user "\n"
               }
            }
			if {$checkState} {
				if {!$waited} {
	           	 send_user "Verifying power state.... Waiting $WaitForPower seconds to query\n"
					sleep $WaitForPower
				}
				set waited 1
            	send -s "show server list\r"
            	exp_continue
			}
        }

        -re "Rebooting Blade (\[0-9]+)" {
			set success 1
            if {$VERBOSE} { 
               send_user "\n\tSuccessfully set reboot"
               if [info exists blade] {
                    send_user " to $blade\n"
               } \
               else {
                    send_user "\n"
               }
            }
			if {$checkState} {
				if {!$waited} {
	           	 send_user "Verifying power state.... Waiting $WaitForPower seconds to query\n"
					sleep $WaitForPower
				}
				set waited 1
				send -s "show server list\r"
				exp_continue
			}
        }

        -re "Blade \[0-9]+ is already" {
			set success 1
            if {$VERBOSE} {
               send_user "Blade is already $state\n"
            }
			if {$checkState} {
            	send -s "show server list\r"
            	exp_continue
			}
        }

        -re "Powering on blade (\[0-9]+)" {
			set success 1
            if {$VERBOSE} {
               send_user "Detected power on for bay $expect_out(1,string)\n"
            }
			if {$checkState} {
				if {!$waited} {
					send_user "Verifying power state.... Waiting $WaitForPower seconds to query\n"
					sleep $WaitForPower
				}
				set waited 1
            	send -s "show server list\r"
            	exp_continue
			}
        }

        -re "Blade \[0-9]+ performing a graceful shutdown" {
			set success 1
            if {$VERBOSE} {
               send_user "Detected OS shutdown\n"
            }
			if {$checkState} {
				if {!$waited} {
	           	 send_user "Verifying power state.... Waiting $WaitForPower seconds to query\n"
					sleep $WaitForPower
				}
				set waited 1
            	send -s "show server list\r"
            	exp_continue
			}
        }
            

		-re "$ilo_prompt" {
			set success 1
            if {$VERBOSE} {
                send_user "\nINFO: Sent command and got back prompt, should be successful\n"
            }
			if {$checkState} {
				if {!$waited} {
					send_user "Verifying power state.... Waiting $WaitForPower seconds to query\n"
					sleep $WaitForPower
				}
				set waited 1
            	send -s "power\r"
            	exp_continue
			}
        }

		-re "$oa_prompt" {
			set success 1
            if {$VERBOSE} {
                send_user "\nINFO: Sent command and got back OA prompt, should be successful\n"
            }
              # Verify the power state
			if {$checkState} {
				if {!$waited} {
					send_user "Verifying power state.... Waiting $WaitForPower seconds to query\n"
					sleep $WaitForPower
				}
				set waited 1
            	send -s "show server list\r"
            	exp_continue
			}
        }

		-re "Invalid target" {
            ErrorMessage "Command is not recognized by iLO, something is horribly wrong!\n" 1
		}

        -re "error_tag" {
            ErrorMessage "'$cmd' is not recognized, something is horribly wrong!\n" 1
        }

        -re "$endl" {
             exp_continue
        }

    }   ;# End expect

	return $success
}  ;# End SetPowerState

##
# vim: tabstop=4
# -*- tab-width:4 -*-
##
