#!/usr/bin/expect -f
# BEGIN_ICS_COPYRIGHT8 ****************************************
#
# Copyright (c) 2015, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# END_ICS_COPYRIGHT8 ****************************************
# [ICS VERSION STRING: unknown]
# assumptions:
# slot numbers are alphanumeric: A-Za-z0-9 (actually are numeric in <=3.1)
# card types are alphanumeric with dot, dash, underscore: A-Za-z0-9._-
# versions are alphanumeric with dot, dash, underscore: A-Za-z0-9._-
# This is an expect (tcl) library of procedures to aid testing
# These functions provide support for chassis operations
# against a test target chassis
## tcl procedures to support testing:
## =============================================
global os_type
proc get_chassis_sftp_status { slot {wait_done 1 }} {
##
## get_chassis_sftp_status
## -------------------------
## return the exit status of the last sftp executed against the given slot
##
## Usage:
## get_chassis_sftp_status slot [wait_done]
## Arguments:
## slot - slot number to get status for
## wait_done - if status is "In Progress", loop until changes to another value
## Returns:
## "success"
## "skipped" - transfer unnecessary
## "invalid" - invalid firmware image for card type
## "inprogress" - transfer still in progress, but wait_done not set
## "error" - other errors or timeouts
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
while { 1 } {
send_chassis_cmd "showLastScpRetCode $slot"
set out [expect_list 60 { "Code: [0-9]+:" } { "usage" "Invalid" "Error" "Failed" "roblem" "roblem" "not found"} ]
regexp {[0-9]+} $out ret
expect_chassis_prompt 60
if { $ret == 0 } {
return "success"
} elseif { $ret == 1 } {
# unneeded
return "skipped"
} elseif { $ret == 2 } {
# wrong card type
return "invalid"
} elseif { $ret == 3 } {
if { ! $wait_done } {
# transfer in progress internal to chassis
return "inprogress"
}
# can be a few minutes, so lets be a little patient
sleep 20
} else {
# other errors
return "error"
}
}
}
proc check_chassis_capability { capability } {
##
## check_chassis_capability
## -------------------------
## return level of support for the given capability by the current chassis
##
## Usage:
## check_chassis_capability capability
## Arguments:
## capability - the capability name to check
## Returns:
## 0 - capability not supported
## >0 - version of capability supported
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "showCapability -key $capability"
# the [:space:] bounds the + so we get all the data on the line
set out [expect_list 60 "{$capability: \[0-9A-Za-z\]+\[\[:space:\]\]}" { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
expect_chassis_prompt 60
#log_message "out=$out"
# throw away the \r\n, there may be trailing spaces, TCL lists will ignore
regexp {: ([A-Za-z0-9]+)[[:space:]]} $out line ret
#log_message "ret=$ret"
if { [ string equal -nocase "$ret" "unavailable" ] } {
set ret 0
}
return "$ret"
}
proc get_chassis_slots { { card_type "" } } {
##
## get_chassis_type_slots
## -------------------------
## return the list of slots in the given chassis of the given card type
## if no card type is specified, a list of all slots in the chassis is returned
##
## Usage:
## get_chassis_type_slots [card_type]
## Arguments:
## card_type - card type, default is "" (eg. all slots)
## Returns:
## list of slots in chassis
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
if { ! [ string equal "$card_type" "" ] } {
send_chassis_cmd "chassisQuery -ignoreInvalidType -type $card_type"
} else {
send_chassis_cmd "chassisQuery"
}
# this could return no slots, in which case "none" is output
# The \r\n is needed to bound the + so we get all the data on the line
set out [expect_list 60 "{slot\[s\]*: \[0-9A-Za-z \]+\[\r\n\]}" { "usage" "Error" "Failed" "roblem" "not found"} ]
expect_chassis_prompt 60
#log_message "out=$out"
# The \r\n is needed to bound the + so we get all the data on the line
# there may be trailing spaces, TCL lists will ignore
regexp {: ([0-9A-Za-z ]+)[\r\n]+} $out line ret
#log_message "ret=$ret"
if { [ regexp -nocase "none" "$ret" ] } {
set ret ""
}
return "$ret"
}
proc list_esm_security_files { } {
##
## list_esm_security_files
## -------------------------
## return the list of FM security files in the given chassis
##
## Usage:
## list_esm_security_files
## Arguments:
## none
## Returns:
## list of .pem files in chassis
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "smListSecurityFiles -showSingleLine"
# this could return no *.pem files, in which case "none" is output
# The \r\n is needed to bound the + so we get all the data on the line
set out [expect_list 60 "{files: \[0-9A-Za-z._ \]+\[\r\n\]}" { "usage" "Error" "Failed" "roblem" "not found"} ]
expect_chassis_prompt 60
#log_message "out=$out"
# The \r\n is needed to bound the + so we get all the data on the line
# there may be trailing spaces, TCL lists will ignore
regexp {: ([0-9A-Za-z._ ]+)[\r\n]+} $out line ret
#log_message "ret=$ret"
if { [ regexp -nocase "none" "$ret" ] } {
set ret ""
}
return "$ret"
}
proc get_chassis_card_type { slot } {
##
## get_chassis_card_type
## -------------------------
## return the card type of the given slot in the given chassis
##
## Usage:
## get_chassis_card_type slot
## Arguments:
## slot - slot to query
## Returns:
## card type of given slot
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
# DONE
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "chassisQuery -showType $slot"
# require trailing whitespace to anchor end of string
set out [expect_list 60 "{type: \[a-zA-Z0-9._-\]+\[\[:space:\]\]+}" { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
# just get the card type field
regexp {: ([a-zA-Z0-9._-]+)[[:space:]]+} $out line ret
expect_chassis_prompt 60
return "$ret"
}
proc is_management_card_type { card_type } {
##
## is_management_card_type
## -------------------------
## is the given card_type a management card for the current chassis
##
## Usage:
## is_management_card_type card_type
## Arguments:
## card_type - card type to test
## Returns:
## 0-> this card type is not the management card type for this chassis
## 1-> this card type is the management card type for this chassis
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## Our best way to do this is to query slot 0 for its card type. Slot 0
## is always the management card
set my_slot [ get_this_slot_number ]
return [string equal [ get_chassis_card_type $my_slot] "$card_type" ]
}
proc get_this_slot_number { } {
##
## get_this_slot_number
## -------------------------
## return the slot number of the management card we are logged into
##
## Usage:
## get_this_slot_number
## Arguments:
## Returns:
## slot number
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "chassisQuery -master"
# this could return no slots, in which case "none" is output
# The \r\n is needed to bound the + so we get all the data on the line
set out [expect_list 60 "{master: \[0-9A-Za-z \]+\[\r\n\]}" { "usage" "Error" "Failed" "roblem" "not found"} ]
expect_chassis_prompt 60
#log_message "out=$out"
# The \r\n is needed to bound the + so we get all the data on the line
# there may be trailing spaces, TCL lists will ignore
regexp {: ([0-9A-Za-z ]+)[\r\n]+} $out line ret
#log_message "ret=$ret"
if { [ regexp -nocase "none" "$ret" ] } {
set ret ""
}
return "$ret"
}
proc get_chassis_active_firmware_version { slot } {
##
## get_chassis_active_firmware_version
## -------------------------
## return the presently running firmware_version of the given slot
## in the given chassis
##
## Usage:
## get_chassis_active_firmware_version slot
## Arguments:
## slot - slot to query
## Returns:
## firmware version of given slot
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "fwVersion $slot"
# echo of command
set out [expect_list 60 { "fwVersion" "Firmware Version: [a-zA-Z0-9.]+[[:space:]]" } { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
regexp {: ([a-zA-Z0-9._-]+)[[:space:]]} $out line ret
expect_chassis_prompt 60
return "$ret"
}
proc get_file_firmware_version { fw_file } {
##
## get_file_firmware_version
## -------------------------
## return the firmware version of the given local file
##
## Usage:
## get_file_firmware_version fw_file
## Arguments:
## fw_file - local pathname of file
## Returns:
## firmware version of given file
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
log_message "opafirmware --showVersion $fw_file\n"
set fw_version [exec /usr/sbin/opafirmware --showVersion $fw_file]
log_message "Version: $fw_version\n"
return "$fw_version"
}
proc get_file_firmware_type { fw_file } {
##
## get_file_firmware_type
## -------------------------
## return the firmware type of the given local file
##
## Usage:
## get_file_firmware_type fw_file
## Arguments:
## fw_file - local pathname of file
## Returns:
## firmware card type of given file
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
log_message "opafirmware --showType $fw_file"
set card_type [exec /usr/sbin/opafirmware --showType $fw_file]
log_message "Card Type: $card_type\n"
return "$card_type"
}
proc get_chassis_firmware_version { slot {location "act"} } {
##
## get_chassis_firmware_version
## -------------------------
## return the firmware version of the given slot in the chassis
##
## Usage:
## get_chassis_firmware_version slot [alternate]
## Arguments:
## slot - slot to query
## location - location to query
## "pri" -> primary
## "alt" -> alternate
## "act" -> active (running) (default)
## Returns:
## firmware version of given slot
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
if { [string equal "$location" "act"] } {
return [get_chassis_active_firmware_version $slot]
}
if { [string equal "$location" "alt"] } {
#set cmd "bootQuery $slot -showVersion -alternate"
set cmd "bootQuery $slot -alternate"
} else {
#set cmd "bootQuery $slot -showVersion"
set cmd "bootQuery $slot"
}
send_chassis_cmd "$cmd"
set out [expect_list 60 "{version: \[a-zA-Z0-9._-\]+\[\[:space:\]\]}" { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
regexp {: ([a-zA-Z0-9._-]+)[[:space:]]} $out line ret
expect_chassis_prompt 60
return "$ret"
}
proc select_chassis_firmware_version { slot fw_version } {
##
## select_chassis_firmware_version
## -------------------------
## select the given firmware version for the given slot in the chassis
##
## Usage:
## select_chassis_firmware_version slot fw_version
## Arguments:
## slot - slot to act on
## fw_version - firmware version to select
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
chassis_cmd 60 0 "bootSelect $slot -version $fw_version"
return 0
}
proc sftp_chassis_firmware { chassis fw_file {select 0} {slot "all"} } {
##
## sftp_chassis_firmware
## -------------------------
## push the given firmware file to the specified slots in the
## given chassis.
##
## Usage:
## sftp_chassis_firmware chassis fw_file [select [slot]]
## Arguments:
## chassis - IP address/name of chassis to sftp to
## fw_file - local pathname of file to push
## select - should image be selected (0->no (default), 1->yes)
## slot - slot to push to, default is "all" of given card type
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
if { $select } {
set nopt ""
} else {
set nopt "n"
}
# Create a basename for the firmware on the server
set base [ exec basename $fw_file ]
if { [ string equal "$slot" "all" ] } {
chassis_sftp_cmd "sftp admin@\\\[$chassis\\\]:" "put $fw_file A$nopt:/firmware/$base"
} else {
chassis_sftp_cmd "sftp admin@\\\[$chassis\\\]:" "put $fw_file $nopt$slot:/firmware/$base"
}
}
proc push_chassis_firmware { chassis fw_file fw_version card_type {action "push"} { slots "all" } } {
##
## push_chassis_firmware
## -------------------------
## push the given firmware file to the specified slots in the
## given chassis.
##
## Usage:
## push_chassis_firmware chassis fw_file fw_version card_type [action [slots]]
## Arguments:
## chassis - IP address/name of chassis to sftp and telnet to
## fw_file - local pathname of file to push
## fw_version - firmware version of fw_file
## card_type - card type of fw_file
## action - action after pushing firmware:
## push - do not reboot, do not change as primary
## is already primary, no change
## select - select as primary, but do not reboot
## run - select as primary and reboot to activate firmware
## slots - list of slot numbers to push to, default is "all" of given card type
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## Any slots which are not of a card type which matches that
## of the firmware file, are quietly skipped. This allows multi-file
## push to work with a slot list.
##
## If the sftp fails for some slots but succeeds for others. The failed slots
## are logged and an exception will be thrown, however the good slots
## are processed to completion [except for the reboot] (if possible)
## so that they are not left in an odd state
global spawn_id expect_out spawn_out timeout
global expecting
# caller has already done this
#set fw_version [get_file_firmware_version $fw_file]
#set card_type [get_file_firmware_type $fw_file]
# variables:
# select - do we want to select the image as primary
# active - do we want to select the image as primary and activate it
if { [ string equal "$action" "push"] } {
set select 0
set active 0
} elseif { [string equal "$action" "select"] } {
set select 1
set active 0
} elseif { [string equal "$action" "run"] } {
set select 1
set active 1
} else {
fail_test "Invalid firmware push action: $action"
}
log_message "\nTest: Seeing if $chassis present running firmware supports firmware push ...\n"
target_chassis_admin_sh $chassis
if { [ catch { set cap [check_chassis_capability "fwPush"] } res ] != 0
|| $cap < 1 } {
fail_test "Chassis $chassis present running firmware does not support fwPush:\n$res"
}
log_message "\nTest: Getting Slot list from $chassis ...\n"
if { [string equal "$slots" "all"] } {
# push to all slots of matching card type
set push_slots [get_chassis_slots $card_type]
set num_slots [llength $push_slots]
#set push_all 0
set push_all 1
} else {
# push to a specific set of slots
set push_slots "$slots"
set push_all 0
}
set num_slots [llength $push_slots]
if { $num_slots == 0 } {
target_chassis_admin_sh_exit
skip_case "No cards of type $card_type"
return 0
}
set is_mgmt [ is_management_card_type $card_type]
set use_rebootall 0
if { $is_mgmt } {
if { [ catch { set cap [check_chassis_capability "redundantMgmtCapable"] } res ] == 0
&& $cap >= 1 } {
set use_rebootall 1
}
}
log_message "\nTest: Getting present installed firmware from $chassis ...\n"
# first get present firmware versions for each of the slots
set act_fw_versions ""
set pri_fw_versions ""
set alt_fw_versions ""
set card_types ""
foreach slot $push_slots {
lappend card_types [get_chassis_card_type $slot]
lappend act_fw_versions [get_chassis_firmware_version $slot "act"]
lappend pri_fw_versions [get_chassis_firmware_version $slot "pri"]
lappend alt_fw_versions [get_chassis_firmware_version $slot "alt"]
}
target_chassis_admin_sh_exit
# DEBUG
#log_message "push_slots: $push_slots"
#log_message "num_slots: $num_slots"
#log_message "card_types: $card_types"
#log_message "act_fw_versions: $act_fw_versions"
#log_message "pri_fw_versions: $pri_fw_versions"
#log_message "alt_fw_versions: $alt_fw_versions"
#log_message "is_mgmt: $is_mgmt"
log_message "\nTest: Determining operations needed to upgrade to $fw_file for $chassis ..."
# now check that card types match and determine if we need to push firmware
set need_push ""
set need_select ""
set need_reboot ""
set have_card 0
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $push_slots $i]
if { ! [ string equal "$card_type" [lindex $card_types $i] ] } {
# if this happens card type is inconsistent with original
# query for slots of given card type (hot swap during update?)
# or user has explicitly specified slots and picked one of
# wrong card type
# quiety ignore this slot
lappend need_push 0
lappend need_select 0
lappend need_reboot 0
log_message "Test: Skipping slot $slot: is of type [lindex $card_types $i], $fw_file is for $card_type"
continue
}
set have_card 1
set in_pri [ string equal "$fw_version" [lindex $pri_fw_versions $i] ]
set in_alt [ string equal "$fw_version" [lindex $alt_fw_versions $i] ]
set booted [string equal "$fw_version" [lindex $act_fw_versions $i]]
if { ! $in_pri && ! $in_alt } {
# firmware is not in slot, need to push and possibly select/reboot
lappend need_push 1
set s $select
log_message "Test: Will need to push firmware to slot $slot"
} else {
# firmware is in one of the images
#set push_all 0
lappend need_push 0
# only need to select if not already the primary image
set s [ expr $select && ! $in_pri]
log_message "Test: Do Not need to push firmware to slot $slot"
}
# could be running something not on chassis but matching pushed version
# only need to reboot if not the active image
set a [ expr $active && ! $booted]
lappend need_select $s
lappend need_reboot $a
if { $s && $a } {
log_message "Test: Need to select firmware and reboot slot $slot\n"
} elseif { $a } {
log_message "Test: Firmware already selected, Need to reboot slot $slot\n"
} elseif { $s } {
log_message "Test: Firmware already booted, but need to select firmware for slot $slot\n"
} else {
log_message "Test: No need to select firmware nor reboot slot $slot\n"
}
}
# only need do a step if applicable to at least 1 slot
set do_push [ expr [lsearch -exact $need_push 1] != -1 ]
set do_select [ expr [lsearch -exact $need_select 1] != -1 ]
set do_reboot [ expr [lsearch -exact $need_reboot 1] != -1 ]
# DEBUG
#log_message "push_all: $push_all"
#log_message "do_push: $do_push"
#log_message "do_select: $do_select"
#log_message "do_reboot: $do_reboot"
#log_message "need_push: $need_push"
#log_message "need_select: $need_select"
#log_message "need_reboot: $need_reboot"
if { ! ( $do_push || $do_select || $do_reboot ) } {
# we are done, everything is already the way we want it
if { $have_card } {
skip_case "No action required"
} else {
skip_case "No selected slots of type $card_type"
}
return 0
}
set fail 0
set pushed 0
set sftp_failures ""
set sftp_statuses ""
if { $do_push && $push_all } {
# push to all applicable slots at once
log_message "Test: sftp $fw_file to all applicable slots\n"
sftp_chassis_firmware $chassis $fw_file $select "all"
# will check status of sftp operations for slots we care about below
log_message "\nTest: Verifying firmware was pushed to $chassis ...\n"
target_chassis_admin_sh $chassis
}
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $push_slots $i]
if { $do_push && ( $push_all || [lindex $need_push $i] ) } {
if { ! $push_all } {
# push one slot at a time
log_message "\nTest: sftp $fw_file to slot $slot\n"
sftp_chassis_firmware $chassis $fw_file $select $slot
target_chassis_admin_sh $chassis
}
# check status, must do immediately after sftp
set status [get_chassis_sftp_status $slot 1]
if { [ string equal "$status" "success" ] || [ string equal "$status" "skipped" ] } {
set pushed 1
lappend sftp_failures 0
lappend sftp_statuses "$status"
} else {
log_message "\nFAILURE: Failed to sftp firmware to chassis $chassis slot $slot\n"
set fail 1
lappend sftp_failures 1
lappend sftp_statuses "$status"
}
if { ! $push_all } {
target_chassis_admin_sh_exit
}
} else {
# we never sftp'ed to this slot
lappend sftp_failures 0
lappend sftp_statuses "skipped"
}
}
# make sure we are logged in for operations below
if { ! ( $do_push && $push_all ) } {
target_chassis_admin_sh $chassis
}
# DEBUG
#log_message "pushed: $pushed"
#log_message "sftp_statuses: $sftp_statuses"
#log_message "sftp_failures: $sftp_failures"
#log_message "fail: $fail"
if { $pushed } {
# we pushed firmware to some or all of the requested slots
# now check firmware versions
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $push_slots $i]
if { [lindex $sftp_failures $i] } {
# it failed, no further checks necessary
continue
}
if { ! $push_all && ! [lindex $need_push $i] } {
# we never sftp'ed to this slot
continue
}
# if we get here, status was success or skipped
# check what is in the slot after the sftp
set pri_fw_version [get_chassis_firmware_version $slot "pri"]
set alt_fw_version [get_chassis_firmware_version $slot "alt"]
set in_pri [ string equal "$fw_version" "$pri_fw_version" ]
set in_alt [ string equal "$fw_version" "$alt_fw_version" ]
if { $push_all && ! [lindex $need_push $i] } {
# sftp should have skipped this slot
if { ! [ string equal [lindex $sftp_statuses $i] "skipped" ] } {
set sftp_failures [lreplace $sftp_failures $i $i 1]
set fail 1
log_message "\nFAILURE: Unexpected sftp transfer for chassis $chassis slot $slot\n"
} elseif { ! $in_pri && ! $in_alt } {
# the version disappeared
set sftp_failures [lreplace $sftp_failures $i $i 1]
set fail 1
log_message "\nFAILURE: Unexpected removal of $fw_version for chassis $chassis slot $slot\n"
} elseif { $select && ! $in_pri } {
# the primary changed?
# we can recover
log_message "\nWARNING: Unexpected change of primary for chassis $chassis slot $slot\n"
set need_select [lreplace $need_select $i $i 1]
set do_select 1
}
continue
}
# if we get here, we needed to push to this slot
# hence the slot should not have been skipped
if { ! [ string equal [lindex $sftp_statuses $i] "success" ] } {
# it was skipped for no reason
set sftp_failures [lreplace $sftp_failures $i $i 1]
set fail 1
log_message "\nFAILURE: Unexpected skip of sftp transfer for chassis $chassis slot $slot\n"
} elseif { ! $in_pri && ! $in_alt } {
# the version never appeared
set sftp_failures [lreplace $sftp_failures $i $i 1]
set fail 1
log_message "\nFAILURE: $fw_version did not transfer to chassis $chassis slot $slot\n"
} elseif { $select && ! $in_pri } {
# the image is there but was not selected
# we can recover
log_message "\nWARNING: sftp transfered to wrong image for chassis $chassis slot $slot\n"
set need_select [lreplace $need_select $i $i 1]
set do_select 1
}
}
}
# DEBUG
#log_message "sftp_failures: $sftp_failures"
#log_message "do_select: $do_select"
#log_message "need_select: $need_select"
if { $do_select } {
# now select the firmware as primary for the necessary slots
for { set i 0 } { $i < $num_slots } { incr i } {
if { [lindex $sftp_failures $i] } {
# it failed, no further ops necessary
continue
}
if { ! [lindex $need_select $i] } {
# does not need select
continue
}
set slot [lindex $push_slots $i]
select_chassis_firmware_version $slot $fw_version
}
}
# if any of the above failed, do not activate or reboot it could
# make things worse
if { $fail } {
fail_test "Unable to sftp firmware to requested slots in $chassis"
}
# DEBUG
#log_message "do_reboot: $do_reboot"
#log_message "need_reboot: $need_reboot"
#log_message "is_mgmt: $is_mgmt"
# now activate the firmware for the necessary slots
if { $do_reboot} {
if { $is_mgmt } {
# we can't tell which slot is the active mgmt slot, so just reboot
# the whole chassis
if { $use_rebootall } {
chassis_cmd_reboot_all
} else {
send_chassis_cmd "reboot now"
expect_list 60 { "Goodbye" }
}
# may not be able to send logout command, so just get out of dodge
ignore_rest
chassis_wait_reboot_done $chassis
return 0
} else {
for { set i 0 } { $i < $num_slots } { incr i } {
if { [lindex $need_reboot $i] } {
set slot [lindex $push_slots $i]
chassis_cmd 60 0 "resetCard $slot now"
}
}
# TBD - is there a way to poll via CLI if card has finished reboot?
# allow 60 seconds for it to come fully on line
sleep 60
}
}
target_chassis_admin_sh_exit
return 0
}
proc test_case_push_chassis_firmware { chassis fw_file fw_version card_type {action "push"} { slots "all" } } {
##
## test_case_push_chassis_firmware
## -------------------------
## test case to push the given firmware file to the specified slots in the
## given chassis.
##
## Usage:
## test_case_push_chassis_firmware chassis fw_file fw_version card_type [action [slots]]
## Arguments:
## chassis - IP address/name of chassis to sftp and telnet to
## fw_file - local pathname of file to push
## fw_version - firmware version of fw_file
## card_type - card type of fw_file
## action - action after pushing firmware:
## push - do not reboot, do not change as primary
## is already primary, no change
## select - select as primary, but do not reboot
## run - select as primary and reboot to activate firmware
## slots - list of slot numbers to push to, default is "all" of given card type
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## see push_chassis_firmware for error handling summary
global env
set base [exec basename $fw_file .pkg]
test_case "$chassis.$base" "upgrade $chassis $base" "upgrade firmware on $chassis
selected slots: $slots
Firmware File: $fw_file
card type: $card_type
Firmware Version: $fw_version
Action: $action
File: TestTools/chassis.exp" noop noop {
upvar chassis chassis
upvar fw_file fw_file
upvar fw_version fw_version
upvar card_type card_type
upvar action action
upvar slots slots
push_chassis_firmware "$chassis" "$fw_file" "$fw_version" "$card_type" "$action" "$slots"
}
}
proc sftp_chassis_security_file { chassis sec_file } {
##
## sftp_chassis_security_file
## -------------------------
## push the given FM security file to the specified chassis the
# given chassis.
##
## Usage:
## sftp_chassis_security_file chassis sec_file
## Arguments:
## chassis - IP address/name of chassis to sftp to
## sec_file - local pathname of file to push
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
chassis_sftp_cmd "sftp admin@\\\[$chassis\\\]:" "put $sec_file /firmware"
}
proc push_chassis_security_file { chassis sec_file {action "push"} { slots "all" } } {
##
## push_chassis_security_file
## -------------------------
## push the given FM security file to the specified slots in the
## given chassis.
##
## Usage:
## push_chassis_security_file chassis sec_file [action [slots]]
## Arguments:
## chassis - IP address/name of chassis to sftp and telnet to
## sec_file - local pathname of file to push
## action - action after pushing FM security files:
## push - push, do not restart SM
## run - make sure FM running on master, not on slave
## runall - make sure FM running on master and slave
## restart - restart FM on master, make sure not running on slave
## restartall - restart FM on master and slave
## stop - stop FM on master and slave
## slots - list of slot numbers to push to, default is "all" to all
# management cards
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## Any slots which are not of a management card, are quietly skipped.
## This allows multi-file push to work with a slot list.
##
## If the sftp fails for some slots but succeeds for others. The failed slots
## are logged and an exception will be thrown, however the good slots
## are processed to completion.
global spawn_id expect_out spawn_out timeout
global expecting
# variables:
# runstate: 0=no change, 1=running, 2=restart, -1=stop
if { "$action" == "push" } {
set push 1
set master_runstate 0
set slave_runstate 0
} elseif { "$action" == "stop" } {
set push 0
set master_runstate -1
set slave_runstate -1
} elseif { "$action" == "run" } {
set push 0
set master_runstate 1
set slave_runstate -1
} elseif { "$action" == "runall" } {
set push 0
set master_runstate 1
set slave_runstate 1
} elseif { "$action" == "restart" } {
set push 0
set master_runstate 2
set slave_runstate -1
} elseif { "$action" == "restartall" } {
set push 0
set master_runstate 2
set slave_runstate 2
} else {
fail_test "Invalid FM security file push action: $action"
}
log_message "\nTest: Seeing if $chassis present running firmware supports FM security file push ...\n"
target_chassis_admin_sh $chassis
if { [ catch { set cap [check_chassis_capability "fwPush"] } res ] != 0
|| $cap < 1 } {
fail_test "Chassis $chassis present running firmware does not support push:\n$res"
}
log_message "\nTest: Getting card type of management card from $chassis ...\n"
set my_slot [ get_this_slot_number ]
set mgmt_card_type [get_chassis_card_type $my_slot]
log_message "\nTest: Getting Slot list from $chassis ...\n"
if { [string equal "$slots" "all"] } {
# push to all slots of matching card type
set push_slots [get_chassis_slots $mgmt_card_type]
set num_slots [llength $push_slots]
#set push_all 0
set push_all 1
} else {
# push to a specific set of slots
set push_slots "$slots"
set push_all 0
}
set num_slots [llength $push_slots]
if { $num_slots == 0 } {
target_chassis_admin_sh_exit
skip_case "No management cards of type $mgmt_card_type"
return 0
}
log_message "\nTest: Getting present installed firmware from $chassis ...\n"
# first get present card types for each of the slots
set card_types ""
foreach slot $push_slots {
lappend card_types [get_chassis_card_type $slot]
}
target_chassis_admin_sh_exit
# DEBUG
#log_message "push_slots: $push_slots"
#log_message "num_slots: $num_slots"
#log_message "card_types: $card_types"
#log_message "mgmt_card_type: $mgmt_card_type"
#log_message "my_slot: $my_slot"
log_message "\nTest: Determining operations needed to upgrade to $sec_file for $chassis ..."
# now check that card types match and determine if we need to push security file
set need_push ""
set have_card 0
set mgmt_slot ""
set fm_running ""
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $push_slots $i]
if { ! [ string equal "$mgmt_card_type" [lindex $card_types $i] ] } {
# if this happens card type is inconsistent with original
# query for slots of given card type (hot swap during update?)
# or user has explicitly specified slots and picked one of
# wrong card type
# quiety ignore this slot
lappend need_push 0
log_message "Test: Skipping slot $slot: is of type [lindex $card_types $i], $sec_file is for $mgmt_card_type"
continue
}
# check whether the slot support running the ESM
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh $chassis
} else {
target_chassis_admin_sh $chassis $slot
}
if { [ catch { set cap [check_chassis_capability "smConfig"] } res ] != 0
|| $cap < 1 } {
# slot will be skipped, doesn't support smConfig
lappend card_capabilities 0
lappend fm_running 0
} else {
lappend card_capabilities 1
lappend fm_running [ is_esm_running ]
if { [ string equal "$mgmt_slot" ""] || [ string equal "$slot" "$my_slot" ] } {
set mgmt_slot "$slot"
}
}
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh_exit
} else {
target_chassis_admin_sh_exit 1
}
set have_card 1
# valid slot so push security file to this slot
lappend need_push $push
if { $push } {
log_message "Test: Will need to push FM security file to slot $slot"
} else {
log_message "Test: No need to push FM security files slot $slot\n"
}
}
# only need do a step if applicable to at least 1 slot
set do_push [ expr [lsearch -exact $need_push 1] != -1 ]
# DEBUG
#log_message "mgmt_slot: $mgmt_slot"
#log_message "card_capabilities: $card_capabilities"
#log_message "fm_running: $fm_running"
if { [ string equal "$mgmt_slot" "" ] } {
skip_case "chassis does not have an ESM license key or does not support smConfig"
return 0
}
# DEBUG
#log_message "push_all: $push_all"
#log_message "do_push: $do_push"
#log_message "need_push: $need_push"
set fail 0
set pushed 0
set sftp_failures ""
set sftp_statuses ""
set need_runstate ""
set do_runstate 0
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $push_slots $i]
if { ! [lindex $card_capabilities $i] } {
# if this happens card is not capable of smConfig ignore this slot
lappend need_runstate 0
log_message "Test: Skipping slot $slot: does not support smConfig"
continue
}
set running [ lindex $fm_running $i]
if { ! [ string equal "$slot" "$my_slot" ] } {
# slave MM
if { ( $slave_runstate < 0 && $running )
|| ($slave_runstate == 1 && ! $running )
|| $slave_runstate == 2 } {
lappend need_runstate $slave_runstate
set do_runstate 1
log_message "Test: Need to change runstate for FM in slot $slot"
} else {
lappend need_runstate 0
log_message "Test: No need to change runstate for FM in slot $slot"
}
} else {
# master MM
if { ( $master_runstate < 0 && $running )
|| ($master_runstate == 1 && ! $running )
|| $master_runstate == 2 } {
lappend need_runstate $master_runstate
set do_runstate 1
log_message "Test: Need to change runstate for FM in slot $slot"
} else {
lappend need_runstate 0
log_message "Test: No need to change runstate for FM in slot $slot"
}
}
if { $do_push && ( $push_all || [lindex $need_push $i] ) } {
# push one slot at a time
log_message "\nTest: sftp $sec_file to slot $slot\n"
sftp_chassis_security_file $chassis $sec_file
target_chassis_admin_sh $chassis
# check status, must do immediately after sftp
set status [get_chassis_sftp_status $slot 1]
if { [ string equal "$status" "success" ] || [ string equal "$status" "skipped" ] } {
set pushed 1
lappend sftp_failures 0
lappend sftp_statuses "$status"
} else {
log_message "\nFAILURE: Failed to sftp FM security file to chassis $chassis slot $slot\n"
set fail 1
lappend sftp_failures 1
lappend sftp_statuses "$status"
}
if { ! $push_all } {
target_chassis_admin_sh_exit
}
} else {
# we never sftp'ed to this slot
lappend sftp_failures 0
lappend sftp_statuses "skipped"
}
}
# make sure we are logged in for operations below
if { ! ( $do_push && $push_all ) } {
target_chassis_admin_sh $chassis
}
# DEBUG
#log_message "pushed: $pushed"
#log_message "sftp_statuses: $sftp_statuses"
#log_message "sftp_failures: $sftp_failures"
#log_message "fail: $fail"
if { $fail } {
fail_test "Unable to sftp FM security file to requested slots in $chassis"
}
# DEBUG
#log_message "do_runstate: $do_runstate"
#log_message "need_runstate: $need_runstate"
if { $do_runstate} {
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $push_slots $i]
set new_runstate [lindex $need_runstate $i]
if { $new_runstate != 0 } {
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh $chassis
} else {
target_chassis_admin_sh $chassis $slot
}
if { $new_runstate > 0 } {
# easier to restart, will start if not already running
chassis_cmd 60 0 "smControl restart"
# don't wait, it would risk having mismatched esm configs
} elseif { $new_runstate < 0 } {
chassis_cmd 60 0 "smControl stop"
}
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh_exit
} else {
target_chassis_admin_sh_exit 1
}
}
}
}
if { ! ( $do_push || $do_runstate ) } {
# we are done, everything is already the way we want it
if { $have_card } {
skip_case "No action required"
} else {
skip_case "No selected slots of type $mgmt_card_type"
}
return 0
}
return 0
}
proc test_case_push_chassis_security_file { chassis sec_file {action "push"} { slots "all" } } {
##
## test_case_push_chassis_security_file
## -------------------------
## test case to push the given FM security file to the specified slots in the
## given chassis.
##
## Usage:
## test_case_push_chassis_security_file chassis sec_file [action [slots]]
## Arguments:
## chassis - IP address/name of chassis to sftp and telnet to
## sec_file - local pathname of file to push
## action - action after pushing firmware:
## push - do not reboot, do not change as primary
## is already primary, no change
## slots - list of slot numbers to push to, default is "all" of given card type
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## see push_chassis_security_file for error handling summary
global env
set base [exec basename $sec_file .pem]
test_case "$chassis.$base" "upgrade $chassis $base" "upgrade FM security file on $chassis
Selected slots: $slots
Security File: $sec_file
Action: $action
File: TestTools/chassis.exp" noop noop {
upvar chassis chassis
upvar sec_file sec_file
upvar action action
upvar slots slots
push_chassis_security_file "$chassis" "$sec_file" "$action" "$slots"
}
}
proc test_case_chassis_configure { chassis chassisIP} {
global tools_case_status
test_case "$chassis.emb.configure" "Chassis $chassis configure" "Configure $chassis
File: TestOnly/chassis.exp" case_setup case_cleanup {
upvar chassis chassis
upvar chassisIP chassisIP
global env
set syslogServer $env(SYSLOG_SERVER)
set syslogPort $env(SYSLOG_PORT)
set syslogFacility $env(SYSLOG_FACILITY)
set ntpServer $env(NTP_SERVER)
set tzOffset $env(TZ_OFFSET)
set dstStart $env(DST_START)
set dstEnd $env(DST_END)
set linkWidthSelection $env(LINKWIDTH_SETTING)
set setname $env(SET_NAME)
set linkCRCMode $env(LINKCRCMODE)
target_chassis_admin_sh $chassis
set chassis_cmd_string "logSyslogConfig"
set setSyslog 0
if { [string equal "$syslogServer" "0.0.0.0"] == 0 } {
append chassis_cmd_string " -h $syslogServer"
set setSyslog 1
}
if { [string equal "$syslogPort" "-1"] == 0 } {
append chassis_cmd_string " -p $syslogPort"
set setSyslog 1
}
if { [string equal "$syslogFacility" "-1"] == 0 } {
append chassis_cmd_string " -f $syslogFacility"
set setSyslog 1
}
if { $setSyslog } {
send_chassis_cmd $chassis_cmd_string
expect_progress 60 {[\r\n]} { "Successfully" "facility" } { "Invalid" "usage" }
check_chassis_exit_status 60 0
}
if { [string equal "$ntpServer" "0.0.0.0"] == 0 } {
send_chassis_cmd "time -S $ntpServer"
expect_progress 60 {[\r\n]} { "Configured" "NTP" } { "not" "valid" }
check_chassis_exit_status 60 0
}
if { [string equal "$tzOffset" "100"] == 0 } {
send_chassis_cmd "timeZoneConf $tzOffset"
expect_progress 60 {[\r\n]} { "successfully" "configured" } { "invalid" "not found" }
check_chassis_exit_status 60 0
}
if { [string equal "$dstStart" "0 0 0"] == 0 } {
send_chassis_cmd "timeDstConf $dstStart $dstEnd"
expect_progress 60 {[\r\n]} { "Timezone" "DST" } { "bounds" "not found" }
check_chassis_exit_status 60 0
}
if { [string equal "$linkWidthSelection" "0"] == 0 } {
send_chassis_cmd "ismChassisSetWidth $linkWidthSelection"
send_chassis_cmd "showLastRetCode"
set out [expect_list 60 { "Last Exit Code: [0-9]+" } { "Error" }]
regexp {([0-9]+)} $out ret
if { ! [ string equal "$ret" "0"] } {
if { [ string equal "$ret" "5"] } {
skip_case "\n$chassis: Skipped changing chassis link width: ismChassisSetWidth command not supported"
} else {
fail_test "\n$chassis: Failed changing chassis link width: ismChassisSetWidth command failed with return code $ret"
}
}
}
if { [string equal "$chassisIP" "0.0.0.0"] == 0 } {
send_chassis_cmd "setChassisIpAddr -h $chassisIP"
expect_progress 60 {[\r\n]} { "reconnect" "updated" } { "valid" "usage" }
check_chassis_exit_status 60 0
}
# $setname is y or n
if { [string equal "$setname" "y"] } {
send_chassis_cmd "setNodeDesc \"$chassis\""
expect_progress 60 {[\r\n]} { "success" "changed" } { "usage:" "not found" }
check_chassis_exit_status 60 0
}
if { [string equal "$linkCRCMode" "-1" ] == 0 } {
send_chassis_cmd "ismChassisSetCrcMode $linkCRCMode"
send_chassis_cmd "showLastRetCode"
set out [expect_list 60 { "Last Exit Code: [0-9]+" } { "Error" }]
regexp {([0-9]+)} $out ret
if { ! [ string equal "$ret" "0"] } {
if { [ string equal "$ret" "5" ] } {
skip_case "\n$chassis: Skipped changing chassis link CRC mode: ismChassisSetCrcMode command not supported"
} else {
fail_test "\n$chassis: Failed changing chassis link CRC mode: ismChassisSetCrcMode command failed with return code $ret"
}
}
check_chassis_exit_status 60 0
}
target_chassis_admin_sh_exit
}
}
proc check_esm_config { config_file } {
##
## check_esm_config
## -------------------------
## check the syntax of the given esm config file
##
## Usage:
## check_esm_config config_file
## Arguments:
## config_file - local pathname of file
## Returns:
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global env
if { [ file exists "/usr/lib/opa/fm_tools/config_check" ] } {
set env(IFS_FM_BASE) ""
set tooldir "/usr/lib/opa/fm_tools"
} else {
set env(IFS_FM_BASE) "/usr/lib/opa-fm"
set tooldir "$env(IFS_FM_BASE)/bin"
}
log_message "IFS_FM_BASE=$env(IFS_FM_BASE)"
log_message "tooldir=$tooldir"
log_message ""
if { [ file exists "$tooldir/config_check" ] } {
log_message "$tooldir/config_check -c $config_file"
if { [ catch { exec "$tooldir/config_check" -c "$config_file" } err_str ] != 0 } {
puts "$tooldir/config_check -c $config_file"
show_message "$err_str"
error "$tooldir/config_check -c $config_file: failed: $err_str"
}
} else {
skip_case "Unable to pre-check config file, Intel OPA FM not installed on this server"
}
return 0
}
proc sftp_esm_config { chassis config_file } {
##
## sftp_esm_config
## -------------------------
## push the given SM config file to the specified chassis
##
## Usage:
## sftp_esm_config chassis config_file
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## config_file - local pathname of file to push
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
chassis_sftp_cmd "sftp admin@\\\[$chassis\\\]:" "put $config_file /firmware/opafm.xml"
return 0
}
proc get_esm_config { chassis config_file } {
##
## get_esm_config
## -------------------------
## get esm config file from the chassis
##
## Usage:
## get_esm_config config_file
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## config_file - local pathname of file to get to
## Returns:
## 1 - match, 0 - no match
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
chassis_sftp_cmd "sftp admin@\\\[$chassis\\\]:" "get /firmware/opafm.xml $config_file" 0
return 0
}
proc compare_esm_config { chassis config_file mgmt_card_type } {
##
## compare_esm_config
## -------------------------
## return if the config_file matches esm config in the chassis
##
## Usage:
## compare_esm_config chassis config_file
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## config_file - local pathname of file to push
## mgmt_card_type - Name of management card type
## Returns:
## 1 - match, 0 - no match
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
global test_tmp
log_message "\nComparing $config_file to FM config...\n"
set temp_file "$test_tmp/get_opafm.xml"
get_esm_config "$chassis" "$test_tmp/get_opafm.xml"
if { [catch { run_cmd "exec cmp -s $config_file $temp_file"} ] == 0 } {
# match
log_message "Test: Matches\n"
return 1
} else {
log_message "Test: Does not Match\n"
return 0
}
}
proc is_esm_running { } {
##
## is_esm_running
## -------------------------
## check the esm status for the current slot in the current chassis
##
## Usage:
## is_esm_running
## Arguments:
## Returns:
## esm status
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
# DONE
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "smControl status"
# require trailing whitespace to anchor end of string
set out [expect_list 60 "{Subnet manager is \[a-zA-Z0-9_ -\]+\\\.}" { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
set ret ""
regexp {is ([a-zA-Z0-9_ -]+)\.} $out line ret
expect_chassis_prompt 60
return [ string equal "$ret" "running"]
}
proc chassis_get_sm_boot_options { } {
##
## chassis_get_sm_boot_options
## -------------------------
## return SM boot options
##
## Usage:
## chassis_get_sm_boot_options
## Arguments:
## Returns:
## { startAtBoot startOnSlaveCmu }
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
send_chassis_cmd "smConfig query"
# the [:space:] bounds the + so we get all the data on the line
set out1 [expect_list 60 "{startAtBoot: \[0-9A-Za-z\]+\[\[:space:\]\]}" { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
set out2 [expect_list 60 "{startOnSlaveCmu: \[0-9A-Za-z/\]+\[\[:space:\]\]}" { "usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
check_chassis_exit_status 60 0
#log_message "out1=$out1"
#log_message "out2=$out2"
set ret1 ""
set ret2 ""
# throw away the \r\n, there may be trailing spaces, TCL lists will ignore
regexp {startAtBoot: ([A-Za-z0-9]+)[[:space:]]} $out1 line ret1
regexp {startOnSlaveCmu: ([A-Za-z0-9/]+)[[:space:]]} $out2 line ret2
return "$ret1 $ret2"
}
proc push_esm_config { chassis config_file {action "push"} {bootstate ""} } {
##
## push_esm_config
## -------------------------
## push the given ESM config file to all the applicable slots in the
## given chassis.
##
## Usage:
## push_esm_config chassis config_file [action [bootstate]]
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## config_file - local pathname of file to push
## if "", no file push will occur
## action - action after pushing config_file:
## push - push, do not restart SM
## select - same as push
## run - make sure FM running on master, not on slave
## runall - make sure FM running on master and slave
## restart - restart FM on master, make sure not running on slave
## restartall - restart FM on master and slave
## stop - stop FM on master and slave
## bootstate - optional selection for FM bootstate
## "" - no change to FM start at boot options
## disable - disable FM start on all MM on boot
## enable - enable FM start on master MM on boot, disable on slave MM
## enableall - enable FM start on all MM on boot
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## If the scp fails for some slots but succeeds for others. The failed slots
## are logged and an exception will be thrown, however the good slots
## are processed to completion [except for the restart] (if possible)
## so that they are not left in an odd state
global spawn_id expect_out spawn_out timeout
global expecting
# variables:
# restart_only - no push, just restart FM
set restart_only [string equal "$config_file" ""]
# runstate: 0=no change, 1=running, 2=restart, -1=stop
if { "$action" == "push" || "$action" == "select" } {
set master_runstate 0
set slave_runstate 0
} elseif { "$action" == "stop" } {
set master_runstate -1
set slave_runstate -1
} elseif { "$action" == "run" } {
set master_runstate 1
set slave_runstate -1
} elseif { "$action" == "runall" } {
set master_runstate 1
set slave_runstate 1
} elseif { "$action" == "restart" } {
set master_runstate 2
set slave_runstate -1
} elseif { "$action" == "restartall" } {
set master_runstate 2
set slave_runstate 2
} else {
fail_test "Invalid FM config file action: $action"
}
if { "$bootstate" != "" && "$bootstate" != "disable"
&& "$bootstate" != "enable" && "$bootstate" != "enableall" } {
fail_test "Invalid FM config bootstate: $bootstate"
}
# DEBUG
#log_message "action: $action"
#log_message "restart_only: $restart_only"
#log_message "bootstate: $bootstate"
target_chassis_admin_sh $chassis
# check if chassis supports slaveCli, if not then skip chassis, older FW
if { [ catch { set cap [check_chassis_capability "slaveCli"] } res ] != 0
|| $cap < 1 } {
skip_case "chassis does not support FM config via FastFabric"
}
log_message "\nTest: Getting Slot list from $chassis ...\n"
set my_slot [ get_this_slot_number ]
# get management card type for this chassis model
set mgmt_card_type [ get_chassis_card_type $my_slot ]
# all slots of matching card type (eg. all management cards)
set mgmt_slots [get_chassis_slots $mgmt_card_type]
set num_slots [llength $mgmt_slots]
if { $num_slots == 0 } {
# this should not happen, we should at least find card 0
target_chassis_admin_sh_exit
fail_test "No cards of type $mgmt_card_type"
}
# lets be paranoid
if { ! [ string equal [get_chassis_card_type $my_slot] "$mgmt_card_type" ] } {
target_chassis_admin_sh_exit
fail_test "IP address given is not for chassis mgmt card $mgmt_card_type"
}
target_chassis_admin_sh_exit
log_message "\nTest: Checking FM capabilities and status of $chassis ...\n"
# flag per slot indicating if slot has smConfig capability
# may not have smConfig on all cards, esp if ESM license key lacking
set card_capabilities ""
# flag per slot indicating if slot has FM currently running
set fm_running ""
# slot which supports sm CLI. Prefer active mgmt card otherwise its
# secondary mgmt card
set mgmt_slot ""
# see if any slots in chassis support smConfig, if so check FM status
foreach slot $mgmt_slots {
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh $chassis
} else {
target_chassis_admin_sh $chassis $slot
}
if { [ catch { set cap [check_chassis_capability "smConfig"] } res ] != 0
|| $cap < 1 } {
# slot will be skipped, doesn't support smConfig
lappend card_capabilities 0
lappend fm_running 0
} else {
lappend card_capabilities 1
lappend fm_running [ is_esm_running ]
if { [ string equal "$mgmt_slot" ""] || [ string equal "$slot" "$my_slot" ] } {
set mgmt_slot "$slot"
}
}
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh_exit
} else {
target_chassis_admin_sh_exit 1
}
}
# DEBUG
#log_message "mgmt_slots: $mgmt_slots"
#log_message "mgmt_slot: $mgmt_slot"
#log_message "num_slots: $num_slots"
#log_message "card_capabilities: $card_capabilities"
#log_message "fm_running: $fm_running"
if { [ string equal "$mgmt_slot" "" ] } {
skip_case "chassis does not have an ESM license key or does not support smConfig"
}
set do_push 0
if { ! $restart_only } {
log_message "\nTest: Checking FM config in $chassis ...\n"
# we trust that secondary management cards will be synced with primary
# so only need to upload this once
set config_matches [compare_esm_config $chassis $config_file $mgmt_card_type ]
# DEBUG
#log_message "config_matches: $config_matches"
log_message "\nTest: Determining operations needed to update to $config_file for $chassis ..."
## now check that card is capable and determine if we need to push config
if { ! $config_matches } {
# config is not in chassis, need to push and possibly restart
set do_push 1
log_message "Test: Will need to push config to chassis"
} else {
# config is in chassis, can skip push
set do_push 0
log_message "Test: No need to push config to chassis"
}
}
# we are assuming we can trust that primary mgmt card's config files
# match those of secondary. So FM runstate is only card specific aspect.
set need_runstate ""
set do_runstate 0
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $mgmt_slots $i]
if { ! [lindex $card_capabilities $i] } {
# if this happens card is not capable of smConfig ignore this slot
lappend need_runstate 0
log_message "Test: Skipping slot $slot: does not support smConfig"
continue
}
set running [ lindex $fm_running $i]
if { ! [ string equal "$slot" "$my_slot" ] } {
# slave MM
if { ( $slave_runstate < 0 && $running )
|| ($slave_runstate == 1 && ! $running )
|| $slave_runstate == 2 } {
lappend need_runstate $slave_runstate
set do_runstate 1
log_message "Test: Need to change runstate for FM in slot $slot"
} else {
lappend need_runstate 0
log_message "Test: No need to change runstate for FM in slot $slot"
}
} else {
# master MM
if { ( $master_runstate < 0 && $running )
|| ($master_runstate == 1 && ! $running )
|| $master_runstate == 2 } {
lappend need_runstate $master_runstate
set do_runstate 1
log_message "Test: Need to change runstate for FM in slot $slot"
} else {
lappend need_runstate 0
log_message "Test: No need to change runstate for FM in slot $slot"
}
}
}
log_message "\n"
# DEBUG
#log_message "do_push: $do_push"
#log_message "do_runstate: $do_runstate"
#log_message "need_runstate: $need_runstate"
if { $do_push } {
set fail 0
set scp_statuses ""
# push is done once to chassis and automatically synced to all
# mgmt cards
log_message "Test: sftp $config_file to all applicable slots\n"
sftp_esm_config $chassis $config_file
# will check status of scp operations for slots we care about below
log_message "\nTest: Verifying config was pushed to $chassis ...\n"
target_chassis_admin_sh $chassis
# check status, must do immediately after scp for each slot
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $mgmt_slots $i]
# do not expect "skipped" status
set status [get_chassis_sftp_status $slot 1]
if { [ string equal "$status" "success" ] } {
lappend scp_statuses "$status"
} else {
log_message "\nFAILURE: Failed to scp config to chassis $chassis slot $slot\n"
set fail 1
lappend scp_statuses "$status"
}
}
target_chassis_admin_sh_exit
# DEBUG
#log_message "scp_statuses: $scp_statuses"
#log_message "fail: $fail"
if { ! $fail } {
# we trust that secondary management cards will be synced with
# primary so only need to upload this once
if { ! [compare_esm_config $chassis $config_file $mgmt_card_type ] } {
# the config disappeared
set fail 1
log_message "\nFAILURE: Unexpected mismatch of config for chassis $chassis slot $slot\n"
}
}
# if any of the above failed, do not change runstate it could make things worse
if { $fail } {
fail_test "Unable to scp config to required slots in $chassis"
}
}
# DEBUG
#log_message "do_runstate: $do_runstate"
#log_message "need_runstate: $need_runstate"
if { $do_runstate} {
for { set i 0 } { $i < $num_slots } { incr i } {
set slot [lindex $mgmt_slots $i]
set new_runstate [lindex $need_runstate $i]
if { $new_runstate != 0 } {
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh $chassis
} else {
target_chassis_admin_sh $chassis $slot
}
if { $new_runstate > 0 } {
# easier to restart, will start if not already running
chassis_cmd 60 0 "smControl restart"
# don't wait, it would risk having mismatched esm configs
} elseif { $new_runstate < 0 } {
chassis_cmd 60 0 "smControl stop"
}
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh_exit
} else {
target_chassis_admin_sh_exit 1
}
}
}
}
# adjust start at boot options
set do_boot 0
if { "$bootstate" != "" } {
if { ! [ string equal "$mgmt_slot" "$my_slot" ] } {
fail_test "Master MM does not have SM license key, unable to configure start options in $chassis"
}
log_message "\nTest: Checking FM Start At Boot Options for $chassis ...\n"
target_chassis_admin_sh $chassis
# get present options to old_startAtBootArg and old_startOnSlaveCmuArg
set boot_options [ chassis_get_sm_boot_options ]
set old_startAtBootArg [lindex $boot_options 0 ]
set old_startOnSlaveCmuArg [lindex $boot_options 1 ]
# determine desired settings
if { "$bootstate" == "disable" } {
set startAtBootArg "no"
set startOnSlaveCmuArg "no"
} elseif { "$bootstate" == "enable" } {
set startAtBootArg "yes"
set startOnSlaveCmuArg "no"
} elseif { "$bootstate" == "enableall" } {
set startAtBootArg "yes"
set startOnSlaveCmuArg "yes"
}
# do what is needed
set arg1 ""
set arg2 ""
if { "$startAtBootArg" != "$old_startAtBootArg" } {
set arg1 "startAtBoot $startAtBootArg"
}
if { "$startOnSlaveCmuArg" != "$old_startOnSlaveCmuArg"
&& "$old_startOnSlaveCmuArg" != "N/A" } {
set arg2 "startOnSlaveCmu $startOnSlaveCmuArg"
}
if { "$arg1" != "" || "$arg2" != "" } {
log_message "\nTest: Changing FM Start At Boot Options for $chassis ...\n"
chassis_cmd 60 0 "smConfig $arg1 $arg2"
set do_boot 1
} else {
log_message "\nTest: No Change to FM Start At Boot Options for $chassis ...\n"
}
target_chassis_admin_sh_exit
}
if { ! $do_push && ! $do_runstate && ! $do_boot } {
# we are done, everything is already the way we want it
# report skip so user knows we didn't change anything on this chassis
skip_case "No action required"
return 0
}
return 0
}
proc test_case_push_esm_config { chassis config_file {action "push"} {bootstate ""} } {
##
## test_case_push_esm_config
## -------------------------
## test case to push the given config file to all applicable slots in
## the given chassis.
##
## Usage:
## test_case_push_esm_config chassis config_file [action [bootstate]]
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## config_file - local pathname of file to push
## action - action after pushing firmware:
## push - push, do not restart SM
## select - same as push
## run - restart FM on master, make sure not running on slave
## runall - restart FM on master and slave
## stop - stop FM on master and slave
## bootstate - optional selection for FM bootstate
## "" - no change to FM start at boot options
## disable - disable FM start on all MM on boot
## enable - enable FM start on master MM on boot, disable on slave MM
## enableall - enable FM start on all MM on boot
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## see push_esm_config for error handling summary
global env
test_case "$chassis.fmconfig" "update $chassis $config_file" "update FM config file on $chassis
Config File: $config_file
Action: $action
BootState: $bootstate
File: TestTools/chassis.exp" noop noop {
upvar chassis chassis
upvar config_file config_file
upvar action action
upvar bootstate bootstate
# translate action, when pushing config file we want to use restart
# so that the new file ends up "running"
if { "$action" == "run" } {
set action "restart"
} elseif { "$action" == "runall" } {
set action "restartall"
}
push_esm_config "$chassis" "$config_file" "$action" "$bootstate"
}
}
proc test_case_get_esm_config { chassis config_file } {
##
## test_case_get_esm_config
## -------------------------
## test case to get the given config file from the given chassis.
##
## Usage:
## test_case_get_esm_config chassis config_file
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## config_file - local pathname of file to fetch to
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## see push_esm_config for error handling summary
global env
test_case "$chassis.fm_get_config" "get $chassis $config_file" "get FM config file from $chassis
To Config File: $config_file
File: TestTools/chassis.exp" noop noop {
upvar chassis chassis
upvar config_file config_file
upvar action action
# check if chassis supports slaveCli, if not then skip chassis, older FW
target_chassis_admin_sh $chassis
if { [ catch { set cap [check_chassis_capability "slaveCli"] } res ] != 0
|| $cap < 1 } {
skip_case "chassis does not support FM config via FastFabric"
}
# TBD - make this common code with fmconfig
log_message "\nTest: Getting Slot list from $chassis ...\n"
set my_slot [ get_this_slot_number ]
# get management card type for this chassis model
set mgmt_card_type [ get_chassis_card_type $my_slot]
# all slots of matching card type (eg. all management cards)
set mgmt_slots [get_chassis_slots $mgmt_card_type]
set num_slots [llength $mgmt_slots]
if { $num_slots == 0 } {
# this should not happen, we should at least find card 0
target_chassis_admin_sh_exit
fail_test "No cards of type $mgmt_card_type"
}
# lets be paranoid
if { ! [ string equal [get_chassis_card_type $my_slot] "$mgmt_card_type" ] } {
target_chassis_admin_sh_exit
fail_test "IP address given is not for chassis mgmt card $mgmt_card_type"
}
target_chassis_admin_sh_exit
log_message "\nTest: Checking FM capabilities and status of $chassis ...\n"
# slot which supports sm CLI. Prefer active mgmt card otherwise its
# secondary mgmt card
set mgmt_slot ""
# see if any slots in chassis support smConfig
foreach slot $mgmt_slots {
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh $chassis
} else {
target_chassis_admin_sh $chassis $slot
}
if { [ catch { set cap [check_chassis_capability "smConfig"] } res ] != 0
|| $cap < 1 } {
# slot will be skipped, doesn't support smConfig
} else {
if { [ string equal "$mgmt_slot" ""] || [ string equal "$slot" "$my_slot" ] } {
set mgmt_slot "$slot"
}
}
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh_exit
} else {
target_chassis_admin_sh_exit 1
}
}
if { [ string equal "$mgmt_slot" "" ] } {
skip_case "chassis does not have an ESM license key or does not support smConfig"
}
log_message "mkdir -p [exec dirname $config_file]"
exec mkdir -p "[exec dirname $config_file]"
get_esm_config "$chassis" "$config_file"
}
}
proc test_case_chassis_getconfig { chassis {fd ""}} {
##
## test_case_chassis_getconfig
## -------------------------
## test case to get basic config from the given chassis.
##
## Usage:
## test_case_chassis_getconfig chassis [fd]
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## fd - file to write config info to
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
global env
test_case "$chassis.getconfig" "get $chassis" "get basic configuration from $chassis
File: TestTools/chassis.exp" noop noop {
upvar chassis chassis
upvar fd fd
set ntpVal ""
set sysLogVal ""
set timeZone ""
set out ""
set linkWidth ""
set nodeDesc ""
set act_fw_version ""
set pri_fw_version ""
set linkCRCmode ""
# check if chassis supports slaveCli, if not then skip chassis, older FW
target_chassis_admin_sh $chassis
# slot 0 is alias for mgmt card in all chassis models
set my_slot [ get_this_slot_number ]
set act_fw_version [get_chassis_firmware_version $my_slot "act"]
set pri_fw_version [get_chassis_firmware_version $my_slot "pri"]
send_chassis_cmd "time"
set out [expect_list 240 { "[Cc]onfigured.+\n" } { "Usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
regexp {([A-Za-z0-9:. ]+)} $out tmp ntpVal
expect_chassis_prompt 60
send_chassis_cmd "logSyslogConfig"
set out [expect_list 240 { "logSyslogConfig" "[Ss]yslog.+\n" } { "Usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
regexp {([A-Za-z0-9:. ]+)} $out tmp sysLogVal
expect_chassis_prompt 60
send_chassis_cmd "timeZoneConf"
set out [expect_list 240 { "timeZoneConf" ".+[tT]ime.+\n" } { "Usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
regexp {([-A-Za-z0-9:. ]+)} $out tmp timeZone
expect_chassis_prompt 60
send_chassis_cmd "ismChassisSetWidth"
set out [expect_list 240 { "ismChassisSetWidth" "link width supported: [^\r\n]*[\r\n]" } { "Usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
# "4X" or "4X/8X" or "UNKNOWN" or similar
regexp {supported: ([^\r\n]+)[\r\n]} $out tmp linkWidth
expect_chassis_prompt 60
send_chassis_cmd "showNodeDesc"
set out [expect_list 240 { "showNodeDesc" "Node" "\(SMA\)" "Description.+[\r\n]" } { "Usage" "Invalid" "Error" "Failed" "roblem" "not found"} ]
regexp {.+= (.+)[\r\n]} $out tmp nodeDesc
expect_chassis_prompt 60
send_chassis_cmd "ismChassisSetCrcMode"
set out [expect_list 240 { "ismChassisSetCrcMode" "CRC_MODE: [^\r\n]*[\r\n]" } { "Usage" "Invalid" "Error" "Failed" "roblem" "not found" } ]
regexp {CRC_MODE: ([^\r\n]+)[\r\n]} $out tmp linkCRCmode
expect_chassis_prompt 60
set out "
Firmware Active : $act_fw_version
Firmware Primary : $pri_fw_version
Syslog Configuration : $sysLogVal
NTP : $ntpVal
Time Zone : $timeZone
LinkWidth Support : $linkWidth
Node Description : $nodeDesc
Link CRC Mode : $linkCRCmode"
show_message "\n$chassis:$out"
if { ! [ string equal "$fd" "" ] } {
puts $fd "$out"
}
target_chassis_admin_sh_exit
}
}
proc get_esm_security_files { chassis security_files dest_dir } {
##
## get_esm_security_files
## -------------------------
## get esm security files from the chassis
##
## Usage:
## get_esm_security security_files
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## security_files - local pathname of file to get to
## Returns:
## 1 - match, 0 - no match
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
global spawn_id expect_out spawn_out timeout
global expecting
chassis_sftp_cmd "sftp admin@\\\[$chassis\\\]:" "get /firmware/$security_files $dest_dir" 0
return 0
}
proc test_case_get_esm_security_files { chassis security_files dest_dir } {
##
## test_case_get_esm_security_file
## -------------------------
## test case to get the given security files from the given chassis.
##
## Usage:
## test_case_get_esm_security_files chassis security_files
## Arguments:
## chassis - IP address/name of chassis to scp and telnet to
## security_files - security files to fetch
## Returns:
## 0 -> success
## -code error on failure
## Additional Information:
## The global timeout is changed by this routine
##
## see push_esm_security_files for error handling summary
global env
test_case "$chassis.fm_get_security_files" "get $chassis $security_files $dest_dir" "get FM security files from $chassis
Get Security Files: $security_files
Dest Directory: $dest_dir
File: TestTools/chassis.exp" noop noop {
upvar chassis chassis
upvar security_files security_files
upvar action action
upvar dest_dir dest_dir
# check if chassis supports slaveCli, if not then skip chassis, older FW
target_chassis_admin_sh $chassis
if { [ catch { set cap [check_chassis_capability "slaveCli"] } res ] != 0
|| $cap < 1 } {
skip_case "chassis does not support FM config via FastFabric"
}
# TBD - make this common code with fmconfig
log_message "\nTest: Getting Slot list from $chassis ...\n"
set my_slot [ get_this_slot_number ]
# get management card type for this chassis model
set mgmt_card_type [ get_chassis_card_type $my_slot]
# all slots of matching card type (eg. all management cards)
set mgmt_slots [get_chassis_slots $mgmt_card_type]
set num_slots [llength $mgmt_slots]
if { $num_slots == 0 } {
# this should not happen, we should at least find card 0
target_chassis_admin_sh_exit
fail_test "No cards of type $mgmt_card_type"
}
# lets be paranoid
if { ! [ string equal [get_chassis_card_type $my_slot] "$mgmt_card_type" ] } {
target_chassis_admin_sh_exit
fail_test "IP address given is not for chassis mgmt card $mgmt_card_type"
}
# all security files on the master management card
set sec_files [ list_esm_security_files ]
set num_sec_files [llength $sec_files]
if { $num_sec_files == 0 } {
target_chassis_admin_sh_exit
fail_test "No FM security files found"
}
target_chassis_admin_sh_exit
log_message "\nTest: Checking FM capabilities and status of $chassis ...\n"
# slot which supports sm CLI. Prefer active mgmt card otherwise its
# secondary mgmt card
set mgmt_slot ""
# see if any slots in chassis support smConfig
foreach slot $mgmt_slots {
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh $chassis
} else {
target_chassis_admin_sh $chassis $slot
}
if { [ catch { set cap [check_chassis_capability "smConfig"] } res ] != 0
|| $cap < 1 } {
# slot will be skipped, doesn't support smConfig
} else {
if { [ string equal "$mgmt_slot" ""] || [ string equal "$slot" "$my_slot" ] } {
set mgmt_slot "$slot"
}
}
if { [ string equal "$slot" "$my_slot" ] } {
target_chassis_admin_sh_exit
} else {
target_chassis_admin_sh_exit 1
}
}
if { [ string equal "$mgmt_slot" "" ] } {
skip_case "chassis does not have an ESM license key or does not support smConfig"
}
log_message "mkdir -p $dest_dir"
exec mkdir -p $dest_dir
foreach sec_file $sec_files {
get_esm_security_files "$chassis" "$sec_file" "$dest_dir"
}
}
}