#!/bin/ksh # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # # # Licensed Materials - Property of IBM # # (C) COPYRIGHT International Business Machines Corp. 1997,2006 # 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 # @(#)22 1.100.1.3 src/avs/fs/mmfs/ts/admin/mmdeldisk.sh, mmfs, avs_rgpfs24, rgpfs24s005a 7/14/06 17:46:27 ############################################################################### # # Usage: # mmdeldisk Device {"DiskDesc[;DiskDesc]" | -F DescFile} [-a] [-c] [-p] # [-r] [-N {all | mount | Node[,Node...] | NodeFile | NodeClass}] # # where # Device is the file system device name # # DiskDesc is a descriptor of a disk to be deleted. Only the disk name # part of the descriptor is significant. If more than one disk # is to be deleted, the list of disk names (descriptors) must # be ";" separated and enclosed in double quotes. # # -F DescFile is a file containing disk descriptors, one per line. # # -p indicates that the disk(s) are permanently damaged. # All references will be deleted without data migration. # # -c ignore IO errors while migrating data off the deleted disks # # -r rebalance stripe group when done # # -a do not wait for rebalancing to finish # # -N parallel restripe options: # all - use all of the nodes in the nodeset # mount - use only the nodes that have mounted the fs # nodelist - use only the specified nodes # ############################################################################### # Include global declarations and service routines. . /usr/lpp/mmfs/bin/mmglobfuncs . /usr/lpp/mmfs/bin/mmsdrfsdef . /usr/lpp/mmfs/bin/mmfsfuncs sourceFile="mmdeldisk.sh" [[ -n $DEBUG || -n $DEBUGmmdeldisk ]] && set -x $mmTRACE_ENTER "$*" # Local work files. Names should be of the form: # fn=${tmpDir}fn.${mmcmd}.$$ descfile=${tmpDir}descfile.${mmcmd}.$$ tempsdrfs=${tmpDir}tempsdrfs.${mmcmd}.$$ LOCAL_FILES=" $descfile $tempsdrfs " # Local variables integer remainingDisks=0 # number of remaining disks in the file system usageMsg=291 # Local functions ##################################################### # This function is called if there is an interrupt # after some sdr changes are committed. ##################################################### function localPosttrap { # Restriping may not have finished. [[ -n $rflag && -z $aflag ]] && \ printErrorMsg 35 $mmcmd # Exit via the standard post trap routine. posttrap } ####################### # Mainline processing ####################### ##################################################### # Process the command arguments. ##################################################### [[ $arg1 = '-?' || $arg1 = '-h' || $arg1 = '--help' || $arg1 = '--' ]] && \ syntaxError "help" $usageMsg [[ $argc -lt 2 ]] && \ syntaxError "missingArgs" $usageMsg # The first argument is always the file system name. device=$arg1 # The disk descriptors were either given on the command line as the # 2nd argument, or should exist in a readable file as the 3rd argument. if [[ $arg2 = "-F" ]] then # -F specified. The third argument must be a file name. Fflag="-F" if [[ -z $arg3 ]] then syntaxError "missingFile" $usageMsg else # Verify the existence of the file and create our own copy. checkUserFile $arg3 $descfile [[ $? -ne 0 ]] && cleanupAndExit shift 3 fi # end of if [[ -z $arg3 ]] else desclist=$arg2 # Ensure that semi-colon is used as a disk name separator. [[ "$desclist" = *+([,${BLANKchar}${TABchar}])* ]] && \ syntaxError "badSeparator_notSemicolon" $noUsageMsg shift 2 fi # Check validity of flags. while getopts :acF:rpN: OPT do case $OPT in a) [[ -n $aflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" aflag=yes ;; c) [[ -n $cflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" cflag="-c" ;; F) [[ -n $Fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" checkForErrors "$mmcmd: -F option should have been processed" 1 ;; N) [[ -n $Nflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" nodeList=$OPTARG Nflag="-N $OPTARG" ;; p) [[ -n $pflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" pflag="-p" ;; r) [[ -n $rflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT" rflag=yes ;; :) syntaxError "missingValue" $usageMsg $OPTARG ;; +[acFNrp]) syntaxError "invalidOption" $usageMsg $OPT ;; *) syntaxError "invalidOption" $usageMsg $OPTARG ;; esac done shift OPTIND-1 [[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1 ##################################################### # Create a file containing the disks to be deleted. ##################################################### if [[ -n $desclist ]] then # The disks to be deleted are specified on the command line. IFS=";" for diskDesc in $desclist do # Extract the disk name and put it in the file. IFS=":" set -f ; set -- $diskDesc ; set +f print -- "$1" >> $diskNamesFile checkForErrors "writing to $diskNamesFile" $? IFS=";" done # end for diskDesc in $desclist IFS="$IFS_sv" else # The disks to be deleted are specified in a file. # Convert the input disk descriptors into a file of disk names. exec 3< $descfile while read -u3 diskDesc do # Skip empty and comment lines. [[ $diskDesc = *([$BLANKchar$TABchar]) ]] && continue [[ $diskDesc = *([$BLANKchar$TABchar])#* ]] && continue # Extract the disk name and put it in the file. IFS=":" set -f ; set -- $diskDesc ; set +f print -- "$1" >> $diskNamesFile checkForErrors "writing to $diskNamesFile" $? IFS="$IFS_sv" done # end while read -u3 diskDesc fi # end of if [[ -n $desclist ]] # Is there anything to do? if [[ ! -s $diskNamesFile ]] then # No disks were specified. printErrorMsg 264 $mmcmd cleanupAndExit 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 # Determine the lookup order for resolving host names. [[ $osName != AIX ]] && resolveOrder=$(setHostResolveOrder) ########################################################### # 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 oddState=$5 # 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 fi # Check whether some of the disks in the file system may be in an odd state. if [[ $oddState = yes ]] then # Some of the disks in the file system appear to be in an odd state. # Reconcile the sdrfs file with the GPFS daemon's view of the filesystem. $cp $mmsdrfsFile $newsdrfs reconcileSdrfsWithDaemon $deviceName $newsdrfs rc=$? if [[ $rc -ne 0 ]] then # reconcileSdrfsWithDaemon failed. printErrorMsg 171 $mmcmd reconcileSdrfsWithDaemon $rc # Tell the user to run mmcommon recoverfs against the file system. printErrorMsg 103 $mmcmd $deviceName $deviceName cleanupAndExit fi # Obtain the generation number from the version line of the new sdrfs file. versionLine=$($head -1 $newsdrfs) IFS=':' set -f ; set -- $versionLine ; set +f newGenNumber=$6 IFS="$IFS_sv" # Commit the reconciled version of the sdrfs file to the server # so the admin scripts and the daemon are in sync. trap "" HUP INT QUIT KILL # Disable interrupts until the commit is done. gpfsObjectInfo=$(commitChanges \ $fsHomeCluster $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer) if [[ $? -ne 0 ]] then # We were unable to replace the file in the sdr. printErrorMsg 381 $mmcmd # Tell the user to run mmcommon recoverfs against the filesystem. printErrorMsg 190 $mmcmd $deviceName $deviceName printErrorMsg 104 $mmcmd mmdeldisk cleanupAndExit fi trap posttrap HUP INT QUIT KILL fi # end of if [[ $oddState = yes ]] ####################################################### # If a list of nodes was specified via the -N option, # convert it to a verified list of daemon node names. ####################################################### if [[ -n $Nflag && $nodeList != all && $nodeList != mount ]] then createVerifiedNodefile "$nodeList" $DAEMON_NODENAME_Field no $nodefile [[ $? -ne 0 ]] && cleanupAndExit # Convert the output data from a file to a comma-separated list. newNodeList=$(print -- $(cat $nodefile) | $sed 's/ /,/g') Nflag="-N $newNodeList" fi # end of if [[ -n $Nflag && $nodeList != all && $nodeList != mount ]] ######################################################################## # Create a new version of the mmsdrfs file to be committed prior # to the invocation of the tsdeldisk command. # # The following changes are made: # - The generation number is incremented by 1. # - The disk status field of all SG_DISKS lines representing disks # that are to be deleted is set to mmdel to indicate the disks # are being deleted. # - The "odd state" flag in the SG_HEADR line for the file system # is set to indicate that an mmdeldisk is in progress. # # Simultaneously, we will make sure that there are no duplicate entries # in the input disk name list, that all disks specified on the command # line indeed belong to the file system, that the file system will # remain with at least one disk, and that the remaining one or more # disks in the file system are allowed to store data and metadata. ######################################################################## dataUsage="" metadataUsage="" descUsage="" $rm -f $newsdrfs $nodefile $allQuorumNodes $diskfile IFS=":" exec 3<&- exec 3< $mmsdrfsFile while read -u3 sdrfsLine do # Parse the line. set -f ; set -A v -- - $sdrfsLine ; set +f IFS="$IFS_sv" # Restore the default IFS settings. printLine=true # Assume the line will be printed. # Change some of the fields depending on the type of line. case ${v[$LINE_TYPE_Field]} in $VERSION_LINE ) # This is the global header line. # Increment the generation number. newGenNumber=${v[$SDRFS_GENNUM_Field]}+1 v[$SDRFS_GENNUM_Field]=$newGenNumber ;; $MEMBER_NODE ) # This line describes a node. # Add the reliable node name to nodefile. print -- "${v[$REL_HOSTNAME_Field]}" >> $nodefile checkForErrors "writing to file $nodefile" $? # Create a list of the quorum nodes. if [[ ${v[$CORE_QUORUM_Field]} = $quorumNode ]] then print -- "${v[$REL_HOSTNAME_Field]}" >> $allQuorumNodes checkForErrors "writing to file $allQuorumNodes" $? fi # If this is the line for the node that is executing # this command, set the preferredNode variable. [[ ${v[$NODE_NUMBER_Field]} = $ourNodeNumber ]] && \ preferredNode=${v[$REL_HOSTNAME_Field]} ;; $SG_HEADR ) # This is the header line for some file system. # Check whether the filesystem has disks that are in an "odd state". if [[ -n ${v[$ODD_STATE_Field]} && ${v[$ODD_STATE_Field]} != no ]] then # Is this filesystem a different one than the one for which # this command was invoked? if [[ ${v[$DEV_NAME_Field]} != $deviceName ]] then # The "odd state" flag is set for a different file system # than our filesystem. Add the name of the file system to a # list of filesystems to be reported to the user later. fsOddStateList="${fsOddStateList} ${v[$DEV_NAME_Field]}" else # The "odd state" flag is set for the file system # for which this command was invoked. : # Allow the command to proceed, since it may succeed. # We will report any failures if it does not. fi else # Is this filesystem the one for which this command was invoked? if [[ ${v[$DEV_NAME_Field]} = $deviceName ]] then # Set the "odd state" field in case we don't succeed. # We will reset it later if tsdeldisk succeeds. v[$ODD_STATE_Field]=mmdeldisk fi fi ;; $SG_DISKS ) # This line describes some disk. if [[ ${v[$DEV_NAME_Field]} = $deviceName ]] then # This is an SG_DISKS line that belongs to our filesystem. # Check whether this disk is one of the disks to be deleted. grep -w ${v[$DISK_NAME_Field]} $diskNamesFile > /dev/null 2>&1 if [[ $? -eq 0 ]] then # The disk represented by the current SG_DISKS line # is one of the disks that should be deleted. # Add the disk to the file that will be passed to tsdeldisk. print -- ${v[$DISK_NAME_Field]} >> $diskfile # Set the disk status to indicate "being deleted by mmdeldisk". v[$EXCLUDE_Field]=$includedDisk v[$DISK_STATUS_Field]=mmdel # Remove the disk from the file of disks to be deleted. grep -vw ${v[$DISK_NAME_Field]} $diskNamesFile > $tmpfile # Initialize the file for the next iteration. $mv $tmpfile $diskNamesFile checkForErrors "mv $tmpfile $diskNamesFile" $? else # This disk will remain in the file system. # Set a few flags that will be needed for some checks later on. # Adjust the line sequence numbers for the remaining disks. remainingDisks=remainingDisks+1 # The remainingDisks value can also be used to correctly # renumber the SG_DISKS lines. v[$LINE_NUMBER_Field]=$remainingDisks if [[ ${v[$DISK_USAGE_Field]} = "dataOnly" ]] then dataUsage=yes elif [[ ${v[$DISK_USAGE_Field]} = "metadataOnly" ]] then metadataUsage=yes elif [[ ${v[$DISK_USAGE_Field]} = "descOnly" ]] then descUsage=yes else # dataAndMetadata or unknown dataUsage=yes metadataUsage=yes fi fi # end of if [[ $? -eq 0 ]] fi # end of if [[ ${v[$DEV_NAME_Field]} = $deviceName ]] ;; * ) # Pass all other lines without a change. ;; esac # end of "Change some of the fields . . . " # Build and write the line to the new mmsdrfs file. if [[ $printLine = true ]] then print_newLine >> $newsdrfs checkForErrors "writing to file $newsdrfs" $? fi IFS=":" # Change the separator back to ":" for the next iteration. done # end while read -u3 sdrfsLine IFS="$IFS_sv" # Restore the default IFS settings. # If the cluster is empty, the command cannot be executed. if [[ ! -s $nodefile ]] then # The cluster is empty; there is nobody to run the command. printErrorMsg 171 $mmcmd "empty $nodefile" 1 cleanupAndExit fi # If there are still entries left in the disk names file, this indicates # the user specified some disks that do not belong to the file system. if [[ -s $diskNamesFile ]] then exec 3< $diskNamesFile while read -u3 diskName do # The disk was not found in the file system. printErrorMsg 315 $mmcmd $diskName $device done # end of while read -u3 diskName cleanupAndExit fi # Make sure that at least one disk will remain in the file system. if [[ $remainingDisks -eq 0 ]] then # Cannot delete all disks. printErrorMsg 89 $mmcmd cleanupAndExit fi # Sort the new version of the mmsdrfs file. LC_ALL=C $SORT_MMSDRFS $newsdrfs -o $newsdrfs # Make a copy of the current mmsdrfs file. It will be needed # to restore the system files if the tsdeldisk command fails. $cp $mmsdrfsFile $oldsdrfs checkForErrors cp $? ############################################################################ # Put the new mmsdrfs file in the sdr. This will ensure the getEFOptions # call that tsdeldisk is going to make shortly will return a list of disks # that does not include any of the disks that are being deleted. This also # serves as the "pre-commit" that indicates an mmdeldisk is in progress. ############################################################################ trap "" HUP INT QUIT KILL # Disable until mmsdrfs update is done. gpfsObjectInfo=$(commitChanges \ $fsHomeCluster $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer) rc=$? if [[ $rc -ne 0 ]] then # We were unable to replace the file in the sdr. printErrorMsg 381 $mmcmd cleanupAndExit fi trap localPosttrap HUP INT QUIT KILL ################################################### # If the changes went into the sdr successfully, # ask Tiger Shark to do the real work. ################################################### # Issue "Deleting disks ..." message. printInfoMsg 94 $mmcommon onactive \ $preferredNode $nodefile $diskfile $NO_MOUNT_CHECK NULL $NO_LINK \ tsdeldisk "$fqDeviceName -F $diskfile $cflag $pflag $Nflag" rc=$? if [[ $rc -ne 0 ]] then # tsdeldisk failed. printErrorMsg 104 $mmcmd tsdeldisk # Since tsdeldisk failed, we must restore the sdrfs file to the # correct state. To do this, we make a modified version of the # old sdrfs file with the following two changes: # - The generation number in the global header is incremented by 2; # - The SG_HEADER line is changed to indicate that an mmdeldisk # command for the filesystem was in progress. $rm -f $tempsdrfs newGenNumber=$($awk -F: ' \ BEGIN { gen = 0 } \ # If this is the global header line, increment the gen number. \ /^'$GLOBAL_ID:$VERSION_LINE:'/ { \ { gen = $'$SDRFS_GENNUM_Field' + 2 } \ { $'$SDRFS_GENNUM_Field' = gen } \ { print $1":" $2":" $3":" $4":" $5":" $6":" $7":" $8":" $9":"$10":" \ $11":"$12":"$13":"$14":"$15":"$16":"$17":"$18":"$19":"$20":" \ $21":"$22":"$23":"$24":"$25":"$26":"$27":" >> "'$tempsdrfs'" } \ { next } \ } \ # If the tsdeldisk was attempted, set an indicator on the header line \ # for the fs to indicate that an mmdeldisk command was in progress. \ /':$SG_HEADR:$deviceName:'/ { \ if ( "'$rc'" != "'$MM_DaemonDown'") { \ { $'$ODD_STATE_Field' = "'$mmcmd'" } \ { print $1":" $2":" $3":" $4":" $5":" $6":" $7":" $8":" $9":"$10":" \ $11":"$12":"$13":"$14":"$15":"$16":"$17":"$18":"$19":"$20":" \ $21":"$22":"$23":"$24":"$25":"$26":"$27":" >> "'$tempsdrfs'" } \ { next } \ } \ } \ # All other lines are printed without change. \ { print $0 >> "'$tempsdrfs'" } \ END { print gen } \ ' $oldsdrfs) checkForErrors awk $? # If the tsdeldisk command was attempted, reconcile this modified # sdrfs file with the GPFS daemon's view of the filesystem. if [[ $rc -ne $MM_DaemonDown ]] then reconcileSdrfsWithDaemon $deviceName $tempsdrfs rc2=$? if [[ $rc2 -ne 0 ]] then # reconcileSdrfsWithDaemon failed. printErrorMsg 171 $mmcmd reconcileSdrfsWithDaemon $rc2 # Tell the user to run mmcommon recoverfs against the filesystem. printErrorMsg 190 $mmcmd $deviceName $deviceName cleanupAndExit $rc fi fi # Obtain the generation number from the version line of the sdrfs file. versionLine=$($head -1 $tempsdrfs) IFS=':' set -f ; set -- $versionLine ; set +f newGenNumber=$6 IFS="$IFS_sv" # Commit the modified version of the sdrfs file to the server. trap "" HUP INT QUIT KILL # Disable interrupts until the commit is done. gpfsObjectInfo=$(commitChanges $fsHomeCluster $nsId \ $gpfsObjectInfo $newGenNumber $tempsdrfs $primaryServer) if [[ $? -ne 0 ]] then # We were unable to replace the file in the sdr. printErrorMsg 381 $mmcmd # Tell the user to run mmcommon recoverfs against the filesystem. printErrorMsg 190 $mmcmd $deviceName $deviceName fi # Unlock the sdr. [[ $sdrLocked = yes ]] && \ freeLockOnServer $primaryServer $ourNodeNumber >/dev/null sdrLocked=no trap localPosttrap HUP INT QUIT KILL # Propagate the new mmsdrfs file. This process is asynchronous. propagateSdrfsFile async $nodefile $tempsdrfs $newGenNumber # Exit. cleanupAndExit $rc fi ############################################################################### # If here, tsdeldisk was successful. # Invoke routine to reset the "odd state" flag on the filesystem's SG_HEADR # line and change the SG_DISKS lines with a disk status of mmdel to free disks # (or delete them in the SP case). The new mmsdrfs file is then committed. ############################################################################### # Reset the status fields of the disks we just deleted, # and convert them into free disks. resetDiskStatus $deviceName $newsdrfs mmdel checkForErrors updateDiskStatus $? # Obtain the generation number from the version line of the new sdrfs file. versionLine=$($head -1 $newsdrfs) IFS=':' set -f ; set -- $versionLine ; set +f newGenNumber=$6 IFS="$IFS_sv" # Commit the final version of the sdrfs file. trap "" HUP INT QUIT KILL # Disable interrupts until the commit is done. gpfsObjectInfo=$(commitChanges \ $fsHomeCluster $nsId $gpfsObjectInfo $newGenNumber $newsdrfs $primaryServer) rc=$? if [[ $rc -ne 0 ]] then # We were unable to replace the file in the sdr. printErrorMsg 381 $mmcmd # Tell the user to run mmcommon recoverfs against the filesystem. printErrorMsg 190 $mmcmd $deviceName $deviceName cleanupAndExit fi ######################################### # Unlock the sdr. ######################################### [[ $sdrLocked = yes ]] && \ freeLockOnServer $primaryServer $ourNodeNumber >/dev/null sdrLocked=no trap localPosttrap HUP INT QUIT KILL ###################################################################### # Tell the daemon to invalidate its currently-cached mount options. ###################################################################### $mmcommon onactive $preferredNode $allQuorumNodes \ $NO_FILE_COPY $NO_MOUNT_CHECK NULL $NO_LINK \ tsctl resetEFOptions $fqDeviceName > $errMsg 2>&1 rc=$? [[ $rc = $MM_DaemonDown ]] && rc=0 [[ $rc -ne 0 && -s $errMsg ]] && cat $errMsg 2>&1 $rm -f $errMsg ################################################################## # Propagate the new mmsdrfs file. This process is asynchronous. ################################################################## propagateSdrfsFile async $nodefile $newsdrfs $newGenNumber ################################################### # If installed, invoke the syncfsconfig user exit. ################################################### if [[ -x $syncfsconfig ]] then print -- "$mmcmd: Starting $syncfsconfig ..." $syncfsconfig print -- "$mmcmd: $syncfsconfig finished." fi ######################################### # Restripe the file system if requested. ######################################### if [[ -n $rflag ]] then if [[ -n $aflag ]] then $ln $nodefile ${nodefile}async2 $mmcommon onactive_async $preferredNode \ ${nodefile}async2 $NO_FILE_COPY $NO_MOUNT_CHECK NULL $NO_LINK \ tsrestripefs "$fqDeviceName -b $Nflag" >/dev/null 2>/dev/null & else # Start restriping the file system. printInfoMsg 95 $device $mmcommon onactive \ $preferredNode $nodefile $NO_FILE_COPY $NO_MOUNT_CHECK NULL $NO_LINK \ tsrestripefs "$fqDeviceName -b $Nflag" rc=$? if [[ $rc -ne 0 ]] then # The restripe step failed. printErrorMsg 104 $mmcmd tsrestripefs # Warning: File system may be unbalanced. printErrorMsg 313 $mmcmd cleanupAndExit else # Disable error message in localPosttrap. rflag="r" # Finished restriping. printInfoMsg 99 fi fi # end if [[ -n $aflag ]] fi # end if [[ -n $rflag ]] ##################################################################### # If an "odd state" flag was encountered for any other file systems # in the mmsdrfs file, tell the user to issue commands to reconcile # the mmsdrfs file with the GPFS daemon's view of the filesystems. ##################################################################### for fsname in $fsOddStateList do # Tell the user to run mmcommon recoverfs against the file system. printErrorMsg 103 $mmcmd $fsname $fsname done cleanupAndExit 0