#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2000,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 
# @(#)09 1.54.1.2 src/avs/fs/mmfs/ts/admin/mmdelnsd.sh, mmfs, avs_rgpfs24, rgpfs24s005a 6/14/06 14:40:18
################################################################################
#
# Usage:
#   mmdelnsd {"DiskName[;DiskName...]" | -F DiskFile}
# or
#   mmdelnsd -p NSDid [-N Node[,Node]]
#
# where:
#
#   "DiskName[;DiskName...]"  is a ";"-separated list of NSD names.
#
#   DiskFile                  is the name of a file containing the names
#                             of the NSDs to be deleted.
#
#   -p NSDid                  is the NSD volume id of a GPFS NSD.
#
#   -N Node[,Node]            is a list of nodes to which the disk is attached.
#                             If not specified, the disk is assumed to be
#                             directly attached to all nodes in the cluster.
#                             Although -N is processed the same as for other
#                             GPFS commands (i.e., you could also specify
#                             a longer node list, a node file, or a node class),
#                             only the first two nodes will be used.
#
# Note:  The second format of the command is intended primarily for use
#        after a successful removal of the disk from the mmsdrfs file,
#        but a failure to clear the NSD volume id of an NSD disk.
#
################################################################################

# Include global declarations and service routines
. /usr/lpp/mmfs/bin/mmglobfuncs
. /usr/lpp/mmfs/bin/mmsdrfsdef
. /usr/lpp/mmfs/bin/mmfsfuncs

sourceFile="mmdelnsd.sh"
[[ -n $DEBUG || -n $DEBUGmmdelnsd ]] && set -x
$mmTRACE_ENTER "$*"


# Local work files.  Names should be of the form:
#   fn=${tmpDir}fn.${mmcmd}.$$
affectedNodes=${tmpDir}affectedNodes.${mmcmd}.$$
PVIDsToDelete=${tmpDir}PVIDsToDelete.${mmcmd}.$$

LOCAL_FILES=" $PVIDsToDelete $affectedNodes "


# Local declarations
usageMsg=413


# Local routines

###############################################################################
#
# Function:  Delete the NSD volume id of an NSD disk.
#
# Input:     pvid nsdSubtype nsdSubname primaryServer backupServer
#
###############################################################################
function clearPVID  # <pvid> <nsdSubtype> <nsdSubname>
                    #   <primaryServer> <backupServer>
{
  typeset sourceFile="mmdelnsd.sh"
  [[ -n $DEBUG || -n $DEBUGclearPVID ]] && set -x
  $mmTRACE_ENTER "$*"
  typeset pvid=$1
  typeset nsdSubtype=$2
  typeset nsdSubname=$3
  typeset primaryServer=$4
  typeset backupServer=$5

  typeset firstChoice secondChoice tspreparediskOutput magicWord rc rc2 retcode

  # Determine where to send the request for deleting the NSD volume id.
  if [[ -z $primaryServer ]]
  then
    firstChoice=$ourNodeName
    secondChoice=""
  elif [[ $primaryServer = $ourNodeName ]]
  then
    firstChoice=$ourNodeName
    secondChoice=$backupServer
  elif [[ $backupServer = $ourNodeName ]]
  then
    firstChoice=$ourNodeName
    secondChoice=$primaryServer
  else
    firstChoice=$primaryServer
    secondChoice=$backupServer
  fi

  nsdSubnameParm="-Z $nsdSubname"
  [[ $nsdSubname = $UNKNOWN ]] && nsdSubnameParm=""

  # Clear (zero out) the NSD volume id.
  if [[ $firstChoice = $ourNodeName ]]
  then
    tspreparediskOutput=$($tspreparedisk -z $pvid -t $nsdSubtype $nsdSubnameParm)
    rc=$?
  else
    tspreparediskOutput=$($mmcommon on1 $firstChoice adminCmd  \
       tspreparedisk -z $pvid -t $nsdSubtype $nsdSubnameParm)
    rc=$?
  fi

  # Parse the output from the tspreparedisk command.
  IFS=":"
  set -f ; set -- $tspreparediskOutput ; set +f
  magicWord=$1
  rc2=$2
  IFS="$IFS_sv"

  if [[ $rc != 0 || $magicWord != tspreparedisk || $rc2 -ne 0 ]]
  then
    # tspreparedisk failed.  If there is a second choice, try again.
    # Note that because of the way firstChoice and secondChoice
    # were determined, secondChoice cannot be the local node.
    if [[ -n $secondChoice ]]
    then
      tspreparediskOutput=$($mmcommon on1 $secondChoice adminCmd  \
         tspreparedisk -z $pvid -t $nsdSubtype $nsdSubnameParm)
      rc=$?
      # Parse the output from the tspreparedisk command.
      IFS=":"
      set -f ; set -- $tspreparediskOutput ; set +f
      magicWord=$1
      rc2=$2
      IFS="$IFS_sv"
    fi
  fi

  if [[ $rc != 0 || $magicWord != tspreparedisk || $rc2 -ne 0 ]]
  then
    retcode=$rc2
  else
    retcode=0
  fi

  return $retcode

}  #----- end of function clearPVID ------------------------



