#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2001,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 
# @(#)10 1.39.1.2 src/avs/fs/mmfs/ts/admin/mmchnsd.sh, mmfs, avs_rgpfs24, rgpfs24s005a 6/14/06 14:39:52
###############################################################################
#
#  Usage:
#    mmchnsd {"DiskDesc[;DiskDesc...]" | -F DescFile}
#
#  where
#    DiskDesc       is a descriptor for the disk to be changed
#    -F DescFile    specifies a file containing the disk descriptors for
#                   the network shared disks to be changed
#
#  Notes:
#         You cannot change the name of a disk.
#
#         Changing the failure group and disk usage is a no-op for mmchnsd.
#         Instead, use the mmchdisk command for this purpose.
#
#         You must explicitly specify the desired new values
#         for both primary and backup server even if changing
#         only one of them.
#
#         The following operations are allowed:
#
#         - Changing the primary and/or backup server for a disk.
#
#         - Defining a backup server for a disk that up to this
#           moment did not have one.
#
#         - Deleting the backup server for a disk.
#
#         - Removing the primary and backup servers for a disk,
#           thereby converting it to a directly-attached disk.
#
#         - Assigning a primary and an optional backup server to
#           a directly-attached disk.
#
###############################################################################

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

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

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

descfile=${tmpDir}descfile.${mmcmd}.$$
existingDisks=${tmpDir}existingDisks.${mmcmd}.$$
affectedFileSystems=${tmpDir}affectedFileSystems.${mmcmd}.$$

LOCAL_FILES=" $descfile $existingDisks $affectedFileSystems "


# Local variables
usageMsg=438
noUsageMsg=0
hadSuccess=false



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


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

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

if [[ $arg1 != "-F" ]]
then
  # If the first argument is not -F, it must be a disk descriptor list.
  desclist=$arg1
  # Ensure that semi-colon is used as a disk name separator.
  [[ "$desclist" = *+([,${BLANKchar}${TABchar}])* ]] &&  \
    syntaxError "badSeparator_notSemicolon" $noUsageMsg
else
  # -F specified.  The second argument must be a file name.
  if [[ -z $arg2 ]]
  then
    syntaxError "missingFile" $usageMsg
  else
    # Verify the existence of the file and create our own copy.
    checkUserFile $arg2 $descfile
    [[ $? -ne 0 ]] && cleanupAndExit
    shift
  fi  # end of if [[ -z $arg2 ]]
fi  # end of if [[ $arg1 != "-F" ]]

# Move past the required parameters.
shift 1
[[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1


#####################################################################
# 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)


###########################################################
# If a descriptor list is specified on the command line,
# create a file with one disk descriptor per line.
# Note: If the input is in a file, descfile was created
#       by the checkUserFile function.
###########################################################
if [[ -n $desclist ]]
then
  # Move the descriptors into a temporary file.
  $rm -f $descfile
  IFS=";"
  for diskDesc in $desclist
  do
    [[ -z $diskDesc ]] && continue
    print -- "$diskDesc" >> $descfile
    checkForErrors "writing to file $descfile" $?
  done  # end for diskDesc in $desclist
  IFS="$IFS_sv"
fi  # end of if [[ -n $desclist ]]


#########################################################################
# Create a new version of the mmsdrfs file with a new generation number.
# Generate a file containing the names of the nodes in the cluster.
# Generate files containing the nodes in the different nodesets.
#########################################################################
$rm -f $nodefile $newsdrfs $existingDisks
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 node to the file with all nodes in the cluster.
       print -- "${v[$REL_HOSTNAME_Field]}" >> $nodefile
       checkForErrors "writing to file $nodefile" $?
       ;;

    $SG_DISKS )  # this line describes a disk that belongs to some file system
       # Generate a file with SG_DISKS lines for all disks in the cluster.
       print -- $sdrfsLine >> $existingDisks
       checkForErrors "writing to file $existingDisks" $?
       printLine=false
       ;;

    * )  # Pass all other lines without a change.
      ;;

  esac  # end 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.


# Give up if there are no NSDs in the cluster.
if [[ ! -s $existingDisks ]]
then
  # Issue message that the requested disks are not known to GPFS.
  printErrorMsg 481 $mmcmd
  cleanupAndExit
