# 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" } } }