######################
# Mainline processing
######################


#################################
# Process the command arguments.
#################################
[[ $arg1 = '-?' || $arg1 = '-h' || $arg1 = '--help' || $arg1 = '--' ]] &&  \
  syntaxError "help" $usageMsg

[[ $argc -lt 1  ]] &&  \
  syntaxError "missingArgs" $usageMsg

while getopts :F:N:p: OPT
do
  case $OPT in

    F) [[ -n $Fflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Farg="$OPTARG"
       Fflag="-F"
       ;;

    N) [[ -n $Nflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       nodeList="$OPTARG"
       Nflag="-N"
       ;;

    p) [[ -n $pflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       pflag="-p"
       pvidToDelete=$OPTARG
       ;;

    +[Np]) # Invalid option
       syntaxError "invalidOption" $usageMsg $OPT
       ;;

    :) # Missing argument
       syntaxError "missingValue" $usageMsg $OPTARG
       ;;

    *) # Invalid option
       syntaxError "invalidOption" $usageMsg $OPTARG
       ;;
  esac
done

# Drop the processed options from the parameter list.
shift OPTIND-1

# Complete the parameter checking.
if [[ -z $pflag && -z $Nflag ]]
then
  if [[ -z $Fflag ]]
  then
    # The diskList form of the command syntax was used.
    if [[ $argc -eq 1 ]]
    then
      # If there is exactly one string left, it is assumed
      # to be the list of disks.
      diskList=$1

      # Ensure that semi-colon is used as a disk name separator.
      [[ "$diskList" = *+([-:,${BLANKchar}${TABchar}])* ]] &&  \
        syntaxError "badSeparator_notSemicolon" $noUsageMsg

      # Convert the list of disks into a file.
      $rm -f $diskNamesFile
      IFS=';'
      for disk in $diskList
      do
        print -- "$disk" >> $diskNamesFile
        checkForErrors "writing to $diskNamesFile" $?
      done
      IFS="$IFS_sv"    # Restore the default IFS setting.

    elif [[ $argc -gt 1 ]]
    then
      # More than one string is left, so we have a syntax error.
      syntaxError "extraArg" $usageMsg "$2"

    else
      # If there are no more parameters,
      # a required parameter is missing.
      syntaxError "missingArgs" $usageMsg
    fi  # end of if [[ $argc -eq 1 ]]

  else
    # The disks names are specified in a file.
    # Check the file and create a temp copy that we can modify.
    checkUserFile $Farg $diskNamesFile
    [[ $? -ne 0 ]] && cleanupAndExit
  fi  # end of if [[ -z $Fflag ]]

else
  # The second form of the syntax was used.
  # The -p flag must have been specified.
  [[ -z $pflag ]] &&  \
    syntaxError "missingArgs" $usageMsg

  # There should be no other parms.
  [[ $# -ne 0 ]] &&   \
    syntaxError "extraArg" $usageMsg "$1"

  # If a list of nodes was specified via the -N option,
  # convert it to a verified list of admin node names.
  if [[ -n $Nflag ]]
  then
    createVerifiedNodefile "$nodeList" $REL_HOSTNAME_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')
  fi  # end of if [[ -n $Nflag ]]
fi  # end of if [[ -z $pflag && -z $Nflag ]]


#####################################################################
# 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 whether disk-based quorum is in effect.
####################################################
diskQuorumInEffect=$(showCfgValue tiebreakerDisks)
[[ $diskQuorumInEffect = no ]] && diskQuorumInEffect=""


#######################################################################
# Create a new version of the mmsdrfs file.  Increment the generation
# number and remove the free disk lines for the disks being deleted.
#
# Note:  A disk cannot be deleted from the cluster if it is still
#        listed as an active disk in some file system.
#######################################################################
$rm -f $newsdrfs $nodefile $PVIDsToDelete
IFS=":"         # Change the field separator to ':'.
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 node's reliable name to the list of nodes
      # to be notified about the sdrfs changes.
      print -- "${v[$REL_HOSTNAME_Field]}" >> $nodefile
      checkForErrors "writing to file $nodefile" $?
      ;;

    $SG_DISKS )      # This line describes some disk.
      # Determine whether this disk is one of the disks to be deleted.
      if [[ -z $pflag ]]  # Was the -p flag specified?
      then
        # We are in the "normal" disk list or disk file cases.
        # Was this disk one of the NSDs 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.
          # Check whether the disk can be removed from the mmsdrfs file.
          if [[ ${v[$PAXOS_Field]} = $PaxosDisk && -n $diskQuorumInEffect ]]
          then
            # The disk is a tiebreaker disk and cannot be deleted.
            # Issue a message but keep going; do not fail the command.
            printErrorMsg 130 $mmcmd ${v[$DISK_NAME_Field]}
            failedDisks=yes
          elif [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]
          then
            # The disk will be deleted; save needed information.
            printLine=false
            deletedDisks=yes
            nsdSubtype=${v[$NSD_SUBTYPE_Field]}
            [[ $nsdSubtype = "" ]] && nsdSubtype=generic
            nsdSubName=${v[$NSD_SUBTYPE_DISKNAME_Field]}
            [[ $nsdSubName = "" ]] && nsdSubName=$UNKNOWN
            print -- ${v[$DISK_NAME_Field]} ${v[$PVID_Field]}  \
                     $nsdSubtype $nsdSubName                   \
                     ${v[$NSD_PRIMARY_NODE_Field]}             \
                     ${v[$NSD_BACKUP_NODE_Field]} >> $PVIDsToDelete
            checkForErrors "writing to file $PVIDsToDelete" $?
          else
            # The disk still belongs to a file system.
            # Issue a message but keep going; do not fail the command.
            printErrorMsg 485 $mmcmd ${v[$DISK_NAME_Field]} ${v[$DEV_NAME_Field]}
            failedDisks=yes
          fi   # end of if [[ ${v[$PAXOS_Field]} = $PaxosDisk && ... ]]

          # 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" $?
        fi   # end of if [[ $? -eq 0]]

      else  # We come here if the -p option for specifying
            # an NSD volume id was used.

        if [[ ${v[$PVID_Field]} = $pvidToDelete ]]
        then    # We come here if the disk represented by the current
                # SG_DISKS line is the one that must be deleted.

          if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]
          then

            # The disk is not in use and can therefore be deleted.
            # Save the information needed for clearing the NSD volume id.
            nsdSubtype=${v[$NSD_SUBTYPE_Field]}
            [[ $nsdSubtype = "" ]] && nsdSubtype=generic
            nsdSubName=${v[$NSD_SUBTYPE_DISKNAME_Field]}
            [[ $nsdSubName = "" ]] && nsdSubName=$UNKNOWN
            if [[ -n $Nflag ]]
            then
              # A node list was specified.
              IFS=','
              set -f ; set -- $newNodeList ; set +f
              nodeName1=$1
              nodeName2=$2
              IFS="$IFS_sv"
              print -- ${v[$DISK_NAME_Field]} ${v[$PVID_Field]}  \
                       $nsdSubtype $nsdSubName                   \
                       ${v[$NSD_PRIMARY_NODE_Field]}             \
                       ${v[$NSD_BACKUP_NODE_Field]}              \
                       $nodeName1 $nodeName2         >> $PVIDsToDelete
              rc=$?
            else
              # A node list was not specified.
              print -- ${v[$DISK_NAME_Field]} ${v[$PVID_Field]}  \
                       $nsdSubtype $nsdSubName                   \
                       ${v[$NSD_PRIMARY_NODE_Field]}             \
                       ${v[$NSD_BACKUP_NODE_Field]}  >> $PVIDsToDelete
              rc=$?
            fi
            checkForErrors "writing to file $PVIDsToDelete" $rc

            # Remove the disk from the mmsdrfs file.
            printLine=false
            deletedDisks=yes

          else
            # The disk still belongs to a file system.
            # Issue a message but keep going; do not fail the command.
            printErrorMsg 485 $mmcmd ${v[$DISK_NAME_Field]} ${v[$DEV_NAME_Field]}
            failedDisks=yes

          fi   # end of if [[ ${v[$NODESETID_Field]} = $FREE_DISK ]]

        fi   # end of if [[ ${v[$PVID_Field]} = $pvidToDelete ]]

      fi   # end of if [[ -z $pflag ]]
      ;;

    * )  # There is no need to look at any of the other lines.
      ;;

  esac  # end Change some of the fields

  # If the line is to be kept, write it 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 we are in the normal disk list or disk file case, inform the user of any
