#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2005,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 
# @(#)93 1.10 src/avs/fs/mmfs/ts/admin/mmapplypolicy.sh, mmfs, avs_rgpfs24, rgpfs240610b 2/17/06 16:50:52
################################################################################
#
# Usage:
#   mmapplypolicy {Device|Directory} [-P PolicyFile] [-I {yes|defer|test}]
#                 [-L n] [-D yyyy-mm-dd[@hh:mm[:ss]]] [-s WorkDirectory]
#
# where:
#   Device            The device name of the file system from which files
#                     are to be deleted or migrated.
#
#   Directory         The fully-qualified path name of a GPFS file system
#                     subtree form which files are to be deleted or migrated.
#
#   -P PolicyFilename The name of the file containing the policy rules to be
#                     applied.  If not specified, the policy rules currently
#                     in effect for the file system will be used.
#
#   -I yes|defer|test  Determines what actions mmapplypolicy will take on files:
#
#                      If -I yes is specified, all applicable MIGRATE and DELETE
#                      policy rules will be executed, and the data movement
#                      between pools is done during the execution of the
#                      mmapplypolicy command.  This is the default action.
#
#                      If -I defer is specified, all applicable MIGRATE and
#                      DELETE policy rules will be executed, but the actual
#                      data movement between pools is deferred until the next
#                      mmrestripefs command.
#
#                      If -I test is specified, all policy rules are evaluated,
#                      but the mmapplypolicy command only displays the actions
#                      that would be executed had -I defer or -I yes been
#                      specified.  This option is intended for testing the
#                      effects of particular policy rules; there is no actual
#                      deletion of files or data movement between pools.
#
#   -L n             Controls the level of information displayed by the command.
#                    n should be one of the following values:
#                      0 - Only display serious errors.
#                      1 - Display some information as the command executes,
#                            but not for each file.  This is the default.
#                      2 - Display each chosen file and the scheduled migration
#                            or deletion action.
#                      3 - And display each candidate file and the applicable
#                            rule.
#                      4 - And display each explicitly EXCLUDEd file and the
#                            applicable rule.
#                      5 - And display the attributes of candidate and EXCLUDEd
#                            files.
#                      6 - And display non-candidate files and their attributes.
#
#   -D yyyy-mm-dd[@hh:mm[:ss]]   Specifies the date and (UTC) time to be used
#                      by the mmapplypolicy command when evaluating the policy
#                      rules.  The default is the current date and time.
#                      If only a date is specified, the time will default to
#                      00:00:00.  
#
#   -s WorkDirectory  The directory to be used for temporary storage during
#                     command processing.  The default directory is /tmp.
#
################################################################################

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

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

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

tspolicyFile=${tmpDir}tspolicyFile.${mmcmd}.$$

LOCAL_FILES=" $tspolicyFile "


# Local variables
usageMsg=457


# Local functions


##########################################################################
#
# Function:  Verifies the correctness of the input date-time string.
#
# Input:     $1 - input date-time string
#
# Output:    The verified string: yyyy-mm-dd@hh:mm:ss
#
# Returns:   0 - no errors encountered
#            1 - invalid date-time string
#
##########################################################################
function parseDateTime  # <dtString>
{
  typeset sourceFile="mmapplypolicy.sh"
  [[ -n $DEBUG || -n $DEBUGparseDateTime ]] && set -x
  typeset dt=$1

  typeset dateTimeString

  if [[ $dt = [0-9][0-9][0-9][0-9]-0[1-9]-[0-3][0-9] ]]
  then
    dateTimeString="${dt}@00:00:00"
  elif [[ $dt = [0-9][0-9][0-9][0-9]-1[012]-[0-3][0-9] ]]
  then
    dateTimeString="${dt}@00:00:00"
  elif [[ $dt = [0-9][0-9][0-9][0-9]-0[1-9]-[0-3][0-9]@[012][0-9]:[0-5][0-9] ]]
  then
    dateTimeString="${dt}:00"
  elif [[ $dt = [0-9][0-9][0-9][0-9]-1[012]-[0-3][0-9]@[012][0-9]:[0-5][0-9] ]]
  then
    dateTimeString="${dt}:00"
  elif [[ $dt = [0-9][0-9][0-9][0-9]-0[1-9]-[0-3][0-9]@[012][0-9]:[0-5][0-9]:[0-5][0-9] ]]
  then
    dateTimeString="$dt"
  elif [[ $dt = [0-9][0-9][0-9][0-9]-1[012]-[0-3][0-9]@[012][0-9]:[0-5][0-9]:[0-5][0-9] ]]
  then
    dateTimeString="$dt"
  else
    # Unexpected date-time string.
    return 1
  fi

  # Return the result.
  print -- "$dateTimeString"
  return 0

}  #----- end of function parseDateTime ----------------------



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

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

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

