#!/bin/ksh # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # # # Licensed Materials - Property of IBM # # (C) COPYRIGHT International Business Machines Corp. 2003,2007 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG # @(#)33 1.30.1.2 src/avs/fs/mmfs/ts/admin/mmfileid.sh, mmfs, avs_rgpfs24, rgpfs24s009a 1/14/07 03:50:17 ############################################################################## # # Usage: # # mmfileid Device # {-d [NodeName]:{DiskName|DiskNum|BROKEN}:[PhysAddr1[-PhysAddr2]] | -F DescFile} # [-o OutputFile] [-f NumThreads] [-t Directory] # # where # # Nodename: specifies a node in the GPFS node set that has access to # the specified disk # DiskName: specifies the physical volume name # if no nodename, this is the GPFS diskname or disk number # (mmlsdisk $fsname -i) # PhysAddr1: the disk physical sector number # if no nodename, this is the GPFS logical sector; if omitted, 0 # PhysAddr2: the last sector number; if omitted, same as PhysAddr1 # # If PhysAddr1 and PhysAddr2 are both 0, the entire disk is searched. # # If BROKEN is used instead of a disk name, files that have disk addresses # marked as "broken" will be found. Broken addresses are left when a disk # has been deleted, but the data could not be salvaged. # # -F DescFile specifies a file containing the physical volume names # and the corresponding disk physical address. # Each line of the file should have the following format: # [NodeName]:{DiskName|DiskNum|BROKEN}:[PhysAddr1[-PhysAddr2]] # # -o outFile format: # # -f num number of threads that need to be created # (used by tsfindinode utility) # # -t tempDirName Override by using /var/mmfs/tmp for temporary files # and sorting. # ############################################################################## # Include global declarations and service routines. . /usr/lpp/mmfs/bin/mmglobfuncs . /usr/lpp/mmfs/bin/mmsdrfsdef . /usr/lpp/mmfs/bin/mmfsfuncs sourceFile="mmfileid.sh" [[ -n $DEBUG || -n $DEBUGmmfileid ]] && set -x $mmTRACE_ENTER "$*" # Local work files. Names should be of the form: # fn=${tmpDir}fn.${mmcmd}.$$ inputPVDesc=${tmpDir}inputPVDesc.${mmcmd}.$$ # file of pvs from the input list newLVDesc=${tmpDir}newLVDesc.${mmcmd}.$$ # LV list passed to tsfileid outFidu=${tmpDir}outFidu.${mmcmd}.$$ # tsfileid output goes here localLspvList=${tmpDir}lspvList.${mmcmd}.$$ # local lspv output list inInodeList=${tmpDir}inInodeList.${mmcmd}.$$ # input to the tsfindinode cmd LOCAL_FILES=" $inputPVDesc $newLVDesc $outFidu $localLspvList $inInodeList" # Local variables usageMsg=560 # mmfileid usage message integer rc=0 # return code default to success integer numThreads=16 # default is 16 threads newLVList="" pvDescFile="" inodeDesc="" specialInode="" fiduDevicename=NULL dflag="" # indicator that -d was specified diskPVList="" # arguments following the -d argument Farg="" # DescFile following -F Fflag="" # indicator that -F was specified fflag="" # indicator that -f was specified oflag="" # indicator that -o was specified tflag="" # indicator that -t was specified # Local routines ############################################################################## # # Preprocessor function # # mmfileidPreprocess scans each line in the DescFile and tokenizes the line. # # 1. Find PVID for hdisk. If error, stop processing. # 2. Get gpfsDiskName. If error, stop processing. # 3. Convert ranges. If error, stop processing. # 4. Echo new values to $newLVDescFile. (Format: gpfsDiskName:lvnum1-lvnum2) # ############################################################################## function mmfileidPreprocess { typeset sourceFile="mmfileid.sh" [[ -n $DEBUG || -n $DEBUGmmfileidPreprocess ]] && set -x $mmTRACE_ENTER "$*" typeset addr1 addr2 pvRange pvDesc lvRange hostResult typeset diskName="" typeset gpfsDiskName="" typeset nodeName="" typeset nodeset1="" typeset nodeset2="" typeset pvID="" typeset relNodeName="" integer lineNumber=0 typeset ec=0 # Loop through the physical volume descriptors file. exec 3<&- exec 3< $pvDescFile while read -u3 pvDesc do IFS=':' set -f ; set -- $pvDesc ; set +f nodeName=$1 diskName=$2 pvRange=$3 IFS='-' set -f ; set -- $pvRange ; set +f addr1=$1 addr2=$2 IFS="$IFS_sv" lineNumber=lineNumber+1 # Skip empty and comment lines. [[ $pvDesc = *([$BLANKchar$TABchar]) ]] && continue [[ $pvDesc = *([$BLANKchar$TABchar])#* ]] && continue # Make sure the input arguments are all defined. [[ -z $addr1 ]] && addr1=-1 [[ -z $addr2 ]] && addr2=$addr1 if [[ -z $diskName ]] then if [[ -n $Fflag ]] then printErrorMsg 567 $mmcmd $lineNumber $pvDescFile print -u2 " $pvDesc" syntaxError "incorrectSyntax" 0 "[NodeName]:{DiskName|DiskNum|BROKEN}:[PhysAddr1[-PhysAddr2]]" else printErrorMsg 560 $mmcmd cleanupAndExit 1 fi fi # Check for address range correctness. if [[ $addr1 -gt $addr2 ]] then if [[ -n $Fflag ]] then printErrorMsg 567 $mmcmd $lineNumber $pvDescFile print -u2 " $pvDesc" fi syntaxError "incorrectRange" 0 $addr1 $addr2 fi if [[ -z $nodeName ]] then gpfsDiskName=$diskName if [[ $addr1 = -1 && $addr2 = -1 ]] then lvRange="0-0" else lvRange="$addr1-$addr2" fi else # Find the reliable hostname for specified node. relNodeName=$(checkAndConvertNodeValue $nodeName $REL_HOSTNAME_Field) [[ $? -ne 0 ]] && cleanupAndExit # Get our nodeset id. nodeset1=$(getNodeInfo $NODESETID_Field $REL_HOSTNAME_Field $relNodeName $GLOBAL_ID $mmsdrfsFile) if [[ -z $nodeset1 ]] then printErrorMsg 282 $mmcmd $relNodeName cleanupAndExit 1 fi # Get the GPFS disk name. if [[ $relNodeName = $ourNodeName ]] then gpfsDiskName=$(getGpfsDiskName $diskName 2>$errMsg) else gpfsDiskName=$($mmcommon on1 $relNodeName getGpfsDiskName $diskName 2>$errMsg) fi ec=$? if [[ $ec -ne 0 || -z $gpfsDiskName ]] then # We were unable to acquire the pvid. [[ -s $errMsg ]] && $cat $errMsg 1>&2 printErrorMsg 585 $mmcmd $diskName $relNodeName $rm -f $errMsg cleanupAndExit $ec fi $rm -f $errMsg nodeset2=$(getDiskInfo $NODESETID_Field $gpfsDiskName $mmsdrfsFile) # Check whether the node and diskName belong to the same GPFS nodeset. if [[ $nodeset1 != $nodeset2 ]] then print -u2 "$mmcmd: Disk $diskName and node $relNodeName do not belong to the same nodeset." cleanupAndExit 1 fi # Invoke tsaddrmap on the specified node to convert # the physical disk address to a logical disk address. if [[ $relNodeName = $ourNodeName ]] then lvRange=$($tsaddrmap $diskName -p1 $addr1 -p2 $addr2 2>$errMsg) else lvRange=$($mmcommon on1 $relNodeName adminCmd tsaddrmap $diskName -p1 $addr1 -p2 $addr2 2>$errMsg) fi ec=$? if [[ $ec -ne 0 || -z $lvRange ]] then # Range error. Show any tsaddrmap errors and issue an error message. [[ -s $errMsg ]] && $cat $errMsg 1>&2 printErrorMsg 587 $mmcmd $diskName $relNodeName $rm -f $errMsg cleanupAndExit $ec fi $rm -f $errMsg fi # end of if [[ -z $nodeName ]] # Add the converted data that needs to be passed to the tsfileid command. print -- "${gpfsDiskName}:${lvRange}" >> $newLVDesc done # end of while read -u3 pvDesc do return 0 } # ----- end of function mmfileidPreprocess --------------------- ############################################################################## # # Function: mmfileidPostprocess # # This function merges the tsfileid output with the tsfindinode output. # # Format of tsfileid output: {inodeNumber address snapshotId} # Format of tsfindinode output: {inodeNumber fileName} # Format after post processing: {inodeNumber address snapshotId fileName} # # Note: Ideally, it would have been better for tsfindinode to do this. # ############################################################################## function mmfileidPostprocess { typeset sourceFile="mmfileid.sh" [[ -n $DEBUG || -n $DEBUGmmfileidPostprocess ]] && set -x $mmTRACE_ENTER "$*" integer indexCnt=0 integer i=0 typeset inode inodeName lvAddress tsInode snapId typeset inodeLine inodeArrayData set -f ; set -A inodeArray ; set +f # Reuse the localLspvList file. $tsfindinode -i $inInodeList $fiduDevicename -t $numThreads > $localLspvList rc=$? if [[ $rc -ne 0 ]] then $cat $inInodeList $localLspvList cleanupAndExit $rc fi sortdir="/tmp" [[ -n $tflag ]] && sortdir=$tmpdir $sort -T $sortdir -b -k 1,1n $localLspvList -o $localLspvList rc=$? [[ $rc = 0 ]] && $sort -T $sortdir -b -k 1,1n $inInodeList -o $inInodeList && rc=$? if [[ $rc -ne 0 ]] then $cat $inInodeList $localLspvList printErrorMsg 171 "$mmcmd" "sort" $rc cleanupAndExit $rc fi $join -1 1 -2 1 $inInodeList $localLspvList rc=$? if [[ $rc -ne 0 ]] then $cat $inInodeList $localLspvList printErrorMsg 171 "$mmcmd" "mmfileidPostprocess: join " $rc cleanupAndExit $rc fi return 0 } # ----- end of function mmfileidPostprocess --------------------- ####################### # Mainline processing ####################### ############################################################################## # Process the input arguments. ############################################################################## [[ $arg1 = '-?' || $arg1 = '-h' || $arg1 = '--help' || $arg1 = '--' ]] && \ syntaxError "help" $usageMsg [[ $argc -lt 2 ]] && \ syntaxError "missingArgs" $usageMsg device=$arg1 # Save stripe group device (always the first parameter). shift 1 while getopts :d:F:f:o:t: OPT do case $OPT in d) # Only one disk address needs to be searched. [[ -n $dflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" [[ -n $Fflag ]] && \ syntaxError "invalidCombination" $usageMsg "-d" "-F" diskPVList=$OPTARG dflag=yes ;; F) # DescFile file [[ -n $Fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" [[ -n $dflag ]] && \ syntaxError "invalidCombination" $usageMsg "-d" "-F" Farg=$OPTARG Fflag=yes ;; o) # output filename [[ -n $oflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" outFname=$OPTARG oflag=yes ;; f) # number of threads to be spawned [[ -n $fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" numThreads=$(checkIntRange "-f" $OPTARG 1) [[ $? -ne 0 ]] && cleanupAndExit fflag=yes ;; t) # temp working dir name [[ -n $tflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" tmpDir=${OPTARG}/ tflag=yes # Reset local work files. inputPVDesc=${tmpDir}inputPVDesc.${mmcmd}.$$ # file of physical volumes # from the input list newLVDesc=${tmpDir}newLVDesc.${mmcmd}.$$ # LV list passed to tsfileid outFidu=${tmpDir}outFidu.${mmcmd}.$$ # tsfileid output goes here localLspvList=${tmpDir}lspvList.${mmcmd}.$$ # local lspv output list inInodeList=${tmpDir}inInodeList.${mmcmd}.$$ # input to the tsfindinode cmd LOCAL_FILES=" $inputPVDesc $newLVDesc $outFidu $localLspvList $inInodeList" ;; +[dFoft]) # invalid option specified syntaxError "invalidOption" $usageMsg $OPT ;; :) # missing required value after an option syntaxError "missingValue" $usageMsg $OPTARG ;; *) # invalid option specified syntaxError "invalidOption" $usageMsg $OPTARG ;; esac done shift OPTIND-1 # Drop the processed options from the parameter list. [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1 # Finish the parameter checking. ##################################################################### # If -d was specified, create a file that contains the physical # disk address information of the specified nodes. If the -F option # was used, just use its operand as the file for disk addresses. ##################################################################### if [[ -n $dflag ]] then # Add the physical volume descriptor to the descriptor file. print -- $diskPVList > $inputPVDesc checkForErrors "writing to file $inputPVDesc" $? pvDescFile=$inputPVDesc fi if [[ -n $Fflag ]] then # The -f option was specified. Check whether the file exists. if [[ ! -f $Farg || ! -r $Farg ]] then # Can't read the input disk address file. printErrorMsg 43 $mmcmd $Farg cleanupAndExit fi # Check that the physical volume descriptors file is not empty. if [[ ! -s $Farg ]] then # The descriptors file is empty. printErrorMsg 561 $mmcmd $Farg cleanupAndExit fi pvDescFile=$Farg fi ###################################################################### # Set up trap exception handling and call the gpfsInit function. # It will ensure that the local copy of the mmsdrfs and the rest of # the GPFS system files are up-to-date and will obtain the sdr lock. ###################################################################### trap pretrap HUP INT QUIT KILL gpfsInitOutput=$(gpfsInit $lockId) setGlobalVar $? $gpfsInitOutput [[ -z $ourNodeName ]] && getLocalNodeData # sanity check ############################################################ # Make sure the specified file system exists and is local. ############################################################ findFSoutput=$(findFS "$device" $mmsdrfsFile) [[ -z $findFSoutput ]] && cleanupAndExit # Parse the output from the findFS function. set -f ; set -- $findFSoutput ; set +f fqDeviceName=$1 deviceName=$2 fsHomeCluster=$3 # Exit with a message if the command was invoked for a remote file system. if [[ $fsHomeCluster != $HOME_CLUSTER ]] then # Command is not allowed for remote file systems. printErrorMsg 106 $mmcmd $device $fsHomeCluster cleanupAndExit 1 fi # Determine the lookup order for resolving host names. [[ $osName != AIX ]] && resolveOrder=$(setHostResolveOrder) # mmfileid requires that the device be mounted on the node on which # mmfileid is executed. If this is not true, tsfindinode will fail. if [[ $osName = Linux ]]; then fiduDevicename=$($awk '{if ($1 == fs) print $2}' fs="/dev/$deviceName" $etcFilesystems) else fiduDevicename=$($lsfs -c "/dev/$deviceName" 2>/dev/null | $tail -n +2 | $awk -F: '{print $1}') fi # Check whether the filesystem is locally mounted. # This is required or the later invocation of tsfindinode will not work. $tsfindinode -i 1 $fiduDevicename > /dev/null 2>&1 rc=$? if [[ $rc -ne 0 ]] then printErrorMsg 563 $mmcmd $deviceName $ourNodeName cleanupAndExit 1 fi # Create a file containing the local output of the lspv command. if [[ $osName = AIX ]] then $(LC_ALL=C $lspv > $localLspvList) rc=$? if [[ $rc -ne 0 ]] then cleanupAndExit $rc fi else # Use the output of the mmlsnsd command in the non-AIX case. $mmlsnsd -f $deviceName -m > $localLspvList rc=$? if [[ $rc -ne 0 ]] then cleanupAndExit $rc fi fi # Pre-process the input. mmfileidPreprocess ################################################# # If the file system is in a different nodeset, # or the local daemon is not available, find an # active node and send the command there. ################################################# # Create a file with the reliable names that form # the cluster to which the file system belongs. nodeCount=$(getNodeFile $REL_HOSTNAME_Field $fsHomeCluster $mmsdrfsFile $nodefile) if [[ $nodeCount -eq 0 ]] then # The cluster is empty; there is no node to run the command. printErrorMsg 171 $mmcmd "getNodeFile (nodeCount=0)" 1 cleanupAndExit fi preferredNode=0 $mmcommon onactive $preferredNode $nodefile \ $newLVDesc $NO_MOUNT_CHECK NULL $NO_LINK \ tsfileid $fqDeviceName -F $newLVDesc > $outFidu rc=$? if [[ $rc -ne 0 ]] then cleanupAndExit $rc fi ####################################################################### # Check whether the output from tsfileid is empty. If so, it means # that tsfileid returned successfully (rc already checked above) # but could not find any disk addresses that matched the input. ####################################################################### if [[ ! -s $outFidu ]] then # tsfileid did not find any addresses that matched the input. printErrorMsg 562 $mmcmd $outFidu cleanupAndExit $rc fi # If oflag is true, then create an empty output filename. [[ -n $oflag ]] && $rm -f $outFname ##################################################################### # Look for special inodes (line starts with "Address"). # tsfindinode cannot understand these special inode comments. # Convert the inode numbers returned by tsfileid to file names. ##################################################################### # If an output file name was specified, redirect stdout to the file. [[ -n $oflag ]] && exec 1>> $outFname exec 3<&- exec 3< $outFidu while read -u3 inodeDesc do set -f ; set -- $inodeDesc ; set +f specialInode=$1 # Don't put lines for special inodes (i.e., those that start with # "Address") into the file that will be passed to tsfindinode. if [[ $specialInode = "Address" ]] then print $inodeDesc else print $inodeDesc >> $inInodeList fi done # Call mmfileidPostprocess only if the inInodeList file is not empty. if [[ ! -s $inInodeList ]] then rc=$? cleanupAndExit $rc fi mmfileidPostprocess cleanupAndExit 0