# disks for which there were no corresponding SG_DISKS lines in the sdrfs file.
# If disks were specified but none were found, print an error message and exit.
if [[ -z $pflag ]]
then
  # We are in the "normal" disk list or disk file case.  If there are any
  # disks to delete that we didn't find in the mmsdrfs file, tell the user.
  if [[ -s $diskNamesFile ]]
  then
    # If the input file was generated by mmcrnsd, it may have comment
    # lines that will trigger a misleading warning message.  Scan the
    # file and remove all comment and empty lines.  In preparation for
    # the message that will be issued shortly, insert a tab character
    # at the begining of the non-comment lines.
    $awk '                           \
      BEGIN { TABchar = "	" }  \
      $1 ~ /^#/ { next }             \
      NF == 0   { next }             \
      { print TABchar $0 }           \
    ' $diskNamesFile > $tmpfile

    # Issue "The following disks are not known to GPFS:" message.
    [[ -s $tmpfile ]] &&  \
      printErrorMsg 486 $mmcmd "$($cat $tmpfile)"
  fi  # end of if [[ -s $diskNamesFile ]]

  # If no disks will be deleted, issue an error and exit.
  if [[ -z $deletedDisks ]]
  then
    # No disks were specified that could be deleted.
    printErrorMsg 487 $mmcmd
    cleanupAndExit
  fi