fi


########################################################
# Process the descriptors with the new disk attributes.
########################################################
foundErrors=false
$rm -f $affectedFileSystems
exec 3<&-
exec 3< $descfile
while read -u3 mmDiskDesc
do
  # Skip empty and comment lines.
  [[ $mmDiskDesc = *([$BLANKchar$TABchar])   ]] && continue
  [[ $mmDiskDesc = *([$BLANKchar$TABchar])#* ]] && continue

  # Parse mmDiskDesc to get the name of the disk.
  IFS=':'
  set -f ; set -- $mmDiskDesc ; set +f
  diskName=$1
  IFS="$IFS_sv"

  if [[ -z $diskName ]]
  then
    # The disk name must be specified in the disk descriptor.
    printErrorMsg 23 $mmcmd

    # Show the bad descriptor.
    printErrorMsg 386 $mmcmd "$mmDiskDesc"

    # The command will fail, but before giving up, we will check the
    # remainder of the descriptors to flush out as many errors as possible.
    foundErrors=true
    [[ $sdrLocked = yes ]] &&  \
      freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
    sdrLocked=no

    # Move to the next descriptor.
    continue
  fi

  # Locate and extract the SG_DISK line for this disk.
  $rm -f $tmpfile
  currentDiskLine=$($awk -F: '                                      \
    # If the line is for our disk, assigned it to currentDiskLine.  \
    # Do not put the line in the temp file.                         \
    $'$DISK_NAME_Field' == "'$diskName'" {                          \
      { print $0 }                                                  \
      { next }                                                      \
    }                                                               \
    # If the line is for some other disk, add it to tmpfile.        \
    { print $0 >> "'$tmpfile'" }                                    \
  ' $existingDisks)
  checkForErrors awk $?

  # Prepare the existingDisks file for the next iteration.
  # It will contain all of its original lines except the line
  # that describes the disk that is currently being processed.
  $touch $tmpfile    # Ensure file exists even if empty.
  $mv $tmpfile $existingDisks
  checkForErrors "mv $tmpfile $existingDisks" $?

  # If not found, issue an appropriate error message.
  if [[ -z $currentDiskLine ]]
  then
    # Check whether the disk was processed earlier.
    alreadyProcessed=$(getDiskInfo $DISK_NAME_Field $diskName $newsdrfs)
    if [[ -n $alreadyProcessed ]]
    then
      # This disk was already processed; it must be a duplicate entry.
      printErrorMsg 88 $mmcmd $diskName
    else
      # Print error stating disk descriptor should refer to an existing NSD.
      printErrorMsg 415 $mmcmd "$mmDiskDesc"
    fi

    # The command will fail, but before giving up, we will check the
    # remainder of the descriptors to flush out as many errors as possible.
    foundErrors=true
    [[ $sdrLocked = yes ]] &&  \
      freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
    sdrLocked=no

    # Move to the next descriptor.
    continue

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

  # Find out the file system and nodeset to which this disk belongs.
  # Parse the line.
  IFS=":"
  set -f ; set -A v -- - $currentDiskLine  ; set +f
  IFS="$IFS_sv"
  print -- "${v[$DEV_NAME_Field]}" >> $affectedFileSystems
  checkForErrors "writing to file $affectedFileSystems" $?

  # Check the correctness of the disk descriptor.  If everything is OK,
  # validateAndConvertNsdDescriptor will create an SG_DISKS line with
  # the new parameters for the disk.
  validateDescriptorOutput=$(validateAndConvertNsdDescriptor "$mmDiskDesc"  \
                    $currentDiskLine $CHANGE_NSD ${v[$NODESETID_Field]} NULL)
  rc=$?
  if [[ $rc -ne 0 ]]
  then
    # If an error was found, the validate routine issued a message.
    # We will now print the entire descriptor to help the guy some more.
    printErrorMsg 386 $mmcmd "$mmDiskDesc"

    # The command will fail, but before giving up, we will check the remainder
    # of the descriptors to flush out as many errors as possible.
    foundErrors=true
    [[ $sdrLocked = yes ]] &&  \
      freeLockOnServer $primaryServer $ourNodeNumber >/dev/null
    sdrLocked=no

  else
    # If the descriptor seems to be OK, parse the output
    # from the validateAndConvertNsdDescriptor routine.
    set -f ; set -- $validateDescriptorOutput ; set +f
    updatedDiskLine="$1"

#esjrm  Add tspreparedisk connectivity verification ???
#esjrm  Do it based on the pvid (see backup server checking in mmcrnsd)
#esjrm  Make it conditional on some input flag ( -v yes|no ??)
#esjrm  Is this the right place ?

    # Add the SG_DISKS line for the disk to the mmsdrfs file.
    print -- $updatedDiskLine >> $newsdrfs
    checkForErrors "writing to file $newsdrfs" $?
  fi  # end of if [[ $rc -ne 0 ]]
done  # end of while read -u3 mmDiskDesc


# Give up if there were errors with one or more of the descriptors.
if [[ $foundErrors = true ]]
then
  # Command failed.
  printErrorMsg 389 $mmcmd
  cleanupAndExit
fi


################################################
# Lock the gpfs object to prevent daemons
# from starting while things are being changed.
################################################
[[ $getCredCalled = no ]] && getCred
setRunningCommand "$mmcmd" $primaryServer
checkForErrors setRunningCommand $?
gpfsLocked=yes


##################################################
# Verify all affected file systems are unmounted.
##################################################
$sort -u -o $affectedFileSystems $affectedFileSystems
checkForErrors "sort -u $affectedFileSystems" $?

exec 3<&-
exec 3< $affectedFileSystems
while read -u3 fsLine
do
  set -f ; set -- $fsLine ; set +f
  fsToCheck=$1

  [[ $fsToCheck = $NO_DEVICE ]] && continue

  # Make sure the file system is not mounted on any of the nodes.
  $mmcommon onactive $preferredNode $nodefile  \
     $NO_FILE_COPY $fsToCheck $CHECK_ALL $NO_LINK $MOUNT_CHECK_ONLY 2>$errMsg
  rc=$?
  if [[ $rc -eq $MM_FsMounted ]]
  then
    # File system is still mounted; messages issued by mmcommon.
    cleanupAndExit
  elif [[ $rc -eq $MM_DaemonDown ]]
  then
    # GPFS is down on all nodes that we care about; that's just fine.
    rc=0
  elif [[ $rc -eq $MM_ConnectionReset ]]
  then
    # An internode connection was reset.
    printErrorMsg 257 $mmcmd
    # The command failed.
    printErrorMsg 389 $mmcmd
    cleanupAndExit $rc
  elif [[ $rc -ne 0 ]]
  then
    # An unexpected error occurred during the mount check.
    if [[ -s $errMsg ]]
    then
      # Show the error messages from the daemon.
      $cat $errMsg 1>&2
    else
      # The mount check failed and there were no messages from the daemon.
      printErrorMsg 171 $mmcmd "mount check for $fsToCheck" $rc
    fi
    # The command was unable to determine whether the file system is mounted.
    printErrorMsg 564 $mmcmd $fsToCheck
    cleanupAndExit $rc
  fi  # end if [[ $rc -eq $MM_FsMounted ]]
  $rm -f $errMsg

done   # end of while read -u3 fsLine


####################################################
# Put the unchanged disks back in the mmsdrfs file.
####################################################
$cat $existingDisks >> $newsdrfs
checkForErrors "cat $existingDisks >> $newsdrfs" $?


############################################
# Sort the new version of the mmsdrfs file.
############################################
LC_ALL=C $SORT_MMSDRFS $newsdrfs -o $newsdrfs


####################################################
# Replace the mmsdrfs file.  This makes the changes
# to the disks visible to the rest of the world.
####################################################
trap "" HUP INT QUIT KILL
gpfsObjectInfo=$(commitChanges  \
   $FREE_DISK $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


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


#################################################################
# Propagate the new mmsdrfs file.  This process is asynchronous.
#################################################################
propagateSdrfsFile async $nodefile $newsdrfs $newGenNumber rereadnsd

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

cleanupAndExit 0