# Determine what was specified on the command line.
if [[ $arg1 = /* && $arg1 != +(/)dev+(/)* ]]
then
  # If the first argument starts with a "/" but not "/dev/",
  # it is assumed to be a fully-qualified directory name.
  directory=$arg1
else
  # Otherwise, the argument is assumed to be a file system device name.
  device=$arg1
  deviceName=${device##+(/)dev+(/)}  # Name stripped of /dev/ prefix.
fi  # end of if [[ $arg1 = "/"* && $arg1 != +(/)dev+(/)* ]]

# Move past the required parameters.
shift 1

# Process the rest of the options.
while getopts :D:I:L:P:s: OPT
do
  case $OPT in
    D) [[ -n $Dflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Dflag="-$OPT"; Darg=$OPTARG;
       dateTimeString=$(parseDateTime "$Darg")
       [[ -z $dateTimeString ]] &&  \
         syntaxError "invalidOption" $usageMsg "-$OPT $OPTARG"
       ;;

    I) [[ -n $Iflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Iflag="-$OPT"; Iarg=$OPTARG;
       [[ $Iarg != yes && $Iarg != defer && $Iarg != test ]] &&  \
         syntaxError "invalidOption" $usageMsg "-$OPT $OPTARG"
       ;;

    L) [[ -n $Lflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Lflag="-$OPT"; Larg=$OPTARG;
       listingLevel=$(checkIntRange $Lflag $Larg 0 6)
       [[ $? -ne 0 ]] && cleanupAndExit
       ;;

    P) [[ -n $Pflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       Pflag="-$OPT"; Parg=$OPTARG;
       policyFile="$Parg"
       ;;

    s) [[ -n $sflag ]] && syntaxError "multiple" $noUsageMsg "-$OPT"
       sflag="-$OPT"; sarg=$OPTARG;
       workDirectory="$sarg"
       ;;

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

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

    *) # Invalid option.
       syntaxError "invalidOption" $usageMsg $OPTARG
       ;;
  esac
done  # end of while getopts :t: OPT do
shift OPTIND-1

[[ $# != 0 ]] && syntaxError "extraArg" $usageMsg $1

# Check the policy file input parameter.
if [[ -n $policyFile && $policyFile != DEFAULT ]]
then
  if [[ ! -f "$policyFile" ]]
  then
    # Can't read the input file.
    printErrorMsg 43 $mmcmd "$policyFile"
    cleanupAndExit
  fi
  if [[ ! -s "$policyFile" ]]
  then
    # Input file is empty.
    printErrorMsg 329 $mmcmd "$policyFile"
    cleanupAndExit
  fi
fi

# Check the working directory input parameter.
if [[ -n $workDirectory && $workDirectory != DEFAULT ]]
then
  if [[ ! -d "$workDirectory" ]]
  then
    # Directory does not exist.
    printErrorMsg 574 $mmcmd "$workDirectory"
    cleanupAndExit
  fi
else
  workDirectory="/tmp"
fi

# Assign a default value for -I.
[[ -z $Iarg ]] && Iarg=yes

# Assign a listing level if the user did not specify one.
[[ -z $listingLevel ]] && listingLevel=1


#####################################################################
# Set up trap exception handling and ensure that the local copy of
# the mmsdrfs is up-to-date.  There is no need to lock mmsdrfs file.
#####################################################################
trap pretrap2 HUP INT QUIT KILL
gpfsInitOutput=$(gpfsInit nolock)
setGlobalVar $? $gpfsInitOutput


###########################################################
# If the target of mmapplypolicy is a directory sub-tree,
# find the file system to which this directory belongs.
###########################################################
if [[ -n $directory ]]
then
  if [[ ! -d "$directory" ]]
  then
    # Directory does not exist.
    printErrorMsg 574 $mmcmd "$directory"
    cleanupAndExit
  fi

  device=$(findDeviceName "$directory")
  [[ $? -ne 0 ]] && cleanupAndExit
fi


######################################################
# Ensure the file system exists, that it belongs to
# this cluster, and that it is mounted on this node.
######################################################
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
fi

# Verify that the file system is mounted.
mountPoint=$(findMountpoint "$fqDeviceName")
if [[ -z $mountPoint ]]
then
  # The filesystem is not mounted.  Issue an error and quit.
  printErrorMsg 563 $mmcmd "$device" "$ourNodeName"
  cleanupAndExit
fi


###############################################
# If a policy file was not specified, retrieve
# the current policy file for the file system.
###############################################
if [[ -z $policyFile ]]
then
  # Invoke the tslspolicy command.
  $tslspolicy "$fqDeviceName" -L >$tspolicyFile
  rc=$(remapRC $?)

  if [[ $rc -eq $MM_DaemonDown ]]
  then
    # GPFS is down on this node.
    printErrorMsg 109 $mmcmd
  elif [[ $rc -eq $MM_QuorumWait ]]
  then
    # GPFS is not ready for commands.
    printErrorMsg 110 $mmcmd
  elif [[ $rc -eq $MM_ConnectionReset ]]
  then
    # An internode connection was reset.
    printErrorMsg 257 $mmcmd
  else
    # Either the command worked, or it is an unexpected error.
    : # [[ $rc -ne 0 ]] && printErrorMsg 113 "$mmcmd" "tslspolicy" $rc
  fi  # end of if [[ $rc -eq $MM_DaemonDown ]]

  if [[ $rc -ne 0 || ! -s $tspolicyFile ]]
  then
    # The policy file cannot be determined.
    printErrorMsg 456 $mmcmd
    [[ $rc -eq 0 ]] && rc=1
    cleanupAndExit $rc
  else
    # The command must have worked.
    policyFile=$tspolicyFile
  fi  # end of if [[ $rc -ne 0 || ! -s $tspolicyFile ]]
fi  # end of if [[ -z $policyFile ]]


####################################################################
# Determine the argument list for the tsmigrate command invocation.
####################################################################
if [[ -n $directory ]]
then
  argList="$directory -P $policyFile -I $Iarg -L $listingLevel -s $workDirectory"
else
  argList="$fqDeviceName -P $policyFile -I $Iarg -L $listingLevel -s $workDirectory"
fi
[[ -n $dateTimeString ]] && argList="${argList} -D $dateTimeString"



##################################################
# Invoke the tsmigrate command on the local node.
##################################################
$tsmigrate $argList
rc=$(remapRC $?)

if [[ $rc -eq $MM_DaemonDown ]]
then
  # GPFS is down on this node.
  printErrorMsg 109 $mmcmd
elif [[ $rc -eq $MM_QuorumWait ]]
then
  # GPFS is not ready for commands.
  printErrorMsg 110 $mmcmd
elif [[ $rc -eq $MM_ConnectionReset ]]
then
  # An internode connection was reset.
  printErrorMsg 257 $mmcmd
else
  # Either the command worked, or it is an unexpected error.
  : # [[ $rc -ne 0 ]] && printErrorMsg 113 "$mmcmd" "tsmigrate" $rc
fi  # end of if [[ $rc -eq $MM_FsNotFound ]]

# if [[ $rc -ne 0 ]]
# then
#   # tsmigrate failed.
#   printErrorMsg 104 $mmcmd tsmigrate
#   cleanupAndExit $rc
# fi

cleanupAndExit $rc

