#!/bin/ksh93 SDIR=$( cd ${ dirname $0; }; print -n "$PWD" ) typeset -r FPROG=${.sh.file} typeset -r PROG=${FPROG##*/} LIC='[-?$Id$] [-copyright?Copyright (c) 2013 Jens Elkner. All rights reserved.] [-license?CDDL 1.0]' export PATH=${PATH}:${SDIR}/../etc for H in log.kshlib man.kshlib ; do HELPER=$SDIR/$H [[ -r $HELPER ]] && . $HELPER && continue HELPER=${ whence $H; } if [[ -n $HELPER ]]; then . $HELPER elif [[ -r $SDIR/etc/$H ]]; then . $SDIR/etc/$H else print -u2 "$H not found - exiting." && exit fi done unset HELPER Man.addVar LSIUTIL 'A recent version of the lsiutil tool. One may find it on the LSI download pages or http://karlsbakk.net/LSIUtil%20Kit%201.63/Solaris/.' LSIUTIL=${LSIUTIL:-${SDIR%/*}/lsiutil} Man.addVar SAS2IRCU 'A recent version of the sas2ircu tool. One may find it on the LSI download pages.' SAS2IRCU=${SAS2IRCU:-${SDIR%/*}/P18/sas2ircu} Man.addVar DISKINFO 'The diskinfo binary to show the location of a device already assigned. If not found, \b-d\b will just not show any location info.' DISKINFO=${ whence diskinfo ; } typeset -T DiskObj_t=( typeset -h 'Product Name' MODEL='' typeset -h 'Vendor Name' VENDOR='' typeset -ih 'Scan Id' SID=-1 typeset -ih 'Physical Slot/Bay Number' SLOT=-1 typeset -ih 'Physical Link' PHY=-1 typeset -h 'SAS Address' WWN='' typeset -uh 'Device GUID' GUID='' typeset -h 'Serial Number' SN='' typeset -h 'Link Speed' SPEED='' typeset -h 'HBA/SES SAS Address' HWWN='' typeset -h 'AppID Base' AIDB='' typeset -h 'Controller ID for cdt' C='' typeset -ih 'VHCI' VHCI=0 # DEVPATH=${HBAS[_.HWWN].DEVPATH}/${_.SDEV}:scsi typeset -h 'Dev SubPath' SDEV='' # usually iport@${ HEX((1<<${_.PHY})); } typeset -h 'Scsi Disk label' SD='' # not always avail, otherwise int ok typeset -h 'Location of the disk' LOC='' function getHeader { printf '\n%3s\t%3s\t%4s\t%10s\t%16s\t%16s\t%16s\t%4s\t%4s\t%4s\t%8s\t%4s\t%8s\t%16s\t%5s\t%16s\t%s' \ 'Bay' 'Phy' 'Scan' 'BIOS' 'Address' 'GUID' 'HBA|SES' 'AIDB' 'Disk' \ 'Ctrl' 'SubDev' 'vhci' 'Vendor' 'Model' 'Speed' 'S/N' 'Location' } typeset -fh ' dump the header for disk info' getHeader function getDetails { typeset BD=${HBAS[${_.HWWN}].SBDF} HWNN if [[ -z "$BD" ]]; then # SES attached HWWN=${ENCLOSURES[${_.HWWN}].HWWN} BD=${HBAS[$HWWN].SBDF} fi BD=${BD#*:} BD=${BD%:*} printf '%3d\t%3d\t%4d\t#%4s ID%02X\t%16s\t%16s\t%16s\t%4s\t%4s\t%4s\t%8s\t%4d\t%8s\t%16s\t%5s\t%16s\t%s' \ ${_.SLOT} ${_.PHY} ${_.SID} "${BD//:}" ${_.SID} \ "${_.WWN}" "${_.GUID}" "${_.HWWN}" "${_.AIDB}" "${_.SD}" "${_.C}" \ "${_.SDEV}" "${_.VHCI}" "${_.VENDOR}" "${_.MODEL}" "${_.SPEED}" \ "${_.SN}" "${_.LOC}" } typeset -fh ' dump the disk info' getDetails ) Man.addVar DISKS 'All HBA attached disks found using \bgetDisks()\b. Key is the target-port (Address).' DiskObj_t -A DISKS typeset -T HbaObj_t=( typeset -ih 'HBA Index aka lsiutil port' IDX=0 typeset -h 'HBA Name' CNAME='' typeset -h 'HBA WWN' WWN='' typeset -h 'Seg:Bus:Dev:Fun in hex' SBDF='' typeset -h 'Board Name' MODEL='' typeset -h 'Serial Number' SN='' typeset -h 'Firmware Version' FW='' typeset -h 'BIOS Version' BIOS='' typeset -ih 'PCI Slot Number' SLOT=-1 typeset -ih 'Phys. Ports' PORTS=0 typeset -h 'Device Path' DEVPATH='' typeset -h 'Space separated target-ports of attached Disks' DISKS='' typeset -h 'Space separated target-ports of attached SES' ENCS='' function getHeader { printf '\n%8s\t%5s\t%4s\t%14s\t%16s\t%12s\t%10s\t%14s\t%24s\t%5s\t%s\n'\ 'Name' 'Index' 'Slot' 'Seg:Bus:Dev:Fn' \ 'WWN' 'Model' 'S/N' 'Firmware' 'BIOS' 'Ports' 'DevPath' } typeset -fh ' dump the header for HBA info' getHeader function getDetails { printf '%8s\t%5d\t%4d\t%14s\t%16s\t%12s\t%10s\t%14s\t%24s\t%5d\t%s\n'\ "${_.CNAME}" ${_.IDX} ${_.SLOT} ${_.SBDF} \ "${_.WWN}" "${_.MODEL}" "${_.SN}" "${_.FW}" "${_.BIOS}" ${_.PORTS} \ "${_.DEVPATH}" } typeset -fh ' dump the HBA info' getDetails ) Man.addVar HBAS 'All HBAs found using \bgetHBAs()\b. Key is its WWN' HbaObj_t -A HBAS typeset -T EnclosureObj_t=( typeset -h 'Enclosure WWN' WWN='' typeset -uh 'Enclosure GUID' GUID='' typeset -h 'Enclosure Vendor' VENDOR='' typeset -h 'Enclosure Model' MODEL='' typeset -h 'HBA SAS Address' HWWN='' function getHeader { printf '\n%16s\t%16s\t%16s\t%12s\t%s' \ 'SES' 'GUID' 'HBA' 'Vendor' 'Model' } typeset -fh ' dump the header for Enclosure info' getHeader function getDetails { printf '%16s\t%16s\t%16s\t%12s\t%s' \ "${_.WWN}" "${_.GUID}" "${_.HWWN}" "${_.VENDOR}" "${_.MODEL}" } typeset -fh ' dump the Enclosure info' getDetails ) Man.addVar ENCLOSURES 'All Enclosures found attached to a HBA. Key is the SES SAS address.' EnclosureObj_t -A ENCLOSURES Man.addFunc getHBAs '' '[+NAME?getHBAs - collect information about installed SAS HBAs (IT-Mode aka mpt_sas).] [+DESCRIPTION?Uses lsiutil (and thus requires additional privileges) to collect data about installed SAS HBAs.] [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage LSIUTIL; }" '} ' function getHBAs { typeset LINE PORTS='' P typeset -a XA=( ) # determine ports $LSIUTIL -p 0 | while read LINE ; do [[ ${LINE} =~ [[:space:]]*([0-9]+)\.[[:space:]]+mpt_sas[0-9]+ ]] && \ PORTS+="${.sh.match[1]} " done # get the details for P in $PORTS ; do integer READNL=0 PHYS=0 typeset CNAME='' FW='' BIOS='' BD='' MODEL='' SN='' WWN='' LINE='' $LSIUTIL -p $P -i | while read LINE ; do if [[ ${LINE} =~ ^mpt_sas[0-9]+ ]]; then CNAME=${.sh.match} elif [[ ${LINE} =~ 'MPTFW' ]]; then FW=${LINE#*MPTFW-} elif [[ ${LINE} =~ 'MPT2BIOS' ]]; then BIOS=${LINE#*MPT2BIOS-} elif [[ ${LINE} =~ ^Seg/Bus/Dev/Fun ]]; then READNL=1 elif (( $READNL )); then INFO=( $LINE ) BD=${ printf "%02x:%02x:%02x:%02x" \ ${INFO[0]} ${INFO[1]} ${INFO[2]} ${INFO[3]}; } MODEL=${INFO[4]} SN=${INFO[6]} READNL=0 elif [[ ${LINE:0:9} == 'SAS WWID:' ]]; then WWN=${LINE##*[[:space:]]} elif [[ ${LINE:0:9} == 'Phy Param' ]]; then XA=( ${LINE#*:} ) PHYS=${#XA[*]} fi # stop scanning when we have all what we need if (( $PHYS )); then HBAS[$WWN]=( CNAME=$CNAME SBDF=$BD MODEL="$MODEL" SN="$SN" IDX=$P FW="$FW" BIOS="$BIOS" WWN=$WWN PORTS=$PHYS ) break fi done done } Man.addFunc printHBAs '' '[+NAME?printHBAs - print out HBA data.] [+DESCRIPTION?Dump out collected information about SAS HBAs found.] [+SEE ALSO?\bgetHBAs()\b] [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage HBAS ; }" '} ' function printHBAs { integer HEADER=$SKIP_HEADER typeset X Y (( ${#HBAS[@]} < 1 )) && Log.info 'No HBAs found' && return for X in ${!HBAS[*]} ; do if (( ! $HEADER )); then print "${HBAS[$X].getHeader}" HEADER=1 fi Y="${HBAS[$X].getDetails}" print "$Y" done | sort -k3n,3n } Man.addFunc getScan2speed '' '[+NAME?getScan2speed - maps target IDs to speed] [+DESCRIPTION?Uses lsiutil (and thus requires additional privileges) to determine the speed in Gbps for SAS targets attached to the HBA with the given \aidx\a. The results are written into an associative array with the given \aaname\a (key=targetId and value=speed).] [+?Only needed for not directly attached disks.] \n\n\aaname\a \aidx\a ' function getScan2speed { typeset -n TAB=$1 typeset LINE H S T typeset -a FIELDS integer NEEDBT=1 IN_TYPE=0 COUNT typeset -A H2T=( ) $LSIUTIL -p $2 16 | while read LINE; do if (( $NEEDBT )); then [[ ${LINE:0:5} == 'B___T' ]] && NEEDBT=0 continue fi if (( IN_TYPE )); then COL=( ${LINE} ) [[ ${COL[0]} == 'Enclosure' ]] && break COUNT=${#COL[*]} (( $COUNT == 0 )) && continue H=${COL[$((COUNT-3))]} S=${COL[$((COUNT-1))]} T=${H2T[$H]} [[ -n $T ]] && TAB[$T]=$S else [[ -z "$LINE" ]] && continue # in BT COL=( ${LINE} ) [[ ${COL[0]} == 'Type' ]] && IN_TYPE=1 && continue [[ ${COL[7]} != 'Target' ]] && continue H2T[${COL[4]}]=${COL[1]} fi done } function getCfgadmInfo { typeset APPID TYPE PHYSID INFO CTRL CPATH typeset -l WWN integer VHCI typeset -A OBP=( ) cfgadm -a -s 'match=partial,select=type(disk):class(scsi),cols=ap_id:type:physid:info,cols2=,noheadings' | while read APPID TYPE PHYSID INFO ; do if [[ $TYPE == 'disk' ]]; then # Ap_Id seems to be truncated in some cases, so better use PhysId X=${PHYSID#*::*t} WWN=${X%d0} VHCI=0 # no client info wrt. sd# avail - so fake info X=${ prtconf /dev/${PHYSID##*::}p0 2>/dev/null ; } [[ -n $X ]] && INFO="sd${X#*#}" || INFO='' elif [[ $TYPE == 'disk-path' ]]; then X=${PHYSID#*::w} WWN=${X%,*} VHCI=1 if [[ -n $INFO && ${INFO#Client Device:} != $INFO ]]; then INFO=${INFO##*\(} INFO=${INFO%\)} [[ ${INFO:0:2} != 'sd' ]] && INFO='' else INFO='' fi else continue fi CTRL=${APPID%::*} CPATH=${PHYSID%::*} # unmangled pathes CPATH=${CPATH%:scsi} # remove phy num CPATH=${CPATH%/iport*} PHY=${.sh.match#/} [[ -z "${DISKS[$WWN].WWN}" ]] && continue OBP["$CPATH"]+="$WWN " DISKS[$WWN].AIDB=$CTRL DISKS[$WWN].VHCI=$VHCI DISKS[$WWN].SDEV=$PHY [[ -n $INFO ]] && DISKS[$WWN].SD=$INFO done # lets find out the phys path for relevant HBAs for CPATH in ${!OBP[*]} ; do VHCI=0 prtconf -v $CPATH | while read TYPE INFO ; do if (( $VHCI )); then WWN=${TYPE#*=} [[ -n ${HBAS[$WWN].WWN} ]] && \ HBAS[$WWN].DEVPATH=${CPATH#/devices} break elif [[ $TYPE == "name='base-wwid'" ]]; then VHCI=1 fi done done } Man.addFunc getDisks '' '[+NAME?getDisks - scan HBAs for attached disks and enclosures.] [+DESCRIPTION?Uses lsiutil and sas2ircu (and thus requires additional privileges) to query all HBAs found for attached disks/enclosures and tries to retrieve details about them.] [+SEE ALSO?\bgetHBAs()\b] [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage LSIUTIL SAS2IRCU HBAS DISKS ENCLOSURES ; }" '} ' function getDisks { (( ${#HBAS[@]} < 1 )) && return typeset HWWN WWN='' SN='' VENDOR='' X LINE typeset -A GUID2C=( ) typeset -u GUID ls -1 /dev/rdsk/c*d*p0 | while read LINE ; do GUID=${LINE#*t} X=${.sh.match##*/} GUID2C[${GUID%D*}]=${X%t} done # lsutil index gets sorted by SAS*i,SLOT and than SAS*e,SLOT # sas2ircu index gets sorted by Seg/Bus/Dev/Fun # so we need to obtain the mapping first typeset -A -i SBDF2SIDX=( ) typeset -a F=( ) $SAS2IRCU list | while read LINE ; do F=( $LINE ) (( ${#F[*]} != 7 )) && continue [[ ${F[0]} == 'Index' || ${F[0]:0:2} == '--' ]] && continue SBDF2SIDX[${F[4]//h/}]=${F[0]} done for HWWN in ${!HBAS[*]} ; do typeset -a SPEEDS=( ) integer NEEDSPEED=1 START=0 typeset -n CDISKS=HBAS[$HWWN].DISKS typeset -a ENTRY=( ) # get all the disks attached to the controller $LSIUTIL -p ${HBAS[$HWWN].IDX} -s | while read LINE ; do if (( $NEEDSPEED )); then if [[ ${LINE} =~ 'links are' ]]; then X=${LINE#*links are} X=${X// G} SPEEDS=( ${X//,/ } ) NEEDSPEED=0 fi continue fi if (( $START )); then ENTRY=( $LINE ) [[ ${ENTRY[3]} != 'Disk' ]] && continue CDISKS+="${ENTRY[7]} " DISKS[${ENTRY[7]}]=( HWWN=$HWWN SID=${ENTRY[1]} VENDOR=${ENTRY[4]} MODEL=${ENTRY[5]} WWN=${ENTRY[7]} PHY=${ENTRY[8]} SPEED=${SPEEDS[${ENTRY[8]}]} ) elif [[ ${LINE} =~ 'B___T___L' ]]; then START=1 fi done # If we would need the slot number only, we could use 'lsiutil -p # 70' # Also some of these infos could be get via: # sasinfo hba-port -vy -a SUNW-mpt_sas-$N and # + sasinfo target-port -s integer NEED_CI=1 IN_EI=0 SLOT=-1 IN_DISK=0 IN_ENCL=0 ID=0 HANDLES=0 typeset -A -i DWWN2ENCL=( ) typeset -A ENCL2SES=( ) typeset -A SCAN2SPEED=( ) typeset DISKWWNS="" KEY typeset SIDX=${SBDF2SIDX[${HBAS[$HWWN].SBDF}]} $SAS2IRCU $SIDX display | while read LINE ; do if (( $NEED_CI )) ; then if [[ ${LINE:0:4} == 'Slot' ]]; then HBAS[$HWWN].SLOT=${LINE##* } NEED_CI=0 fi continue fi if (( $IN_DISK )) ; then KEY=${LINE:0:4} [[ ${KEY} == 'Encl' ]] && ID=${LINE##* } && continue [[ ${KEY} == 'Slot' ]] && SLOT=${LINE##* } && continue [[ ${KEY} == 'SAS ' ]] && WWN=${LINE##* } && continue [[ ${KEY} == 'Seri' ]] && SN=${LINE##* } && continue if [[ ${KEY} == 'GUID' ]]; then WWN=${WWN//-} DISKS[$WWN].SLOT=$SLOT DISKS[$WWN].SN=$SN GUID=${LINE##* } DISKS[$WWN].GUID=${GUID} X=${GUID2C[${GUID}]} if [[ -z $X ]]; then # Zeus RAM workaround: GUID != Attached Device Address GUID=${WWN} X=${GUID2C[${GUID}]} fi DISKS[$WWN].C=${X} DWWN2ENCL[$WWN]=$ID DISKWWNS+="$WWN " SLOT=-1 ; SN=''; WWN=''; ID=0; IN_DISK=0 fi elif (( $IN_ENCL )); then KEY=${LINE:0:4} [[ ${KEY} == 'Encl' ]] && ID=${LINE##* } && continue [[ ${KEY} == 'SAS ' ]] && WWN=${LINE##* } && continue [[ ${KEY} == 'Manu' ]] && VENDOR=${LINE#*: } && continue if [[ ${KEY} == 'Mode' ]]; then WWN=${WWN//-} ENCLOSURES[$WWN]=( WWN=$WWN HWWN=$HWWN MODEL="${LINE##*: }" VENDOR="$VENDOR" ) ENCL2SES[$ID]=$WWN ID=0 ; IN_ENCL=0; WWN=''; VENDOR='' fi elif (( $IN_EI )); then KEY=${LINE:0:4} [[ ${KEY} == 'Encl' ]] && ID=${LINE##* } && continue if [[ ${KEY} == 'Logi' ]]; then X=${LINE##* } X=${X//:} if [[ $HWWN != $X ]]; then if (( ! $HANDLES )); then getScan2speed SCAN2SPEED ${HBAS[$HWWN].IDX} HANDLES=1 fi typeset SES=${ENCL2SES[$ID]} ENCLOSURES[$SES].GUID=$X for WWN in $DISKWWNS ; do (( ${DWWN2ENCL[$WWN]} == $ID )) && \ DISKS[$WWN].HWWN=$SES DISKS[$WWN].SPEED=${SCAN2SPEED[${DISKS[$WWN].SID}]} done fi ID=0 fi else X=${LINE:12:4} [[ "$X" == 'Hard' ]] && IN_DISK=1 && continue [[ "$X" == 'Encl' ]] && IN_ENCL=1 && continue [[ "$LINE" == 'Enclosure information' ]] && IN_EI=1 fi done done if [[ -n $DISKINFO && -x $DISKINFO ]]; then $DISKINFO -O Dp | while read LINE ; do X=${LINE%:*} WWN=${.sh.match#:} [[ -z $WWN ]] && continue WWN=${WWN##*@w} WWN=${WWN%,*} X=${X#/dev/chassis/} X=${X%/disk} if [[ -z ${DISKS[$WWN].WWN} ]]; then print -u2 "DISK[$WWN] not found" else DISKS[$WWN].LOC="$X" fi done fi getCfgadmInfo } function printDisks { integer HEADER=$SKIP_HEADER typeset X Y (( ${#DISKS[@]} < 1 )) && Log.info 'No Disks found' && return for X in ${!DISKS[*]} ; do if (( ! $HEADER )); then print "${DISKS[$X].getHeader}" HEADER=1 fi Y="${DISKS[$X].getDetails}" print "$Y" done | sort -k9,9 -k1n,1n # yes, we are lazy ;-) } function printEnclosures { integer HEADER=$SKIP_HEADER typeset X Y (( ${#ENCLOSURES[@]} < 1 )) && Log.info 'No Enclosures found' && return for X in ${!ENCLOSURES[*]} ; do if (( ! $HEADER )); then print "${ENCLOSURES[$X].getHeader}" HEADER=1 fi Y="${ENCLOSURES[$X].getDetails}" print "$Y" done } Man.addFunc printBayMap '' '[+NAME?printBayMap - Generate a HDD bay map for fmtopo] [+DESCRIPTION?Generates a HDD bay map for fmtopo based on the PCI-Slot number of the installed HBAs and directly attached disks. Per default HDDs on the HBA with the smallest PCI-Slot number are enumerated first (for now using the fixed LSI standard phys2slot map).] [+ENVIRONMENT VARIABLES]{ [+IDXORDER?If set to \br\b, HDDs of the HBA with the biggest PCI-Slot number get enumerated first.] } ' function printBayMap { integer HEAD=0 HEADER=$SKIP_HEADER IDX=0 USE_LSIMAP=1 K SKIP_HADER=1 # idx == phys val == bay aka slot typeset -a -i LSI_MAP=( 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12 ) typeset REVERSE PRODUCT LINE SHBAS HWWN SDISKS D PHY2WWN BD X typeset PARAMS="Generated with: ${PROG}" typeset GUID smbios -i 1 | while read LINE ; do [[ ${LINE:0:8} == 'Product:' ]] && PRODUCT=${LINE:9} && break done PRODUCT=${PRODUCT//\//-} typeset REDIR='' if (( $1 )); then REDIR="/usr/platform/i86pc/lib/fm/topo/maps/${PRODUCT},bay_labels" Log.info "Writing baymap to $REDIR" PARAMS+=' -B' else PARAMS+=' -b' fi # get the HBAs to inspect and put them into the order to use SHBAS='' X='' if [[ -n "$IDXORDER" && "$IDXORDER" != 'r' ]]; then typeset -a -i DSLOTS=( ${IDXORDER//\n/ } ) integer I MAX MAX=$(( ${#DSLOTS[*]} )) LINE="${!HBAS[*]}" for (( I=0; I < MAX; I++ )); do for HWWN in ${LINE}; do K=${HBAS[$HWWN].IDX} if (( ${DSLOTS[$I]} == $K )); then SHBAS+="$HWWN " X+="${K}," break fi done done else for HWWN in ${!HBAS[*]}; do [[ ${HBAS[$HWWN].CNAME:0:7} != 'mpt_sas' ]] && continue SHBAS+="${HBAS[$HWWN].SLOT}:$HWWN\n" done [[ "$IDXORDER" == 'r' ]] && { REVERSE='-r'; PARAMS+=' -r'; } || REVERSE='' # sort by HBA's PCI slot SHBAS=${ print "$SHBAS" | sort -t: -k1,1 -n $REVERSE | cut -f2 -d: ; } fi [[ -n $X ]] && PARAMS+=" -o ${X%,}" # go through each HBA IDX=0 ( for HWWN in $SHBAS ; do SDISKS='' PHY2WWN='' BD=${HBAS[${HWWN}].SBDF} BD=${BD#*:} BD=${BD%:*} BD=${BD//:} for D in ${HBAS[$HWWN].DISKS} ; do # not directly attached - SES fm plugin takes care of it [[ ${DISKS[$D].HWWN} != $HWWN ]] && continue SDISKS+="${DISKS[$D].SLOT}:$D\n" X=${ printf "#%4s ID%02X" "${BD}" "${DISKS[$D].SID}" ; } GUID=${DISKS[$D].GUID} PHY2WWN+="\n#\t${DISKS[$D].PHY}: ${DISKS[$D].WWN} ${DISKS[$D].C}t${GUID}d0 $X" done [[ -z ${SDISKS} ]] && continue (( ! HEAD )) && HEAD=1 && print '# $Id$ # '"${PARAMS}"' # /usr/platform/i86pc/lib/fm/topo/maps/'"${PRODUCT}"',bay_labels # Product : HBA : HBA Instance : Chassis Name : Chassis S/N : PHY : Label # ----------------------------------------------------------------------- #' X='# phy: wwn ctd bios_label' [[ -n ${REDIR} ]] && X+='\n#\tNOTE: wwn, ctd, bios_label might have changed since '"${ date '+%Y-%m-%d'; } !" print "${X}${PHY2WWN}" INSTANCE=${HBAS[$HWWN].CNAME:7} if (( USE_LSIMAP )); then for (( K=0; K < ${HBAS[$HWWN].PORTS}; K++ )); do print "${PRODUCT}:mpt_sas:${INSTANCE}:SYS:-:${K}:HDD$(( ${LSI_MAP[$K]} + IDX ))" done else # perhaps an alternative for other ctrls, but would lsiutil work # there as well? SDISKS=${ print "$SDISKS" | sort -t: -k1,1 -n | cut -f2 -d: ; } for WWN in $SDISKS ; do print "${PRODUCT}:mpt_sas:${INSTANCE}:SYS:-:${DISKS[$WWN].PHY}:HDD$(( IDX + ${DISKS[$WWN].SLOT} ))" done fi (( IDX+=${HBAS[$HWWN].PORTS} )) done ) | tee $REDIR SKIP_HEADER=$HEADER } function showUsage { typeset WHAT="$2" getopts -a "$PROG" "${ print ${Man.FUNC[$WHAT]}; }" OPT --man } Man.addFunc MAIN '' '[+NAME?'"$PROG"' - script to get information about installed SAS HBAs and attached disks.] [+DESCRIPTION?The script tries to find out, what SAS HBAs are installed on this machine and to determine, where disks are attached. It requires a recent version of \blsiutils\b as well as \bsas2ircu\b and may work for LSI HBAs, only. Columns in the output are tab (0x9) delimited.] [h:help?Print this help and exit.] [F:functions?Print out a list of names of all currently defined script functions and exit (see \btypeset +f\b).] [H:usage]:[function?Show the usage info for the function with the given name if available and exit. (see also option \b-F\b).] [T:trace]:[fname_list?A comma ior whitespace separated list of function names, which should be traced when running this script. Because tracing gets activated when this option gets processed, it should be given as the first option to the script. Otherwise you may miss some traces for options processed earlier.] [s:summary?Print a summary about the HBAs found and details like the PCIe slot they are attached to, the corresponding Bus:Device, Model and serial number, etc..] [d:disks?Show the list of attached drives and details about them, like BIOS Boot HDD name, the bay number they are in, the device GUID and serial number, etc.] [e:ses?Print a list of SES devices found.] [b:baymap?Print fmtopo bay map. It can be installed within \b/usr/platform/i86pc/lib/fm/topo/maps/\b and after a "touch /reconfigure ; init 6" \bdiskinfo\b(1M), \bzpool status -l\b and the like will display the disks per default as /dev/chassis/SYS/HDDn[/disk]] and makes it easier for the system/admin to determine the physical location of the disk (e.g. if a replacement ior format is needed). NOTE: The map is a best guess based on the information found. But since direct attached disk do not have a burned in/programmable/retrievable "bay number", the enumeration depends on cabling. LSI controller usually report phys port 0..3 (upper wide port on the card) as bay 3..0, phys port 4..7 (lower wide port on the card) as bay 7..4. So for now, this mapping will be used when the maps are generated. To verify the mapping, one may toggle the disk location LED of the desired HDD, or put some work on it, e.g using "gdd if=/dev/zero of=bla bs=512 count=1000000 oflag=sync" to get it blink continueously (even on zfs ;-)).] [B:Baymap?Same as \b-b\b, but also stores/overwrites the generated baymap in \b/usr/platform/i86pc/lib/fm/topo/maps/\b.] [o:order]:[idxlist?A comma or space separated list of HBA indices, which determines the order of disk enumeration when generating a bay map (see option \b-b\b). Per default disks are enumerated according to the PCIe slot number of the HBA (from low to high). If the list consists of a single r, enumeration start at the HBA with highest index. Note that if such a list is given, only the HBAs with the corresponding index are considered for enumeration (see option \b-s\b)!] [p:noheader?Do not print headers. Columns are always delimited by a single tab.] ' integer TODO=0 SKIP_HEADER=0 INSTMAP=0 IDXORDER='' while getopts "${ print ${Man.FUNC[MAIN]}; }" option ; do case "$option" in h) showUsage $PROG MAIN ; exit 0 ;; F) typeset +f ; exit 0 ;;F) typeset +f ; exit 0 ;; T) typeset -ft ${OPTARG//,/ } ;; H) if [[ ${OPTARG%_t} != $OPTARG ]]; then $OPTARG --man # self-defined types else showUsage "$OPTARG" "$OPTARG" # functions fi exit 0 ;; s) (( TODO|=1 )) ;; d) (( TODO|=2 )) ;; e) (( TODO|=4 )) ;; b) (( TODO|=8 )) ;; B) (( TODO|=8 )) ; INSTMAP=1 ;; o) IDXORDER="${OPTARG//,/ }" ;; p) SKIP_HEADER=1 ;; esac done if (( $TODO > 0 )); then getHBAs # to get the HBA slot, we need to sas2ircu what gets parsed in: getDisks (( $TODO & 1 )) && printHBAs (( $TODO & 2 )) && printDisks (( $TODO & 4 )) && printEnclosures (( $TODO & 8 )) && printBayMap $INSTMAP else showUsage $PROG MAIN fi