fi  # end of if [[ -z $pflag ]]


############################################
# Put the new mmsdrfs file into the sdr.
# This deletes the disks from the cluster.
############################################

# If no disks are being deleted from the sdrfs file,
# there is no need to commit changes.
# This test is only needed for the -p case, since we exit above
# if there were no changes in the regular case.
if [[ -n $deletedDisks ]]
then
  trap "" HUP INT QUIT KILL
  gpfsObjectInfo=$(commitChanges  \
    $HOME_CLUSTER $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
fi  # end of if [[ -n $deletedDisks ]]


##################
# Unlock the sdr.
##################
[[ $sdrLocked = yes ]] &&  \
  freeLockOnServer $primaryServer $ourNodeNumber > /dev/null
sdrLocked=no
trap posttrap HUP INT QUIT KILL


########################################################################
# Handle the case where -p was specified but the NSD volume id was not
# found in the mmsdrfs file.  In such a case we must still try to clear
# the NSD volume id.  This is one of the main uses of the -p option.
########################################################################

# If no match was found in the sdrfs file, we must be in the -p case,
# since we exited earlier if no match was found in the disk list or
# disk file case.
if [[ -z $deletedDisks ]]
then
  if [[ -z $failedDisks ]]
  then
    # If here, the NSD volume id was not found in the mmsdrfs file.
    # Store the info needed for deleting the NSDid in the delete file.
    if [[ -n $Nflag ]]
    then
      # A node list was specified.
      IFS=','
      set -f ; set -- $newNodeList ; set +f
      nodeName1=$1
      nodeName2=$2
      IFS="$IFS_sv"
      print -- $UNKNOWN $pvidToDelete generic $UNKNOWN  \
               $nodeName1 $nodeName2 >> $PVIDsToDelete
      rc=$?
    else
      # A node list was not specified.
      print -- $UNKNOWN $pvidToDelete generic $UNKNOWN >> $PVIDsToDelete
      rc=$?
    fi
    checkForErrors "writing to file $PVIDsToDelete" $rc
  else
    # If here, the NSD volume id was found in the mmsdrfs file, but an
    # error (probably "disk still belongs to a file system") was found.
    # Issue a message that none of the disks were deleted, and quit.
    printErrorMsg 487 $mmcmd
    cleanupAndExit
  fi  # end of if [[ -z $failedDisks ]]
fi  # end of if [[ -z $deletedDisks ]]


#############################################################################
# Clear (zero-out) the NSD volume id on each disk in the PVIDsToDelete file.
#############################################################################

if [[ -s $PVIDsToDelete ]]
then
  # Process the PVIDsToDelete file one line at a time.
  exec 3<&-
  exec 3< $PVIDsToDelete
  while read -u3 diskLine
  do
    # Parse the line.
    set -f ; set -- $diskLine ; set +f
    diskName=$1
    pvid=$2
    nsdSubtype=$3
    nsdSubname=$4
    primarySvr=$5
    backupSvr=$6
    nodeName1=$7
    nodeName2=$8

    # Issue an informational progress message (processing disk xyz ...).
    printInfoMsg 251 $mmcmd $diskName

    # Clear the NSD volume id using one of the first two servers.
    clearPVID $pvid $nsdSubtype $nsdSubname $primarySvr $backupSvr
    rc=$?
    if [[ $rc -ne 0 ]]  # if the clearPVID call failed
    then

      if [[ -z $nodeName1 ]]  # if there are no other nodes to try
      then
        # tspreparedisk failed and we have no other nodes to try.
        # Tell the guy what to do.
        if [[ $rc -eq $MM_DeviceNotFound ]]
        then
          # The disk with the specified NSD volume id was not found.
          printErrorMsg 397 $mmcmd $pvid
        elif [[ -z $primarySvr ]]
        then
          # Tell the user to issue "mmdelnsd -p <pvid>"
          # to delete the disk.
          if [[ $diskName = $UNKNOWN ]]
          then
            printErrorMsg 247 $mmcmd $pvid $pvid
          else
            printErrorMsg 488 $mmcmd $diskName $pvid
          fi
        else
          # Tell the user to issue "mmdelnsd -p <pvid> -N <nodeList>"
          # to delete the disk.
          [[ -n $backupSvr ]] && primarySvr="${primarySvr},${backupSvr}"
          if [[ $diskName = $UNKNOWN ]]
          then
            printErrorMsg 248 $mmcmd $pvid $pvid $primarySvr
          else
            printErrorMsg 489 $mmcmd $diskName $pvid $primarySvr
          fi
        fi

      else
        # Besides the servers found on the SG_DISKS line of the mmsdrfs file,
        # the user specified other nodes via the -N option on the command.
        # Try to clear the NSD volume id using these nodes.
        clearPVID $pvid $nsdSubtype $nsdSubname $nodeName1 $nodeName2
        rc=$?
        if [[ $rc -ne 0 ]]
        then
          # tspreparedisk failed and we have no other nodes to try.
          # Tell the guy what to do.
          # The NSD volume id was not erased from the disk.
          if [[ $rc -eq $MM_DeviceNotFound ]]
          then
            # The disk with the specified NSD volume id was not found.
            printErrorMsg 397 $mmcmd $pvid
          elif [[ -z $nodeName1 ]]
          then
            # Tell the user to issue "mmdelnsd -p <pvid>"
            # to delete the disk.
            if [[ $diskName = $UNKNOWN ]]
            then
              printErrorMsg 247 $mmcmd $pvid $pvid
            else
              printErrorMsg 488 $mmcmd $diskName $pvid
            fi
          else
            # Tell the user to issue "mmdelnsd -p <pvid> -N <nodeList>"
            # to delete the disk.
            [[ -n $nodeName2 ]] && nodeName1="${nodeName1},${nodeName2}"
            if [[ $diskName = $UNKNOWN ]]
            then
              printErrorMsg 248 $mmcmd $pvid $pvid $nodeName1
            else
              printErrorMsg 489 $mmcmd $diskName $pvid $nodeName1
            fi
          fi    # end of if [[ -z $nodeName1 ]]
        fi    # end of if [[ $rc -ne 0 ]]

      fi    # end of if [[ -z $nodeName1 ]]

    fi    # end of if [[ $rc -ne 0 ]]

  done  # end of while read -u3 diskLine

fi   # end of if [[ -s $PVIDsToDelete ]]


####################################################################
# Asynchronously propagate the sdrfs file to the rest of the nodes
# if there were any changes made.
# Any nodes that are unreachable now will pick up the changes the
# next time GPFS is started or some other mm command is executed.
####################################################################
[[ -n $deletedDisks ]] &&  \
  propagateSdrfsFile async $nodefile $newsdrfs $newGenNumber rereadnsd


###################################################
# If installed, invoke the syncfsconfig user exit.
###################################################
if [[ -x $syncfsconfig && -n $deletedDisks ]]
then
   print -- "$mmcmd:  Starting $syncfsconfig ..."
   $syncfsconfig
   print -- "$mmcmd:  $syncfsconfig finished."
fi

cleanupAndExit 0

