#!/bin/sh # # Eric A. Bautsch - Swangage Computing Ltd. # # creates a table of mappings with LDEV/LUN, fabric and DG information # # Usage: luntable # for detailed options see usage() function below. # # # Version Control # v1.0 - EAB - 2003 initial creation # v1.1 - EAB - 10 May 2004 added -p: option, reformatted table output # v1.2 - EAB - 26 May 2004 fixed bug when there is no vxdisk command # v1.3 - EAB - 27 May 2004 fixed bug # v1.4 - EAB - 15 Jul 2004 complete re-write to speed up using vxdisk -e list # # DISCLAIMER OF WARRANTIES: THIS TOOLKIT IS OFFERED "AS IS" AND # "WITH ALL FAULTS" AND WITHOUT WARRANTY OF ANY KIND WHATSOEVER. # SWANGAGE DISCLAIMS, AND USERS OF THE TOOLKIT WAIVE, ANY AND ALL EXPRESS # OR IMPLIED WARRANTIES AND REPRESENTATIONS, INCLUDING BUT NOT LIMITED # TO ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR # PURPOSE, OR NON-INFRINGEMET. THE TOOLKIT IS TO BE USED AT YOUR OWN # RISK. # # NO LIABILITY: IN NO EVENT SHALL SWANGAGE BE LIABLE FOR ANY DIRECT, # INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGE # IN CONNECTION WITH OR ARISING OUT OF THE USE OF THE TOOLKIT # (INCLUDING, BUT NOT LIMITED TO, LOSS OF BUSINESS, REVENUE, PROFITS, # USE, DATA, OR OTHER ECONOMIC ADVANTAGE) HOWEVER IT ARISES, WHETHER # FOR BREACH OR IN TORT, EVEN IF SWANGAGE HAS BEEN PREVIOUSLY ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THIS TOOLKIT IS DISTRIBUTED UNDER THE GNU LICENSE WHICH IS AVAILABLE HERE: # http://www.gnu.org/licenses/gpl.html # # # This script makes a few assumptions: # - one parity group only contains disks of the same size which are all # partitioned into LUNs of the same size # - all SAN attached disk are HDS disks and are prefixed by veritas with # fabric_ # usage() { echo "Usage: $0 [-t] [-d] [-v] [-p:] [disk]" echo " where: -t -- print in table format, ready for a spreadsheet" echo " -d -- print diskgroup for Veritas systems" echo " -r -- print raw disk names not fabrics" echo " -v -- run verbose for debugging" echo " -p: -- set disks per parity group to " echo " disk -- only report on the named disk" echo "" echo "e.g. ./$0 -t -d -p0:15 -p1:15 -p2:29 -p3:29 -p4:15 -p5:11" echo "" exit 0 } parsecommandline() { for option in ${ALLOPTIONS} do cut_option=`echo $option | cut -c-2` cut_value=`echo $option | cut -c3-` case $cut_option in -h ) usage ;; -t ) TABLE=1 ;; -d ) PRINTDG=1 ;; -r ) RAW=1 ;; -v ) VERBOSE=1 ;; -p ) cu=`echo ${cut_value} | awk -F':' '{print $1}'` no=`echo ${cut_value} | awk -F':' '{print $2}'` # assign $dpg${cu}=${no} eval `echo dpg${cu}`=${no} ;; -* ) usage ;; * ) DISKLIST="${DISKLIST} ${option}" ;; esac done if [ $VERBOSE -eq 1 ] then echo "TABLE=${TABLE}" echo "VERBOSE=${VERBOSE}" echo "PRINTDG=${PRINTDG}" echo "RAW=${RAW}" cu=0 # we're just guessing a maximum no here while [ $cu -lt 10 ] do thisdpg=$`echo dpg${cu}` echo "dpg${cu}=`eval echo $thisdpg`" cu=`expr $cu + 1 ` done # echo "disks_per_pg=${disks_per_pg}" echo "DISKLIST=${DISKLIST}" fi } #### #### #### main Main MAIN bit #### #### TMPFILE1="/tmp/fabaloc.$$" TMPFILE2="/tmp/fabaloc2.$$" TABLE=0 # print in table format? PRINTDG=0 # print the disk group? VERBOSE=0 # run verbose? RAW=0 # print raw disk groups as opposed to fabrics DISKLIST="" # disks to run on default_disks_per_pg=15 # default no of disks in a parity group ALLOPTIONS=$* rm -f ${TMPFILE1} > /dev/null 2>&1 VXDISK=`which vxdisk` VXDISKLIST="${VXDISK} list" [ "`echo ${VXDISK} | awk '{print $1}'" = "no" ] && VXDISKLIST="echo ''" LUXADM=`which luxadm` FORMAT=`which format` if [ `id | sed -e 's/^uid=//' -e 's/(.*//'` -ne 0 ] then VXDISK="pfexec ${VXDISK}" VXDISKLIST="pfexec ${VXDISKLIST}" LUXADM="pfexec ${LUXADM}" FORMAT="pfexec ${FORMAT}" fi parsecommandline [ $VERBOSE -eq 1 ] && echo "\n\nGenerating DISKLIST\n" pkginfo VRTSvxvm > /dev/null 2>&1 VRTSmissing=$? if [ "X${DISKLIST}" = "X" -a ${VRTSmissing} -eq 0 ] then [ $VERBOSE -eq 1 ] && echo "Skipping Step 1 - using vxdisk -e list\n" DISKLIST=`${VXDISK} -e list | tail +2 | awk '{print $NF ":" $1 ":" $4}'` else # check for Veritas disks if no disk list was given if [ "X${DISKLIST}" = "X" ] then DISKLIST=`${VXDISKLIST} | \ tail +2 | \ awk '{print $1}' 2> /dev/null` fi # if we still have no list, go for format if [ "X${DISKLIST}" = "X" ] then DISKLIST=`echo "" | ${FORMAT} | \ egrep '^ .......\. ' | \ awk '{print $2 "s2" }'` fi if [ $VERBOSE -eq 1 ] then echo "changed disklist to:" echo "DISKLIST=${DISKLIST}" fi [ $VERBOSE -eq 1 ] && echo "\n\nStarting Step 1\n" # convert disklist into a list of cXtWWN:fabric:DG TMPDISKLIST="" for disk in $DISKLIST do fabricdg=`$VXDISKLIST | \ awk '$1 == thisdisk {print $1 ":" $4 }' thisdisk=$disk` if [ "X${fabricdg}" = "X" ] then # this is not a veritas disk, ie we do not have a fabric or DG TMPDISKLIST="${TMPDISKLIST} ${disk}::" else wwn=`$VXDISKLIST ${disk} | tail -1 | awk '{print $1}'` TMPDISKLIST="${TMPDISKLIST} ${wwn}:${fabricdg}" fi [ $VERBOSE -eq 1 ] && echo "${wwn}:${fabricdg}" done DISKLIST=${TMPDISKLIST} fi [ $VERBOSE -eq 1 ] && echo "\n\nStarting Step 2\n" # DISKLIST now looks like cXtWWN:fabric:DG for disk in ${DISKLIST} do fabric=`echo $disk | awk -F: '{print $2}'` diskgroup=`echo $disk | awk -F':' '{print $3}'` wwn=`echo $disk | awk -F':' '{print $1}'` [ "X${fabric}" = "X" -o ${RAW} -eq 1 ] && fabric="${wwn}" LDEV=`echo $wwn | cut -b34-35` CU=`echo $wwn | cut -b32-33` LUNNO=`$LUXADM display /dev/rdsk/${wwn} 2> /dev/null | \ grep 'Device Address' | \ tail -1 | \ awk -F',' '{printf ("%02s", $2)}' | \ tr [a-z] [A-Z]` echo "${CU}:${LDEV}:${LUNNO}:${fabric}:${diskgroup}" >> ${TMPFILE1} [ $VERBOSE -eq 1 ] && \ echo "${CU}:${LDEV}:${LUNNO}:${fabric}:${diskgroup}" done sort ${TMPFILE1} > ${TMPFILE2} ################# ################# [ $VERBOSE -eq 1 ] && echo "\n\nStarting Step 3 - Output\n" # now we are ready to output if [ $TABLE -eq 0 ] then echo " LDEV/LUN TARGET/FABRIC\c" [ $PRINTDG -eq 1 ] && echo " DISKGROUP\c" echo "" # TMPFILE2 entries look like: # ${CU}:${LDEV}:${LUNNO}:${fabric}:${diskgroup} for line in `cat ${TMPFILE2}` do CULDEV=`echo $line | awk -F: '{printf ( "%s:%s/%s", $1, $2, $3) }'` if [ "${CULDEV}" = ":/" ] then # this isnt a fabric disk echo "not a fabric\c" else echo "${CULDEV}\c" fi echo $line | awk -F: '{printf ( "\t%s", $4) }' [ $PRINTDG -eq 1 ] && \ echo $line | awk -F: '{printf ( "\t%s", $5) }' echo "" done else # # printing in table format # custack="" fabstack="" dgstack="" col=0 oldcu=0 # TMPFILE2 entries look like: # ${CU}:${LDEV}:${LUNNO}:${fabric}:${diskgroup} for line in `cat ${TMPFILE2}` do col=`expr $col + 1` CU=`echo $line | awk -F: '{ print $1 }' | sed 's/^0//'` # set the disks_per_pg to the value for this cu cuvar=$`echo dpg${CU}` disks_per_pg=`eval echo $cuvar` [ "X${disks_per_pg}" = "X" ] && disks_per_pg=$default_disks_per_pg CULDEV=`echo $line | awk -F: '{printf ( "%s:%s/%s", $1, $2, $3) }'` if [ "${CULDEV}" = ":/" ] then # this isnt a fabric disk cutmp="not a fabric\t" CU=0 disks_per_pg=$default_disks_per_pg else cutmp="${CULDEV}\t" fi # this is complex. # if we found a new cu, we mustn't add it to fabstack # but save it for later. if [ ${oldcu} -ne ${CU} ] then cusave="${cutmp}" fabsave=":`echo $line | awk -F: '{print $4}'`" dgsave=":`echo $line | awk -F: '{print $5}'`" else custack="${custack}${cutmp}" fabstack=${fabstack}:`echo $line | awk -F: '{print $4}'` dgstack="${dgstack}:`echo $line | awk -F: '{print $5}'`" fi # we now need to print on either condition if [ $col -eq $disks_per_pg -o ${oldcu} -ne ${CU} ] then echo "${custack}" echo "${fabstack}" | sed -e 's/://' -e 's/:/ /g' [ $PRINTDG -eq 1 ] && \ echo "${dgstack}" | sed -e 's/://' -e 's/:/ /g' echo "" col=0 custack="" fabstack="" dgstack="" fi # if we've got a new CU then we have data in fabsave which # needs to be added to fabstack, col needs to be 1 already. # also reset the oldcu now. if [ ${oldcu} -ne ${CU} ] then echo "" custack="${cusave}" fabstack=${fabsave} dgstack=${dgsave} col=1 oldcu=${CU} fi done # print anything remaining on fabstack echo "${custack}" echo "${fabstack}" | sed -e 's/://' -e 's/:/ /g' [ $PRINTDG -eq 1 ] && \ echo "${dgstack}" | sed -e 's/://' -e 's/:/ /g' fi rm ${TMPFILE1} > /dev/null 2>&1 rm ${TMPFILE2} > /dev/null 2>&1