#!/usr/bin/expect --
#*************************************************************************
# File:	libcli
# Description:	Library of routines for OpenSAF command-line
#				interface tests.
#
# Routines:
#	StartCli			- Start the command-line interface
#	StopCli				- Log out of the command-line interface
#	RunCliCmd			- Run a CLI command.
#	GetCliCmd			- generate a CLI command from blade ID, chassis ID 
#						  and power state or reset type.
#
#      -*- 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 __LIBCLI_SOURCED]} {
	set __LIBCLI_SOURCED 1
} \
else {
	return
}

#****************************** Defaults/Globals *******************************
source "$basepath/defaults"
set cli_cmd "ncs_cli_maa"
set cli_prompt "NCS\[>#-]\[^ \t]\*"
set cli_avs_prompt "NCS-config-avsv"

#********************************** Libraries **********************************
# Get access to library routines
source "$basepath/libsm"
source "$basepath/stdlib"

#********************************* Subroutines *********************************

#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# StartCli: start up OpenSAF command-line interface
#
# Input:
#		rservice - reference to a variable with the target service to connect
#
# Output: Sets global variable cli_pid with the session PID to indicate that
#		  we're connected to the cli.
#
# Return:
#		 0 => failed to start the CLI
#		 1 => successfully started the CLI
#
# Notes:
#		
#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
proc StartCli {rservice} {
	global VERBOSE DRYRUN
	global prompt cli_prompt
	global endl ws send_slow
	global timeout_setting
	global cli_pid

	upvar $rservice service
	if {$VERBOSE} {
		send_user "\n\[SM] StartCli:\n"
		if {[info exists service]} {
			send_user "\tservice = $service\n"
		}
	}

	 # The return value for this routine
	set success 0

	 # The commands that we use to set up CLI to run commands
	set cli_cmd "/usr/bin/ncs_cli_maa"
	set cliConfig(0) "enable"
	set cliConfig(1) "configure terminal"
	set numCliCmds 2
	if {[info exists service]} {
		if {[regexp -nocase "avsv" $service]} {
			set cliConfig($numCliCmds) "avsv"
			incr numCliCmds
		} \
		elseif {[regexp -nocase "dtsv" $service]} {
			set cliConfig($numCliCmds) "dtsv"
			incr numCliCmds
		} \
		elseif {[regexp -nocase "pssv" $service]} {
			set cliConfig($numCliCmds) "pssv"
			incr numCliCmds
		} \
		elseif {[regexp -nocase "interfaces" $service]} {
			set cliConfig($numCliCmds) "interfaces"
			incr numCliCmds
		}
	}
	
	send_user "$cli_cmd\n"
	if {$DRYRUN} {
		for { set i 0} { $i < $numCliCmds } {incr i} {
			send_user "$cliConfig($i)\n"
		}
		return 1
	}

	# Spawn the cli session
	if {[catch {spawn -noecho $cli_cmd} cmdOut]} {
		send_user "\nERROR: Couldn't start cli session with '$cli_cmd'\n"
		return 0
	}
	set cli_pid $spawn_id

	# Keep track of what level we're in the cli session
	set cliLevel 0
    expect {
		timeout {
			send_user "\nTimed out trying to start a CLI session after $timeout_setting seconds\n"
			return 0
		}

		-re "enable\r" {
			# Command echoed
			exp_continue
		}

		-re "configure terminal\r" {
			# Command echoed
			exp_continue
		}

		-re "avsv*\r" {
			# Command echoed
			exp_continue
		}

		-re "dtsv*\r" {
			# Command echoed
			exp_continue
		}

		-re "pssv*\r" {
			# Command echoed
			exp_continue
		}

		-re "inter.*\r" {
			# Command echoed
			exp_continue
		}

		-re "Invalid input" {
			# Something's broken
			ErrorMessage "Something's broken -- detected invalid input for cli at StartCli" 1
		}

		-re "$cli_prompt" {
			set prompt "$expect_out(0,string)";
			if {$VERBOSE} {
				send_user "\tSaw cli prompt\n"
				send_user "\tprompt = '$prompt'\n"
				send_user "\tcliLevel = $cliLevel\n"
			}
			if {$cliLevel < $numCliCmds} {
				send -s "$cliConfig($cliLevel)\r"
				incr cliLevel
				exp_continue
			}
			set success 1
		}
	}	;# End expect

	return $success
}	;# End StartCli

#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# StopCli: shutdown OpenSAF command-line interface
#
# Input:  None
#
# Output: unsets cli_pid if successful
#
# Return:
#		 0 => failed to quit the CLI
#		 1 => successfully quit the CLI
#
# Notes:
#		The current spawn_id must be set to the cli session PID.
#		
#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
proc StopCli {} {
	global VERBOSE DRYRUN
	global prompt cli_prompt
	global root_prompt
	global endl ws send_slow spawn_id
	global cli_pid timeout_setting

	set success 0
	

	 # Clear the expect buffer before sending the shutdown command
	expect *

	 # This is the command to exit the command-line interface;
	set cmd "clishut"
	if {$VERBOSE} {
		send_user "\n\[SM] StopCli:\n"
	}
	if {$DRYRUN} {
		send_user "$cmd\n"
		return 1
	}

	 # Make sure that we send the command to the appropriate PID
	if {![info exists cli_pid]} {
		ErrorMessage "StopCli called without valid spawn_id!\n" 1
	}
	set spawn_id $cli_pid
	set sentCmd 0
	set maxTries 3
	send -s "$cmd\r"
	expect {
		timeout {
			send_user "\nERROR: Timed out trying to stop the cli after $timeout_setting seconds\n\n"
		}

		-re "$cmd" {
			# command echoed
			exp_continue
		}

		-re "$cli_prompt" {
			# I don't think we'd see this, but I need it here
			#  to keep root prompt's '#' from matching the
			#  perverse cli prompt "NCS-config-interfaces#"
			send_user "\nERROR: Saw the cli prompt again, something's wrong\n"
		}

		-re "bash: .*: command not found" {
				# Not in the CLI
			send_user "\nERROR: Not in the CLI!\n\n"
		}


	}	;# End expect{}
	return $success
}	;# End StopCli

