#!/bin/sh # # Restart with Expect \ exec expect $0 $@ #-------------------------------------------------------------------------- # $Id: fcmap,v 1.2 2008/08/26 18:07:32 robroy Exp robroy $ # # Fcmap logs in to one or more Brocade FC SAN switches and shows a list of # WWPNs correlated with their alias names. If a WWPN has more than one # alias, only the first one is shown--this is usually enough to identify # it. # # Copyright Robroy Gregg, Computer Consultant 2010. All rights reserved. #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # These FC SAN switches will be scanned. The left column is for the # switch hostnames, while the right column is a description of the # SAN fabric each switch is part of. #-------------------------------------------------------------------------- set ::switches { brocade100 "IBM DS fabric" brocade101 "IBM DS fabric" brocade102 "IBM SVC fabric" brocade103 "IBM SVC fabric" brocade104 "Hitachi AMS fabric" brocade105 "Hitachi AMS fabric" brocade106 "Hitachi USP fabric" brocade107 "Hitachi USP fabric" } ########################################################################### # # ScanSwitch # # Logs in to an FC switch, runs switchshow and zoneshow, and stores the # results in a list. The list contains port numbers, WWPNs, and their # corresponding aliases (or an empty set if there are none): # # % set returnVal # 1 21:01:00:1b:31:2a:21:69 freebsd_ql0a # 2 50:05:07:68:08:29:10:3f hitachi_target_1b # ... # # If the connection to the switch fails or times out, "eof" or # "timeout" will be returned, respectively. If telnet fails to # start for any reason it'll stop the whole program--if the # switch hostnames were entered properly, this should only happen # when there are network problems. # # Arguments: # # hostname (mandatory) - The FC switch to log in to. # # Results: # # Returns the list described above on success; returns "eof," # "timeout," or exits the entire program on failure (depending on # the failure type). # ########################################################################### proc ScanSwitch {hostname} { set ::timeout 45 log_user 0 if {[catch {spawn telnet $hostname} error]} { puts stderr "[info script]: failed to spawn telnet: $error" exit 1 } expect_before eof|timeout { puts "timeout or eof" catch close exit 1 } expect "word:"; send \r ;# Work around FreeBSD telnet username thing. expect "ogin: " exp_send sanadmin\r expect "word:" exp_send secret\r expect "sanadmin> " #-------------------------------------------------------------------------- # Match all active ports on the switch and their port numbers and wwpns # to the returnVal list. Flush the input matching buffer first to avoid # a false positive on the "${hostname}:sanadmin> " pattern (which signals # the end of the switchshow output). #-------------------------------------------------------------------------- set expect_out(buffer) {} exp_send switchshow\r while {1} { expect -re "\[0-9]+\[ ]+(\[0-9]+)\[ \tA-Za-z0-9]+\ Online\[ A-Za-z-]+(\[:0-9a-z]+)\r\n" { lappend returnVal $expect_out(1,string) lappend returnVal $expect_out(2,string) } -re "${hostname}:sanadmin> " { break } eof { break } timeout { break } } #-------------------------------------------------------------------------- # Increase the expect matching buffer to fit the entire output of zoneshow, # even for switches with lots of zones. Then decrease it back to the # original value afterwards. #-------------------------------------------------------------------------- set oldMatchMax [match_max] match_max 500000 ;# Accommodate super long zone lists. exp_send zoneshow\r expect "${hostname}:sanadmin> " set zoneshow $expect_out(buffer) match_max $oldMatchMax #-------------------------------------------------------------------------- # Insert any aliases found in the output of zoneshow in to the list # after each wwpn. If no alias is found for a particular wwpn, insert an # empty set. #-------------------------------------------------------------------------- set index 2 ;# Index to begin inserting aliases in the returnVal list. foreach "port wwpn" $returnVal { if {[regexp "alias:\[ \t]+(\[a-zA-Z0-9_]+)\[ \t\r\n\ ]*(..:..:..:..:..:..:..:..;|\[ \t\r\n])*$wwpn"\ $zoneshow notUsed alias] >= 1} { set returnVal [linsert $returnVal $index $alias] } else { set returnVal [linsert $returnVal $index {}] } incr index 3 ;# Seek to the next alias insertion point. } exp_close exp_wait return $returnVal } set scriptName [file tail $argv0] #-------------------------------------------------------------------------- # If an fcmap.cache file exists, read from it instead of gathering new # information from switches and hosts. Let us know how old the cache # file is in case we want to remove it to start fresh. # # This hasn't been fully implemented yet. #-------------------------------------------------------------------------- set time [clock seconds] if {[file exists fcmap.cache]} { set fileMtime [file mtime fcmap.cache] set fileAge [expr $time - $fileMtime] set days 0; set hours 0; set minutes 0; set seconds 0 while {$fileAge >= 86400} { incr days incr fileAge -86400 } while {$fileAge >= 3600} { incr hours incr fileAge -3600 } while {$fileAge >= 60} { incr minutes incr fileAge -60 } if {$fileAge > 0} {set seconds $fileAge} if {$days > 0} { set age "$days day" if {$days > 1} {append age s} } elseif {$hours > 0} { set age "$hours hour" if {$hours > 1} {append age s} } elseif {$minutes > 0} { set age "$minutes minute" if {$minutes > 1} {append age s} } elseif {$seconds > 0} { set age "$seconds second" if {$seconds > 1} {append age s} } puts -nonewline stderr "${scriptName}: Using fcmap.cache" if {[info exists age]} {puts -nonewline stderr " (${age} old)"} puts stderr {; remove it to start fresh.} #-------------------------------------------------------------------------- # Read the cache file contents. #-------------------------------------------------------------------------- if {[catch {open fcmap.cache r} fd]} { puts stderr "${scriptName}: Unable to open fcmap.cache: $fd" } else { # Read information from cache. } } foreach "hostname description" $::switches { puts "Switch ${hostname}: ${description}" set switch($hostname) [ScanSwitch $hostname] foreach "port wwpn alias" $switch($hostname) { puts " Port ${port}: $wwpn alias: $alias" } } exit