#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# RunCliCmd: Run a CLI command
#
# Input:  cmd - the cli command
#
# Output: None
#
# Return:
#		 0 => failed to run the command
#		 1 => successfully ran the command
#
# Notes:
#		The current spawn_id must be set to the cli session PID.
#		
#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
proc RunCliCmd {cmd} {
	global VERBOSE DRYRUN
	global prompt cli_prompt
	global root_prompt
	global endl ws send_slow spawn_id
	global timeout_setting cli_pid

	set success 0
	set send_human {.1 .3 1 .05 2}

	if {$VERBOSE} {
		send_user "\n\[SM] RunCliCmd:\n"
		send_user "\tcommand = '$cmd'\n"
	}

	if {$DRYRUN} {
		send_user "$cmd\n"
		return 1
	}
	  # Only grab the first twenty characters of the string to
	  #  match the echo, since long command lines can get spurious
	  #  newlines, depending on the terminal setting.
	set echoed [format "%.20s" $cmd]

	 # Make sure that we send the command to the appropriate PID
	if {![info exists cli_pid]} {
		ErrorMessage "StopCli called without valid spawn_id!\n" 1
	}
	set spawn_id $cli_pid


	set tryCR 1
	send -h "$cmd\r"
	expect {
		timeout {
			if {$tryCR} {
				send "\r"
				set tryCR 0
				exp_continue
			} \
			else {
				send_user "\nERROR: Timed out running '$cmd' after $timeout_setting seconds\n"
				return 0
			}
		}

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

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


		-re "Do you really want to continue with .*\\\? - enter Y or y to confirm" {
			send "y\r"
			exp_continue
		}

		-re "Success:" {
			if {$VERBOSE} {
				send_user "\t\[SM] Detected command success\n"
			}
			set success 1
			exp_continue
		}

		-re "Failure: Admin State Set Failed" {
			if {$VERBOSE} {
				send_user "\t\[SM] Detected command failure!\n"
			}
			set success 0
			exp_continue
		}

		-re "Invalid input" {
			send_user "\nERROR: command not recognized!\n\n"
	 	}

		-re "FAILURE" {
			send_user "\nERROR: command failed!\n\n"
	 	}

		-re "Command Execution Function failed" {
			send_user "\nERROR: command failed!\n\n"
	 	}

		-re "$prompt" {
			# Completed the command
			if {$VERBOSE} {
				send_user "\tSaw prompt '$prompt' again.  Command should have completed.\n"
			}
		}

		-re "cli will quit if it is idle for 1 more minute" {
			send_user "\nERROR: cli is going to time out! Something's amiss\n\n"
		
		}

		-re "$endl" {
			# Just capturing command output
			exp_continue
		}
	}	;# End expect{}
	return $success
}	;# End RunCliCmd


#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
# GetCliCmd:	generate a CLI command from blade ID, chassis ID and final
#				power state or reset type
# Input:	function	=> what HISv function (reset or power) to perform
# 			rblade		=> reference to blade ID
#			chassis		=> reference to chassis ID
#			path		=> reference to entity path
#
# Return:
#		cmd	=>  The CLI command that will transition the given blade to the
#				given power state.
#		
#:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
proc GetCliCmd {function rblade rchassis rpath} {
	global VERBOSE DRYRUN
	global prompt cli_prompt
	global root_prompt
	global reset powerState
	global Force

	upvar $rblade blade
	upvar $rchassis chassis
	upvar $rpath path

	if {$VERBOSE} {
		send_user "\n\[SM] GetCliCmd:\n"
		send_user "\tfunction = $function\n"
		if {[info exists Force]} {send_user "\tForce = $Force\n"}
		if {[info exists blade]} {send_user "\tblade = $blade\n"}
		if {[info exists chassis]} {send_user "\tchassis = $chassis\n"}
		if {[info exists powerState]} {send_user "\tpowerState = $powerState\n"}
	}

	if {![info exists Force]} {set Force 0}
	if {![info exists path]} {
		set path "/$chassis/$blade"
	}

	if {$function == "power"} {
        if {![info exists powerState]} {
			if {!$Force} {
				send_user "\nTest is broken -- GetCliCommand called with function = \"power\", but powerState not set\n"
				return
			} \
			else {
				set powerState ""
			}
		}
		# In case the caller uses "on" or "off" for the power state.
		if {[regexp -nocase "off" $powerState]} {
			return "admreq $path operation shutdown"
		}
		if {[regexp -nocase "on" $powerState]} {
			return "admreq $path operation unlock"
		}
        return "admreq $path operation $powerState"
	}
	if {$function == "reset"} {
		if {[regexp -nocase "hard" $reset] || [regexp -nocase "cold" $reset]} {
			return "reset $path operation hardreset"
		}
		if {[regexp -nocase "soft" $reset] || [regexp -nocase "warm" $reset]} {
			return "reset $path operation softreset"
		}
		return "reset $path operation $reset"
#		if {$Force} {return "reset $path operation $reset"}
	}
}	;# End GetCliCmd


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